diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 00000000..78a7b3d2 Binary files /dev/null and b/.DS_Store differ diff --git a/.codeclimate.yml b/.codeclimate.yml index 29cc0fda..a3115c8e 100644 --- a/.codeclimate.yml +++ b/.codeclimate.yml @@ -1,4 +1,4 @@ -version: "2" # required to adjust maintainability checks +version: '2' # required to adjust maintainability checks checks: argument-count: config: @@ -32,12 +32,12 @@ checks: threshold: # language-specific defaults. an override will affect all languages. exclude_patterns: -- "conf/" -- "dist/" -- "misc/" -- "Tests/" -- "node_modules/" -- "**/*.d.ts" -- "**/*.js" -- "**/*test.ts" -- "**/*comp.ts" \ No newline at end of file + - 'conf/' + - 'dist/' + - 'misc/' + - 'Tests/' + - 'node_modules/' + - '**/*.d.ts' + - '**/*.js' + - '**/*test.ts' + - '**/*comp.ts' diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..60f555d0 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,21 @@ +node_modules +dist/ +conf/ +coverage/ +docs/ +examples/ +https-cert/ +misc/ +output/ +temp/ + +.codeclimate.yml +.gitignore +.npmignore +.prettierignore +.prettierrc +.travis.yml +CNAME +CONTRIBUTING.md +SECURITY.md +TODO.md \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index fb703d17..94208b71 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -4,7 +4,6 @@ about: Create a report to help us improve title: '' labels: '' assignees: '' - --- **Describe the bug** @@ -12,6 +11,7 @@ A clear and concise description of what the bug is. **To Reproduce** Steps to reproduce the behavior: + 1. Go to '...' 2. Click on '....' 3. Scroll down to '....' @@ -24,9 +24,10 @@ A clear and concise description of what you expected to happen. If applicable, add screenshots to help explain your problem. **Desktop (please complete the following information):** - - OS: [e.g. iOS] - - Test files - - Version [e.g. 22] + +- OS: [e.g. iOS] +- Test files +- Version [e.g. 22] **Additional context** Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index bbcbbe7d..2f28cead 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -4,7 +4,6 @@ about: Suggest an idea for this project title: '' labels: '' assignees: '' - --- **Is your feature request related to a problem? Please describe.** diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml new file mode 100644 index 00000000..9b8479cb --- /dev/null +++ b/.github/workflows/codeql-analysis.yml @@ -0,0 +1,54 @@ +name: 'CodeQL' + +on: + push: + branches: [master] + pull_request: + # The branches below must be a subset of the branches above + branches: [master] + schedule: + - cron: '0 19 * * 4' + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v2 + with: + # We must fetch at least the immediate parents so that if this is + # a pull request then we can checkout the head. + fetch-depth: 2 + + # If this run was triggered by a pull request event, then checkout + # the head of the pull request instead of the merge commit. + - run: git checkout HEAD^2 + if: ${{ github.event_name == 'pull_request' }} + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v1 + # Override language selection by uncommenting this and choosing your languages + # with: + # languages: go, javascript, csharp, python, cpp, java + + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@v1 + + # ℹī¸ Command-line programs to run using the OS shell. + # 📚 https://git.io/JvXDl + + # ✏ī¸ If the Autobuild fails above, remove it and uncomment the following three lines + # and modify them (or add more) to build your code if your project + # uses a compiled language + + #- run: | + # make bootstrap + # make release + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v1 diff --git a/.gitignore b/.gitignore index 932910f8..d693b1e0 100644 --- a/.gitignore +++ b/.gitignore @@ -67,6 +67,7 @@ src/**/*.js src/inceptionTest/*.json temp/*.file js/* +dist/* sonic/** coverage/* erroredTests/* diff --git a/.npmignore b/.npmignore index 989cfb28..24163e6f 100644 --- a/.npmignore +++ b/.npmignore @@ -15,7 +15,6 @@ sonic/ examples/ misc/ training/ -protocols/ conf/ .github/ https-cert/ diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 00000000..ddda4f7b --- /dev/null +++ b/.prettierignore @@ -0,0 +1,10 @@ +.husky/ +.env.* + +.gitignore +.prettierignore + +Dockerfile +open-api-specs/Makefile + +.circleci/Makefile \ No newline at end of file diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 00000000..5ff413b4 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,10 @@ +{ + "singleQuote": true, + "trailingComma": "none", + "semi": true, + "tabWidth": 2, + "printWidth": 120, + "quoteProps": "as-needed", + "bracketSpacing": true, + "arrowParens": "avoid" +} diff --git a/.push_dockerhub b/.push_dockerhub new file mode 100644 index 00000000..1ad908f8 --- /dev/null +++ b/.push_dockerhub @@ -0,0 +1,5 @@ +https://docs.docker.com/get-started/introduction/build-and-push-first-image/ + +docker login +docker build -t virgulation/enqueuer . +docker push virgulation/enqueuer \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index cbfbb4c1..a6407fe3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,28 +1,30 @@ language: node_js - -sudo: required +dist: xenial +os: linux node_js: -- v10.8.0 + - v12.6.0 + - 14 + - lts/* notifications: - email: - on_success: never - on_failure: always + email: + on_success: never + on_failure: always before_install: -- npm install -- npm install codecov -g + - npm install + - npm install codecov -g script: -- rm output/* -- npm run lint -- npm run build -- npm link -- npm run codeCoverage -- npm run examplesTest -- npm run listsDescriptions + - rm output/* + - npm run lint + - npm run build + - npm link + - npm run codeCoverage + - npm run examplesTest + - npm run listsDescriptions after_success: -- codecov -- npx semantic-release + - codecov + - npx semantic-release diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 00000000..b89ba181 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,46 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Launch", + "program": "${workspaceFolder}/dist/index.js", + "request": "launch", + "skipFiles": ["/**"], + "args": [ + // "-et" + // "-c./conf/config-example.yml" + // "examples/file-placeholder.yml" + // "examples/parallel-test*.yml" + // "examples/tcp.yml" + "examples/http.yml" + ], + "type": "node" + }, + { + "type": "node", + "request": "launch", + "name": "Run tests", + "envFile": "${workspaceFolder}/.env", + "program": "${workspaceFolder}/node_modules/.bin/jest", + "console": "integratedTerminal", + "windows": { + "program": "${workspaceFolder}/node_modules/jest/bin/jest" + } + }, + { + "type": "node", + "request": "launch", + "name": "Test current file", + "envFile": "${workspaceFolder}/.env", + "program": "${workspaceFolder}/node_modules/.bin/jest", + "args": ["${fileBasenameNoExtension}"], + "console": "integratedTerminal", + "windows": { + "program": "${workspaceFolder}/node_modules/jest/bin/jest" + } + } + ] +} diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1c1a4e2a..0465931c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -5,7 +5,7 @@ Feel free to reach me out anytime at guilherme.moraes@outlook.com ## New Stuff -If you are opening a pull request/issue/suggestion (*thanks!*), please include: +If you are opening a pull request/issue/suggestion (_thanks!_), please include: - a description of the fix/improvement - test(s) that could prove that the sutff is working @@ -14,17 +14,18 @@ If you are opening a pull request/issue/suggestion (*thanks!*), please include: To build: - ```npm run build``` +`npm run build` To run the test: - - ```npm tst``` + +`npm tst` Or, just: - - ```npm run all``` + +`npm run all` And get everything at the same time. ## Writing commits + Instead of the usual `$ git commit -m "my message"`, use `$ npm run commit`. This helps to write a pretty and formatted commit message that triggers a proper release version. diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..a9a79ca3 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,31 @@ +#### 1st stage +FROM node:lts-alpine AS builder + +# Create app directory +WORKDIR /app + +# Install app dependencies +COPY package*.json ./ + +RUN npm install + +COPY . . + +RUN npm run build + +#### 2nd stage +FROM node:lts-slim AS production + +ENV NODE_ENV=production +USER node + +# Create app directory +WORKDIR /app + +# Install app dependencies +COPY --from=builder /app/package*.json ./ +COPY --from=builder /app/js ./js + +RUN npm ci --omit=dev + +CMD [ "node", "dist/index.js" ] \ No newline at end of file diff --git a/README.md b/README.md index 318ede74..865d5ef7 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,12 @@ -[![npm](https://img.shields.io/npm/dt/enqueuer.svg)]() +# Enqueuer + +[![npm](https://img.shields.io/npm/dt/enqueuer.svg)](https://www.npmjs.com/package/enqueuer) [![Build Status](https://travis-ci.org/enqueuer-land/enqueuer.svg?branch=master)](https://travis-ci.org/enqueuer-land/enqueuer) [![Greenkeeper badge](https://badges.greenkeeper.io/enqueuer-land/enqueuer.svg)](https://greenkeeper.io/) [![Known Vulnerabilities](https://snyk.io/test/npm/enqueuer/badge.svg)](https://snyk.io/test/npm/enqueuer) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) - -![enqueuerlogo](https://raw.githubusercontent.com/enqueuer-land/enqueuer/master/docs/images/fullLogo1.png "Enqueuer Giant Logo") +![enqueuerlogo](https://raw.githubusercontent.com/enqueuer-land/enqueuer/master/docs/images/fullLogo1.png 'Enqueuer Giant Logo') ## Why @@ -15,6 +16,7 @@ its outputs ## What Enqueuer is a platform that provides the following capabilities: + - Support for multi protocols
- Chainable message flows
- Friendly for developers and non developers
@@ -26,7 +28,7 @@ Enqueuer is a platform that provides the following capabilities: ## Official page -[https://enqueuer.com](https://enqueuer.com) +[https://enqueuer.com](https://enqueuer.com) ## Docs diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 00000000..034e8480 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,21 @@ +# Security Policy + +## Supported Versions + +Use this section to tell people about which versions of your project are +currently being supported with security updates. + +| Version | Supported | +| ------- | ------------------ | +| 5.1.x | :white_check_mark: | +| 5.0.x | :x: | +| 4.0.x | :white_check_mark: | +| < 4.0 | :x: | + +## Reporting a Vulnerability + +Use this section to tell people how to report a vulnerability. + +Tell them where to go, how often they can expect to get an update on a +reported vulnerability, what to expect if the vulnerability is accepted or +declined, etc. diff --git a/conf/config-example.yml b/conf/config-example.yml index 38aef0a0..2c897be2 100644 --- a/conf/config-example.yml +++ b/conf/config-example.yml @@ -1,6 +1,6 @@ files: -- examples/*.yml -- examples/*.json + - examples/*.yml + - examples/*.json parallel: true @@ -9,72 +9,108 @@ log-level: warn max-report-level-print: 3 outputs: -- type: file + - type: file format: json filename: output/examples.json -- type: file + - type: file format: yml #default filename: output/examples.yml -#- type: standard-output -# format: console + # - type: standard-output + # format: console #plugins: #- enqueuer-plugin-amqp store: - filePlaceholderInjection: I'm persisted in config file - tcpPayload: enqueuer Rocks + tcpPayload: enqueuer Rocks - skipped: 5 + skipped: 5 - privateKey: |- - -----BEGIN RSA PRIVATE KEY----- - MIIEowIBAAKCAQEA5dJgImUrAPpWRZE59ZO/MXhJA+mu4PeD6s3wBS6gu7DzPZ8S - 5Vhfo6VhWA7SrMmUA1UE1ab0NibsrbOPKfhkGwn9/a/rTEW9d178UpLOabJBndDd - cYv2G7AnjbDDhm53sVni1zt3a8HIITppfKslRRLjyHWZPPaSFtcbg+fHk0yrUu+U - SBjKnWE73oWBWlbWbMzUf1SQEzAE+ojYQy/7iEDjxvesmhWVY96AdmBwD/pZifXY - Eqqt3Cd7xvWPJFu9t8GMgo3ZiQLHBaF3np6J6D+Cwxw6iN1S5uHe7kbGpBrBGbd9 - IKOgog1qbOB6xUEI0C0yMVV0ubkkU0DewwjRAQIDAQABAoIBAQCALqNR681dgULn - EEYcc6ia5fULiqXwcf0Q1IX/ze72YI56myPF6VGGi7JMlA/rMY8tgJjutWzWW0V8 - 6H58MT9cWWlhumHpq6guUTs4rwsTpsm0RQ+NyjudW9Xj0GhtrWVy9Khx1YgZAP8n - kVXOpAJokU2cvAKZHwmCVb3SB0+oFndN8QBXFLt05CdNi2RvdxtSG6+y6Ww+l6o2 - NaMIMxCVVfxDiACYZ5tMLSLspZAIGShdGSMuCAm3jDG7jm5vvaIsOuhYT+1Oaf9b - Yo0kGqbKpdszxxDHuCRlPY7AB6qNLq8L4esiYw6ijV8m1XJ4XF2WO/vTqqHhhfjH - bGBp0x1lAoGBAP1xcBE8KDst/79BfKH6qIK4zfO1f08/cxxq4hUXOF7hjEI6jm63 - GiGKzPaZAMogZcF2LTQYS+TZG4mSEKQ/VdLIj/jD4nCEVcN+R3l5cBxd9tDzPEEZ - +xrtSYv/nvakkeHOxMotG95FR3+Qy1ZIdoZ8cne1Opt3Ke29C5GIkK9HAoGBAOgj - 7nIh7iW2YqQYLD9deJcjek141FBMO/mPQNadXFaT/Pkqf/lsnwWkVDKKDJpDaxoW - yOXRJyhuTmvxltI9sb4uyP8UUFbJIv8w28yq76uE8VcyJ07ynt81po4hA9W1BcAk - 5B/jmI9/nZmlNKuYCTKzIm3lii/biBMTeq/Qz3F3AoGANHVPafHWrfEmd3LZljlo - ua4AywntMP0EGYSyCQWGYHU079xo8Nera/fr1rryo+OyzClmz9qbkuGxRndaRHTr - kbZ7vlTlQL7TdaRkrOsK3oRAex1tglgbVoZ466ZcQ9bqbx+6MdLGaupSE0BoJhLX - nZySoN59JAK1RgUUKOpocC8CgYAZYuyNoLZJe4OYDGiyWUd5x9B1LnSRf3TDmu3t - SnIxqadtnD84iK9vT4RBrqUoLZ1F0h4QO+o7WqzK0zW6MjMA6F7kvlT22LIyFCbF - YCArsrMt5Pc1hATpxhU7z9J6iXxigeZMsngZoUwb5Fw5M7w1xi9Ks2KET063P1cY - aQ52iwKBgGcCWmskSbHiCYtWfIANTnzDAXbWnm1wL77You9w/MjhHefcRa60BrPf - 7h4x4ZN+KTyTneR9Wgc3XbLTIy8zmbX8KfduZM/ZOmIDKuqvqzHSh5VTYq3pptsu - xa9rKdITdFrhuLb+qZ6k103m8JfylsxAtJBwdoab6QfBQhkkQYul - -----END RSA PRIVATE KEY----- + privateKey: |- + -----BEGIN PRIVATE KEY----- + MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQCvS13lL5sOsrZ8 + pNKIOPWzs077+dmuafQBSosXyC8aUh6eNvGbDjc/KcL42ri1YgkrITZfdwt7lOh0 + /R4gge7QslakuARpFa6uS9VcHTocj/YYqDLKEzutxhZR5DLv77YP9HEXXy6BoUNH + XZJBhEF/PsUzeQdiywtF3oMvcyJd0ubxsheZTk+1MBS5xECMkWh92vwtby6lnXC8 + VGUEWLCypSx8clqZM84NT48rEqczW2YSeIsfu3OslEutzMtzEiw++jqHh5MzLwXQ + c9KGpCXEQv6OkabKMQXRQqHopUT7tELesClOTPeQdb9CFjHgZZEYCnmlx33rIIFG + 7s3Y2Idy8S2O3LUDr+ITg65wlqqRUz8adPtlSrUHWBZxWsVG1pMAwrZY8SAgy1UT + RGGCrqLf7qx2yA+F1941r+QUWfTH/glO7EQV0kVJuJQk6gqyFatJa6IxoJwk1HAv + e26+hSRNYj82Hon8BcQTvo0tIrXy8vc/eoEuY0zu07hJaZyE09UfuuajZdaDvOHB + sCQ2gysakTBNN3cosLOndgOCC3f9odmrZn0MBAn8WJcq18G+gqspVSdgbVsbjsIj + J9EDov24uECrHsPg/I9HYdx02tGyTUYGXCPBZ3DF1+OWkg0oTlqqLl2iiPBBCu9a + 3YL4RAskr4JyNOc0wE1T+nTkzGmwdwIDAQABAoICAArX6KkCtt7D41H2iCdxDpHd + J6einvqRB00FJXGFKpTAO7ChyXRD/trjZHIsZXyi4dpPJaPmI0mzvmVuOuzb3MAA + KCfsUimF89ZITpxyGmQl650+2lVxtyFavYq3SQdalrKJ9mzxsFjIdvb1LNTsr3iF + k4FJnVFZKgtaZlcLEjcnkq2kzJzpITkeDkonf66yV/phD3RoCkSoZo7f0ZfhPJzw + Zr4T/NK5R6e/IiYR81HT945w8no2CEPGsJ/BqVwm5aHHrNMxacOixwNMBtvZT5TC + aCi9jcm4GpoFOyq+/nNrhS9tSkDTtgSzo+8cnF07KyyHiVrd6kYfLqAKqaUeT9KM + Lxd88UU8XnMkDvvta20uql23qu3AxqXDVoRcdtimgrtxeRTETRoyQhDoOZ07p/fn + SqUQ2+uUYDGY+0qIMLkUJjRSGdUvcb2xwLsaiokzMDL1WwO9PzXLb4xQ4bQtVcNa + dLhVXlzyXl9+ipCEN82K3hlS3XtPz0mWb6HGk7nz6lago03PmIXjj3G+H95jAUeF + dGku/jx6qG3qtCJdFvimHTSJMa31EW+aAj0bMlRGo5shtnVazgiHPYFx4Rc5dEC4 + CmuhQieJdPp4rna/k00ESIjIOUEfB3uQLLAaGCSKQ7yjQFWeJ7+Y8MbwUlgGiBbn + GzR/ZNf21BERpeEsFOfBAoIBAQDV8+ZeiVzG+g+Zkko/DTl/y7As2JjOEaoffaSj + RZxQe/UiZNiVa3O2MBjcq3VnulzeLSXAIrI4lR3UZSivevbUtccmtFKxROuNBphE + QJdiVjvm5+c5S+75N2Di94IPjKKE2JKhR2Y/Te1dlAXnrKSv5Zay4MQvhMHCwb1v + Qdjq7ZiVZ6DS+acnhAxSt3A4nVwpbpORcebJH320Il0y4lsaH67vJytxcn5zoAri + vOsszA724v/2dtYTrzDq0rykgdlQKF4kxICZiY4jpSINXFRAJjTTI8vbr9msiKI1 + bzmGF6oME8/fgbWqCJmj2KpwW6AV/zR8OJFK8RZ4HC3n3grxAoIBAQDRvoqPC9B7 + zqkz27197D1MV/wmgCrZD5CfwyCkGWxNCy01L+RL7VnlAm0QB4ve5D7pcAMjZSh5 + SPf084WGp7VSSoii8nGWhgrY7dvBF4ocMe77swXPEWBALFHJ+N/RyqWsRH03X/3s + I/DFoG1mttmmO7QV4PDAT3ne/+EWbFHB/LYRCmlSLbkW3EzR5Lj5nqJSSnZe8/0E + cWVMCwGoomg9WROt4Zz1I+O5Jqrq0xk94gWy6R/ZD8JOXQnEma1CNgVGzrEFwbG+ + zmkxhw02J2fttIbY0OR6ab3XOhmyGmto5Djnf/LWDdFtwoQHcfRa0GPwpj/092zL + HNi+Zy/CcOHnAoIBAQCeqPg1MvgyNk5LPEkC6POy6YIEP7tRVZgrMildSsnYhZQL + pW1XoQ92E8TcH+o4ueMUf7Z2/UPc4ff1Ae9h8nYI16SP9zk7bDihpDpWou+gZQKX + AXJe8wiMl5yps5Inpcr/aPuTzjbsywUzWAno/AZEyqUuka/q7TAjFTk9wD70OK3m + XarNyYVDsJDS5mW6Fje6Id4dBbJ3g3rckn86tNGKVeSMRSfaQWW0qQJpkl90IP8X + 5s6aHxYyEsDb4tsxw3k8EDOAYJhdQ7y8gkD2V3nC+JJD4U/T4lwxrkFcDtdBsVrR + CgAlZzMonKgS8SGzoL7ekRqC5BAa2x4mg/8m62qBAoIBAAeEidjYto9Jd/0Y2jDi + P8w1tx72JxMCoM9pIglfs+cLCILIhalHB5rSvG//pT7G9Y5Oan5hSNMYD3MjK10J + M9GQRFd60yFvCoJ9/AtuuY+LJqK05vdmCwohzIitt/AAEHIdPlO2yBYnr6CVwHYX + BbQUnb7PAjISZy7cAwhXt5J5aVkxA1djtiegm8xGI28nBXBnpNiAbEg1fEj+d//A + WnYgdDkeRg7vS2E+ho/GIzh5mnSwMGkKTI+cfBYk3/Xok8XW8LdhGurY4SJaUdno + DaySdObejeHpWlmVhohAtCrdS0DY8C5F7oS9nRfbOMPshjiA7PftymN5VDd24VpZ + y28CggEBAL2gs8CZCVx73oqa8orfkBKsJtcI2u26AjeX4SJO9iKd0hOryajNOTEd + 1mReBWLLJ3Az4PaQMpdUGXDub3E4XvlJsMnCAfN+YHY3sWzcmepML19V0uzQ7duj + rYGJDxUB+5pI/xF5MKmg6852mXCkaqbbgBLhUmtSI263pgogJJD3qqwYA5q2BtUk + 1hgKYX2VDdfQ6ddF+a/IOgUebDl2Sqmjz+ZOkkne3OQ48Dfpw90rchfJ0qg8jwvH + 9ZSUK3GFSY7pgN6io9js5B2+UBbOLGIp9SC3cTg/Eo9zEKVhcMvtiV1xSMwAGJ2z + jJbWxM6E9pAcqPiVGvMJ/RdmPk6rqjs= + -----END PRIVATE KEY----- - certificate: |- - -----BEGIN CERTIFICATE----- - MIIDcjCCAloCCQDWt6re3wwXVTANBgkqhkiG9w0BAQUFADB7MQswCQYDVQQGEwJF - TjEWMBQGA1UECAwNRW5xdWV1ZXIgTGFuZDEWMBQGA1UEBwwNRW5xdWV1ZXIgVG93 - bjEWMBQGA1UECgwNRW5xdWV1ZXIgSW5jLjERMA8GA1UECwwIRW5xdWV1ZXIxETAP - BgNVBAMMCEVucXVldWVyMB4XDTE4MDgwOTAzMzUxNVoXDTE4MDkwODAzMzUxNVow - ezELMAkGA1UEBhMCRU4xFjAUBgNVBAgMDUVucXVldWVyIExhbmQxFjAUBgNVBAcM - DUVucXVldWVyIFRvd24xFjAUBgNVBAoMDUVucXVldWVyIEluYy4xETAPBgNVBAsM - CEVucXVldWVyMREwDwYDVQQDDAhFbnF1ZXVlcjCCASIwDQYJKoZIhvcNAQEBBQAD - ggEPADCCAQoCggEBAOXSYCJlKwD6VkWROfWTvzF4SQPpruD3g+rN8AUuoLuw8z2f - EuVYX6OlYVgO0qzJlANVBNWm9DYm7K2zjyn4ZBsJ/f2v60xFvXde/FKSzmmyQZ3Q - 3XGL9huwJ42ww4Zud7FZ4tc7d2vByCE6aXyrJUUS48h1mTz2khbXG4Pnx5NMq1Lv - lEgYyp1hO96FgVpW1mzM1H9UkBMwBPqI2EMv+4hA48b3rJoVlWPegHZgcA/6WYn1 - 2BKqrdwne8b1jyRbvbfBjIKN2YkCxwWhd56eieg/gsMcOojdUubh3u5GxqQawRm3 - fSCjoKINamzgesVBCNAtMjFVdLm5JFNA3sMI0QECAwEAATANBgkqhkiG9w0BAQUF - AAOCAQEAbgWl5hpJyXNXqZIRL1RnK7+849z+R/iW/89BkW6JFnoYXeOrl//TTRDK - VY36RF3Pd4mDocwrrNPk348Xqvsq6yf2aqz8LQTbCm2anZPPZfOsvywEaTjKl9R+ - EpWFRAvm6yw58FfoQtN8ujMTCQavERYyUMvh//z64H4g1udlkv2RMZmr7R999PFW - tIxc1ZY7jg7Z6dghAqic8f85wqdlHyu/C8O2ZUlWhf9hi9/NWku4mPf03teiAVyU - wUwIs5SDMqYHxKjatva4cd4piJeB0eG4/47HbhYGfhtz4BvIZ5/pwql7myfJYekH - mLX8DjMYA3XuADxkmlUhY9xBAfBeVw== - -----END CERTIFICATE----- + certificate: |- + -----BEGIN CERTIFICATE----- + MIIFxTCCA62gAwIBAgIUbGsuXsC9D4FiukBFNWQLbsG0jJ8wDQYJKoZIhvcNAQEL + BQAwcjELMAkGA1UEBhMCQ0ExGTAXBgNVBAgMEEJyaXRpc2ggQ29sdW1iaWExEjAQ + BgNVBAcMCVZhbmNvdXZlcjEUMBIGA1UECgwLVmlyZ3VsYXRpb24xDjAMBgNVBAsM + BVZpcmdzMQ4wDAYDVQQDDAVWaXJnczAeFw0yNDA4MTkyMTU0MzFaFw0zNDA4MTcy + MTU0MzFaMHIxCzAJBgNVBAYTAkNBMRkwFwYDVQQIDBBCcml0aXNoIENvbHVtYmlh + MRIwEAYDVQQHDAlWYW5jb3V2ZXIxFDASBgNVBAoMC1Zpcmd1bGF0aW9uMQ4wDAYD + VQQLDAVWaXJnczEOMAwGA1UEAwwFVmlyZ3MwggIiMA0GCSqGSIb3DQEBAQUAA4IC + DwAwggIKAoICAQCvS13lL5sOsrZ8pNKIOPWzs077+dmuafQBSosXyC8aUh6eNvGb + Djc/KcL42ri1YgkrITZfdwt7lOh0/R4gge7QslakuARpFa6uS9VcHTocj/YYqDLK + EzutxhZR5DLv77YP9HEXXy6BoUNHXZJBhEF/PsUzeQdiywtF3oMvcyJd0ubxsheZ + Tk+1MBS5xECMkWh92vwtby6lnXC8VGUEWLCypSx8clqZM84NT48rEqczW2YSeIsf + u3OslEutzMtzEiw++jqHh5MzLwXQc9KGpCXEQv6OkabKMQXRQqHopUT7tELesClO + TPeQdb9CFjHgZZEYCnmlx33rIIFG7s3Y2Idy8S2O3LUDr+ITg65wlqqRUz8adPtl + SrUHWBZxWsVG1pMAwrZY8SAgy1UTRGGCrqLf7qx2yA+F1941r+QUWfTH/glO7EQV + 0kVJuJQk6gqyFatJa6IxoJwk1HAve26+hSRNYj82Hon8BcQTvo0tIrXy8vc/eoEu + Y0zu07hJaZyE09UfuuajZdaDvOHBsCQ2gysakTBNN3cosLOndgOCC3f9odmrZn0M + BAn8WJcq18G+gqspVSdgbVsbjsIjJ9EDov24uECrHsPg/I9HYdx02tGyTUYGXCPB + Z3DF1+OWkg0oTlqqLl2iiPBBCu9a3YL4RAskr4JyNOc0wE1T+nTkzGmwdwIDAQAB + o1MwUTAdBgNVHQ4EFgQUQxL+b147fKJnrFqpqBpH6aowMnMwHwYDVR0jBBgwFoAU + QxL+b147fKJnrFqpqBpH6aowMnMwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0B + AQsFAAOCAgEAgM3sjsSiJVpNjoIYokRoJFQmv1ZcYmZ4x01CzQQu98wdNFq0Uhlk + 0h9EvbBzqlQ0Uu57ylTwsRepl8B08r1oAW93EAxweRPWo6enC+FalVLh9HWcpKGU + sDCfZfwyFugrTCtSJd+qiYB9PMp/xmjvZyT1k4h2ZFYmjJWEC1T1+nx+g3hxUH8H + KxLG5KyfdFmQ5FD9Wx9lT0pPaDrRZYjsU7JEgp3uM1egY9wRJYSWI8euhFodnEIB + zvdzhSoPXPcyVhrtJeNocnHhB17/dkD6oU/9nUGdfUjPBnBYWSJDZ+IR5yicjO0a + pyPFlNYYXgDFqpn2ocinw/VREAZnjhlLyPVs3sGhCzApQleyUqUGcR03D367N/nf + OdUO3QyQramWAhxZwyD1p1V9ouozR1jCyLRQ4NdpQDRw8T8VJ0ZHBj9pIvc8uNey + pkMEysrWuCEmO5QMGPsOz6Nc0WsdxefNfUfOBddQSWqSN89umBV9LWTMU8Ss8yra + jb4mSiiWJBzCzFHVPpgNIsBDmJpcmg1vG3jS941AXftjJTHTMrL+hIM9TWRd2kEk + jkpvXwhjCP53LlmQudR+wwf8tcuRJ5+3Hdl03amAdMmRfQKseZdpDitY9jtZUuE2 + oo/64xDCrMTqa1k+hgRPu6+4Q8s4pdZAqHUqIceYkBveONk/2Ly5nNc= + -----END CERTIFICATE----- diff --git a/docs/CODE_OF_CONDUCT.md b/docs/CODE_OF_CONDUCT.md index 56664352..4944a2d9 100644 --- a/docs/CODE_OF_CONDUCT.md +++ b/docs/CODE_OF_CONDUCT.md @@ -8,19 +8,19 @@ In the interest of fostering an open and welcoming environment, we as contributo Examples of behavior that contributes to creating a positive environment include: -* Using welcoming and inclusive language -* Being respectful of differing viewpoints and experiences -* Gracefully accepting constructive criticism -* Focusing on what is best for the community -* Showing empathy towards other community members +- Using welcoming and inclusive language +- Being respectful of differing viewpoints and experiences +- Gracefully accepting constructive criticism +- Focusing on what is best for the community +- Showing empathy towards other community members Examples of unacceptable behavior by participants include: -* The use of sexualized language or imagery and unwelcome sexual attention or advances -* Trolling, insulting/derogatory comments, and personal or political attacks -* Public or private harassment -* Publishing others' private information, such as a physical or electronic address, without explicit permission -* Other conduct which could reasonably be considered inappropriate in a professional setting +- The use of sexualized language or imagery and unwelcome sexual attention or advances +- Trolling, insulting/derogatory comments, and personal or political attacks +- Public or private harassment +- Publishing others' private information, such as a physical or electronic address, without explicit permission +- Other conduct which could reasonably be considered inappropriate in a professional setting ## Our Responsibilities diff --git a/docs/README.md b/docs/README.md index 02a2391c..3c838ca8 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,26 +1,30 @@ -[![npm](https://img.shields.io/npm/dt/enqueuer.svg)]() +# Enqueuer + +[![npm](https://img.shields.io/npm/dt/enqueuer.svg)](https://www.npmjs.com/package/enqueuer) [![Build Status](https://travis-ci.org/enqueuer-land/enqueuer.svg?branch=master)](https://travis-ci.org/enqueuer-land/enqueuer) [![Greenkeeper badge](https://badges.greenkeeper.io/enqueuer-land/enqueuer.svg)](https://greenkeeper.io/) [![Known Vulnerabilities](https://snyk.io/test/npm/enqueuer/badge.svg)](https://snyk.io/test/npm/enqueuer) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) +![enqueuerlogo](https://raw.githubusercontent.com/enqueuer-land/enqueuer/master/docs/images/fullLogo1.png 'Enqueuer Giant Logo') -![enqueuerlogo](https://raw.githubusercontent.com/enqueuer-land/enqueuer/master/docs/images/fullLogo1.png "Enqueuer Giant Logo") +## Docs ### Welcome Be our guest and have fun. #### install it + First things first, let's get the enqueuer installed on your machine. - $ npm install --global enqueuer - -Alright, it's time to create a requisition file. + npm install --global enqueuer + +Alright, it's time to create a task file. Something like: - + #enqueuer-repo-hit.yml - publishers: + actuators: - type: http url: https://github.com/enqueuer-land/enqueuer onResponseReceived: @@ -31,13 +35,13 @@ Something like: Pretty simple, hum? Small and concise, how it should be! Run it: - $ enqueuer enqueuer-repo-hit.yml + enqueuer enqueuer-repo-hit.yml -Now you know how to hit a http server. +Now you know how to hit a http server. What if I want to mock a http server response, you may ask. Not a big deal for enqueuer lovers: - + timeout: -1 - subscriptions: + sensors: - type: http timeout: -1 name: mock endpoint @@ -50,14 +54,14 @@ What if I want to mock a http server response, you may ask. Not a big deal for e onMessageReceived: assertions: - expect: body - toContain: `enqueuer` - + toContain: "'enqueuer'" + Now go ahead and try hitting it using the browser. Tip: remove timeout values and check what happens. I told you it was simple. Yes, of course you can hit your own mocked http server: - publishers: + actuators: - type: http url: http://localhost:23075/resource method: POST @@ -68,7 +72,7 @@ Yes, of course you can hit your own mocked http server: toBeGreaterThan: 400 - expect: body toBeEqualTo: "'blah'" - subscriptions: + sensors: - type: http endpoint: /resource port: 23075 @@ -80,19 +84,19 @@ Yes, of course you can hit your own mocked http server: assertions: - expect: body toContain: "'queue'" - + Now, let's say you want to mix different protocols to test a bit more complex flow. How about publishing an amqp message and making sure that, once a service consumes that message an endpoint of your is hit? In order to achieve that, we have to make use of a [plugin](#plugins), given that amqp support is provided by a plugin. In this scenario, we're talking about the [amqp plugin](https://github.com/enqueuer-land/enqueuer-plugin-amqp). -Once we get this [plugin installed](#plugins_installation) we are able to create and run files like this: +Once we get this [plugin installed](#plugins installation) we are able to create and run files like this: - publishers: + actuators: - type: amqp payload: 123456 exchange: enqueuer.exchange routingKey: enqueuer.readme.routing.key - subscriptions: + sensors: - type: http endpoint: /polyglot-flow port: 8080 @@ -107,314 +111,346 @@ Once we get this [plugin installed](#plugins_installation) we are able to create Now go nuts! It's all yours. Have fun. -If you want more examples about `http`, consider looking at [this test](https://github.com/enqueuer-land/enqueuer/blob/master/examples/http-more-examples.yml). -Check [this out](https://github.com/enqueuer-land/enqueuer/blob/master/examples/), you'll find countless examples. -Certainly one is what you need. - +If you want more examples about "'http'", consider looking at [this test](https://github.com/enqueuer-land/enqueuer/blob/master/examples/http-more-examples.yml). +Check [this out](https://github.com/enqueuer-land/enqueuer/blob/master/examples/), you'll find countless examples. +Certainly one is what you need. + #### if you need more $ nqr -h - Usage: nqr [options] [other-test-files...] - + + Usage: index [options] [test-files...] + Take a look at the full documentation: https://enqueuer.com - + + Arguments: + test-files other files to be tested + Options: - -v, --version output the version number - -b, --verbosity set verbosity (default: "warn") - -c, --config-file set configurationFile - -e, --parsers-list [parser] list available object parsers - -f, --formatters-description [formatter] describe report formatters - -o, --stdout-requisition-output add stdout as requisition output - -m, --max-report-level-print set max report level print - -p, --protocols-description [protocol] describe protocols - -t, --tests-list [expectedField] list available tests assertions - -u, --loaded-modules-list list loaded modules - -i, --show-passing-tests show passing tests - -s, --store [store] add variables values to this session (default: []) - -l, --add-plugin [plugin] add plugin (default: []) - -a, --add-file add file to be tested (default: []) - -A, --add-file-and-ignore-others add file to be tested and ignore others (default: []) - -h, --help output usage information - + -v, --version output the current version + -b, --verbosity set verbosity (choices: "trace", "debug", "info", "warn", "error", "fatal", default: "warn") + -c, --config-file set configurationFile + -d, --show-explicit-tests-only show explicit tests only (default: false) + -o, --stdout-task-output add stdout as task output (default: false) + -m, --max-report-level-print set max report level print + -i, --show-passing-tests show passing tests + -s, --store [store] add variables values to this session (default: []) + -l, --add-plugin [plugin] add plugin + -e, --parsers-list [parser] list available object parsers + -q, --parallel should run tests files parallely (default: false) + -f, --formatters-description [formatter] describe report formatters (default: false) + -p, --protocols-description [protocol] describe protocols (default: false) + -t, --tests-list [expectedField] list available tests assertions (default: false) + -u, --loaded-modules-list list loaded modules (default: false) + -h, --help display help for command + Examples: - $ nqr --config-file config-file.yml --verbosity error --store key=value - $ enqueuer -c config-file.yml test-file.yml --add-file another-test-file.yml -b info - $ enqueuer test-file.yml --store someKey=true --store someOtherKey=false - $ nqr --protocols-description -s key=value - $ nqr -t expect - $ nqr -l my-enqueuer-plugin-name -p plugin-protocol - $ nqr -p http - $ nqr --formatters-description json - + $ nqr --config-file config-file.yml --verbosity error --store key=value + $ enqueuer -c config-file.yml test-file.yml another-test-file.yml -b info + $ enqueuer test-file.yml --store someKey=true --store someOtherKey=false + $ nqr --protocols-description -s key=value + $ nqr -t expect + $ nqr -l my-enqueuer-plugin-name -p plugin-protocol + $ nqr -p http + $ nqr --formatters-description json + Contributing: - https://github.com/enqueuer-land/enqueuer + https://github.com/enqueuer-land/enqueuer ----- +--- ### Components + In order to accomplish more than just hitting enqueuer's repo or doing a quick self http hit, there are a few things that you'll probably need to know. -Don't worry, it's not too much and, as mentioned earlier, there is a lot of examples [here](https://github.com/enqueuer-land/enqueuer/blob/master/examples/), just in case. -There are only three important component concepts: [requisitions](#requisition), [publishers](#publisher) and [subscriptions](#subscription). +Don't worry, it's not too much and, as mentioned earlier, there is a lot of examples [here](https://github.com/enqueuer-land/enqueuer/blob/master/examples/), just in case. +There are only three important component concepts: [tasks](#task), [actuators](#actuator) and [sensors](#sensor). They work along with each other and are responsible for the full behavior of enqueuer. -#### requisition +#### task + Test scenario description. It tells what, when, and how test your applications and services. -Picture it as if it was a collection of [publishers](#publisher), [subscriptions](#subscription) and other [requisitions](#requisition). +Picture it as if it was a collection of [actuators](#actuator), [sensors](#sensor) and other [tasks](#task). It helps because this is exactly what it is. As the others components, it has some attributes. All of them are optionals. And it supports multi-level test scenarios out of the box. Yeap, go as recursive as you want. -Every test file is a requisition. -You don't know some of these attributes values yet? Don't worry, just put a variable there and let enqueuer replace it with the value you set later. -[Variable replacements](#variables) are available through the entire requisition. +Every test file is a task. +You don't know some of these attributes values yet? Don't worry, just put a variable there and let enqueuer replace it with the value you set later. +[Variable replacements](#variables) are available through the entire task. -##### requisition attributes -These are the requisition attributes: +##### task attributes + +These are the task attributes: **name** -Describes what the requisition is suppose to do. -Defaults to requisition index. +Describes what the task is suppose to do. +Defaults to task index. - name: requisition action + name: task action **timeout** Defaults to 5000. -Sets in milliseconds how long the requisition waits to expire. +Sets in milliseconds how long the task waits to expire. Set to zero or less than zero to run it endlessly. timeout: 3000 - + **delay** -Defaults to 0. Sets in milliseconds how long the test waits before starting. Check [this](https://github.com/enqueuer-land/enqueuer/blob/master/examples/requisition-delay-iterations.yml) to get the full idea. +Defaults to 0. Sets in milliseconds how long the test waits before starting. Check [this](https://github.com/enqueuer-land/enqueuer/blob/master/examples/task-delay-iterations.yml) to get the full idea. delay: 0 **iterations** -Defaults to 1. Sets how many times this test will be executed. Check [this](https://github.com/enqueuer-land/enqueuer/blob/master/examples/requisition-delay-iterations.yml) and [this](https://github.com/enqueuer-land/enqueuer/blob/master/examples/recursion.yml) to get the full idea. +Defaults to 1. Sets how many times this test will be executed. Check [this](https://github.com/enqueuer-land/enqueuer/blob/master/examples/task-delay-iterations.yml) and [this](https://github.com/enqueuer-land/enqueuer/blob/master/examples/recursion.yml) to get the full idea. iterations: 3 **id** -Defaults to an auto-generated one. Uniquely identify this component among the others. It gets implicitly set by enqueuer if it is not set initially. +Defaults to an auto-generated one. Uniquely identify this component among the others. It gets implicitly set by enqueuer if it is not set initially. id: ID-0123456789 **ignore** -Defaults to false. Tells to enqueuer that this requisitions should be skipped. Check [this](https://github.com/enqueuer-land/enqueuer/blob/master/examples/ignore.yml) to see it working. +Defaults to false. Tells to enqueuer that this tasks should be skipped. Check [this](https://github.com/enqueuer-land/enqueuer/blob/master/examples/ignore.yml) to see it working. ignore: true - + **parallel** -Defaults to false. Immediate children requisitions should be executed in parallel mode. -Take a look at [this](https://github.com/enqueuer-land/enqueuer/blob/master/examples/parallel-requisition.yml) to see it working. +Defaults to false. Immediate children tasks should be executed in parallel mode. +Take a look at [this](https://github.com/enqueuer-land/enqueuer/blob/master/examples/parallel-task.yml) to see it working. parallel: true **import** -Allows requisition to be dynamically defined, be it by loading an external file or creating dynamically by other requisitions. Want to reuse the same requisition multiple times? This is you you need. +Allows task to be dynamically defined, be it by loading an external file or creating dynamically by other tasks. Want to reuse the same task multiple times? This is you you need. Take a look at [this](https://github.com/enqueuer-land/enqueuer/blob/master/examples/import.yml) to behold this feature. - import: path/to/another/requisition/file + import: path/to/another/task/file + +**actuators** +List of [actuators](#actuator). They're executed simultaneously, therefore, the order is **irrelevant**. -**publishers** -List of [publishers](#publisher). They're executed simultaneously, therefore, the order is **irrelevant**. - - publishers: - - name: some publisher name + actuators: + - name: some actuator name type: http - type: tcp -**subscriptions** -List of [subscriptions](#subscription). They're executed simultaneously, therefore, the order is **irrelevant**. +**sensors** +List of [sensors](#sensor). They're executed simultaneously, therefore, the order is **irrelevant**. - subscriptions: - - name: some subscription name + sensors: + - name: some sensor name type: udp - - name: another subscription name + - name: another sensor name type: file - -**requisitions** -A list of child scenarios. List of [requisitions](#requisition). -By default, they're executed **sequentially**, therefore, the order is *relevant*. -Unless the *parallel* attribute is set to true, what makes them get executed **simultaneously**, +**tasks** +A list of child scenarios. List of [tasks](#task). +By default, they're executed **sequentially**, therefore, the order _is relevant_. +Unless the _parallel_ attribute is set to true, what makes them get executed **simultaneously**, Check [this](https://github.com/enqueuer-land/enqueuer/blob/master/examples/recursion.yml) example, it may help. - requisitions: - - name: some requisition name + tasks: + - name: some task name iterations: 2 - - name: another requisition name + - name: another task name delay: 200 ##### events -Requisitions have `onInit` and `onFinish` events. -Available events are described [here](#event). A `this` object is available to access and change requisition attributes. + +Tasks have `onInit` and `onFinish` events. +Available events are described [here](#event). A `this` object is available to access and change task attributes. name: my name onInit: script: this.delay = 3000; assertions: - expectToBeDefined: this.name - onFinish: + onFinish: assertions: - expectToBeDefined: this.name -#### publisher +#### actuator -A publisher action is triggered by enqueuer itself. It **acts** whereas a [subscription](#subscription) **reacts**. -It publishes something, it writes, it enqueues, hits and endpoint... These kinds of actions. -It's worth noting that it always **creates** a message. +An actuator action is triggered by enqueuer itself. It **acts** whereas a [sensor](#sensor) **reacts**. +It publishes something, it writes, it enqueues, hits an endpoint... These kinds of actions. +It's worth noting that it always **creates** a message. That's the reason why there's an implicitly created test in **onFinish** hook verifying if the message got published. -##### publisher attributes -Every publisher has its own properties, depending on its protocol and implementation. -The built-in [`http` publisher](https://github.com/enqueuer-land/enqueuer/blob/master/examples/http.yml) implementation, for instance, demands a `url`, a `method`, and a `payload`, if the method is not a `GET`. -On the other hand, the built-in [`tcp` publisher](https://github.com/enqueuer-land/enqueuer/blob/master/examples/tcp.yml) implementation requires a `serverAddress` and a `port`. -These are the publisher attributes: +##### actuator attributes + +Every actuator has its own properties, depending on its protocol and implementation. +The built-in [`http` actuator](https://github.com/enqueuer-land/enqueuer/blob/master/examples/http.yml) implementation, for instance, demands a `url`, a `method`, and a `payload`, if the method is not a `GET`. +On the other hand, the built-in [`tcp` actuator](https://github.com/enqueuer-land/enqueuer/blob/master/examples/tcp.yml) implementation requires a `serverAddress` and a `port`. +These are the actuator attributes: **name** -Defaults to publisher index. -Describes what the publisher is supposed to do. +Defaults to actuator index. +Describes what the actuator is supposed to do. - name: publisher action + name: actuator action **type** -Mandatory. Key tag to identify which publisher will be instantiated +Mandatory. Key tag to identify which actuator will be instantiated type: http **payload** -Since a publisher usually publishes something, it's very likely you have to set a value here. +Since a actuator usually publishes something, it's very likely you have to set a value here. The message itself that will be send through this protocol. Be it a string, a number, a boolean value or even whole objects. payload: value - + **ignore** -Defaults to false. Tells to enqueuer that this publisher should be skipped. Check [this](https://github.com/enqueuer-land/enqueuer/blob/master/examples/ignore.yml) to see it working. +Defaults to false. Tells to enqueuer that this actuator should be skipped. Check [this](https://github.com/enqueuer-land/enqueuer/blob/master/examples/ignore.yml) to see it working. ignore: true **id** -Defaults to an auto-generated one. Uniquely identify this component among the others. It gets implicitly set by enqueuer if it is not set initially. +Defaults to an auto-generated one. Uniquely identify this component among the others. It gets implicitly set by enqueuer if it is not set initially. id: ID-0123456789 - -##### events -Available events are described [here](#event). A `this` object is available to access and change publisher attributes. -Depending on the protocol and its implementation, such as `http` and `tcp`, there may exist special events, such as `onMessageReceived` event and a special object given `message`. -On the other hand, an asynchronous protocol, like: `udp` and `amqp`, usually do not provide it. + +##### actuator events + +Available events are described [here](#event). A `this` object is available to access and change actuator attributes. +Depending on the protocol and its implementation, such as `http` and `tcp`, there may exist custom events, such as `onResponseReceived` event and an attribute `message` passed to it. +On the other hand, an asynchronous protocol, like: `udp` and `amqp`, usually do not provide it. onInit: script: this.ignore = false assertions: - expectToBeDefined: this.type - onMessageReceived: #Provided in synchronous protocols + onResponseReceived: #Provided in synchronous protocols assertions: - expectToBeDefined: message - onFinish: + onFinish: assertions: - expectToBeDefined: this.type - -#### subscription -A subscription is an "under demand" event. It **reacts** whereas a [publisher](#publisher) **acts**. + +#### sensor + +A sensor is an "under demand" event. It **reacts** whereas a [actuator](#actuator) **acts**. It consumes something, it reads, it dequeues, gets hit... These kinds of actions. -This means that it is not triggered by enqueuer itself. -Rather than that, enqueuer waits on an external event to be triggered and then it asserts against the message that was passed to the subscription. -It's worth noting that it always **receives** a message. +This means that it is not triggered by enqueuer itself. +Rather than that, enqueuer waits on an external event to be triggered and then it asserts against the message that was passed to the sensor. +It's worth noting that it always **receives** a message. That's the reason why there's an implicitly created test in **onFinish** hook verifying if a message got received. -##### subscription attributes -Every subscription has its own properties, depending on its protocol and implementation. -The built-in [`http` subscription](https://github.com/enqueuer-land/enqueuer/blob/master/examples/http.yml) implementation, for instance, demands an `endpoint`, a `method`, and a `port`, if the method is not a GET. -On the other hand, the built-in [`tcp` subscription](https://github.com/enqueuer-land/enqueuer/blob/master/examples/tcp.yml) implementation requires only a `port`. - -These are the subscription attributes: +##### sensor attributes + +Every sensor has its own properties, depending on its protocol and implementation. +The built-in [`http` sensor](https://github.com/enqueuer-land/enqueuer/blob/master/examples/http.yml) implementation, for instance, demands an `endpoint`, a `method`, and a `port`, if the method is not a GET. +On the other hand, the built-in [`tcp` sensor](https://github.com/enqueuer-land/enqueuer/blob/master/examples/tcp.yml) implementation requires only a `port`. + +These are the sensor attributes: **name** -Defaults to subscription index. -Describes what the subscription is supposed to do. +Defaults to sensor index. +Describes what the sensor is supposed to do. + + name: sensor action - name: subscription action - **type** -Mandatory. Key tag to identify which subscription will be instantiated +Mandatory. Key tag to identify which sensor will be instantiated type: http **avoid** -Identifies whether or not this subscription should not receive any message. Defaults to false. +Identifies whether or not this sensor should not receive any message. Defaults to false. If set and a message is received a failing test will be generated. Take a look at [this](https://github.com/enqueuer-land/enqueuer/blob/master/examples/avoid.yml) to see it working. -On the other hand, when it's false and no message is received in a given timeout. The subscription is valid. - +On the other hand, when it's false and no message is received in a given timeout. The sensor is valid. + avoid: false **timeout** -Sets in milliseconds how long the subscription waits to expire. Defaults to 3000. +Sets in milliseconds how long the sensor waits to expire. Defaults to 3000. Set to zero or less than zero to run it endlessly. timeout: 3000 - + **ignore** -Defaults to false. Tells to enqueuer that this subscription should be skipped. Check [this](https://github.com/enqueuer-land/enqueuer/blob/master/examples/ignore.yml) to see it working. +Defaults to false. Tells to enqueuer that this sensor should be skipped. Check [this](https://github.com/enqueuer-land/enqueuer/blob/master/examples/ignore.yml) to see it working. - ignore: true + ignore: true **id** -Defaults to an auto-generated one. Uniquely identify this component among the others. It gets implicitly set by enqueuer if it is not set initially. +Defaults to an auto-generated one. Uniquely identify this component among the others. It gets implicitly set by enqueuer if it is not set initially. id: ID-0123456789 - -##### events -Available events are described [here](#event). A `this` object is available to access and change subscription attributes. + +##### sensor events + +Available events are described [here](#event). A `this` object is available to access and change sensor attributes. onInit: script: this.avoid = false; assertions: - expectToBeDefined: this.type - onMessageReceived: + onMessageReceived: assertions: - expectToBeDefined: message - onFinish: + onFinish: assertions: - expectToBeDefined: this.type - ----- + +--- ### Event -Events are hook methods executed by enqueuer when an action occurs on publishers, subscriptions or requisitions. +Events are hook methods executed by enqueuer when something happen on actuators, sensors or tasks. This is where you'll write your tests. In its `assertions` field. -There will be a variable called `this` and, depending on the event's owner, it has an alias `publisher`, `subscription` or `requisition`. +There will be a variable called `this` and, depending on the event's owner, it has an alias `actuator`, `sensor` or `task`. You're free to explore them however you want, even doing things like this: - publisher.parent.subscriptions[0].timeout = 1000; - + actuator.parent.sensors[0].timeout = 1000; + #### hooks -By default, there are three hook events available: +Every component has at least two hook events available: **onInit** -Available in requisitions, publishers and subscriptions. It gets executed as soon as the component is initialized. -As available parameter, an `elapsedTime` variable is given, counting every milliseconds since the instantiation of this component. +Available in tasks, actuators and sensors. It gets executed as soon as the component is initialized. +As available parameter, an `elapsedTime` variable is given, counting every millisecond since the instantiation of this component. **onFinish** -Available in requisitions, publishers and subscriptions. It gets executed when the component is about to finish. -As available parameter, an `elapsedTime` variable is given, counting every milliseconds since the instantiation of this component. +Available in tasks, actuators and sensors. It gets executed when the component is about to finish. +As available parameter, an `elapsedTime` variable is given, counting every millisecond since the instantiation of this component. The `onFinish` hook also provides the argument `executedHooks`. A list of strings enumerating which hooks were executed by the componen + +`Actuators` and `sensors` also provide custom hooks. Check their documentation to find which ones are available and what arguments they provide. **custom** -Depending on the protocol implementation/library/author's mood, the publisher/subscription may have additional hooks. -Such as `onError`, `onResponseReceived`, `onFileNotFound` and `onRedirect`... -[Http-proxy subscription test file](https://github.com/enqueuer-land/enqueuer/blob/master/examples/http-proxy.yml) is an excellent example, check it out. +Depending on the protocol implementation/library/author's mood, the actuator/sensor may have additional hooks. +Such as `onError`, `onResponseReceived`, `onFileNotFound` and `onRedirect`... +[Http-proxy sensor test file](https://github.com/enqueuer-land/enqueuer/blob/master/examples/http-proxy.yml) is an excellent example, check it out. + +A good way to identify that is to run the following command line `nqr -p `. Like: + + nqr -p http + +**_available variables_** +Given that the variables and theirs names may vary according to the scenario, it's interesting to have a special one to retrieve every argument passed to the hook. To retrieve that information, you can use "'argumentNames'" as a regular argument. So, let's say you have this task: + + onFinish: + script: console.log(argumentNames) + +You'd get this printed out to the console: + + [ 'task', 'elapsedTime', 'executedHooks' ] #### fields -Every hook object has 3 properties: + +Every hook object has 4 properties: **script** Javascript code snippet executed when the event is triggered. Yeah, I mean it. See it [it](https://github.com/enqueuer-land/enqueuer/blob/master/examples/crypto-require.yml) by yourself. -But be careful, with great power comes great responsibility. +Be careful, with great power comes great responsibility. + +**debug** +A boolean value that prints to the console the available arguments and their respective values. **store** -Data to be persisted across requisitions. +Data to be persisted across tasks. **assertions** Array of assertions. @@ -425,19 +461,19 @@ You can [check them out](#plugins_list) or even [write your own](https://github. onInit: script: variableIdentifier = 'string value' - + assertions: - expect: variableIdentifier - toBeEqualTo: `string value` - + toBeEqualTo: "'string value'" + onMessageReceived: script: |- message += 3; - console.log(`Message received plus 3 is: ${message}`); - + console.log("'Message received plus 3 is: ${message}'"); + store: key: message - + assertions: - name: anyValue #optional expect: message @@ -446,33 +482,38 @@ You can [check them out](#plugins_list) or even [write your own](https://github. toBeGreaterThan: 3 #### event example + Check [this](https://github.com/enqueuer-land/enqueuer/blob/master/examples/hooks.yml) test file to see it in practice. ----- +--- + +### Task Flow -### Requisition Flow -Now that you know what are requisitions, publishers, subscriptions and events. How about seeing how they interact with each other in a fancier way? +Now you know what are tasks, actuators, sensors and events. How about seeing how they interact with each other in a fancier way? -![enqueuerInstanceFlow](https://raw.githubusercontent.com/enqueuer-land/enqueuer/master/docs/images/nqrFlow.png "Enqueuer Instance Flow") +![enqueuerInstanceFlow](https://raw.githubusercontent.com/enqueuer-land/enqueuer/master/docs/images/nqrFlow.png 'Enqueuer Instance Flow') ----- +--- ### Configuration File + To save yourself some time, a configuration file may be used. Configuration files tell enqueuer which tests will be executed, log-level, and which output test report files should be generated. This file tells how enqueuer should be executed. To run enqueuer with the configuration: - $ nqr -c path/to/configuration/file.yml + nqr -c path/to/configuration/file.yml + or - - $ nqr --config-file path/to/configuration/file.yml + + nqr --config-file path/to/configuration/file.yml #### attributes + These are the configuration file attributes: **files** -Requisition file names or glob. Enqueuer runs every file that matches an element value. +Task file names or glob. Enqueuer runs every file that matches an element value. files: - 1.yml @@ -480,7 +521,7 @@ Requisition file names or glob. Enqueuer runs every file that matches an element - *.json **parallel** -Defaults to false. Requisition files should be executed in parallel mode. The requisition file itself is still sequential, but the files are executes in parallel. +Defaults to false. Task files should be executed in parallel mode. The task file itself is still sequential, but the files are executes in parallel. parallel: true @@ -495,22 +536,22 @@ Defaults to 1. The deepest level of report to be printed to the console. max-report-level-print: 2 **plugins** -List of in [plugins](#plugins) used by the test scenarios. You can [check them out](https://github.com/enqueuer-land/plugins-list#enqueuer-plugins) or [write your own](https://github.com/enqueuer-land/plugin-scaffold). - +List of in [plugins](#plugins) used by the test scenarios. You can [check them out](https://github.com/enqueuer-land/plugins-list#enqueuer-plugins) or [write your own](https://github.com/enqueuer-land/plugin-scaffold). + plugins: - - enqueuer-plugin-amqp - - enqueuer-plugin-ws + - enqueuer-plugin-amqp + - enqueuer-plugin-ws - enqueuer-plugin-mqtt - enqueuer-plugin-html-report **outputs** -Once enqueuer runs every execution, it compiles a summary and sends it to every publisher listed in output. -An important thing to note is that every available report publisher is available here. -Yes, it means that you are able to send this report through `http`, `tcp`, etc. or through a [plugin one](https://github.com/enqueuer-land/plugins-list#enqueuer-plugins) or a [custom one](https://github.com/enqueuer-land/plugin-scaffold). -You can run `$ nqr -p` to check available report publishers installed. +Once enqueuer runs every execution, it compiles a summary and sends it to every actuator listed in output. +An important thing to note is that every available report actuator is available here. +Yes, it means that you are able to send this report through `http`, `tcp`, etc. or through a [plugin](https://github.com/enqueuer-land/plugins-list#enqueuer-plugins) or a [custom one](https://github.com/enqueuer-land/plugin-scaffold). +You can run `$ nqr -p` to check available report actuators installed. Another important thing to note is the `format` value. By default a `json` summary is generated, but you can change it to whatever format you would like, such as: [Xunit](https://github.com/williamsdevaccount/enqueuer-plugin-xunit-report), [html](https://github.com/enqueuer-land/enqueuer-plugin-html-report) You can run `$ nqr -f` to check available installed formats or even [write your own](https://github.com/enqueuer-land/plugin-scaffold) - + outputs: - type: file format: json (default) @@ -525,21 +566,23 @@ You can run `$ nqr -f` to check available installed formats or even [write your Values defined here use the 'key: value' pattern and are available to every test scenario throughout the entire execution store: - variableKey: "my value" # Defines 'variableKey' key and its value 'my value'. - + variableKey: "my value" # Defines 'variableKey' key and its value 'my value'. + 'separated key': 6 - + object: # You can even define whole objects here: first: first value second: nested: thing #### example + [Here's](https://github.com/enqueuer-land/enqueuer/blob/master/conf/config-example.yml) a complete example of a configuration file. ----- +--- ### Variables + Providing power and flexibility, enqueuer allows you to use variables placeholder replacement. That's why there is a `store` field and you'll see a lot of `<<` and `{{` being used in the examples files. It works as simple as this: @@ -547,97 +590,110 @@ It works as simple as this: name: my name is <> Every time enqueuer sees these kind of notations, it searches in its store for a key/value pair like: - - variableKey: `enqueuer` + + variableKey: "'enqueuer'" Then, when enqueuer parses the original map, it gets translated to this: - + name: my name is enqueuer - + By default, every ENV_VAR set is loaded automatically to the store. Check [this example](https://github.com/enqueuer-land/enqueuer/blob/64198b944849df2cb5bd23cbfb6d0a224d6b5167/examples/store.yml#L11). #### set a variable + There are a few ways to set a value in the store. ##### configuration file + Configuration file store object. Set it as you wish, as you can see [here](https://github.com/enqueuer-land/enqueuer/blob/64198b944849df2cb5bd23cbfb6d0a224d6b5167/conf/singleRun.yml#L27) ##### command line -A command line argument using the `key=value` format. This way: - - $ nqr --store key=value -s anotherVariable=true -##### event -Dynamically set it through any [event](#event). +A command line argument using the `key=value` format. This way: + + nqr --store key=value -s anotherVariable=true + +##### event set variable + +Dynamically set it through any [event](#event). Be it in its [script](https://github.com/enqueuer-land/enqueuer/blob/64198b944849df2cb5bd23cbfb6d0a224d6b5167/examples/store.yml#L5) field or straight through its store [field](https://github.com/enqueuer-land/enqueuer/blob/64198b944849df2cb5bd23cbfb6d0a224d6b5167/examples/store.yml#L3). Both ways work: onInit: script: store.key = 123; store: - anotherKey: `another Value` + anotherKey: "'another Value'" + +#### use a variable -#### use a variable There are two ways two use a variable: -##### non js code snippet +##### non-js code snippet + The easiest one is to type `<>` or `{{variableKey}}` where you want it to be replaced in a test file, as you can see [here](https://github.com/enqueuer-land/enqueuer/blob/64198b944849df2cb5bd23cbfb6d0a224d6b5167/examples/store.yml#L8) ##### js code snippet -Using the `store` object. It's attributes are the keys and their values are their respective values. -Therefore, you're free to use `store.variableKey`, `console.log(store.variableKey);` or `console.log(2 * store['separated key']);` and get them. + +Using the `store` object. It's attributes are the keys and their values are their respective values. +Therefore, you're free to use `store.variableKey`, `console.log(store.variableKey);` or `console.log(2 \* store['separated key']);` and get them. Like [this](https://github.com/enqueuer-land/enqueuer/blob/64198b944849df2cb5bd23cbfb6d0a224d6b5167/examples/store.yml#L5) one. -#### variables example +#### variables example + Check out [this test example](https://github.com/enqueuer-land/enqueuer/blob/master/examples/variables.yml) test to see it working. ----- +--- ### Content File Injection -You are able to inject file content into a requisition/publisher/subscription field. + +You are able to inject file content into a task/actuator/sensor field. file: <> - + Other than that, enqueuer can read it and parse its content as an object using this familiar syntax: `<>`. - requisition: + task: json: <> yml: <> csv: <> file: <> - + Once the object is parsed, your free to use it as a regular object in any event - + onInit: - script: console.log(requisition.yml.deep.field); + script: console.log(task.yml.deep.field); onFinish: assertions: - expect: json.key toBeEqualTo: csv[0].key -It get's even better. -Due its fantastic plugin architecture design, you can extend its default modules and use any of [these](#plugins_list) plugins or event [write your own](https://github.com/enqueuer-land/plugin-scaffold) to parse however you want. -The built-in modules for object parsers are: `json`, `yml`, `csv` and `file`. +It gets even better. +Due to its fantastic plugin architecture design, you can extend its default modules and use any of [these](#plugins_list) plugins or event [write your own](https://github.com/enqueuer-land/plugin-scaffold) to parse however you want. +The built-in modules for object parsers are: `json`, `yml`, `csv` and `file`. Run `$ nqr -e` to see available ones. -#### example +#### content file injection example + Check out [this test example](https://github.com/enqueuer-land/enqueuer/blob/master/examples/file-placeholder.yml) test to get a full picture of it. ----- +--- ### Plugins + You're probably aware by now but it doesn't hurt do emphasize it: enqueuer provides an amazingly powerful plugin extensible architecture. It has [several plugins available](#plugins_list), but if none of them pleases you, you're free to [create your own](https://github.com/enqueuer-land/plugin-scaffold). -Albeit you don't have to share the one you created, we encourage you to do so. Then go ahead and publish yours to npm and add it to our [plugins repository](https://github.com/enqueuer-land/plugins-list#enqueuer-plugins). +Albeit you don't have to share the one you created, we encourage you to do so. Then go ahead and publish yours to npm and add it to our [plugins repository](https://github.com/enqueuer-land/plugins-list#enqueuer-plugins). #### plugin types + So far, you're able to extend enqueuer default behavior in four ways. Using a protocol plugin, an object parser plugin, an asserter plugin and using a report formatter plugin. ##### protocol -A protocol plugin enables you to use a different publisher/subscription types. + +A protocol plugin enables you to use a different actuator/sensor types. Run `$ nqr -p [protocol-name]` to get the full available list: - publishers: + actuators: - name: custom - name: file - name: http @@ -645,7 +701,7 @@ Run `$ nqr -p [protocol-name]` to get the full available list: - name: stdout - name: tcp - ... - subscriptions: + sensors: - name: custom - name: file messageReceivedParams: content, name, size, modified, created @@ -657,28 +713,29 @@ Run `$ nqr -p [protocol-name]` to get the full available list: - ... Each one listed above has a respective example in [the examples folder](https://github.com/enqueuer-land/enqueuer/blob/master/examples). -[This one](https://github.com/enqueuer-land/enqueuer-plugin-amqp), for instance, provides support for amqp protocol, so you can create this publisher and subscription: - - publishers: +[This one](https://github.com/enqueuer-land/enqueuer-plugin-amqp), for instance, provides support for amqp protocol, so you can create this actuator and sensor: + + actuators: - type: amqp payload: enqueuermaniac exchange: enqueuer.exchange routingKey: enqueuer.integration.test.routing.key - subscriptions: + sensors: - type: amqp exchange: enqueuer.exchange routingKey: enqueuer.integration.test.routing.# onMessageReceived: assertions: - expect: payload - toBeEqualTo: `enqueuermaniac` + toBeEqualTo: "'enqueuermaniac'" ##### object parser + An object parser plugin enables you to read and parse files as you wish. [This test example](https://github.com/enqueuer-land/enqueuer/blob/master/examples/file-placeholder.yml) demonstrates how to use it, Run `$ nqr -e [object-parser-name]` to check available ones: - parsers: + parsers: - yml, yaml - json - file @@ -689,38 +746,41 @@ Run `$ nqr -e [object-parser-name]` to check available ones: xmlContent: <> ##### asserter + An asserter plugin provides you a nicely way to use different assertions than these built-in ones. - asserters: - - expect: + asserters: + - expect: required: true type: string, array description: actual value - not: + not: required: false type: null description: negates - toContain: + toContain: required: true type: string, any description: element Looking at the asserter above, we can create assertions like these: - + assertions: - - expect: [`a`, 1, true] + - expect: ["'a'", 1, true] not: - toContain: `b` - - expect: [`a`, 1, true] + toContain: "'b'" + - expect: ["'a'", 1, true] toContain: 1 Run `$ nqr -t` to get the full available list. Consider looking at [this](https://github.com/enqueuer-land/enqueuer/blob/master/examples/assertions.yml) test example. + ##### report formatter + A report formatter plugin gives you the ability to export enqueuer reports the way you want. Run `$ nqr -f [formatter-name]` to list available report formatters: - formatters: + formatters: - console, stdout - json - yml, yaml @@ -729,6 +789,7 @@ Consider looking at the example of [configuration file](https://github.com/enque [This one](https://github.com/williamsdevaccount/enqueuer-plugin-xunit-report), for instance, generates xUnit like reports from enqueuer's output. #### plugins list + Enqueuer community offers support to the following plugins: {{plugins list placeholder}} @@ -736,96 +797,106 @@ Enqueuer community offers support to the following plugins: Want to see yours here too? [Write your own](https://github.com/enqueuer-land/plugin-scaffold) and make a PR [here](https://github.com/enqueuer-land/plugins-list#enqueuer-plugins). #### plugins installation + In order to enqueuer get awareness that you want to use a plugin, you have to tell it, right? You can tell enqueuer to use a plugin in three different ways: using it as a command line argument, through the configuration file or letting enqueuer finding it in a default location. -##### command line +##### use plugins via command line + Tell enqueuer to use your plugin through command line this way `$ nqr -l -l `. Where plugin-folder and another-plugin-folder are the directories where the plugins are installed in. - -##### configuration file + +##### use plugins via configuration file + Tell enqueuer to use your plugin through configuration file this way: - - plugins: + + plugins: - plugin-folder - another-plugin-folder Where plugin-folder and another-plugin-folder are the directories where the plugins are installed in. ##### implicitly -When enqueuer runs, it looks for modules in its same installation directory or in `.nqr` folder in the home directory, a.k.a. ~/ folder in linux distributions. + +When enqueuer runs, it looks for modules in its same installation directory or in "'.nqr'" folder in the home directory, a.k.a. ~/ folder in linux distributions. Therefore, if you run: - - $ npm install --global enqueuer - $ mkdir ~/.nqr - $ cd ~/.nqr - $ npm install enqueuer-plugin-amqp - $ nqr -p amqp + + npm install --global enqueuer + mkdir ~/.nqr + cd ~/.nqr + npm install enqueuer-plugin-amqp + nqr -p amqp + Or - - $ npm install --global enqueuer enqueuer-plugin-amqp - $ nqr -p amqp - + + npm install --global enqueuer enqueuer-plugin-amqp + nqr -p amqp + You'll see that the `enqueuer-plugin-amqp` plugin will be loaded. Every enqueuer compatible module gets implicitly loaded. -In order to be enqueuer compatible, a module has to have an `entryPoint` exported function in its main file and, in its package.json file, it has to have either 'enqueuer' or 'nqr' as keywords. +In order to be enqueuer compatible, a module has to have an `entryPoint` exported function in its main file and, its `package.json` file has to have either `enqueuer` or `qr` as keywords. ### Stacker -Looking for ~~a really really good looking~~ an human error proof solution way of writing these requisition files? -Consider taking a look at [stacker](https://virgs.github.io/stacker/): open source, cross-platform, multi protocol client testing tool. + +Looking for an human error proof solution way of writing these task files? +Consider taking a look at [stacker](https://enqueuer-land.github.io/stacker/): open source, cross-platform, multi protocol client testing tool. The official enqueuer's best friend forever. Do amazing things and change the world with enqueuer’s GUI! -With them, you create, manage and run requisitions and and see their results in a really nice way. -See this amazing beauty with your own eyes to get an idea of how it works: +With them, you create, manage and run tasks and and see their results in a really nice way. +See this amazing beauty with your own eyes to get an idea of how it works: -![screenshot-passing](https://raw.githubusercontent.com/virgs/stacker/master/docs/img/http-passing-test.png) +![screenshot-passing](https://raw.githubusercontent.com/enqueuer-land/stacker/master/docs/screenshot-1.png) ----- +--- ### Open source + We (by 'we' we mean enqueuer's maintainers not the human race, unfortunately) are very opened to any kind of contributions in general. As long as they make sense and add value to the product, you're free to go. We mean it, do it. Even if it's a typo fix in this README file. Go ahead. If you like it but don't want to waste time creating a pull request, no problem either. Create an issue, or, even easier, give it a github star. It's cheap and it doesn't hurt anyone. You know what? Just head up to [enqueuer's github repo](https://github.com/enqueuer-land/enqueuer) and keep staring at its repo. -It may help somehow. +It may help somehow. #### contributors + Thanks goes to these wonderful people: {{contributors list placeholder}} -It sounds *clichÊ*, but this project wouldn't be the same without the massive contribution of everyone. +It sounds _clichÊ_, but this project wouldn't be the same without the massive contribution of everyone. #### code it + In order to contribute with some code, you have to follow a few steps. First of all, get the code: - $ git clone git@github.com:enqueuer-land/enqueuer.git - $ cd enqueuer - + git clone git@github.com:enqueuer-land/enqueuer.git + cd enqueuer + Get its dependencies installed: - - $ npm install -Build it: - - $ npm run build - + npm install + +Build it: + + npm run build + Go for it. Make the changes you want. After everything is done: - $ npm run all - + npm run all + Commit it: - $ npm run commit + npm run commit Push it: - $ git push + git push #### feedback + We'd love to get your feedback! If you have any comments, suggestions, etc. you can reach us [here](mailto:guilherme.moraes@outlook.com). diff --git a/docs/docs.html b/docs/docs.html index 18912de1..306f8ecc 100644 --- a/docs/docs.html +++ b/docs/docs.html @@ -1,252 +1,284 @@ - + - + - - - - - - - + + + + + + + Enqueuer - - - - - - - + - - - - + + + - -

1 Enqueuer

+ +

npm Build Status Greenkeeper badge Known Vulnerabilities @@ -254,22 +286,24 @@

enqueuerlogo

-

1 Welcome

+

1.1 Docs

+ +

1.1.1 Welcome

Be our guest and have fun.

-

1.1 install it

+

1.1.1.1 install it

First things first, let's get the enqueuer installed on your machine.

-
$ npm install --global enqueuer
+
npm install --global enqueuer
 
-

Alright, it's time to create a requisition file. +

Alright, it's time to create a task file. Something like:

#enqueuer-repo-hit.yml
-publishers:
+actuators:
 -   type: http
     url: https://github.com/enqueuer-land/enqueuer
     onResponseReceived:
@@ -281,14 +315,14 @@ 

1.1 install it

Pretty simple, hum? Small and concise, how it should be! Run it:

-
$ enqueuer enqueuer-repo-hit.yml
+
enqueuer enqueuer-repo-hit.yml
 
-

Now you know how to hit a http server. +

Now you know how to hit a http server. What if I want to mock a http server response, you may ask. Not a big deal for enqueuer lovers:

timeout: -1
-subscriptions:
+sensors:
 -   type: http
     timeout: -1
     name: mock endpoint
@@ -301,7 +335,7 @@ 

1.1 install it

onMessageReceived: assertions: - expect: body - toContain: `enqueuer` + toContain: "'enqueuer'"

Now go ahead and try hitting it using the browser.
@@ -309,7 +343,7 @@

1.1 install it

I told you it was simple.
Yes, of course you can hit your own mocked http server:

-
publishers:
+
actuators:
 -   type: http
     url: http://localhost:23075/resource
     method: POST
@@ -320,7 +354,7 @@ 

1.1 install it

toBeGreaterThan: 400 - expect: body toBeEqualTo: "'blah'" -subscriptions: +sensors: - type: http endpoint: /resource port: 23075 @@ -338,14 +372,14 @@

1.1 install it

How about publishing an amqp message and making sure that, once a service consumes that message an endpoint of your is hit? In order to achieve that, we have to make use of a plugin, given that amqp support is provided by a plugin. In this scenario, we're talking about the amqp plugin. -Once we get this plugin installed we are able to create and run files like this:

+Once we get this [plugin installed](#plugins installation) we are able to create and run files like this:

-
publishers:
+
actuators:
 -   type: amqp
     payload: 123456
     exchange: enqueuer.exchange
     routingKey: enqueuer.readme.routing.key
-subscriptions:
+sensors:
 -   type: http
     endpoint: /polyglot-flow
     port: 8080
@@ -361,350 +395,376 @@ 

1.1 install it

Now go nuts! It's all yours. Have fun. -If you want more examples about http, consider looking at this test. -Check this out, you'll find countless examples. -Certainly one is what you need.

+If you want more examples about "'http'", consider looking at this test. +Check this out, you'll find countless examples. +Certainly one is what you need.

-

1.2 if you need more

+

1.1.1.2 if you need more

$ nqr -h
-Usage: nqr [options] <test-file> [other-test-files...]
+
+Usage: index [options] [test-files...]
 
 Take a look at the full documentation: https://enqueuer.com
 
+Arguments:
+test-files                                other files to be tested
+
 Options:
-  -v, --version                             output the version number
-  -b, --verbosity <level>                   set verbosity (default: "warn")
-  -c, --config-file <path>                  set configurationFile
-  -e, --parsers-list [parser]               list available object parsers
-  -f, --formatters-description [formatter]  describe report formatters
-  -o, --stdout-requisition-output           add stdout as requisition output
-  -m, --max-report-level-print <level>      set max report level print
-  -p, --protocols-description [protocol]    describe protocols
-  -t, --tests-list [expectedField]          list available tests assertions
-  -u, --loaded-modules-list                 list loaded modules
-  -i, --show-passing-tests                  show passing tests
-  -s, --store [store]                       add variables values to this session (default: [])
-  -l, --add-plugin [plugin]                 add plugin (default: [])
-  -a, --add-file <file>                     add file to be tested (default: [])
-  -A, --add-file-and-ignore-others <file>   add file to be tested and ignore others (default: [])
-  -h, --help                                output usage information
+-v, --version                             output the current version
+-b, --verbosity <level>                   set verbosity (choices: "trace", "debug", "info", "warn", "error", "fatal", default: "warn")
+-c, --config-file <path>                  set configurationFile
+-d, --show-explicit-tests-only            show explicit tests only (default: false)
+-o, --stdout-task-output           add stdout as task output (default: false)
+-m, --max-report-level-print <level>      set max report level print
+-i, --show-passing-tests                  show passing tests
+-s, --store [store]                       add variables values to this session (default: [])
+-l, --add-plugin [plugin]                 add plugin
+-e, --parsers-list [parser]               list available object parsers
+-q, --parallel                            should run tests files parallely (default: false)
+-f, --formatters-description [formatter]  describe report formatters (default: false)
+-p, --protocols-description [protocol]    describe protocols (default: false)
+-t, --tests-list [expectedField]          list available tests assertions (default: false)
+-u, --loaded-modules-list                 list loaded modules (default: false)
+-h, --help                                display help for command
 
 Examples:
-  $ nqr --config-file config-file.yml --verbosity error --store key=value
-  $ enqueuer -c config-file.yml test-file.yml --add-file another-test-file.yml -b info
-  $ enqueuer test-file.yml --store someKey=true --store someOtherKey=false
-  $ nqr --protocols-description -s key=value
-  $ nqr -t expect
-  $ nqr -l my-enqueuer-plugin-name -p plugin-protocol
-  $ nqr -p http
-  $ nqr --formatters-description json
+$ nqr --config-file config-file.yml --verbosity error --store key=value
+$ enqueuer -c config-file.yml test-file.yml another-test-file.yml -b info
+$ enqueuer test-file.yml --store someKey=true --store someOtherKey=false
+$ nqr --protocols-description -s key=value
+$ nqr -t expect
+$ nqr -l my-enqueuer-plugin-name -p plugin-protocol
+$ nqr -p http
+$ nqr --formatters-description json
 
 Contributing:
-  https://github.com/enqueuer-land/enqueuer
+https://github.com/enqueuer-land/enqueuer
 

-

2 Components

+

1.1.2 Components

In order to accomplish more than just hitting enqueuer's repo or doing a quick self http hit, there are a few things that you'll probably need to know. -Don't worry, it's not too much and, as mentioned earlier, there is a lot of examples here, just in case. -There are only three important component concepts: requisitions, publishers and subscriptions. +Don't worry, it's not too much and, as mentioned earlier, there is a lot of examples here, just in case. +There are only three important component concepts: tasks, actuators and sensors. They work along with each other and are responsible for the full behavior of enqueuer.

-

2.1 requisition

+

1.1.2.1 task

Test scenario description. It tells what, when, and how test your applications and services. -Picture it as if it was a collection of publishers, subscriptions and other requisitions. +Picture it as if it was a collection of actuators, sensors and other tasks. It helps because this is exactly what it is. As the others components, it has some attributes. All of them are optionals. And it supports multi-level test scenarios out of the box. Yeap, go as recursive as you want. -Every test file is a requisition. -You don't know some of these attributes values yet? Don't worry, just put a variable there and let enqueuer replace it with the value you set later. -Variable replacements are available through the entire requisition.

+Every test file is a task. +You don't know some of these attributes values yet? Don't worry, just put a variable there and let enqueuer replace it with the value you set later. +Variable replacements are available through the entire task.

-
2.1.1 requisition attributes
+
1.1.2.1.1 task attributes
-

These are the requisition attributes:

+

These are the task attributes:

name
-Describes what the requisition is suppose to do. -Defaults to requisition index.

+Describes what the task is suppose to do. +Defaults to task index.

-
name: requisition action
+
name: task action
 

timeout
Defaults to 5000. -Sets in milliseconds how long the requisition waits to expire. +Sets in milliseconds how long the task waits to expire. Set to zero or less than zero to run it endlessly.

timeout: 3000
 

delay
-Defaults to 0. Sets in milliseconds how long the test waits before starting. Check this to get the full idea.

+Defaults to 0. Sets in milliseconds how long the test waits before starting. Check this to get the full idea.

delay: 0
 

iterations
-Defaults to 1. Sets how many times this test will be executed. Check this and this to get the full idea.

+Defaults to 1. Sets how many times this test will be executed. Check this and this to get the full idea.

iterations: 3
 

id
-Defaults to an auto-generated one. Uniquely identify this component among the others. It gets implicitly set by enqueuer if it is not set initially.

+Defaults to an auto-generated one. Uniquely identify this component among the others. It gets implicitly set by enqueuer if it is not set initially.

id: ID-0123456789
 

ignore
-Defaults to false. Tells to enqueuer that this requisitions should be skipped. Check this to see it working.

+Defaults to false. Tells to enqueuer that this tasks should be skipped. Check this to see it working.

ignore: true
 

parallel
-Defaults to false. Immediate children requisitions should be executed in parallel mode. -Take a look at this to see it working.

+Defaults to false. Immediate children tasks should be executed in parallel mode. +Take a look at this to see it working.

parallel: true
 

import
-Allows requisition to be dynamically defined, be it by loading an external file or creating dynamically by other requisitions. Want to reuse the same requisition multiple times? This is you you need. +Allows task to be dynamically defined, be it by loading an external file or creating dynamically by other tasks. Want to reuse the same task multiple times? This is you you need. Take a look at this to behold this feature.

-
import: path/to/another/requisition/file
+
import: path/to/another/task/file
 
-

publishers
-List of publishers. They're executed simultaneously, therefore, the order is irrelevant.

+

actuators
+List of actuators. They're executed simultaneously, therefore, the order is irrelevant.

-
publishers:
-- name: some publisher name
+
actuators:
+- name: some actuator name
   type: http
 - type: tcp
 
-

subscriptions
-List of subscriptions. They're executed simultaneously, therefore, the order is irrelevant.

+

sensors
+List of sensors. They're executed simultaneously, therefore, the order is irrelevant.

-
subscriptions:
-- name: some subscription name
+
sensors:
+- name: some sensor name
   type: udp
-- name: another subscription name
+- name: another sensor name
   type: file
 
-

requisitions
-A list of child scenarios. List of requisitions. -By default, they're executed sequentially, therefore, the order is relevant. -Unless the parallel attribute is set to true, what makes them get executed simultaneously, +

tasks
+A list of child scenarios. List of tasks. +By default, they're executed sequentially, therefore, the order is relevant. +Unless the parallel attribute is set to true, what makes them get executed simultaneously, Check this example, it may help.

-
requisitions:
-- name: some requisition name
+
tasks:
+- name: some task name
   iterations: 2
-- name: another requisition name
+- name: another task name
   delay: 200
 
-
2.1.2 events
+
1.1.2.1.2 events
-

Requisitions have onInit and onFinish events. -Available events are described here. A this object is available to access and change requisition attributes.

+

Tasks have onInit and onFinish events. +Available events are described here. A this object is available to access and change task attributes.

name: my name
 onInit:
   script: this.delay = 3000;
   assertions:
   - expectToBeDefined: this.name
-onFinish:  
+onFinish:
   assertions:
   - expectToBeDefined: this.name
 
-

2.2 publisher

+

1.1.2.2 actuator

-

A publisher action is triggered by enqueuer itself. It acts whereas a subscription reacts. -It publishes something, it writes, it enqueues, hits and endpoint... These kinds of actions. -It's worth noting that it always creates a message. +

An actuator action is triggered by enqueuer itself. It acts whereas a sensor reacts. +It publishes something, it writes, it enqueues, hits an endpoint... These kinds of actions. +It's worth noting that it always creates a message. That's the reason why there's an implicitly created test in onFinish hook verifying if the message got published.

-
2.2.1 publisher attributes
+
1.1.2.2.1 actuator attributes
-

Every publisher has its own properties, depending on its protocol and implementation. -The built-in http publisher implementation, for instance, demands a url, a method, and a payload, if the method is not a GET. -On the other hand, the built-in tcp publisher implementation requires a serverAddress and a port. -These are the publisher attributes:

+

Every actuator has its own properties, depending on its protocol and implementation. +The built-in http actuator implementation, for instance, demands a url, a method, and a payload, if the method is not a GET. +On the other hand, the built-in tcp actuator implementation requires a serverAddress and a port. +These are the actuator attributes:

name
-Defaults to publisher index. -Describes what the publisher is supposed to do.

+Defaults to actuator index. +Describes what the actuator is supposed to do.

-
name: publisher action
+
name: actuator action
 

type
-Mandatory. Key tag to identify which publisher will be instantiated

+Mandatory. Key tag to identify which actuator will be instantiated

type: http
 

payload
-Since a publisher usually publishes something, it's very likely you have to set a value here. +Since a actuator usually publishes something, it's very likely you have to set a value here. The message itself that will be send through this protocol. Be it a string, a number, a boolean value or even whole objects.

payload: value
 

ignore
-Defaults to false. Tells to enqueuer that this publisher should be skipped. Check this to see it working.

+Defaults to false. Tells to enqueuer that this actuator should be skipped. Check this to see it working.

ignore: true
 

id
-Defaults to an auto-generated one. Uniquely identify this component among the others. It gets implicitly set by enqueuer if it is not set initially.

+Defaults to an auto-generated one. Uniquely identify this component among the others. It gets implicitly set by enqueuer if it is not set initially.

id: ID-0123456789
 
-
2.2.2 events
+
1.1.2.2.2 actuator events
-

Available events are described here. A this object is available to access and change publisher attributes. -Depending on the protocol and its implementation, such as http and tcp, there may exist special events, such as onMessageReceived event and a special object given message. -On the other hand, an asynchronous protocol, like: udp and amqp, usually do not provide it.

+

Available events are described here. A this object is available to access and change actuator attributes. +Depending on the protocol and its implementation, such as http and tcp, there may exist custom events, such as onResponseReceived event and an attribute message passed to it. +On the other hand, an asynchronous protocol, like: udp and amqp, usually do not provide it.

onInit:
   script: this.ignore = false
   assertions:
   - expectToBeDefined: this.type
-onMessageReceived: #Provided in synchronous protocols  
+onResponseReceived: #Provided in synchronous protocols
   assertions:
   - expectToBeDefined: message
-onFinish:  
+onFinish:
   assertions:
   - expectToBeDefined: this.type
 
-

2.3 subscription

+

1.1.2.3 sensor

-

A subscription is an "under demand" event. It reacts whereas a publisher acts. +

A sensor is an "under demand" event. It reacts whereas a actuator acts. It consumes something, it reads, it dequeues, gets hit... These kinds of actions. -This means that it is not triggered by enqueuer itself. -Rather than that, enqueuer waits on an external event to be triggered and then it asserts against the message that was passed to the subscription. -It's worth noting that it always receives a message. +This means that it is not triggered by enqueuer itself. +Rather than that, enqueuer waits on an external event to be triggered and then it asserts against the message that was passed to the sensor. +It's worth noting that it always receives a message. That's the reason why there's an implicitly created test in onFinish hook verifying if a message got received.

-
2.3.1 subscription attributes
+
1.1.2.3.1 sensor attributes
-

Every subscription has its own properties, depending on its protocol and implementation. -The built-in http subscription implementation, for instance, demands an endpoint, a method, and a port, if the method is not a GET. -On the other hand, the built-in tcp subscription implementation requires only a port.

+

Every sensor has its own properties, depending on its protocol and implementation. +The built-in http sensor implementation, for instance, demands an endpoint, a method, and a port, if the method is not a GET. +On the other hand, the built-in tcp sensor implementation requires only a port.

-

These are the subscription attributes:

+

These are the sensor attributes:

name
-Defaults to subscription index. -Describes what the subscription is supposed to do.

+Defaults to sensor index. +Describes what the sensor is supposed to do.

-
name: subscription action
+
name: sensor action
 

type
-Mandatory. Key tag to identify which subscription will be instantiated

+Mandatory. Key tag to identify which sensor will be instantiated

type: http
 

avoid
-Identifies whether or not this subscription should not receive any message. Defaults to false. +Identifies whether or not this sensor should not receive any message. Defaults to false. If set and a message is received a failing test will be generated. Take a look at this to see it working. -On the other hand, when it's false and no message is received in a given timeout. The subscription is valid.

+On the other hand, when it's false and no message is received in a given timeout. The sensor is valid.

avoid: false
 

timeout
-Sets in milliseconds how long the subscription waits to expire. Defaults to 3000. +Sets in milliseconds how long the sensor waits to expire. Defaults to 3000. Set to zero or less than zero to run it endlessly.

timeout: 3000
 

ignore
-Defaults to false. Tells to enqueuer that this subscription should be skipped. Check this to see it working.

+Defaults to false. Tells to enqueuer that this sensor should be skipped. Check this to see it working.

-
ignore: true    
+
ignore: true
 

id
-Defaults to an auto-generated one. Uniquely identify this component among the others. It gets implicitly set by enqueuer if it is not set initially.

+Defaults to an auto-generated one. Uniquely identify this component among the others. It gets implicitly set by enqueuer if it is not set initially.

id: ID-0123456789
 
-
2.3.2 events
+
1.1.2.3.2 sensor events
-

Available events are described here. A this object is available to access and change subscription attributes.

+

Available events are described here. A this object is available to access and change sensor attributes.

onInit:
   script: this.avoid = false;
   assertions:
   - expectToBeDefined: this.type
-onMessageReceived:  
+onMessageReceived:
   assertions:
   - expectToBeDefined: message
-onFinish:  
+onFinish:
   assertions:
   - expectToBeDefined: this.type
 

-

3 Event

+

1.1.3 Event

-

Events are hook methods executed by enqueuer when an action occurs on publishers, subscriptions or requisitions. +

Events are hook methods executed by enqueuer when something happen on actuators, sensors or tasks. This is where you'll write your tests. In its assertions field. -There will be a variable called this and, depending on the event's owner, it has an alias publisher, subscription or requisition. +There will be a variable called this and, depending on the event's owner, it has an alias actuator, sensor or task. You're free to explore them however you want, even doing things like this:

-
publisher.parent.subscriptions[0].timeout = 1000;
+
actuator.parent.sensors[0].timeout = 1000;
 
-

3.1 hooks

+

1.1.3.1 hooks

-

By default, there are three hook events available:

+

Every component has at least two hook events available:

onInit
-Available in requisitions, publishers and subscriptions. It gets executed as soon as the component is initialized. -As available parameter, an elapsedTime variable is given, counting every milliseconds since the instantiation of this component.

+Available in tasks, actuators and sensors. It gets executed as soon as the component is initialized. +As available parameter, an elapsedTime variable is given, counting every millisecond since the instantiation of this component.

onFinish
-Available in requisitions, publishers and subscriptions. It gets executed when the component is about to finish. -As available parameter, an elapsedTime variable is given, counting every milliseconds since the instantiation of this component.

+Available in tasks, actuators and sensors. It gets executed when the component is about to finish. +As available parameter, an elapsedTime variable is given, counting every millisecond since the instantiation of this component. The onFinish hook also provides the argument executedHooks. A list of strings enumerating which hooks were executed by the componen

+ +

Actuators and sensors also provide custom hooks. Check their documentation to find which ones are available and what arguments they provide.

custom -Depending on the protocol implementation/library/author's mood, the publisher/subscription may have additional hooks. -Such as onError, onResponseReceived, onFileNotFound and onRedirect... -Http-proxy subscription test file is an excellent example, check it out.

+Depending on the protocol implementation/library/author's mood, the actuator/sensor may have additional hooks. +Such as onError, onResponseReceived, onFileNotFound and onRedirect... +Http-proxy sensor test file is an excellent example, check it out.

+ +

A good way to identify that is to run the following command line nqr -p <type>. Like:

+ +
nqr -p http
+
-

3.2 fields

+

available variables +Given that the variables and theirs names may vary according to the scenario, it's interesting to have a special one to retrieve every argument passed to the hook. To retrieve that information, you can use "'argumentNames'" as a regular argument. So, let's say you have this task:

-

Every hook object has 3 properties:

+
onFinish:
+  script: console.log(argumentNames)
+
+ +

You'd get this printed out to the console:

+ +
[ 'task', 'elapsedTime', 'executedHooks' ]
+
+ +

1.1.3.2 fields

+ +

Every hook object has 4 properties:

script
Javascript code snippet executed when the event is triggered. Yeah, I mean it. See it it by yourself. -But be careful, with great power comes great responsibility.

+Be careful, with great power comes great responsibility.

+ +

debug +A boolean value that prints to the console the available arguments and their respective values.

store
-Data to be persisted across requisitions.

+Data to be persisted across tasks.

assertions
Array of assertions. @@ -718,12 +778,12 @@

3.2 fields

assertions: - expect: variableIdentifier - toBeEqualTo: `string value` + toBeEqualTo: "'string value'" onMessageReceived: script: |- message += 3; - console.log(`Message received plus 3 is: ${message}`); + console.log("'Message received plus 3 is: ${message}'"); store: key: message @@ -736,41 +796,41 @@

3.2 fields

toBeGreaterThan: 3
-

3.3 event example

+

1.1.3.3 event example

Check this test file to see it in practice.


-

4 Requisition Flow

+

1.1.4 Task Flow

-

Now that you know what are requisitions, publishers, subscriptions and events. How about seeing how they interact with each other in a fancier way?

+

Now you know what are tasks, actuators, sensors and events. How about seeing how they interact with each other in a fancier way?

enqueuerInstanceFlow


-

5 Configuration File

+

1.1.5 Configuration File

To save yourself some time, a configuration file may be used. Configuration files tell enqueuer which tests will be executed, log-level, and which output test report files should be generated. This file tells how enqueuer should be executed. To run enqueuer with the configuration:

-
$ nqr -c path/to/configuration/file.yml
+
nqr -c path/to/configuration/file.yml
 

or

-
$ nqr --config-file path/to/configuration/file.yml
+
nqr --config-file path/to/configuration/file.yml
 
-

5.1 attributes

+

1.1.5.1 attributes

These are the configuration file attributes:

files
-Requisition file names or glob. Enqueuer runs every file that matches an element value.

+Task file names or glob. Enqueuer runs every file that matches an element value.

files:
 - 1.yml
@@ -779,7 +839,7 @@ 

5.1 attributes

parallel
-Defaults to false. Requisition files should be executed in parallel mode. The requisition file itself is still sequential, but the files are executes in parallel.

+Defaults to false. Task files should be executed in parallel mode. The task file itself is still sequential, but the files are executes in parallel.

parallel: true
 
@@ -797,20 +857,20 @@

5.1 attributes

plugins
-List of in plugins used by the test scenarios. You can check them out or write your own.

+List of in plugins used by the test scenarios. You can check them out or write your own.

plugins:
-- enqueuer-plugin-amqp 
-- enqueuer-plugin-ws 
+- enqueuer-plugin-amqp
+- enqueuer-plugin-ws
 - enqueuer-plugin-mqtt
 - enqueuer-plugin-html-report
 

outputs
-Once enqueuer runs every execution, it compiles a summary and sends it to every publisher listed in output. -An important thing to note is that every available report publisher is available here. -Yes, it means that you are able to send this report through http, tcp, etc. or through a plugin one or a custom one. -You can run $ nqr -p to check available report publishers installed. +Once enqueuer runs every execution, it compiles a summary and sends it to every actuator listed in output. +An important thing to note is that every available report actuator is available here. +Yes, it means that you are able to send this report through http, tcp, etc. or through a plugin or a custom one. +You can run $ nqr -p to check available report actuators installed. Another important thing to note is the format value. By default a json summary is generated, but you can change it to whatever format you would like, such as: Xunit, html You can run $ nqr -f to check available installed formats or even write your own

@@ -829,7 +889,7 @@

5.1 attributes

Values defined here use the 'key: value' pattern and are available to every test scenario throughout the entire execution

store:
-  variableKey: "my value" # Defines 'variableKey' key and its value 'my value'. 
+  variableKey: "my value" # Defines 'variableKey' key and its value 'my value'.
 
   'separated key': 6
 
@@ -839,13 +899,13 @@ 

5.1 attributes

nested: thing
-

5.2 example

+

1.1.5.2 example

Here's a complete example of a configuration file.


-

6 Variables

+

1.1.6 Variables

Providing power and flexibility, enqueuer allows you to use variables placeholder replacement. That's why there is a store field and you'll see a lot of << and {{ being used in the examples files. @@ -856,7 +916,7 @@

6 Variables

Every time enqueuer sees these kind of notations, it searches in its store for a key/value pair like:

-
variableKey: `enqueuer`
+
variableKey: "'enqueuer'"
 

Then, when enqueuer parses the original map, it gets translated to this:

@@ -866,63 +926,63 @@

6 Variables

By default, every ENV_VAR set is loaded automatically to the store. Check this example.

-

6.1 set a variable

+

1.1.6.1 set a variable

There are a few ways to set a value in the store.

-
6.1.1 configuration file
+
1.1.6.1.1 configuration file

Configuration file store object. Set it as you wish, as you can see here

-
6.1.2 command line
+
1.1.6.1.2 command line
-

A command line argument using the key=value format. This way:

+

A command line argument using the key=value format. This way:

-
$ nqr --store key=value -s anotherVariable=true
+
nqr --store key=value -s anotherVariable=true
 
-
6.1.3 event
+
1.1.6.1.3 event set variable
-

Dynamically set it through any event. +

Dynamically set it through any event. Be it in its script field or straight through its store field. Both ways work:

onInit:
     script: store.key = 123;
     store:
-        anotherKey: `another Value` 
+        anotherKey: "'another Value'"
 
-

6.2 use a variable

+

1.1.6.2 use a variable

There are two ways two use a variable:

-
6.2.1 non js code snippet
+
1.1.6.2.1 non-js code snippet

The easiest one is to type <<variableKey>> or {{variableKey}} where you want it to be replaced in a test file, as you can see here

-
6.2.2 js code snippet
+
1.1.6.2.2 js code snippet
-

Using the store object. It's attributes are the keys and their values are their respective values. -Therefore, you're free to use store.variableKey, console.log(store.variableKey); or console.log(2 * store['separated key']); and get them. +

Using the store object. It's attributes are the keys and their values are their respective values. +Therefore, you're free to use store.variableKey, console.log(store.variableKey); or console.log(2
* store['separated key']);
and get them. Like this one.

-

6.3 variables example

+

1.1.6.3 variables example

Check out this test example test to see it working.


-

7 Content File Injection

+

1.1.7 Content File Injection

-

You are able to inject file content into a requisition/publisher/subscription field.

+

You are able to inject file content into a task/actuator/sensor field.

file: <<file://path/to/file.txt>>
 

Other than that, enqueuer can read it and parse its content as an object using this familiar syntax: <<tag://path/to/file?query=value&other=true>>.

-
requisition:
+
task:
     json: <<json://path/to/file.json>>
     yml: <<yml://path/to/file.yml>>
     csv: <<csv://path/to/file.csv?header=true&delimiter=;>>
@@ -932,40 +992,40 @@ 

7 Content File Injecti

Once the object is parsed, your free to use it as a regular object in any event

onInit:
-    script: console.log(requisition.yml.deep.field);
+    script: console.log(task.yml.deep.field);
 onFinish:
     assertions:
     -   expect: json.key
         toBeEqualTo: csv[0].key
 
-

It get's even better. -Due its fantastic plugin architecture design, you can extend its default modules and use any of these plugins or event write your own to parse however you want. -The built-in modules for object parsers are: json, yml, csv and file. +

It gets even better. +Due to its fantastic plugin architecture design, you can extend its default modules and use any of these plugins or event write your own to parse however you want. +The built-in modules for object parsers are: json, yml, csv and file. Run $ nqr -e to see available ones.

-

7.1 example

+

1.1.7.1 content file injection example

Check out this test example test to get a full picture of it.


-

8 Plugins

+

1.1.8 Plugins

You're probably aware by now but it doesn't hurt do emphasize it: enqueuer provides an amazingly powerful plugin extensible architecture. It has several plugins available, but if none of them pleases you, you're free to create your own. -Albeit you don't have to share the one you created, we encourage you to do so. Then go ahead and publish yours to npm and add it to our plugins repository.

+Albeit you don't have to share the one you created, we encourage you to do so. Then go ahead and publish yours to npm and add it to our plugins repository.

-

8.1 plugin types

+

1.1.8.1 plugin types

So far, you're able to extend enqueuer default behavior in four ways. Using a protocol plugin, an object parser plugin, an asserter plugin and using a report formatter plugin.

-
8.1.1 protocol
+
1.1.8.1.1 protocol
-

A protocol plugin enables you to use a different publisher/subscription types. +

A protocol plugin enables you to use a different actuator/sensor types. Run $ nqr -p [protocol-name] to get the full available list:

-
publishers: 
+
actuators:
 -   name:                  custom
 -   name:                  file
 -   name:                  http
@@ -973,7 +1033,7 @@ 
8.1.1 protocol
- name: stdout - name: tcp - ... -subscriptions: +sensors: - name: custom - name: file messageReceivedParams: content, name, size, modified, created @@ -986,30 +1046,30 @@
8.1.1 protocol

Each one listed above has a respective example in the examples folder. -This one, for instance, provides support for amqp protocol, so you can create this publisher and subscription:

+This one, for instance, provides support for amqp protocol, so you can create this actuator and sensor:

-
publishers:
+
actuators:
 -   type: amqp
     payload: enqueuermaniac
     exchange: enqueuer.exchange
     routingKey: enqueuer.integration.test.routing.key
-subscriptions:
+sensors:
 -   type: amqp
     exchange: enqueuer.exchange
     routingKey: enqueuer.integration.test.routing.#
     onMessageReceived:
         assertions:
         -   expect: payload
-            toBeEqualTo: `enqueuermaniac`
+            toBeEqualTo: "'enqueuermaniac'"
 
-
8.1.2 object parser
+
1.1.8.1.2 object parser

An object parser plugin enables you to read and parse files as you wish. This test example demonstrates how to use it, Run $ nqr -e [object-parser-name] to check available ones:

-
parsers: 
+
parsers:
 - yml, yaml
 - json
 - file
@@ -1021,20 +1081,20 @@ 
8.1.2 object parser
xmlContent: <<xml://path/to/xml/file.xml>>
 
-
8.1.3 asserter
+
1.1.8.1.3 asserter

An asserter plugin provides you a nicely way to use different assertions than these built-in ones.

-
asserters: 
--   expect: 
+
asserters:
+-   expect:
         required:    true
         type:        string, array
         description: actual value
-    not: 
+    not:
         required:    false
         type:        null
         description: negates
-    toContain: 
+    toContain:
         required:    true
         type:        string, any
         description: element
@@ -1043,22 +1103,22 @@ 
8.1.3 asserter

Looking at the asserter above, we can create assertions like these:

assertions:
--   expect: [`a`, 1, true]
+-   expect: ["'a'", 1, true]
     not:
-    toContain: `b`
--   expect: [`a`, 1, true]
+    toContain: "'b'"
+-   expect: ["'a'", 1, true]
     toContain: 1
 

Run $ nqr -t to get the full available list. Consider looking at this test example.

-
8.1.4 report formatter
+
1.1.8.1.4 report formatter

A report formatter plugin gives you the ability to export enqueuer reports the way you want. Run $ nqr -f [formatter-name] to list available report formatters:

-
formatters: 
+
formatters:
 - console, stdout
 - json
 - yml, yaml
@@ -1067,7 +1127,7 @@ 
8.1.4 report formatter

Consider looking at the example of configuration file to see it in use. This one, for instance, generates xUnit like reports from enqueuer's output.

-

8.2 plugins list

+

1.1.8.2 plugins list

Enqueuer community offers support to the following plugins:

@@ -1191,6 +1251,20 @@

8.2 plugins list

Enqueuer plugin for redis pub/sub channel support
+
+
+ + +
+ Enqueuer plugin to handle redis as a database +
@@ -1309,62 +1383,62 @@

8.2 plugins list

Want to see yours here too? Write your own and make a PR here.

-

8.3 plugins installation

+

1.1.8.3 plugins installation

In order to enqueuer get awareness that you want to use a plugin, you have to tell it, right? You can tell enqueuer to use a plugin in three different ways: using it as a command line argument, through the configuration file or letting enqueuer finding it in a default location.

-
8.3.1 command line
+
1.1.8.3.1 use plugins via command line

Tell enqueuer to use your plugin through command line this way $ nqr -l <plugin-folder> -l <another-plugin-folder>. Where plugin-folder and another-plugin-folder are the directories where the plugins are installed in.

-
8.3.2 configuration file
+
1.1.8.3.2 use plugins via configuration file

Tell enqueuer to use your plugin through configuration file this way:

-
plugins: 
+
plugins:
 -   plugin-folder
 -   another-plugin-folder
 

Where plugin-folder and another-plugin-folder are the directories where the plugins are installed in.

-
8.3.3 implicitly
+
1.1.8.3.3 implicitly
-

When enqueuer runs, it looks for modules in its same installation directory or in .nqr folder in the home directory, a.k.a. ~/ folder in linux distributions. +

When enqueuer runs, it looks for modules in its same installation directory or in "'.nqr'" folder in the home directory, a.k.a. ~/ folder in linux distributions. Therefore, if you run:

-
$ npm install --global enqueuer
-$ mkdir ~/.nqr
-$ cd ~/.nqr
-$ npm install enqueuer-plugin-amqp
-$ nqr -p amqp
+
npm install --global enqueuer
+mkdir ~/.nqr
+cd ~/.nqr
+npm install enqueuer-plugin-amqp
+nqr -p amqp
 

Or

-
$ npm install --global enqueuer enqueuer-plugin-amqp
-$ nqr -p amqp
+
npm install --global enqueuer enqueuer-plugin-amqp
+nqr -p amqp
 

You'll see that the enqueuer-plugin-amqp plugin will be loaded. Every enqueuer compatible module gets implicitly loaded. -In order to be enqueuer compatible, a module has to have an entryPoint exported function in its main file and, in its package.json file, it has to have either 'enqueuer' or 'nqr' as keywords.

+In order to be enqueuer compatible, a module has to have an entryPoint exported function in its main file and, its package.json file has to have either enqueuer or qr as keywords.

-

9 Stacker

+

1.1.9 Stacker

-

Looking for a really really good looking an human error proof solution way of writing these requisition files?
-Consider taking a look at stacker: open source, cross-platform, multi protocol client testing tool. +

Looking for an human error proof solution way of writing these task files?
+Consider taking a look at stacker: open source, cross-platform, multi protocol client testing tool. The official enqueuer's best friend forever. Do amazing things and change the world with enqueuer’s GUI! -With them, you create, manage and run requisitions and and see their results in a really nice way. -See this amazing beauty with your own eyes to get an idea of how it works:

+With them, you create, manage and run tasks and and see their results in a really nice way. +See this amazing beauty with your own eyes to get an idea of how it works:

-

screenshot-passing

+

screenshot-passing


-

10 Open source

+

1.1.10 Open source

We (by 'we' we mean enqueuer's maintainers not the human race, unfortunately) are very opened to any kind of contributions in general. As long as they make sense and add value to the product, you're free to go. @@ -1372,9 +1446,9 @@

10 Open source

If you like it but don't want to waste time creating a pull request, no problem either. Create an issue, or, even easier, give it a github star. It's cheap and it doesn't hurt anyone. You know what? Just head up to enqueuer's github repo and keep staring at its repo. -It may help somehow.

+It may help somehow.

-

10.1 contributors

+

1.1.10.1 contributors

Thanks goes to these wonderful people:

@@ -1384,17 +1458,17 @@

10.1 contributors

- - - -
+
- + - - - - - - - - - - - - - - - + + + + + + + + + + + + + diff --git a/docs/images/http-result.png b/docs/images/http-result.png index eacd0608..bc00bc92 100644 Binary files a/docs/images/http-result.png and b/docs/images/http-result.png differ diff --git a/docs/images/http.png b/docs/images/http.png index 38b8fdf9..72b2f24f 100644 Binary files a/docs/images/http.png and b/docs/images/http.png differ diff --git a/docs/images/nqrFlow.png b/docs/images/nqrFlow.png index 9992a8fb..e38b37d6 100644 Binary files a/docs/images/nqrFlow.png and b/docs/images/nqrFlow.png differ diff --git a/docs/index.html b/docs/index.html index e3874ae0..0ae187d8 100644 --- a/docs/index.html +++ b/docs/index.html @@ -145,7 +145,7 @@

Polyglot flow testing tool

- + diff --git a/examples/argumentNames.yml b/examples/argumentNames.yml new file mode 100644 index 00000000..91576ad4 --- /dev/null +++ b/examples/argumentNames.yml @@ -0,0 +1,4 @@ +onFinish: + assertions: + - expect: argumentNames + toBeEqualTo: ['task', 'elapsedTime'] diff --git a/examples/assertions.yml b/examples/assertions.yml index d4d52537..2412a17d 100644 --- a/examples/assertions.yml +++ b/examples/assertions.yml @@ -1,60 +1,71 @@ definedVariable: value onInit: - assertions: - - expectToBeDefined: requisition.definedVariable - - expectToBeUndefined: requisition.undefinedVariable - - expectToBeTruthy: true - - expectToBeFalsy: false - - expect: 1 - toBeGreaterThan: 0 - - expect: 1 - not: - toBeGreaterThan: 2 - - expect: 1 - toBeGreaterThanOrEqualTo: 0 - - expect: 1 - not: - toBeGreaterThanOrEqualTo: 2 - - expect: 1 - toBeLessThan: 2 - - expect: 1 - not: - toBeLessThan: 0 - - expect: 1 - toBeLessThanOrEqualTo: 2 - - expect: 1 - not: - toBeLessThanOrEqualTo: 0 - - expect: `enqueuer` - toContain: `queue` - - expect: `enqueuer` - not: - toContain: `nqr` - - expect: [true, 'string', 10] - toContain: `string` - - expect: [true, 'string', 10] - not: - toContain: false - - expect: - deep: - object: - with: - array: - - 1 - - 2 - - 3 - boolean: true - toBeEqualTo: - deep: - boolean: true - object: - with: - array: - - 1 - - 2 - - 3 - - expect: 1 - toBeEqualTo: 1 - - expect: 1 - not: - toBeEqualTo: 0 + assertions: + - expectToBeDefined: task.definedVariable + - expectToBeUndefined: task.undefinedVariable + - expectToBeTruthy: true + - expectToBeFalsy: false + - expect: 1 + toBeGreaterThan: 0 + - expect: 1 + not: + toBeGreaterThan: 2 + - expect: 1 + toBeGreaterThanOrEqualTo: 0 + - expect: 1 + not: + toBeGreaterThanOrEqualTo: 2 + - expect: 1 + toBeLessThan: 2 + - expect: 1 + not: + toBeLessThan: 0 + - expect: 1 + toBeLessThanOrEqualTo: 2 + - expect: 1 + not: + toBeLessThanOrEqualTo: 0 + - expect: "'enqueuer'" + toContain: "'queue'" + - expect: "'enqueuer'" + not: + toContain: "'nqr'" + - expect: [true, 'string', 10] + toContain: "'string'" + - expect: [true, 'string', 10] + not: + toContain: false + - expect: + deep: + object: + with: + array: + - 1 + - 2 + - 3 + boolean: true + toBeEqualTo: + deep: + boolean: true + object: + with: + array: + - 1 + - 2 + - 3 + - expect: 1 + toBeEqualTo: 1 + - expect: 1 + not: + toBeEqualTo: 0 + - expect: "'blue'" + toBeAnyOf: + - red + - green + - blue + - expect: "'yellow'" + not: + toBeAnyOf: + - red + - green + - blue diff --git a/examples/avoid.yml b/examples/avoid.yml index 9b8e0662..d5e168c6 100644 --- a/examples/avoid.yml +++ b/examples/avoid.yml @@ -1,14 +1,14 @@ -subscriptions: -- type: tcp +sensors: + - type: tcp timeout: 2000 port: 23232 avoid: true -- type: HTTP + - type: HTTP endpoint: /not/reachable port: 23084 method: POST timeout: 3000 response: - status: 200 - payload: https + status: 200 + payload: https avoid: true diff --git a/examples/crypto-require.yml b/examples/crypto-require.yml index a9bfcb29..c7b1a018 100644 --- a/examples/crypto-require.yml +++ b/examples/crypto-require.yml @@ -1,16 +1,15 @@ -toEncrypt: `123456789` +toEncrypt: "'123456789'" onInit: - script: |- - const crypto= require('crypto'); + script: |- + const crypto = require('crypto'); - function encryption(payload) { - const hash = crypto.createHash('sha256'); - hash.update(payload, 'utf8'); - return hash.digest('hex'); - } - requisition.toEncrypt = encryption(requisition.toEncrypt); + function encryption(payload) { + const hash = crypto.createHash('sha256'); + hash.update(payload, 'utf8'); + return hash.digest('hex'); + } + task.toEncrypt = encryption(task.toEncrypt); onFinish: - assertions: - - expect: requisition.toEncrypt - toBeEqualTo: `7aad9a1a6a91e0f18c417cb3aa0e0217b283778e636c580509e494eeec1472e0` - + assertions: + - expect: task.toEncrypt + toBeEqualTo: "'68fa8a566bf6c20d2c1f93ab4c71c224032850e7a6fd4c29e59681b3f7937c2f'" diff --git a/examples/custom.yml b/examples/custom.yml index 9d5495cc..62dfca30 100644 --- a/examples/custom.yml +++ b/examples/custom.yml @@ -1,22 +1,22 @@ -publishers: -- type: custom +actuators: + - type: custom module: misc/custom-protocol.js - name: publisher description + name: actuator description serverAddress: localhost port: 23071 payload: it -subscriptions: -- name: subscription description +sensors: + - name: sensor description type: custom port: 23071 module: misc/custom-protocol.js timeout: 1000 onMessageReceived: - script: payload = Buffer.from(payload); - assertions: - - name: Payload - expect: payload - toBeEqualTo: `it` - - name: Remote Adderss - expect: remoteInfo.address - toBeEqualTo: `127.0.0.1` + script: payload = Buffer.from(payload); + assertions: + - name: Payload + expect: payload + toBeEqualTo: "'it'" + - name: Remote Adderss + expect: remoteInfo.address + toBeEqualTo: "'127.0.0.1'" diff --git a/examples/file-placeholder.yml b/examples/file-placeholder.yml index caa1f53f..c59a36fa 100644 --- a/examples/file-placeholder.yml +++ b/examples/file-placeholder.yml @@ -1,22 +1,23 @@ -- csv: <> - json: <> - yml: <> - onInit: - script: requisition.javascript = <> - assertions: - - name: fileRead (yml) - expect: requisition.yml.key - toBeEqualTo: `I'm persisted in config file` - - name: fileRead (.json) - expect: requisition.json.key - toBeEqualTo: `value` - - name: fileRead (.csv) - expect: requisition.csv[1].title - toBeEqualTo: `secondRow` - - name: fileRead (.js) - expect: requisition.javascript(20) - toBeEqualTo: 40 -- onInit: - assertions: - - name: it's not stored - expectToBeUndefined: requisition.yml +- csv: <> + json: <> + yml: <> + onInit: + script: | + task.javascript = <> + assertions: + - name: fileRead (yml) + expect: task.yml.key + toBeEqualTo: "'ymlValue'" + - name: fileRead (.json) + expect: task.json.key + toBeEqualTo: "'value'" + - name: fileRead (.csv) + expect: task.csv[1].title + toBeEqualTo: "'secondRow'" + - name: fileRead (.js) + expect: task.javascript(20) + toBeEqualTo: 40 +- onInit: + assertions: + - name: it's not stored + expectToBeUndefined: task.yml diff --git a/examples/file.yml b/examples/file.yml index 5973d1b2..c9e26baf 100644 --- a/examples/file.yml +++ b/examples/file.yml @@ -1,32 +1,32 @@ timeout: 3000 -publishers: -- type: file - name: publisher description - payload: filePublisher +actuators: + - type: file + name: actuator description + payload: fileActuator filenamePrefix: temp/fileTest filenameExtension: file onInit: - script: publisher.payload=new Date().getTime(); -subscriptions: -- name: subscription description + script: actuator.payload = new Date().getTime().toString(); +sensors: + - name: sensor description type: file-system-watcher timeout: 3000 fileNamePattern: temp/fileTest* onMessageReceived: - script: now = new Date().getTime(); - assertions: - - name: Some time has passed - expect: now - toBeGreaterThanOrEqualTo: content - - name: Filename - expect: name - toContain: `temp/` - - name: Content - expectToBeDefined: content - - name: Created - expectToBeDefined: created - - name: Modified - expectToBeDefined: modified - - name: Size - expect: size - toBeGreaterThan: 0 + script: now = new Date().getTime(); + assertions: + - name: Some time has passed + expect: now + toBeGreaterThanOrEqualTo: content + - name: Filename + expect: name + toContain: "'temp/'" + - name: Content + expectToBeDefined: content + - name: Created + expectToBeDefined: created + - name: Modified + expectToBeDefined: modified + - name: Size + expect: size + toBeGreaterThan: 0 diff --git a/examples/hooks.yml b/examples/hooks.yml index c54bf8b3..b596f6f6 100644 --- a/examples/hooks.yml +++ b/examples/hooks.yml @@ -1,69 +1,69 @@ timeout: 3000 onInit: - store: - sequence: `requisition::onInit` - assertions: - - name: Requisitions exists onInit - expectToBeDefined: requisition.name - - expect: this.name - toBeEqualTo: requisition.name -publishers: -- type: tcp + store: + sequence: "'task::onInit'" + assertions: + - name: Tasks exists onInit + expectToBeDefined: task.name + - expect: this.name + toBeEqualTo: task.name +actuators: + - type: tcp timeout: 2000 serverAddress: localhost port: 23080 payload: it onInit: - script: publisher.name = 'changed'; - store: - sequence: store.sequence + `-> publisher::onInit` - assertions: - - name: Publisher exists onInit - expectToBeDefined: publisher - - name: Publisher name - expectToBeDefined: publisher.name - - expectToBeDefined: publisher - - expectToBeDefined: this.name - - expect: this.name - toBeEqualTo: publisher.name + script: actuator.name = 'changed'; + store: + sequence: store.sequence + "'-> actuator::onInit'" + assertions: + - name: Actuator exists onInit + expectToBeDefined: actuator + - name: Actuator name + expectToBeDefined: actuator.name + - expectToBeDefined: actuator + - expectToBeDefined: this.name + - expect: this.name + toBeEqualTo: actuator.name onMessageReceived: - store: - sequence: store.sequence + `-> publisher::onMessageReceived` - assertions: - - name: Publisher exists onMessageReceived - expectToBeDefined: publisher - - name: Requisition onInit - expect: store.sequence - toContain: `requisition::onInit` - - name: Publisher name changed - expect: publisher.name - toBeEqualTo: `changed` - - name: Publisher onInit executed - expect: store.sequence - toContain: `publisher::onInit` -subscriptions: -- type: tcp + store: + sequence: store.sequence + "'-> actuator::onMessageReceived'" + assertions: + - name: Actuator exists onMessageReceived + expectToBeDefined: actuator + - name: Task onInit + expect: store.sequence + toContain: "'task::onInit'" + - name: Actuator name changed + expect: actuator.name + toBeEqualTo: "'changed'" + - name: Actuator onInit executed + expect: store.sequence + toContain: "'actuator::onInit'" +sensors: + - type: tcp port: 23080 response: hook response onInit: - script: subscription.name = 'changed'; - store: - sequence: store.sequence + `-> subscription::onInit` - assertions: - - name: Subscription exists onInit - expectToBeDefined: subscription - - expect: this.name - toBeEqualTo: subscription.name - - name: Subscription name - expect: subscription.name - toBeEqualTo: `changed` + script: sensor.name = 'changed'; + store: + sequence: store.sequence + "'-> sensor::onInit'" + assertions: + - name: Sensor exists onInit + expectToBeDefined: sensor + - expect: this.name + toBeEqualTo: sensor.name + - name: Sensor name + expect: sensor.name + toBeEqualTo: "'changed'" onMessageReceived: - assertions: - - name: Subscription exists onMessageReceived - expectToBeDefined: subscription - - name: Subscription name changed - expect: subscription.name - toBeEqualTo: `changed` - - name: Subscription onInit executed - expect: store.sequence - toContain: `subscription::onInit` + assertions: + - name: Sensor exists onMessageReceived + expectToBeDefined: sensor + - name: Sensor name changed + expect: sensor.name + toBeEqualTo: "'changed'" + - name: Sensor onInit executed + expect: store.sequence + toContain: "'sensor::onInit'" diff --git a/examples/http-publisher-reuse.nqr b/examples/http-actuator-reuse.nqr similarity index 78% rename from examples/http-publisher-reuse.nqr rename to examples/http-actuator-reuse.nqr index c3649cbc..67e43b08 100644 --- a/examples/http-publisher-reuse.nqr +++ b/examples/http-actuator-reuse.nqr @@ -1,6 +1,6 @@ { "type": "http", - "url": "http://localhost:23075/subscription-reuse", + "url": "http://localhost:23075/sensor-reuse", "method": "POST", "payload": "virgs", "onMessageReceived": { diff --git a/examples/http-auth-basic.yml b/examples/http-auth-basic.yml index 62101df0..6861640c 100644 --- a/examples/http-auth-basic.yml +++ b/examples/http-auth-basic.yml @@ -1,34 +1,34 @@ timeout: 3000 -publishers: -- type: http +actuators: + - type: http url: http://localhost:23068/basic method: POST payload: basic auth headers: - content-type: application/json + content-type: application/json authentication: - basic: - user: user - password: password + basic: + user: user + password: password onMessageReceived: - assertions: - - expect: body - toBeEqualTo: `basic auth response` -subscriptions: -- type: http + assertions: + - expect: body + toBeEqualTo: "'basic auth response'" +sensors: + - type: http endpoint: /basic port: 23068 method: POST timeout: 10000 authentication: - basic: - user: user - password: password + basic: + user: user + password: password response: - status: 200 - payload: basic auth response + status: 200 + payload: basic auth response onMessageReceived: - assertions: - - name: Payload - expect: body - toBeEqualTo: `basic auth` + assertions: + - name: Payload + expect: body + toBeEqualTo: "'basic auth'" diff --git a/examples/http-auth-bearer.yml b/examples/http-auth-bearer.yml index dcc5009c..23480bf5 100644 --- a/examples/http-auth-bearer.yml +++ b/examples/http-auth-bearer.yml @@ -1,33 +1,33 @@ timeout: 3000 -publishers: -- type: http +actuators: + - type: http url: http://localhost:23067/bearer method: POST payload: Rech headers: - content-type: application/json + content-type: application/json authentication: - bearer: - token: bearerToken + bearer: + token: bearerToken onMessageReceived: - assertions: - - name: Body - expect: body - toBeEqualTo: `responsePayload` -subscriptions: -- type: http + assertions: + - name: Body + expect: body + toBeEqualTo: "'responsePayload'" +sensors: + - type: http endpoint: /bearer port: 23067 method: POST timeout: 10000 authentication: - bearer: - token: bearerToken + bearer: + token: bearerToken response: - status: 321 - payload: responsePayload + status: 321 + payload: responsePayload onMessageReceived: - assertions: - - name: Payload - expect: body - toBeEqualTo: `Rech` + assertions: + - name: Payload + expect: body + toBeEqualTo: "'Rech'" diff --git a/examples/http-auth-digest.yml b/examples/http-auth-digest.yml index ae17c855..5d7c9679 100644 --- a/examples/http-auth-digest.yml +++ b/examples/http-auth-digest.yml @@ -1,52 +1,51 @@ timeout: 3000 -publishers: -- type: http +actuators: + - type: http url: http://localhost:23067/digest method: POST payload: Rech headers: - content-type: application/json + content-type: application/json authentication: - digest: - username: guest - password: guest - realm: nqrRealm - method: GET - uri: /digest - opaque: opaque - nonce: 58bac26865505 - nonceCount: 00000001 - clientNonce: 72ae56dde9406045 - qop: auth + digest: + username: guest + password: guest + realm: nqrRealm + method: GET + uri: /digest + opaque: opaque + nonce: 58bac26865505 + nonceCount: 00000001 + clientNonce: 72ae56dde9406045 + qop: auth onMessageReceived: - assertions: - - name: Body - expect: body - toBeEqualTo: `responsePayload` -subscriptions: -- type: http + assertions: + - name: Body + expect: body + toBeEqualTo: "'responsePayload'" +sensors: + - type: http endpoint: /digest port: 23067 method: POST timeout: 10000 authentication: - digest: - username: guest - password: guest - realm: nqrRealm - method: GET - uri: /digest - opaque: opaque - nonce: 58bac26865505 - nonceCount: 00000001 - clientNonce: 72ae56dde9406045 - qop: auth + digest: + username: guest + password: guest + realm: nqrRealm + method: GET + uri: /digest + opaque: opaque + nonce: 58bac26865505 + nonceCount: 00000001 + clientNonce: 72ae56dde9406045 + qop: auth response: - status: 321 - payload: responsePayload + status: 321 + payload: responsePayload onMessageReceived: - assertions: - - name: Payload - expect: body - toBeEqualTo: `Rech` - + assertions: + - name: Payload + expect: body + toBeEqualTo: "'Rech'" diff --git a/examples/http-more-examples.yml b/examples/http-more-examples.yml index 69ae0e4c..952a2f3a 100644 --- a/examples/http-more-examples.yml +++ b/examples/http-more-examples.yml @@ -1,137 +1,30 @@ -- timeout: 3000 - publishers: - - type: http - url: http://localhost:23075/enqueuer/idStuff?query=2345 - method: POST - payload: - enqueuer: virgs - headers: - content-type: application/json - nqr: publisher - onResponseReceived: - assertions: - - name: Status Code - expect: statusCode - toBeEqualTo: 321 - - name: Body - expect: body - toBeEqualTo: `dynamically changed payload` - - name: Header - expect: headers.nqr - toBeEqualTo: `subscription` - - type: http - url: http://localhost:23075/samePort - method: POST - payload: virgs - onResponseReceived: - assertions: - - name: Status Code - expect: statusCode - toBeEqualTo: 444 - - expect: JSON.parse(body).deep - toBeEqualTo: `object` - subscriptions: - - type: http - endpoint: /enqueuer/:firstId - port: 23075 - method: POST - timeout: 10000 - response: - headers: - nqr: subscription - status: 321 - payload: responsePayload - onMessageReceived: - script: |- - subscription.response.payload = 'dynamically changed payload' - assertions: - - name: Payload - expect: JSON.parse(body).enqueuer - toBeEqualTo: `virgs` - - name: Params - expect: params.firstId - toBeEqualTo: `idStuff` - - name: Query - expect: query.query - toBeEqualTo: 2345 - - name: Header - expect: headers.nqr - toBeEqualTo: `publisher` - - name: same port - type: http - endpoint: /samePort - port: 23075 - method: POST - response: - status: 444 - payload: - deep: object - - name: yet another, but avoidable - type: http - endpoint: /avoidable - port: 23075 - method: POST - avoid: true - timeout: 300 -- timeout: 5000 - publishers: - - name: publisher description - type: http - url: http://localhost:23075/enqueuer/idStuff?query=111 - method: POST - payload: - duplicated: true - headers: - content-type: application/json - onResponseReceived: - assertions: - - name: Status Code - expect: statusCode - toBeEqualTo: 321 - - name: Body - expect: body - toBeEqualTo: `duplicatedResponsePayload` - subscriptions: - - type: http - endpoint: /enqueuer/:secondId - port: 23075 - method: POST - timeout: 10000 - response: - status: 321 - payload: duplicatedResponsePayload - onMessageReceived: - assertions: - - name: Payload - expectToBeTruthy: JSON.parse(body).duplicated -- name: requisition 2 (port 23076) - timeout: 5000 - subscriptions: - - name: subscription description - type: http - endpoint: /differentPort - port: 23076 - method: POST - timeout: 1000 - avoid: true -- name: check port releasing (23076) - timeout: 5000 - subscriptions: - - type: tcp - port: 23076 - timeout: 1000 - avoid: true -- publishers: - - type: http - url: http://localhost:23080/number-payload - method: POST - payload: virgs - subscriptions: - - name: numbered payload - type: http - endpoint: /number-payload - port: 23080 - method: POST - response: - status: 444 - payload: 4.45 +- name: task 2 (port 23076) + timeout: 5000 + sensors: + - name: sensor description + type: http + endpoint: /differentPort + port: 23076 + method: POST + timeout: 1000 + avoid: true +- name: check port releasing (23076) + timeout: 5000 + sensors: + - type: tcp + port: 23076 + timeout: 1000 + avoid: true +- actuators: + - type: http + url: http://localhost:23095/number-payload + method: POST + payload: virgs + sensors: + - type: http + endpoint: /number-payload + port: 23095 + method: POST + response: + status: 444 + payload: 4.45 diff --git a/examples/http-parallel.yml b/examples/http-parallel.yml index 386d1393..7ba3c4a8 100644 --- a/examples/http-parallel.yml +++ b/examples/http-parallel.yml @@ -1,36 +1,43 @@ parallel: true -requisitions: -- iterations: 2 - subscriptions: - - type: http +timeout: 10000 +tasks: + - iterations: 2 + timeout: 10000 + sensors: + - type: http + timeout: 10000 endpoint: /first port: 23023 response: - status: 200 - payload: first -- iterations: 2 - subscriptions: - - type: http + status: 200 + payload: first + - iterations: 2 + timeout: 10000 + sensors: + - type: http + timeout: 10000 endpoint: /second port: 23023 response: - status: 200 - payload: second -- iterations: 2 + status: 200 + payload: second + - iterations: 2 delay: 200 - publishers: - - type: http + timeout: 10000 + actuators: + - type: http url: http://localhost:23023/first onResponseReceived: - assertions: - - expect: statusCode - toBeEqualTo: 200 -- iterations: 2 + assertions: + - expect: statusCode + toBeEqualTo: 200 + - iterations: 2 delay: 200 - publishers: - - type: http + timeout: 10000 + actuators: + - type: http url: http://localhost:23023/second onResponseReceived: - assertions: - - expect: statusCode - toBeEqualTo: 200 + assertions: + - expect: statusCode + toBeEqualTo: 200 diff --git a/examples/http-proxy.yml b/examples/http-proxy.yml index 3fba882f..9baef166 100644 --- a/examples/http-proxy.yml +++ b/examples/http-proxy.yml @@ -1,49 +1,49 @@ -publishers: -- name: publisher proxy +actuators: + - name: actuator proxy type: http url: http://localhost:23085/proxy/enqueuer/123456?query=proxied - method: PATCH + method: put payload: original onMessageReceived: - assertions: - - expect: statusCode - toBeEqualTo: 400 - - expect: body - toBeEqualTo: `original -> proxy -> real -> proxied again` -subscriptions: -- name: proxy subscription + assertions: + - expect: statusCode + toBeEqualTo: 400 + - expect: body + toBeEqualTo: "'original -> proxy -> real -> proxied again'" +sensors: + - name: proxy sensor type: http-proxy - endpoint: /proxy + endpoint: /proxy/enqueuer/:id port: 23085 - method: PATCH + method: put redirect: - url: http://localhost:23086/real - method: POST + url: http://localhost:23086/real/enqueuer/999?query=proxied + method: POST onOriginalMessageReceived: - script: this.redirect.payload = body + ` -> proxy`; - assertions: - - expect: body - toBeEqualTo: `original` + script: this.redirect.payload = body + " -> proxy"; + assertions: + - expect: body + toBeEqualTo: "'original'" onMessageReceived: - script: this.response.payload = body + ` -> proxied again`; this.response.status = 2 * statusCode; - assertions: - - expect: body - toBeEqualTo: `original -> proxy -> real` - - expect: statusCode - toBeEqualTo: 200 -- name: real + script: this.response.payload = body + "' -> proxied again'"; this.response.status = 2 * statusCode; + assertions: + - expect: body + toBeEqualTo: "'original -> proxy -> real'" + - expect: statusCode + toBeEqualTo: 200 + - name: real type: http endpoint: /real/enqueuer/:id port: 23086 method: POST response: - status: 200 + status: 200 onMessageReceived: - script: this.response.payload = body + ' -> real'; - assertions: - - expect: body - toBeEqualTo: `original -> proxy` - - expect: params.id - toBeEqualTo: 123456 - - expect: query.query - toBeEqualTo: `proxied` + script: this.response.payload = body + ' -> real'; + assertions: + - expect: body + toBeEqualTo: "'original -> proxy'" + - expect: params.id + toBeEqualTo: 999 + - expect: query.query + toBeEqualTo: "'proxied'" diff --git a/examples/http-same-port.yml b/examples/http-same-port.yml new file mode 100644 index 00000000..7e191f1d --- /dev/null +++ b/examples/http-same-port.yml @@ -0,0 +1,88 @@ +- timeout: 3000 + actuators: + - type: http + url: http://localhost:23065/enqueuer/idStuff?query=2345 + method: POST + payload: + enqueuer: virgs + headers: + content-type: application/json + nqr: actuator + onResponseReceived: + assertions: + - name: Status Code + expect: statusCode + toBeEqualTo: 321 + - name: Body + expect: body + toBeEqualTo: "'dynamically changed payload'" + - name: Header + expect: headers.nqr + toBeEqualTo: "'sensor'" + sensors: + - type: http + endpoint: /enqueuer/:firstId + port: 23065 + method: POST + timeout: 10000 + response: + headers: + nqr: sensor + connection: close + status: 321 + payload: responsePayload + onMessageReceived: + script: |- + sensor.response.payload = 'dynamically changed payload' + assertions: + - name: Payload + expect: JSON.parse(body).enqueuer + toBeEqualTo: "'virgs'" + - name: Params + expect: params.firstId + toBeEqualTo: "'idStuff'" + - name: Query + expect: query.query + toBeEqualTo: 2345 + - name: Header + expect: headers.nqr + toBeEqualTo: "'actuator'" + - name: avoidable + type: http + endpoint: /avoidable + port: 23065 + method: POST + avoid: true + timeout: 200 +- timeout: 35000 + actuators: + - name: actuator description + type: http + url: http://localhost:23065/enqueuer/idStuff?query=111 + method: POST + payload: + duplicated: true + headers: + content-type: application/json + onResponseReceived: + assertions: + - name: Status Code + expect: statusCode + toBeEqualTo: 321 + - name: Body + expect: body + toBeEqualTo: "'duplicatedResponsePayload'" + sensors: + - name: fourth + type: http + endpoint: /enqueuer/:secondId + port: 23065 + method: POST + timeout: 50000 + response: + status: 321 + payload: duplicatedResponsePayload + onMessageReceived: + assertions: + - name: Payload + expectToBeTruthy: JSON.parse(body).duplicated diff --git a/examples/http-subscription-reuse.nqr b/examples/http-sensor-reuse.nqr similarity index 76% rename from examples/http-subscription-reuse.nqr rename to examples/http-sensor-reuse.nqr index d9df5888..840e36e6 100644 --- a/examples/http-subscription-reuse.nqr +++ b/examples/http-sensor-reuse.nqr @@ -1,6 +1,6 @@ { "type": "http", - "endpoint": "/subscription-reuse", + "endpoint": "/sensor-reuse", "port": 23075, "method": "POST", "response": { diff --git a/examples/http.yml b/examples/http.yml index ba4570ad..83b55e8f 100644 --- a/examples/http.yml +++ b/examples/http.yml @@ -1,24 +1,31 @@ -publishers: -- type: http +actuators: + - type: http url: http://localhost:23075/resource method: POST + headers: + requestHeaderKey: requestHeaderValue payload: enqueuer onResponseReceived: - assertions: - - expect: statusCode - toBeGreaterThan: 400 - - expect: body - toBeEqualTo: "'blah'" -subscriptions: -- type: http + assertions: + - expect: statusCode + toBeGreaterThan: 400 + - expect: body + toBeEqualTo: "'blah'" + - expect: headers.responseheaderkey + toBeEqualTo: "'responseHeaderValue'" +sensors: + - type: http endpoint: /resource port: 23075 method: POST response: - status: 444 - payload: blah + headers: + responseHeaderKey: responseHeaderValue + status: 444 + payload: blah onMessageReceived: - assertions: - - expect: body - toContain: "'queue'" - + assertions: + - expect: body + toContain: "'queue'" + - expect: headers.requestheaderkey + toBeEqualTo: "'requestHeaderValue'" diff --git a/examples/https.yml b/examples/https.yml index f96fa2f9..8e4df825 100644 --- a/examples/https.yml +++ b/examples/https.yml @@ -1,34 +1,32 @@ -timeout: 3000 -publishers: -- type: HTTPS - url: https://localhost:4430/enqueuer +actuators: + - type: HTTPS + url: https://127.0.0.1:4430/enqueuer method: POST payload: - https: works! + https: works! headers: - content-type: application/json + content-type: application/json onMessageReceived: - assertions: - - name: Status Code - expect: statusCode - toBeEqualTo: 200 - - name: Body - expect: body - toBeEqualTo: `https` -subscriptions: -- type: https + assertions: + - name: Status Code + expect: statusCode + toBeEqualTo: 200 + - name: Body + expect: body + toBeEqualTo: "'https'" +sensors: + - type: https endpoint: /enqueuer port: 4430 method: POST - timeout: 3000 - credentials: - key: <> - cert: <> + credentials: # Mandatory fields of https connections + cert: <> # read from the config file + key: <> # read from the config file response: - status: 200 - payload: https + status: 200 + payload: https onMessageReceived: - assertions: - - name: Https payload - expect: JSON.parse(body).https - toBeEqualTo: `works!` + assertions: + - name: Https payload + expect: JSON.parse(body).https + toBeEqualTo: "'works!'" diff --git a/examples/ignore.yml b/examples/ignore.yml index 62c0745e..67a26866 100644 --- a/examples/ignore.yml +++ b/examples/ignore.yml @@ -1,47 +1,47 @@ -- publishers: - - ignore: true - filename: /dev/null - type: file - onInit: - assertions: - - expectToBeTruthy: false - onFinish: - assertions: - - expectToBeTruthy: false -- subscriptions: - - ignore: true - type: file - onInit: - assertions: - - expectToBeTruthy: false - onFinish: - assertions: - - expectToBeTruthy: false -- ignore: true - requisitions: - - onInit: - assertions: - - expectToBeTruthy: false - onFinish: - assertions: - - expectToBeTruthy: false - publishers: - - onInit: - assertions: - - expectToBeTruthy: false - onFinish: - assertions: - - expectToBeTruthy: false - subscriptions: - - onInit: - assertions: - - expectToBeTruthy: false - onFinish: - assertions: - - expectToBeTruthy: false - onInit: - assertions: - - expectToBeTruthy: false - onFinish: - assertions: - - expectToBeTruthy: false +- actuators: + - ignore: true + filename: /dev/null + type: file + onInit: + assertions: + - expectToBeTruthy: false + onFinish: + assertions: + - expectToBeTruthy: false +- sensors: + - ignore: true + type: file + onInit: + assertions: + - expectToBeTruthy: false + onFinish: + assertions: + - expectToBeTruthy: false +- ignore: true + tasks: + - onInit: + assertions: + - expectToBeTruthy: false + onFinish: + assertions: + - expectToBeTruthy: false + actuators: + - onInit: + assertions: + - expectToBeTruthy: false + onFinish: + assertions: + - expectToBeTruthy: false + sensors: + - onInit: + assertions: + - expectToBeTruthy: false + onFinish: + assertions: + - expectToBeTruthy: false + onInit: + assertions: + - expectToBeTruthy: false + onFinish: + assertions: + - expectToBeTruthy: false diff --git a/examples/import.yml b/examples/import.yml index bab7402b..2a6d6e52 100644 --- a/examples/import.yml +++ b/examples/import.yml @@ -1,33 +1,32 @@ -- name: static importRequisition - import: <> - onFinish: +- name: static importTask + import: <> + onFinish: + assertions: + - expectToBeTruthy: task.imported +- name: dynamic importTask + priority: lower + import: + priority: higher + onInit: + script: task.dynamicallyImported = true; + onFinish: + assertions: + - expectToBeTruthy: task.dynamicallyImported + - expect: task.priority + toBeEqualTo: "'higher'" +- actuators: + - import: <> + onFinish: assertions: - - expectToBeTruthy: requisition.imported -- name: dynamic importRequisition - priority: lower - import: - priority: higher - onInit: - script: requisition.dynamicallyImported = true; - onFinish: + - expect: actuator.type + toBeEqualTo: "'http'" + - expect: actuator.method + toBeEqualTo: "'POST'" + sensors: + - import: <> + onFinish: assertions: - - expectToBeTruthy: requisition.dynamicallyImported - - expect: requisition.priority - toBeEqualTo: `higher` -- publishers: - - import: <> - onFinish: - assertions: - - expect: publisher.type - toBeEqualTo: `http` - - expect: publisher.method - toBeEqualTo: `POST` - subscriptions: - - import: <> - onFinish: - assertions: - - expect: subscription.type - toBeEqualTo: `http` - - expect: subscription.endpoint - toBeEqualTo: `/subscription-reuse` - + - expect: sensor.type + toBeEqualTo: "'http'" + - expect: sensor.endpoint + toBeEqualTo: "'/sensor-reuse'" diff --git a/examples/no-tests.yml b/examples/no-tests.yml index a195b3cb..22c66d61 100644 --- a/examples/no-tests.yml +++ b/examples/no-tests.yml @@ -1,3 +1,3 @@ delay: 100 -requisitions: -- delay: 100 +tasks: + - delay: 100 diff --git a/examples/number.json b/examples/number.json index 09f74e40..e127465d 100644 --- a/examples/number.json +++ b/examples/number.json @@ -3,11 +3,11 @@ "onInit": { "assertions": [ { - "expect": "typeof requisition.number", + "expect": "typeof task.number", "toBeEqualTo": "'number'" }, { - "expect": "requisition.number", + "expect": "task.number", "toBeEqualTo": 123 } ] diff --git a/examples/parallel-requisition.yml b/examples/parallel-requisition.yml deleted file mode 100644 index 0b06be08..00000000 --- a/examples/parallel-requisition.yml +++ /dev/null @@ -1,33 +0,0 @@ -tollerance: 100 -requisitions: -- parallel: true - requisitions: - - delay: 200 - onInit: - script: requisition.startTime = new Date(); - - delay: 300 - onInit: - script: requisition.startTime = new Date(); - onFinish: - assertions: - - name: started at the same time - expect: Math.abs(requisition.requisitions[1].startTime.getTime() - requisition.requisitions[0].startTime.getTime()) - toBeLessThanOrEqualTo: requisition.parent.tollerance - - expect: elapsedTime - toBeLessThanOrEqualTo: 300 + requisition.parent.tollerance -- parallel: false - requisitions: - - delay: 200 - onInit: - script: requisition.startTime = new Date(); - - delay: 300 - onInit: - script: |- - script: requisition.startTime = new Date(); - onFinish: - assertions: - - name: started after the other - expect: requisition.requisitions[1].startTime.getTime() - requisition.requisitions[0].startTime.getTime() - toBeGreaterThanOrEqualTo: requisition.requisitions[0].delay - - expect: elapsedTime - toBeLessThanOrEqualTo: 200 + 300 + requisition.parent.tollerance diff --git a/examples/parallel-task.yml b/examples/parallel-task.yml new file mode 100644 index 00000000..dcaaf557 --- /dev/null +++ b/examples/parallel-task.yml @@ -0,0 +1,33 @@ +tollerance: 100 +tasks: + - parallel: true + tasks: + - delay: 200 + onInit: + script: task.startTime = new Date(); + - delay: 300 + onInit: + script: task.startTime = new Date(); + onFinish: + assertions: + - name: started at the same time + expect: Math.abs(task.tasks[1].startTime.getTime() - task.tasks[0].startTime.getTime()) + toBeLessThanOrEqualTo: task.parent.tollerance + - expect: elapsedTime + toBeLessThanOrEqualTo: 300 + task.parent.tollerance + - parallel: false + tasks: + - delay: 200 + onInit: + script: task.startTime = new Date(); + - delay: 300 + onInit: + script: |- + script: task.startTime = new Date(); + onFinish: + assertions: + - name: started after the other + expect: task.tasks[1].startTime.getTime() - task.tasks[0].startTime.getTime() + toBeGreaterThanOrEqualTo: task.tasks[0].delay + - expect: elapsedTime + toBeLessThanOrEqualTo: 200 + 300 + task.parent.tollerance diff --git a/examples/parallel-test-actuator.yml b/examples/parallel-test-actuator.yml new file mode 100644 index 00000000..1778a19c --- /dev/null +++ b/examples/parallel-test-actuator.yml @@ -0,0 +1,12 @@ +delay: 1000 +timeout: 10000 +actuators: + - type: tcp + serverAddress: 127.0.0.1 + port: 23081 + payload: anything + onMessageReceived: + assertions: + - name: Back and forth + expect: payload + toBeEqualTo: "'anyValue'" diff --git a/examples/parallel-test-publisher.yml b/examples/parallel-test-publisher.yml deleted file mode 100644 index a4f7a85d..00000000 --- a/examples/parallel-test-publisher.yml +++ /dev/null @@ -1,12 +0,0 @@ -delay: 500 -timeout: 3000 -publishers: -- type: tcp - serverAddress: localhost - port: 23081 - payload: anything - onMessageReceived: - assertions: - - name: Back and forth - expect: payload - toBeEqualTo: `anyValue` diff --git a/examples/parallel-test-sensor.yml b/examples/parallel-test-sensor.yml new file mode 100644 index 00000000..de37f809 --- /dev/null +++ b/examples/parallel-test-sensor.yml @@ -0,0 +1,11 @@ +timeout: 10000 +sensors: + - type: tcp + timeout: 10000 + port: 23081 + response: anyValue + onMessageReceived: + assertions: + - name: Paralleling + expect: payload + toBeEqualTo: "'anything'" diff --git a/examples/parallel-test-subscription.yml b/examples/parallel-test-subscription.yml deleted file mode 100644 index ecca7082..00000000 --- a/examples/parallel-test-subscription.yml +++ /dev/null @@ -1,10 +0,0 @@ -timeout: 3000 -subscriptions: -- type: tcp - port: 23081 - response: anyValue - onMessageReceived: - assertions: - - name: Paralleling - expect: payload - toBeEqualTo: `anything` diff --git a/examples/parent.yml b/examples/parent.yml index cddba9b9..01c9f038 100644 --- a/examples/parent.yml +++ b/examples/parent.yml @@ -1,12 +1,11 @@ -- file: <> - delay: 0 # makes this a valid requisition -- subscriptions: - - type: tcp - timeout: 100 - port: 23700 - avoid: true - onInit: - assertions: - - expect: subscription.parent.parent.requisitions[0].file.length - toBeEqualTo: 10 - +- file: <> + delay: 0 # makes this a valid task +- sensors: + - type: tcp + timeout: 100 + port: 23700 + avoid: true + onInit: + assertions: + - expect: sensor.parent.parent.tasks[0].file.length + toBeEqualTo: 10 diff --git a/examples/readme-enqueuer-repo-hit.yml b/examples/readme-enqueuer-repo-hit.yml index d11f2b5c..b3a8f5da 100644 --- a/examples/readme-enqueuer-repo-hit.yml +++ b/examples/readme-enqueuer-repo-hit.yml @@ -1,7 +1,7 @@ -publishers: -- type: http +actuators: + - type: http url: https://github.com/enqueuer-land/enqueuer onResponseReceived: - assertions: - - expect: statusCode - toBeEqualTo: 200 + assertions: + - expect: statusCode + toBeEqualTo: 200 diff --git a/examples/recursion.yml b/examples/recursion.yml index f7758d10..6ca42557 100644 --- a/examples/recursion.yml +++ b/examples/recursion.yml @@ -1,14 +1,14 @@ -- onInit: - store: - counter: 0 -- iterations: 3 - requisitions: - - iterations: 3 - requisitions: - - iterations: 3 - onInit: - script: ++store.counter; -- onInit: - assertions: - - expect: store.counter - toBeEqualTo: 3*3*3 +- onInit: + store: + counter: 0 +- iterations: 3 + tasks: + - iterations: 3 + tasks: + - iterations: 3 + onInit: + script: ++store.counter; +- onInit: + assertions: + - expect: store.counter + toBeEqualTo: 3*3*3 diff --git a/examples/requisition-delay-iterations.yml b/examples/requisition-delay-iterations.yml deleted file mode 100644 index 26c3981e..00000000 --- a/examples/requisition-delay-iterations.yml +++ /dev/null @@ -1,19 +0,0 @@ -- name: iterations - counter: 0 - iterations: 10 - onInit: - script: ++requisition.counter; -- name: delayed - delay: 3000 - onInit: - script: requisition.startTime = new Date(); - onFinish: - assertions: - - name: It was executed 10 times - expect: requisition.parent.requisitions[0].counter - toBeEqualTo: 10 - - name: Elapsed time - expect: elapsedTime - toBeGreaterThanOrEqualTo: 2950 - - expect: new Date().getTime() - requisition.startTime.getTime() - toBeGreaterThanOrEqualTo: 2950 diff --git a/examples/requisition-navigation.yaml b/examples/requisition-navigation.yaml deleted file mode 100644 index 0c251a8f..00000000 --- a/examples/requisition-navigation.yaml +++ /dev/null @@ -1,10 +0,0 @@ -- name: first - onInit: - assertions: - - expect: requisition.parent.requisitions[1].name - toBeEqualTo: `second` -- name: second - onInit: - assertions: - - expect: requisition.parent.requisitions[0].name - toBeEqualTo: `first` diff --git a/examples/reuse.yml.nqr b/examples/reuse.yml.nqr index 559fd77a..2bb2d2c6 100644 --- a/examples/reuse.yml.nqr +++ b/examples/reuse.yml.nqr @@ -1,2 +1,2 @@ onInit: - script: requisition.imported = true; + script: task.imported = true; diff --git a/examples/skipped.yml b/examples/skipped.yml index 1c36be25..7a145e30 100644 --- a/examples/skipped.yml +++ b/examples/skipped.yml @@ -1,36 +1,36 @@ -- iterations: <> - onInit: - script: |- - if (!store.skippedExecutions) { - store.skippedExecutions = 0; - } - ++store.skippedExecutions; - store: - skipped: 0 -- onInit: - assertions: - - expect: store.skippedExecutions - toBeEqualTo: 5 -- iterations: 1 === 0 - onInit: - assertions: - - expectToBeTruthy: false -- iterations: -1 - onInit: - assertions: - - expectToBeTruthy: false -- iterations: 0 - onInit: - assertions: - - expectToBeTruthy: false -- iterations: `1` - onInit: - assertions: - - expectToBeTruthy: true -- iterations: 1 === 1 - onInit: - store: - executed: true -- onInit: - assertions: - - expectToBeDefined: store.executed +- iterations: <> + onInit: + script: |- + if (!store.skippedExecutions) { + store.skippedExecutions = 0; + } + ++store.skippedExecutions; + store: + skipped: 0 +- onInit: + assertions: + - expect: store.skippedExecutions + toBeEqualTo: 5 +- iterations: 1 === 0 + onInit: + assertions: + - expectToBeTruthy: false +- iterations: -1 + onInit: + assertions: + - expectToBeTruthy: false +- iterations: 0 + onInit: + assertions: + - expectToBeTruthy: false +- iterations: "'1'" + onInit: + assertions: + - expectToBeTruthy: true +- iterations: 1 === 1 + onInit: + store: + executed: true +- onInit: + assertions: + - expectToBeDefined: store.executed diff --git a/examples/ssl.yml b/examples/ssl.yml index fa07b71a..af3edae3 100644 --- a/examples/ssl.yml +++ b/examples/ssl.yml @@ -1,47 +1,47 @@ -- publishers: - - type: ssl - port: 23082 - serverAddress: localhost - saveStream: sslFirst - options: - cert: <> - rejectUnauthorized: false - timeout: 2000 - payload: secureMessage - onMessageReceived: - assertions: - - expect: payload - toBeEqualTo: `hisecureResponse` - subscriptions: - - type: ssl - port: 23082 - saveStream: sslSecond - options: - key: <> - cert: <> - rejectUnauthorized: true - response: secureResponse - greeting: hi - timeout: 2000 - onMessageReceived: - assertions: - - expect: payload - toBeEqualTo: `secureMessage` -- publishers: - - type: ssl - loadStream: sslFirst - timeout: 2000 - payload: reusingSecureMessage - onMessageReceived: - assertions: - - expect: payload - toBeEqualTo: `reusingSecureResponse` - subscriptions: - - type: ssl - loadStream: sslSecond - response: reusingSecureResponse - timeout: 2000 - onMessageReceived: - assertions: - - expect: payload - toBeEqualTo: `reusingSecureMessage` +- actuators: + - type: ssl + port: 23082 + serverAddress: localhost + saveStream: sslFirst + options: + cert: <> + rejectUnauthorized: false + timeout: 2000 + payload: secureMessage + onMessageReceived: + assertions: + - expect: payload + toBeEqualTo: "'hisecureResponse'" + sensors: + - type: ssl + port: 23082 + saveStream: sslSecond + options: + key: <> + cert: <> + rejectUnauthorized: true + response: secureResponse + greeting: hi + timeout: 2000 + onMessageReceived: + assertions: + - expect: payload + toBeEqualTo: "'secureMessage'" +- actuators: + - type: ssl + loadStream: sslFirst + timeout: 2000 + payload: reusingSecureMessage + onMessageReceived: + assertions: + - expect: payload + toBeEqualTo: "'reusingSecureResponse'" + sensors: + - type: ssl + loadStream: sslSecond + response: reusingSecureResponse + timeout: 2000 + onMessageReceived: + assertions: + - expect: payload + toBeEqualTo: "'reusingSecureMessage'" diff --git a/examples/stdin.yml b/examples/stdin.yml index afbb85ae..011b8aea 100644 --- a/examples/stdin.yml +++ b/examples/stdin.yml @@ -1,10 +1,10 @@ -subscriptions: -- name: subscription description +sensors: + - name: sensor description id: anyIdTest type: standard-input timeout: 3000 onMessageReceived: - assertions: - - name: payload - expect: message - toBeEqualTo: `enqueuer standard-input payload` + assertions: + - name: payload + expect: message + toBeEqualTo: "'enqueuer standard-input payload'" diff --git a/examples/store.yml b/examples/store.yml index 04b809b3..bcc31751 100644 --- a/examples/store.yml +++ b/examples/store.yml @@ -1,16 +1,16 @@ -- onInit: - store: - value: 123 -- onFinish: - script: ++store.value - assertions: - - name: Payload - expect: <> - toBeEqualTo: 123 - - name: Environment Variables added - expectToBeDefined: store.PATH -- onFinish: - assertions: - - name: Payload - expect: <> - toBeEqualTo: 124 +- onInit: + store: + value: 123 +- onFinish: + script: ++store.value + assertions: + - name: Payload + expect: <> + toBeEqualTo: 123 + - name: Environment Variables added + expectToBeDefined: store.PATH +- onFinish: + assertions: + - name: Payload + expect: <> + toBeEqualTo: 124 diff --git a/examples/task-delay-iterations.yml b/examples/task-delay-iterations.yml new file mode 100644 index 00000000..2e36605c --- /dev/null +++ b/examples/task-delay-iterations.yml @@ -0,0 +1,19 @@ +- name: iterations + counter: 0 + iterations: 10 + onInit: + script: ++task.counter; +- name: delayed + delay: 3000 + onInit: + script: task.startTime = new Date(); + onFinish: + assertions: + - name: It was executed 10 times + expect: task.parent.tasks[0].counter + toBeEqualTo: 10 + - name: Elapsed time + expect: elapsedTime + toBeGreaterThanOrEqualTo: 2950 + - expect: new Date().getTime() - task.startTime.getTime() + toBeGreaterThanOrEqualTo: 2950 diff --git a/examples/task-navigation.yaml b/examples/task-navigation.yaml new file mode 100644 index 00000000..70b39b9d --- /dev/null +++ b/examples/task-navigation.yaml @@ -0,0 +1,10 @@ +- name: first + onInit: + assertions: + - expect: task.parent.tasks[1].name + toBeEqualTo: "'second'" +- name: second + onInit: + assertions: + - expect: task.parent.tasks[0].name + toBeEqualTo: "'first'" diff --git a/examples/tcp.yml b/examples/tcp.yml index d91ddaed..b21446cf 100644 --- a/examples/tcp.yml +++ b/examples/tcp.yml @@ -1,63 +1,65 @@ -- timeout: 3000 - publishers: - - type: tcp - timeout: 1000 - serverAddress: localhost - port: 23069 - payload: Hey Jude - onMessageReceived: - assertions: - - expect: payload - toBeEqualTo: `Do not make it bad` - subscriptions: - - type: tcp - port: 23069 - timeout: 1000 - response: Do not make it bad - onMessageReceived: - assertions: - - expect: payload - toBeEqualTo: `Hey Jude` - - expect: stream.address - toContain: `127.0.0.1` -- timeout: 3000 - publishers: - - type: tcp - timeout: 800 - serverAddress: localhost - port: 23070 - payload: I do not care - saveStream: tcpPublisherSocket - onMessageReceived: - assertions: - - expect: payload - toBeEqualTo: `EnqueuerRocks` - subscriptions: - - type: tcp - port: 23070 - timeout: 500 - saveStream: tcpSubscriptionSocket - greeting: Enqueuer - response: Rocks - onMessageReceived: - assertions: - - expect: payload - toBeEqualTo: `I do not care` -- timeout: 3000 - publishers: - - type: tcp - payload: The socket is still open - loadStream: tcpPublisherSocket - onMessageReceived: - assertions: - - expect: payload - toBeEqualTo: `<>` - subscriptions: - - type: tcp - loadStream: tcpSubscriptionSocket - response: <> - timeout: 1000 - onMessageReceived: - assertions: - - expect: payload - toBeEqualTo: `The socket is still open` +- timeout: 3000 + actuators: + - type: tcp + timeout: 1000 + serverAddress: localhost + port: 23069 + payload: Hey Jude + onMessageReceived: + assertions: + - expect: payload + toBeEqualTo: "'Do not make it bad'" + sensors: + - type: tcp + port: 23069 + timeout: 1000 + response: Do not make it bad + onMessageReceived: + assertions: + - expect: payload + toBeEqualTo: "'Hey Jude'" + - expect: stream.address + toBeAnyOf: + - "'127.0.0.1'" + - ::1 +- timeout: 3000 + actuators: + - type: tcp + timeout: 800 + serverAddress: localhost + port: 23070 + payload: I do not care + saveStream: tcpActuatorSocket + onMessageReceived: + assertions: + - expect: payload + toBeEqualTo: "'EnqueuerRocks'" + sensors: + - type: tcp + port: 23070 + timeout: 500 + saveStream: tcpSensorSocket + greeting: Enqueuer + response: Rocks + onMessageReceived: + assertions: + - expect: payload + toBeEqualTo: "'I do not care'" +- timeout: 3000 + actuators: + - type: tcp + payload: The socket is still open + loadStream: tcpActuatorSocket + onMessageReceived: + assertions: + - expect: payload + toBeEqualTo: "'<>'" + sensors: + - type: tcp + loadStream: tcpSensorSocket + response: <> + timeout: 1000 + onMessageReceived: + assertions: + - expect: payload + toBeEqualTo: "'The socket is still open'" diff --git a/examples/udp.yml b/examples/udp.yml index 9fa0c09c..1620bdeb 100644 --- a/examples/udp.yml +++ b/examples/udp.yml @@ -1,20 +1,20 @@ -- publishers: - - type: udp - name: publisher description - serverAddress: localhost - port: 23072 - payload: it - subscriptions: - - name: subscription description - type: udp - port: 23072 - timeout: 1000 - onMessageReceived: - script: payload = Buffer.from(payload); - assertions: - - name: Payload - expect: payload - toBeEqualTo: `it` - - name: Remote Address - expect: remoteInfo.address - toBeEqualTo: `127.0.0.1` +- actuators: + - type: udp + name: actuator description + serverAddress: localhost + port: 23072 + payload: it + sensors: + - name: sensor description + type: udp + port: 23072 + timeout: 1000 + onMessageReceived: + script: payload = Buffer.from(payload); + assertions: + - name: Payload + expect: payload + toBeEqualTo: "'it'" + - name: Remote Address + expect: remoteInfo.address + toBeEqualTo: "'127.0.0.1'" diff --git a/examples/uds.yml b/examples/uds.yml index 4b489a15..79df4b6d 100644 --- a/examples/uds.yml +++ b/examples/uds.yml @@ -1,64 +1,64 @@ -timeout: 7000 -requisitions: -- publishers: - - type: uds +timeout: 10000 +tasks: + - actuators: + - type: uds path: /tmp/unix.sock - timeout: 2000 + timeout: 3000 payload: I'll be replaced in onInit onInit: - script: publisher.payload = 'enqueuer' - store: - udsPayload: `enqueuer` - uds2Timeout: `2000` + script: actuator.payload = 'enqueuer' + store: + udsPayload: "'enqueuer'" + uds2Timeout: "'3000'" onMessageReceived: - assertions: - - expect: payload - toBeEqualTo: `responsePayload` - subscriptions: - - type: uds + assertions: + - expect: payload + toBeEqualTo: "'responsePayload'" + sensors: + - type: uds path: /tmp/unix.sock response: responsePayload - timeout: 2000 + timeout: 3000 onMessageReceived: - assertions: - - name: Message received is enqueuer - expect: payload - toBeEqualTo: store.udsPayload - - expect: path - toBeEqualTo: `/tmp/unix.sock` -- publishers: - - type: uds + assertions: + - name: Message received is enqueuer + expect: payload + toBeEqualTo: store.udsPayload + - expect: path + toBeEqualTo: "'/tmp/unix.sock'" + - actuators: + - type: uds path: /tmp/unix2.sock payload: <> saveStream: udsPubStreamName onInit: - assertions: - - expect: publisher.payload - toBeEqualTo: store.udsPayload - subscriptions: - - type: uds + assertions: + - expect: actuator.payload + toBeEqualTo: store.udsPayload + sensors: + - type: uds path: /tmp/unix2.sock saveStream: udsSubStreamName onMessageReceived: - assertions: - - name: real error - expect: payload - toBeEqualTo: store.udsPayload + assertions: + - name: real error + expect: payload + toBeEqualTo: store.udsPayload timeout: <> -- publishers: - - type: uds - timeout: 2000 + - actuators: + - type: uds + timeout: 3000 payload: I am still opened loadStream: udsSubStreamName onMessageReceived: - assertions: - - expect: payload - toBeEqualTo: `I am still bidirectional` - subscriptions: - - type: uds + assertions: + - expect: payload + toBeEqualTo: "'I am still bidirectional'" + sensors: + - type: uds loadStream: udsPubStreamName response: I am still bidirectional onMessageReceived: - assertions: - - expect: payload - toBeEqualTo: `I am still opened` + assertions: + - expect: payload + toBeEqualTo: "'I am still opened'" diff --git a/examples/variables.yml b/examples/variables.yml index 90fb4d4f..8952a79a 100644 --- a/examples/variables.yml +++ b/examples/variables.yml @@ -1,19 +1,19 @@ -- onInit: - script: store.otherKey = 2 - store: - key: 10 - onFinish: - assertions: - - expect: store.key - toBeEqualTo: 10 - - expect: store.otherKey / 2 - toBeEqualTo: 1 -- anyName: <> - onInit: - assertions: - - expect: requisition.anyName - toBeEqualTo: 10 - - expect: <> + 1 - toBeEqualTo: 11 - - expect: <> * 5 - toBeEqualTo: 10 +- onInit: + script: store.otherKey = 2 + store: + key: 10 + onFinish: + assertions: + - expect: store.key + toBeEqualTo: 10 + - expect: store.otherKey / 2 + toBeEqualTo: 1 +- anyName: <> + onInit: + assertions: + - expect: task.anyName + toBeEqualTo: 10 + - expect: <> + 1 + toBeEqualTo: 11 + - expect: <> * 5 + toBeEqualTo: 10 diff --git a/misc/.DS_Store b/misc/.DS_Store new file mode 100644 index 00000000..1c6d38c2 Binary files /dev/null and b/misc/.DS_Store differ diff --git a/misc/array.json b/misc/array.json index cdbafefb..2fd0bd08 100644 --- a/misc/array.json +++ b/misc/array.json @@ -1,12 +1 @@ -[ - 0, - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9 -] +[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] diff --git a/misc/custom-protocol.js b/misc/custom-protocol.js index 10eb9d3b..dca1d727 100644 --- a/misc/custom-protocol.js +++ b/misc/custom-protocol.js @@ -1,63 +1,64 @@ -const dgram = require("dgram"); - -class Subscription { - constructor(subscription) { - this.subscription = subscription; - } - - subscribe(context) { - - return new Promise((resolve, reject) => { - this.server = dgram.createSocket('udp4'); - try { - this.server.bind(this.subscription.port); - resolve(); - } catch (err) { - const message = `Udp server could not listen to ${this.subscription.port}`; - context.logger.error(message); - reject(message); - } - }); - }; - - receiveMessage(context) { - return new Promise((resolve, reject) => { - this.server.on('error', (err) => { - this.server.close(); - reject(err); - }); - - this.server.on('message', (msg, remoteInfo) => { - this.server.close(); - this.subscription.executeHookEvent('onMessageReceived', {payload: msg, remoteInfo: remoteInfo}); - resolve(); - }); +const dgram = require('dgram'); + +class Sensor { + constructor(sensor) { + this.sensor = sensor; + } + + mount(context) { + return new Promise((resolve, reject) => { + this.server = dgram.createSocket('udp4'); + try { + this.server.bind(this.sensor.port); + resolve(); + } catch (err) { + const message = `Udp server could not listen to ${this.sensor.port}`; + context.logger.error(message); + reject(message); + } + }); + } + + receiveMessage(context) { + return new Promise((resolve, reject) => { + this.server.on('error', err => { + this.server.close(); + reject(err); + }); + + this.server.on('message', (msg, remoteInfo) => { + this.server.close(); + this.sensor.executeHookEvent('onMessageReceived', { + payload: msg, + remoteInfo: remoteInfo }); - }; + resolve(); + }); + }); + } } -class Publisher { - constructor(publisher) { - this.publisher = publisher; - } - - publish(context) { - return new Promise((resolve, reject) => { - const client = dgram.createSocket('udp4'); - context.logger.debug('Udp client trying to send message'); - - client.send(Buffer.from(this.publisher.payload), this.publisher.port, this.publisher.serverAddress, (error) => { - if (error) { - client.close(); - reject(error); - return; - } - context.logger.debug('Udp client sent message'); - resolve(); - }); +class Actuator { + constructor(actuator) { + this.actuator = actuator; + } - }); - }; + act(context) { + return new Promise((resolve, reject) => { + const client = dgram.createSocket('udp4'); + context.logger.debug('Udp client trying to send message'); + + client.send(Buffer.from(this.actuator.payload), this.actuator.port, this.actuator.serverAddress, error => { + if (error) { + client.close(); + reject(error); + return; + } + context.logger.debug('Udp client sent message'); + resolve(); + }); + }); + } } -module.exports = {Subscription, Publisher}; +module.exports = { Sensor, Actuator }; diff --git a/misc/file-code.js b/misc/file-code.js index 256aabda..47d63836 100644 --- a/misc/file-code.js +++ b/misc/file-code.js @@ -1 +1 @@ -(value) => value * 2; +value => value * 2; diff --git a/misc/file-content.json b/misc/file-content.json index d5df76e7..7a9e8644 100644 --- a/misc/file-content.json +++ b/misc/file-content.json @@ -1,3 +1,3 @@ { "key": "value" -} \ No newline at end of file +} diff --git a/misc/file-content.yml b/misc/file-content.yml index 90968b0f..ad7f7372 100644 --- a/misc/file-content.yml +++ b/misc/file-content.yml @@ -1 +1 @@ -key: <> +key: ymlValue diff --git a/misc/html/template.html b/misc/html/template.html index 381eef71..7afa63f9 100755 --- a/misc/html/template.html +++ b/misc/html/template.html @@ -1,286 +1,328 @@ - + - + - - - - - - - + + + + + + + Enqueuer - - - - - - - + - - - - + + + - - - + + + + + + + + + + + + + + + + + - - - - - - - - - - - + const enqueuerFlow = $("[title='Enqueuer Instance Flow']"); + enqueuerFlow.css({ 'max-width': '90%' }); + enqueuerFlow.parent().css({ 'text-align': 'center' }); + + const npmBigBadge = $("[href='https://nodei.co/npm/enqueuer/']"); + npmBigBadge.parent().css({ 'text-align': 'center' }); + + const npmBadges = $("[href='https://travis-ci.org/enqueuer-land/enqueuer']"); + npmBadges.parent().css({ 'text-align': 'center' }); + npmBadges.parent().addClass('npm-badges-container'); + + + + diff --git a/misc/mdToHtml.js b/misc/mdToHtml.js index f0a8b0e4..b72f5b57 100755 --- a/misc/mdToHtml.js +++ b/misc/mdToHtml.js @@ -1,12 +1,12 @@ #!/usr/bin/env node /*jshint node:true, es5:true */ const http = require('https'); -const url = require('url') +const url = require('url'); const pagedown = require('pagedown'); const converter = new pagedown.Converter(); const fs = require('fs'); -const template = fs.readFileSync(__dirname + "/html/template.html").toString(); -const md = fs.readFileSync("docs/README.md").toString(); +const template = fs.readFileSync(__dirname + '/html/template.html').toString(); +const md = fs.readFileSync('docs/README.md').toString(); let spyHtml = ``; - const content = spyHtml + - `
` + - readMeHtmlized + - `
`; - - const htmlResult = template.replace('', content); - - fs.writeFileSync('docs/docs.html', htmlResult); - console.log("Html generated"); + const contributors = Object.values( + (await getContributors()).concat(contributorsUsersList).reduce((acc, contributor) => { + acc[contributor.name] = contributor; + return acc; + }, {}) + ); + const contributorsHtml = createContributorsHtml(contributors); + const readMeHtmlized = converter + .makeHtml(md) + .replace('{{plugins list placeholder}}', pluginsListTable) + .replace('{{contributors list placeholder}}', contributorsHtml); + spyHtml += ``; + const content = + spyHtml + `
` + readMeHtmlized + `
`; + + const htmlResult = template.replace('', content); + + fs.writeFileSync('docs/docs.html', htmlResult); + console.log('Html generated'); } createContributorsHtml = function (contributors) { - return `
` + ); +}; async function getContributors() { - return new Promise((resolve, reject) => { - httpGet(`https://api.github.com/orgs/enqueuer-land/repos?per_page=200`) - .then(payload => payload.data) - .then(data => { - return Promise.all(data.concat(pluginsListContributors).map(repo => httpGet(repo.contributors_url).then(payload => payload.data))) - }) - .then(repoContributors => repoContributors.reduce((acc, repoContributor) => acc.concat(repoContributor), [])) - .then(contributors => contributors.reduce((acc, contributor) => { - if (contributor.type === 'User' /*&& contributor.login !== 'enqueuer-land'*/) { - acc[contributor.login] = { - avatar_url: contributor.avatar_url, - html_url: contributor.html_url, - login: contributor.login, - }; - } - return acc; - }, {})) - .then(contributors => Object.values(contributors)) - .then(contributors => contributors.map(contributor => ({ - picture: `http://github.com/${contributor.login}.png`, - name: contributor.login, - html_url: `http://github.com/${contributor.login}` - }))) - .then(resolve) - .catch(reject); - }); + return new Promise((resolve, reject) => { + httpGet(`https://api.github.com/orgs/enqueuer-land/repos?per_page=200`) + .then(payload => payload.data) + .then(data => { + return Promise.all( + data.concat(pluginsListContributors).map(repo => httpGet(repo.contributors_url).then(payload => payload.data)) + ); + }) + .then(repoContributors => repoContributors.reduce((acc, repoContributor) => acc.concat(repoContributor), [])) + .then(contributors => + contributors.reduce((acc, contributor) => { + if (contributor.type === 'User' /*&& contributor.login !== 'enqueuer-land'*/) { + acc[contributor.login] = { + avatar_url: contributor.avatar_url, + html_url: contributor.html_url, + login: contributor.login + }; + } + return acc; + }, {}) + ) + .then(contributors => Object.values(contributors)) + .then(contributors => + contributors.map(contributor => ({ + picture: `http://github.com/${contributor.login}.png`, + name: contributor.login, + html_url: `http://github.com/${contributor.login}` + })) + ) + .then(resolve) + .catch(error => { + console.error( + `Error creating contributors section. It's probably due to throttling. Open the link https://api.github.com/orgs/enqueuer-land/repos?per_page=200 in the browser and check if that works` + ); + reject(error); + }); + }); } - diff --git a/output/examples.json b/output/examples.json index bc378243..3b929548 100644 --- a/output/examples.json +++ b/output/examples.json @@ -1,10 +1,10 @@ { "valid": true, "name": "enqueuer", - "id": "0233360790_44deaa57a3_629459", + "id": "1355570986_3b99cdfebb_733514", "level": 0, - "subscriptions": [], - "publishers": [], + "sensors": [], + "actuators": [], "iteration": 0, "totalIterations": 1, "hooks": { @@ -19,7 +19,7 @@ "valid": true, "tests": [], "arguments": { - "elapsedTime": 5156 + "elapsedTime": 7136 } }, "onParsed": { @@ -28,18 +28,57 @@ } }, "time": { - "startTime": "2020-03-08T05:33:36.829Z", - "endTime": "2020-03-08T05:33:41.985Z", - "totalTime": 5156 + "startTime": "2025-03-18T20:55:58.002Z", + "endTime": "2025-03-18T20:56:05.138Z", + "totalTime": 7136 }, - "requisitions": [ + "tasks": [ + { + "valid": true, + "name": "examples/argumentNames.yml", + "id": "1355570986_bd8a0b0ab4_928299", + "level": 1, + "sensors": [], + "actuators": [], + "iteration": 0, + "totalIterations": 1, + "hooks": { + "onInit": { + "valid": true, + "tests": [], + "arguments": { + "elapsedTime": 0 + } + }, + "onFinish": { + "valid": true, + "tests": [ + { + "name": "Assertion #0", + "valid": true, + "description": "Expected '\"argumentNames\"' to be equal to '[\n \"task\",\n \"elapsedTime\"\n]'. Received '[\n \"task\",\n \"elapsedTime\"\n]'" + } + ], + "arguments": { + "elapsedTime": 90 + } + } + }, + "time": { + "startTime": "2025-03-18T20:55:58.003Z", + "endTime": "2025-03-18T20:55:58.094Z", + "totalTime": 91, + "timeout": 5000 + }, + "tasks": [] + }, { "valid": true, "name": "examples/assertions.yml", - "id": "0233360790_2ca609e341_792411", + "id": "1355570986_38d38742c0_542950", "level": 1, - "subscriptions": [], - "publishers": [], + "sensors": [], + "actuators": [], "iteration": 0, "totalIterations": 1, "hooks": { @@ -49,12 +88,12 @@ { "name": "Assertion #0", "valid": true, - "description": "Expecting 'requisition.definedVariable' to be defined" + "description": "Expecting 'task.definedVariable' to be defined" }, { "name": "Assertion #1", "valid": true, - "description": "Expecting 'requisition.undefinedVariable' to be undefined. Received: undefined" + "description": "Expecting 'task.undefinedVariable' to be undefined. Received: undefined" }, { "name": "Assertion #2", @@ -109,12 +148,12 @@ { "name": "Assertion #12", "valid": true, - "description": "Expecting 'enqueuer' (`enqueuer`) to contain 'queue'" + "description": "Expecting 'enqueuer' ('enqueuer') to contain 'queue'" }, { "name": "Assertion #13", "valid": true, - "description": "Expecting 'enqueuer' (`enqueuer`) not to contain 'nqr'" + "description": "Expecting 'enqueuer' ('enqueuer') not to contain 'nqr'" }, { "name": "Assertion #14", @@ -140,6 +179,16 @@ "name": "Assertion #18", "valid": true, "description": "Expected '1' not to be equal to '0'. Received '1'" + }, + { + "name": "Assertion #19", + "valid": true, + "description": "Expected ''blue'' to be any of '[red, green, blue]'. Received 'blue'" + }, + { + "name": "Assertion #20", + "valid": true, + "description": "Expected ''yellow'' not to be any of '[red, green, blue]'. Received 'yellow'" } ], "arguments": { @@ -150,92 +199,96 @@ "valid": true, "tests": [], "arguments": { - "elapsedTime": 85 + "elapsedTime": 90 } } }, "time": { - "startTime": "2020-03-08T05:33:36.831Z", - "endTime": "2020-03-08T05:33:36.917Z", - "totalTime": 86, + "startTime": "2025-03-18T20:55:58.004Z", + "endTime": "2025-03-18T20:55:58.094Z", + "totalTime": 90, "timeout": 5000 }, - "requisitions": [] + "tasks": [] }, { "valid": true, "name": "examples/avoid.yml", - "id": "0233360790_852d538668_983762", + "id": "1355570987_4f0a9a3a43_363429", "level": 1, - "subscriptions": [ + "sensors": [ { - "id": "0233360790_2e0cc1fd7c_358902", - "name": "Subscription #0", + "id": "1355570987_cd969fe2c2_864102", + "name": "Sensor #0", "type": "tcp", "hooks": { "onInit": { "valid": true, "tests": [], - "arguments": { - "elapsedTime": 0 - } + "arguments": {} }, "onFinish": { "valid": true, "tests": [ { "valid": true, - "name": "Subscription avoided", - "description": "Avoidable subscription has not received any message" + "implicit": true, + "name": "Sensor avoided", + "description": "Avoidable sensor has not received any message" } ], "arguments": { - "executedHooks": [ - "onInit", - "onFinish" - ], - "elapsedTime": 3059 + "executedHooks": { + "onInit": [], + "onFinish": [ + "executedHooks", + "elapsedTime" + ] + }, + "elapsedTime": 3020 } } }, "valid": true, - "subscriptionTime": "2020-03-08T05:33:36.951Z" + "sensorTime": "2025-03-18T20:55:58.111Z" }, { - "id": "0233360791_8444956d6a_667353", - "name": "Subscription #1", + "id": "1355570987_955bbbf611_959597", + "name": "Sensor #1", "type": "HTTP", "hooks": { "onInit": { "valid": true, "tests": [], - "arguments": { - "elapsedTime": 0 - } + "arguments": {} }, "onFinish": { "valid": true, "tests": [ { "valid": true, - "name": "Subscription avoided", - "description": "Avoidable subscription has not received any message" + "implicit": true, + "name": "Sensor avoided", + "description": "Avoidable sensor has not received any message" } ], "arguments": { - "executedHooks": [ - "onInit", - "onFinish" - ], - "elapsedTime": 3058 + "executedHooks": { + "onInit": [], + "onFinish": [ + "executedHooks", + "elapsedTime" + ] + }, + "elapsedTime": 3019 } } }, "valid": true, - "subscriptionTime": "2020-03-08T05:33:36.951Z" + "sensorTime": "2025-03-18T20:55:58.111Z" } ], - "publishers": [], + "actuators": [], "iteration": 0, "totalIterations": 1, "hooks": { @@ -250,25 +303,25 @@ "valid": true, "tests": [], "arguments": { - "elapsedTime": 3059 + "elapsedTime": 3020 } } }, "time": { - "startTime": "2020-03-08T05:33:36.834Z", - "endTime": "2020-03-08T05:33:39.894Z", - "totalTime": 3060, + "startTime": "2025-03-18T20:55:58.005Z", + "endTime": "2025-03-18T20:56:01.025Z", + "totalTime": 3020, "timeout": 5000 }, - "requisitions": [] + "tasks": [] }, { "valid": true, "name": "examples/crypto-require.yml", - "id": "0233360791_1d33e31a27_738675", + "id": "1355570987_56a8860916_14740", "level": 1, - "subscriptions": [], - "publishers": [], + "sensors": [], + "actuators": [], "iteration": 0, "totalIterations": 1, "hooks": { @@ -285,56 +338,62 @@ { "name": "Assertion #0", "valid": true, - "description": "Expected 'requisition.toEncrypt' to be equal to '7aad9a1a6a91e0f18c417cb3aa0e0217b283778e636c580509e494eeec1472e0'. Received '7aad9a1a6a91e0f18c417cb3aa0e0217b283778e636c580509e494eeec1472e0'" + "description": "Expected 'task.toEncrypt' to be equal to '68fa8a566bf6c20d2c1f93ab4c71c224032850e7a6fd4c29e59681b3f7937c2f'. Received '68fa8a566bf6c20d2c1f93ab4c71c224032850e7a6fd4c29e59681b3f7937c2f'" } ], "arguments": { - "elapsedTime": 81 + "elapsedTime": 88 } } }, "time": { - "startTime": "2020-03-08T05:33:36.835Z", - "endTime": "2020-03-08T05:33:36.917Z", - "totalTime": 82, + "startTime": "2025-03-18T20:55:58.006Z", + "endTime": "2025-03-18T20:55:58.094Z", + "totalTime": 88, "timeout": 5000 }, - "requisitions": [] + "tasks": [] }, { "valid": true, "name": "examples/custom.yml", - "id": "0233360791_6da5232086_556677", + "id": "1355570987_4c3cd94026_865882", "level": 1, - "subscriptions": [ + "sensors": [ { - "id": "0233360791_f6315a7fbc_215435", - "name": "subscription description", + "id": "1355570987_3ec24b875c_165075", + "name": "sensor description", "type": "custom", "hooks": { "onInit": { "valid": true, "tests": [], - "arguments": { - "elapsedTime": 0 - } + "arguments": {} }, "onFinish": { "valid": true, "tests": [ { + "implicit": true, "valid": true, "name": "Message received", - "description": "Subscription has received its message" + "description": "Sensor has received its message" } ], "arguments": { - "executedHooks": [ - "onInit", - "onMessageReceived", - "onFinish" - ], - "elapsedTime": 151 + "executedHooks": { + "onInit": [], + "onMessageReceived": [ + "payload", + "remoteInfo", + "elapsedTime" + ], + "onFinish": [ + "executedHooks", + "elapsedTime" + ] + }, + "elapsedTime": 123 } }, "onMessageReceived": { @@ -362,21 +421,21 @@ "remoteInfo": { "address": "127.0.0.1", "family": "IPv4", - "port": 63327, + "port": 61322, "size": 2 }, - "elapsedTime": 150 + "elapsedTime": 123 } } }, "valid": true, - "subscriptionTime": "2020-03-08T05:33:36.912Z" + "sensorTime": "2025-03-18T20:55:58.067Z" } ], - "publishers": [ + "actuators": [ { - "id": "0233360791_816cec1424_302873", - "name": "publisher description", + "id": "1355570987_235ae67dd8_750350", + "name": "actuator description", "valid": true, "hooks": { "onInit": { @@ -388,24 +447,28 @@ }, "onFinish": { "arguments": { - "executedHooks": [ - "onInit", - "onFinish" - ], - "elapsedTime": 151 + "executedHooks": { + "onInit": [], + "onFinish": [ + "executedHooks", + "elapsedTime" + ] + }, + "elapsedTime": 122 }, "tests": [ { "name": "Published", "valid": true, - "description": "Published successfully" + "description": "Published successfully", + "implicit": true } ], "valid": true } }, "type": "custom", - "publishTime": "2020-03-08T05:33:36.960Z" + "messageSentInstant": "2025-03-18T20:55:58.125Z" } ], "iteration": 0, @@ -422,25 +485,25 @@ "valid": true, "tests": [], "arguments": { - "elapsedTime": 152 + "elapsedTime": 123 } } }, "time": { - "startTime": "2020-03-08T05:33:36.836Z", - "endTime": "2020-03-08T05:33:36.988Z", - "totalTime": 152, + "startTime": "2025-03-18T20:55:58.006Z", + "endTime": "2025-03-18T20:55:58.129Z", + "totalTime": 123, "timeout": 5000 }, - "requisitions": [] + "tasks": [] }, { "valid": true, "name": "examples/file-placeholder.yml", - "id": "0233360791_a5f62352f4_516197", + "id": "1355570987_70295ec20e_421976", "level": 1, - "subscriptions": [], - "publishers": [], + "sensors": [], + "actuators": [], "iteration": 0, "totalIterations": 1, "hooks": { @@ -455,24 +518,24 @@ "valid": true, "tests": [], "arguments": { - "elapsedTime": 88 + "elapsedTime": 92 } } }, "time": { - "startTime": "2020-03-08T05:33:36.839Z", - "endTime": "2020-03-08T05:33:36.928Z", - "totalTime": 89, + "startTime": "2025-03-18T20:55:58.008Z", + "endTime": "2025-03-18T20:55:58.100Z", + "totalTime": 92, "timeout": 5000 }, - "requisitions": [ + "tasks": [ { "valid": true, - "name": "Requisition #0", - "id": "0233360791_374c18eaf8_811103", + "name": "Task #0", + "id": "1355570987_6cb2ec3367_261969", "level": 2, - "subscriptions": [], - "publishers": [], + "sensors": [], + "actuators": [], "iteration": 0, "totalIterations": 1, "hooks": { @@ -482,22 +545,22 @@ { "name": "fileRead (yml)", "valid": true, - "description": "Expected 'requisition.yml.key' to be equal to 'I'm persisted in config file'. Received 'I'm persisted in config file'" + "description": "Expected 'task.yml.key' to be equal to 'ymlValue'. Received 'ymlValue'" }, { "name": "fileRead (.json)", "valid": true, - "description": "Expected 'requisition.json.key' to be equal to 'value'. Received 'value'" + "description": "Expected 'task.json.key' to be equal to 'value'. Received 'value'" }, { "name": "fileRead (.csv)", "valid": true, - "description": "Expected 'requisition.csv[1].title' to be equal to 'secondRow'. Received 'secondRow'" + "description": "Expected 'task.csv[1].title' to be equal to 'secondRow'. Received 'secondRow'" }, { "name": "fileRead (.js)", "valid": true, - "description": "Expected 'requisition.javascript(20)' to be equal to '40'. Received '40'" + "description": "Expected 'task.javascript(20)' to be equal to '40'. Received '40'" } ], "arguments": { @@ -508,25 +571,25 @@ "valid": true, "tests": [], "arguments": { - "elapsedTime": 38 + "elapsedTime": 74 } } }, "time": { - "startTime": "2020-03-08T05:33:36.879Z", - "endTime": "2020-03-08T05:33:36.917Z", - "totalTime": 38, + "startTime": "2025-03-18T20:55:58.020Z", + "endTime": "2025-03-18T20:55:58.095Z", + "totalTime": 75, "timeout": 5000 }, - "requisitions": [] + "tasks": [] }, { "valid": true, - "name": "Requisition #1", - "id": "0233360791_8067e3ed86_612706", + "name": "Task #1", + "id": "1355570987_c473d27cb3_918157", "level": 2, - "subscriptions": [], - "publishers": [], + "sensors": [], + "actuators": [], "iteration": 0, "totalIterations": 1, "hooks": { @@ -536,7 +599,7 @@ { "name": "it's not stored", "valid": true, - "description": "Expecting 'requisition.yml' to be undefined. Received: undefined" + "description": "Expecting 'task.yml' to be undefined. Received: undefined" } ], "arguments": { @@ -547,54 +610,63 @@ "valid": true, "tests": [], "arguments": { - "elapsedTime": 4 + "elapsedTime": 2 } } }, "time": { - "startTime": "2020-03-08T05:33:36.920Z", - "endTime": "2020-03-08T05:33:36.924Z", - "totalTime": 4, + "startTime": "2025-03-18T20:55:58.097Z", + "endTime": "2025-03-18T20:55:58.099Z", + "totalTime": 2, "timeout": 5000 }, - "requisitions": [] + "tasks": [] } ] }, { "valid": true, "name": "examples/file.yml", - "id": "0233360791_d7a8d31f32_110495", + "id": "1355570987_b3282d2997_469801", "level": 1, - "subscriptions": [ + "sensors": [ { - "id": "0233360791_3dd85e61fc_645581", - "name": "subscription description", + "id": "1355570987_c27b643ad5_221057", + "name": "sensor description", "type": "file-system-watcher", "hooks": { "onInit": { "valid": true, "tests": [], - "arguments": { - "elapsedTime": 1 - } + "arguments": {} }, "onFinish": { "valid": true, "tests": [ { + "implicit": true, "valid": true, "name": "Message received", - "description": "Subscription has received its message" + "description": "Sensor has received its message" } ], "arguments": { - "executedHooks": [ - "onInit", - "onMessageReceived", - "onFinish" - ], - "elapsedTime": 143 + "executedHooks": { + "onInit": [], + "onMessageReceived": [ + "content", + "name", + "size", + "modified", + "created", + "elapsedTime" + ], + "onFinish": [ + "executedHooks", + "elapsedTime" + ] + }, + "elapsedTime": 142 } }, "onMessageReceived": { @@ -603,12 +675,12 @@ { "name": "Some time has passed", "valid": true, - "description": "Expected 'now' to be greater than or equal to '1583645616841'. Received '1583645616982'" + "description": "Expected 'now' to be greater than or equal to '1724908054863'. Received '1742331358150'" }, { "name": "Filename", "valid": true, - "description": "Expecting 'temp/fileTest0233360915_5a0f35cf7e_347122.file' (name) to contain 'temp/'" + "description": "Expecting 'temp/fileTest2207340943_2b3564f8c7_709692.file' (name) to contain 'temp/'" }, { "name": "Content", @@ -632,23 +704,23 @@ } ], "arguments": { - "content": "1583645616841", - "name": "temp/fileTest0233360915_5a0f35cf7e_347122.file", + "content": "1724908054863", + "name": "temp/fileTest2207340943_2b3564f8c7_709692.file", "size": 13, - "modified": "2020-03-08T05:33:36.916Z", - "created": "2020-03-08T05:33:36.916Z", - "elapsedTime": 141 + "modified": "2024-08-29T05:07:34.944Z", + "created": "2024-08-29T05:07:34.944Z", + "elapsedTime": 142 } } }, "valid": true, - "subscriptionTime": "2020-03-08T05:33:36.910Z" + "sensorTime": "2025-03-18T20:55:58.064Z" } ], - "publishers": [ + "actuators": [ { - "id": "0233360791_4b5a457015_558435", - "name": "publisher description", + "id": "1355570987_2fbdddbfc9_782373", + "name": "actuator description", "valid": true, "hooks": { "onInit": { @@ -660,24 +732,28 @@ }, "onFinish": { "arguments": { - "executedHooks": [ - "onInit", - "onFinish" - ], + "executedHooks": { + "onInit": [], + "onFinish": [ + "executedHooks", + "elapsedTime" + ] + }, "elapsedTime": 142 }, "tests": [ { "name": "Published", "valid": true, - "description": "Published successfully" + "description": "Published successfully", + "implicit": true } ], "valid": true } }, "type": "file", - "publishTime": "2020-03-08T05:33:36.917Z" + "messageSentInstant": "2025-03-18T20:55:58.091Z" } ], "iteration": 0, @@ -694,36 +770,36 @@ "valid": true, "tests": [], "arguments": { - "elapsedTime": 143 + "elapsedTime": 142 } } }, "time": { - "startTime": "2020-03-08T05:33:36.840Z", - "endTime": "2020-03-08T05:33:36.984Z", - "totalTime": 144, + "startTime": "2025-03-18T20:55:58.008Z", + "endTime": "2025-03-18T20:55:58.150Z", + "totalTime": 142, "timeout": 3000 }, - "requisitions": [] + "tasks": [] }, { "valid": true, "name": "examples/hooks.yml", - "id": "0233360791_75b8872357_380660", + "id": "1355570987_4671381b7a_13072", "level": 1, - "subscriptions": [ + "sensors": [ { - "id": "0233360791_fbe2d8d804_24917", - "name": "Subscription #0", + "id": "1355570987_c997e9cb93_291457", + "name": "Sensor #0", "type": "tcp", "hooks": { "onInit": { "valid": true, "tests": [ { - "name": "Subscription exists onInit", + "name": "Sensor exists onInit", "valid": true, - "description": "Expecting 'subscription' to be defined" + "description": "Expecting 'sensor' to be defined" }, { "name": "Assertion #1", @@ -731,71 +807,78 @@ "description": "Expected 'this.name' to be equal to 'changed'. Received 'changed'" }, { - "name": "Subscription name", + "name": "Sensor name", "valid": true, - "description": "Expected 'subscription.name' to be equal to 'changed'. Received 'changed'" + "description": "Expected 'sensor.name' to be equal to 'changed'. Received 'changed'" } ], - "arguments": { - "elapsedTime": 0 - } + "arguments": {} }, "onFinish": { "valid": true, "tests": [ { + "implicit": true, "valid": true, "name": "Message received", - "description": "Subscription has received its message" + "description": "Sensor has received its message" } ], "arguments": { - "executedHooks": [ - "onInit", - "onMessageReceived", - "onFinish" - ], - "elapsedTime": 2152 + "executedHooks": { + "onInit": [], + "onMessageReceived": [ + "payload", + "stream", + "path", + "elapsedTime" + ], + "onFinish": [ + "executedHooks", + "elapsedTime" + ] + }, + "elapsedTime": 2125 } }, "onMessageReceived": { "valid": true, "tests": [ { - "name": "Subscription exists onMessageReceived", + "name": "Sensor exists onMessageReceived", "valid": true, - "description": "Expecting 'subscription' to be defined" + "description": "Expecting 'sensor' to be defined" }, { - "name": "Subscription name changed", + "name": "Sensor name changed", "valid": true, - "description": "Expected 'subscription.name' to be equal to 'changed'. Received 'changed'" + "description": "Expected 'sensor.name' to be equal to 'changed'. Received 'changed'" }, { - "name": "Subscription onInit executed", + "name": "Sensor onInit executed", "valid": true, - "description": "Expecting 'requisition::onInit-> subscription::onInit-> publisher::onInit' (store.sequence) to contain 'subscription::onInit'" + "description": "Expecting 'task::onInit'-> sensor::onInit''-> actuator::onInit'' (store.sequence) to contain 'sensor::onInit'" } ], "arguments": { "payload": "it", "stream": { - "address": "::ffff:127.0.0.1", + "address": "::1", "family": "IPv6", "port": 23080 }, - "elapsedTime": 151 + "elapsedTime": 143 } } }, "valid": true, - "subscriptionTime": "2020-03-08T05:33:36.951Z" + "sensorTime": "2025-03-18T20:55:58.111Z" } ], - "publishers": [ + "actuators": [ { - "id": "0233360791_ca0fe1e8c5_215816", - "name": "Publisher #0", + "id": "1355570987_9390463906_407947", + "name": "Actuator #0", "valid": true, "hooks": { "onInit": { @@ -804,19 +887,19 @@ }, "tests": [ { - "name": "Publisher exists onInit", + "name": "Actuator exists onInit", "valid": true, - "description": "Expecting 'publisher' to be defined" + "description": "Expecting 'actuator' to be defined" }, { - "name": "Publisher name", + "name": "Actuator name", "valid": true, - "description": "Expecting 'publisher.name' to be defined" + "description": "Expecting 'actuator.name' to be defined" }, { "name": "Assertion #2", "valid": true, - "description": "Expecting 'publisher' to be defined" + "description": "Expecting 'actuator' to be defined" }, { "name": "Assertion #3", @@ -833,18 +916,26 @@ }, "onFinish": { "arguments": { - "executedHooks": [ - "onInit", - "onMessageReceived", - "onFinish" - ], - "elapsedTime": 2152 + "executedHooks": { + "onInit": [], + "onMessageReceived": [ + "payload", + "stream", + "elapsedTime" + ], + "onFinish": [ + "executedHooks", + "elapsedTime" + ] + }, + "elapsedTime": 2125 }, "tests": [ { "name": "Published", "valid": true, - "description": "Published successfully" + "description": "Published successfully", + "implicit": true } ], "valid": true @@ -853,59 +944,59 @@ "arguments": { "payload": "hook response", "stream": { - "address": "127.0.0.1", - "family": "IPv4", - "port": 61962 + "address": "::1", + "family": "IPv6", + "port": 57106 }, - "elapsedTime": 2153 + "elapsedTime": 2126 }, "tests": [ { - "name": "Publisher exists onMessageReceived", + "name": "Actuator exists onMessageReceived", "valid": true, - "description": "Expecting 'publisher' to be defined" + "description": "Expecting 'actuator' to be defined" }, { - "name": "Requisition onInit", + "name": "Task onInit", "valid": true, - "description": "Expecting 'requisition::onInit-> subscription::onInit-> publisher::onInit-> publisher::onMessageReceived' (store.sequence) to contain 'requisition::onInit'" + "description": "Expecting 'task::onInit'-> sensor::onInit''-> actuator::onInit''-> actuator::onMessageReceived'' (store.sequence) to contain 'task::onInit'" }, { - "name": "Publisher name changed", + "name": "Actuator name changed", "valid": true, - "description": "Expected 'publisher.name' to be equal to 'changed'. Received 'changed'" + "description": "Expected 'actuator.name' to be equal to 'changed'. Received 'changed'" }, { - "name": "Publisher onInit executed", + "name": "Actuator onInit executed", "valid": true, - "description": "Expecting 'requisition::onInit-> subscription::onInit-> publisher::onInit-> publisher::onMessageReceived' (store.sequence) to contain 'publisher::onInit'" + "description": "Expecting 'task::onInit'-> sensor::onInit''-> actuator::onInit''-> actuator::onMessageReceived'' (store.sequence) to contain 'actuator::onInit'" }, { - "name": "Publisher exists onMessageReceived", + "name": "Actuator exists onMessageReceived", "valid": true, - "description": "Expecting 'publisher' to be defined" + "description": "Expecting 'actuator' to be defined" }, { - "name": "Requisition onInit", + "name": "Task onInit", "valid": true, - "description": "Expecting 'requisition::onInit-> subscription::onInit-> publisher::onInit-> publisher::onMessageReceived-> publisher::onMessageReceived' (store.sequence) to contain 'requisition::onInit'" + "description": "Expecting 'task::onInit'-> sensor::onInit''-> actuator::onInit''-> actuator::onMessageReceived''-> actuator::onMessageReceived'' (store.sequence) to contain 'task::onInit'" }, { - "name": "Publisher name changed", + "name": "Actuator name changed", "valid": true, - "description": "Expected 'publisher.name' to be equal to 'changed'. Received 'changed'" + "description": "Expected 'actuator.name' to be equal to 'changed'. Received 'changed'" }, { - "name": "Publisher onInit executed", + "name": "Actuator onInit executed", "valid": true, - "description": "Expecting 'requisition::onInit-> subscription::onInit-> publisher::onInit-> publisher::onMessageReceived-> publisher::onMessageReceived' (store.sequence) to contain 'publisher::onInit'" + "description": "Expecting 'task::onInit'-> sensor::onInit''-> actuator::onInit''-> actuator::onMessageReceived''-> actuator::onMessageReceived'' (store.sequence) to contain 'actuator::onInit'" } ], "valid": true } }, "type": "tcp", - "publishTime": "2020-03-08T05:33:38.996Z" + "messageSentInstant": "2025-03-18T20:56:00.134Z" } ], "iteration": 0, @@ -915,9 +1006,9 @@ "valid": true, "tests": [ { - "name": "Requisitions exists onInit", + "name": "Tasks exists onInit", "valid": true, - "description": "Expecting 'requisition.name' to be defined" + "description": "Expecting 'task.name' to be defined" }, { "name": "Assertion #1", @@ -933,52 +1024,61 @@ "valid": true, "tests": [], "arguments": { - "elapsedTime": 2153 + "elapsedTime": 2126 } } }, "time": { - "startTime": "2020-03-08T05:33:36.843Z", - "endTime": "2020-03-08T05:33:38.996Z", - "totalTime": 2153, + "startTime": "2025-03-18T20:55:58.008Z", + "endTime": "2025-03-18T20:56:00.134Z", + "totalTime": 2126, "timeout": 3000 }, - "requisitions": [] + "tasks": [] }, { "valid": true, "name": "examples/http-auth-basic.yml", - "id": "0233360791_6420f75659_500201", + "id": "1355570987_91ac9852fd_972995", "level": 1, - "subscriptions": [ + "sensors": [ { - "id": "0233360791_94e20b540b_615297", - "name": "Subscription #0", + "id": "1355570987_27bcb0751a_80934", + "name": "Sensor #0", "type": "http", "hooks": { "onInit": { "valid": true, "tests": [], - "arguments": { - "elapsedTime": 0 - } + "arguments": {} }, "onFinish": { "valid": true, "tests": [ { + "implicit": true, "valid": true, "name": "Message received", - "description": "{\n \"headers\": {\n \"content-type\": \"application/json\",\n \"authorization\": \"Basic dXNlcjpwYXNzd29yZA==\",\n \"content-length\": \"10\",\n \"host\": \"localhost:23068\",\n \"connection\": \"close\"\n },\n \"params\": {},\n \"query\": {},\n \"url\": \"/basic\",\n \"body\": \"basic auth\",\n \"elapsedTime\": 155\n}" + "description": "{\n \"headers\": {\n \"host\": \"localhost:23068\",\n \"connection\": \"keep-alive\",\n \"content-type\": \"application/json\",\n \"authorization\": \"Basic dXNlcjpwYXNzd29yZA==\",\n \"accept\": \"*/*\",\n \"accept-language\": \"*\",\n \"sec-fetch-mode\": \"cors\",\n \"user-agent\": \"node\",\n \"accept-encoding\": \"gzip, deflate\",\n \"content-length\": \"10\"\n },\n \"params\": {},\n \"query\": {},\n \"url\": \"/basic\",\n \"body\": \"basic auth\"\n}" } ], "arguments": { - "executedHooks": [ - "onInit", - "onMessageReceived", - "onFinish" - ], - "elapsedTime": 186 + "executedHooks": { + "onInit": [], + "onMessageReceived": [ + "headers", + "params", + "query", + "url", + "body", + "elapsedTime" + ], + "onFinish": [ + "executedHooks", + "elapsedTime" + ] + }, + "elapsedTime": 170 } }, "onMessageReceived": { @@ -992,28 +1092,33 @@ ], "arguments": { "headers": { + "host": "localhost:23068", + "connection": "keep-alive", "content-type": "application/json", "authorization": "Basic dXNlcjpwYXNzd29yZA==", - "content-length": "10", - "host": "localhost:23068", - "connection": "close" + "accept": "*/*", + "accept-language": "*", + "sec-fetch-mode": "cors", + "user-agent": "node", + "accept-encoding": "gzip, deflate", + "content-length": "10" }, "params": {}, "query": {}, "url": "/basic", "body": "basic auth", - "elapsedTime": 155 + "elapsedTime": 153 } } }, "valid": true, - "subscriptionTime": "2020-03-08T05:33:36.951Z" + "sensorTime": "2025-03-18T20:55:58.111Z" } ], - "publishers": [ + "actuators": [ { - "id": "0233360791_f4551bd7af_263114", - "name": "Publisher #0", + "id": "1355570987_b0b95487e7_157099", + "name": "Actuator #0", "valid": true, "hooks": { "onInit": { @@ -1025,112 +1130,56 @@ }, "onFinish": { "arguments": { - "executedHooks": [ - "onInit", - "onResponseReceived", - "onMessageReceived", - "onFinish" - ], - "elapsedTime": 186 + "executedHooks": { + "onInit": [], + "onResponseReceived": [ + "status", + "statusCode", + "body", + "headers", + "elapsedTime" + ], + "onFinish": [ + "executedHooks", + "elapsedTime" + ] + }, + "elapsedTime": 170 }, "tests": [ { "name": "Published", "valid": true, - "description": "{\n \"statusCode\": 200,\n \"headers\": {\n \"x-powered-by\": \"Express\",\n \"access-control-allow-origin\": \"*\",\n \"access-control-allow-headers\": \"Origin, X-Requested-With, Content-Type, Accept\",\n \"content-type\": \"text/html; charset=utf-8\",\n \"content-length\": \"19\",\n \"etag\": \"W/\\\"13-nGN7LEwCUKXpQoLxNqKZXSglYNc\\\"\",\n \"date\": \"Sun, 08 Mar 2020 05:33:36 GMT\",\n \"connection\": \"close\"\n },\n \"body\": \"basic auth response\"\n}" + "description": "{\n \"statusCode\": 200,\n \"headers\": {\n \"access-control-allow-headers\": \"Origin, X-Requested-With, Content-Type, Accept\",\n \"access-control-allow-origin\": \"*\",\n \"connection\": \"keep-alive\",\n \"content-length\": \"19\",\n \"content-type\": \"text/html; charset=utf-8\",\n \"date\": \"Tue, 18 Mar 2025 20:55:58 GMT\",\n \"etag\": \"W/\\\"13-nGN7LEwCUKXpQoLxNqKZXSglYNc\\\"\",\n \"keep-alive\": \"timeout=5\",\n \"x-powered-by\": \"Express\"\n },\n \"body\": \"basic auth response\"\n}", + "implicit": true } ], "valid": true }, "onResponseReceived": { "arguments": { + "status": 200, "statusCode": 200, "body": "basic auth response", "headers": { - "x-powered-by": "Express", - "access-control-allow-origin": "*", "access-control-allow-headers": "Origin, X-Requested-With, Content-Type, Accept", - "content-type": "text/html; charset=utf-8", - "content-length": "19", - "etag": "W/\"13-nGN7LEwCUKXpQoLxNqKZXSglYNc\"", - "date": "Sun, 08 Mar 2020 05:33:36 GMT", - "connection": "close" - }, - "request": { - "uri": { - "protocol": "http:", - "slashes": true, - "auth": null, - "host": "localhost:23068", - "port": "23068", - "hostname": "localhost", - "hash": null, - "search": null, - "query": null, - "pathname": "/basic", - "path": "/basic", - "href": "http://localhost:23068/basic" - }, - "method": "post", - "headers": { - "content-type": "application/json", - "authorization": "Basic dXNlcjpwYXNzd29yZA==", - "Content-Length": 10 - } - } - }, - "tests": [], - "valid": true - }, - "onMessageReceived": { - "arguments": { - "statusCode": 200, - "body": "basic auth response", - "headers": { - "x-powered-by": "Express", "access-control-allow-origin": "*", - "access-control-allow-headers": "Origin, X-Requested-With, Content-Type, Accept", - "content-type": "text/html; charset=utf-8", + "connection": "keep-alive", "content-length": "19", + "content-type": "text/html; charset=utf-8", + "date": "Tue, 18 Mar 2025 20:55:58 GMT", "etag": "W/\"13-nGN7LEwCUKXpQoLxNqKZXSglYNc\"", - "date": "Sun, 08 Mar 2020 05:33:36 GMT", - "connection": "close" - }, - "request": { - "uri": { - "protocol": "http:", - "slashes": true, - "auth": null, - "host": "localhost:23068", - "port": "23068", - "hostname": "localhost", - "hash": null, - "search": null, - "query": null, - "pathname": "/basic", - "path": "/basic", - "href": "http://localhost:23068/basic" - }, - "method": "post", - "headers": { - "content-type": "application/json", - "authorization": "Basic dXNlcjpwYXNzd29yZA==", - "Content-Length": 10 - } - } + "keep-alive": "timeout=5", + "x-powered-by": "Express" + }, + "elapsedTime": 169 }, - "tests": [ - { - "name": "Assertion #0", - "valid": true, - "description": "Expected 'body' to be equal to 'basic auth response'. Received 'basic auth response'" - } - ], + "tests": [], "valid": true } }, "type": "http", - "publishTime": "2020-03-08T05:33:37.032Z" + "messageSentInstant": "2025-03-18T20:55:58.179Z" } ], "iteration": 0, @@ -1147,52 +1196,61 @@ "valid": true, "tests": [], "arguments": { - "elapsedTime": 187 + "elapsedTime": 170 } } }, "time": { - "startTime": "2020-03-08T05:33:36.846Z", - "endTime": "2020-03-08T05:33:37.033Z", - "totalTime": 187, + "startTime": "2025-03-18T20:55:58.010Z", + "endTime": "2025-03-18T20:55:58.180Z", + "totalTime": 170, "timeout": 3000 }, - "requisitions": [] + "tasks": [] }, { "valid": true, "name": "examples/http-auth-bearer.yml", - "id": "0233360791_5e821857a3_700740", + "id": "1355570987_4ea6593e25_828153", "level": 1, - "subscriptions": [ + "sensors": [ { - "id": "0233360792_dab3c9a05e_724388", - "name": "Subscription #0", + "id": "1355570987_66a2a1123e_112700", + "name": "Sensor #0", "type": "http", "hooks": { "onInit": { "valid": true, "tests": [], - "arguments": { - "elapsedTime": 0 - } + "arguments": {} }, "onFinish": { "valid": true, "tests": [ { + "implicit": true, "valid": true, "name": "Message received", - "description": "{\n \"headers\": {\n \"content-type\": \"application/json\",\n \"authorization\": \"Bearer bearerToken\",\n \"content-length\": \"4\",\n \"host\": \"localhost:23067\",\n \"connection\": \"close\"\n },\n \"params\": {},\n \"query\": {},\n \"url\": \"/bearer\",\n \"body\": \"Rech\",\n \"elapsedTime\": 157\n}" + "description": "{\n \"headers\": {\n \"host\": \"localhost:23067\",\n \"connection\": \"keep-alive\",\n \"content-type\": \"application/json\",\n \"authorization\": \"Bearer bearerToken\",\n \"accept\": \"*/*\",\n \"accept-language\": \"*\",\n \"sec-fetch-mode\": \"cors\",\n \"user-agent\": \"node\",\n \"accept-encoding\": \"gzip, deflate\",\n \"content-length\": \"4\"\n },\n \"params\": {},\n \"query\": {},\n \"url\": \"/bearer\",\n \"body\": \"Rech\"\n}" } ], "arguments": { - "executedHooks": [ - "onInit", - "onMessageReceived", - "onFinish" - ], - "elapsedTime": 186 + "executedHooks": { + "onInit": [], + "onMessageReceived": [ + "headers", + "params", + "query", + "url", + "body", + "elapsedTime" + ], + "onFinish": [ + "executedHooks", + "elapsedTime" + ] + }, + "elapsedTime": 172 } }, "onMessageReceived": { @@ -1206,11 +1264,16 @@ ], "arguments": { "headers": { + "host": "localhost:23067", + "connection": "keep-alive", "content-type": "application/json", "authorization": "Bearer bearerToken", - "content-length": "4", - "host": "localhost:23067", - "connection": "close" + "accept": "*/*", + "accept-language": "*", + "sec-fetch-mode": "cors", + "user-agent": "node", + "accept-encoding": "gzip, deflate", + "content-length": "4" }, "params": {}, "query": {}, @@ -1221,13 +1284,13 @@ } }, "valid": true, - "subscriptionTime": "2020-03-08T05:33:36.951Z" + "sensorTime": "2025-03-18T20:55:58.111Z" } ], - "publishers": [ + "actuators": [ { - "id": "0233360791_388c1593e3_970276", - "name": "Publisher #0", + "id": "1355570987_e17058e556_620375", + "name": "Actuator #0", "valid": true, "hooks": { "onInit": { @@ -1239,112 +1302,56 @@ }, "onFinish": { "arguments": { - "executedHooks": [ - "onInit", - "onResponseReceived", - "onMessageReceived", - "onFinish" - ], - "elapsedTime": 186 + "executedHooks": { + "onInit": [], + "onResponseReceived": [ + "status", + "statusCode", + "body", + "headers", + "elapsedTime" + ], + "onFinish": [ + "executedHooks", + "elapsedTime" + ] + }, + "elapsedTime": 172 }, "tests": [ { "name": "Published", "valid": true, - "description": "{\n \"statusCode\": 321,\n \"headers\": {\n \"x-powered-by\": \"Express\",\n \"access-control-allow-origin\": \"*\",\n \"access-control-allow-headers\": \"Origin, X-Requested-With, Content-Type, Accept\",\n \"content-type\": \"text/html; charset=utf-8\",\n \"content-length\": \"15\",\n \"etag\": \"W/\\\"f-V9sBEzo+y6k6sqGXsr1Ql2B0tAk\\\"\",\n \"date\": \"Sun, 08 Mar 2020 05:33:36 GMT\",\n \"connection\": \"close\"\n },\n \"body\": \"responsePayload\"\n}" + "description": "{\n \"statusCode\": 321,\n \"headers\": {\n \"access-control-allow-headers\": \"Origin, X-Requested-With, Content-Type, Accept\",\n \"access-control-allow-origin\": \"*\",\n \"connection\": \"keep-alive\",\n \"content-length\": \"15\",\n \"content-type\": \"text/html; charset=utf-8\",\n \"date\": \"Tue, 18 Mar 2025 20:55:58 GMT\",\n \"etag\": \"W/\\\"f-V9sBEzo+y6k6sqGXsr1Ql2B0tAk\\\"\",\n \"keep-alive\": \"timeout=5\",\n \"x-powered-by\": \"Express\"\n },\n \"body\": \"responsePayload\"\n}", + "implicit": true } ], "valid": true }, "onResponseReceived": { "arguments": { + "status": 321, "statusCode": 321, "body": "responsePayload", "headers": { - "x-powered-by": "Express", - "access-control-allow-origin": "*", "access-control-allow-headers": "Origin, X-Requested-With, Content-Type, Accept", - "content-type": "text/html; charset=utf-8", - "content-length": "15", - "etag": "W/\"f-V9sBEzo+y6k6sqGXsr1Ql2B0tAk\"", - "date": "Sun, 08 Mar 2020 05:33:36 GMT", - "connection": "close" - }, - "request": { - "uri": { - "protocol": "http:", - "slashes": true, - "auth": null, - "host": "localhost:23067", - "port": "23067", - "hostname": "localhost", - "hash": null, - "search": null, - "query": null, - "pathname": "/bearer", - "path": "/bearer", - "href": "http://localhost:23067/bearer" - }, - "method": "post", - "headers": { - "content-type": "application/json", - "authorization": "Bearer bearerToken", - "Content-Length": 4 - } - } - }, - "tests": [], - "valid": true - }, - "onMessageReceived": { - "arguments": { - "statusCode": 321, - "body": "responsePayload", - "headers": { - "x-powered-by": "Express", "access-control-allow-origin": "*", - "access-control-allow-headers": "Origin, X-Requested-With, Content-Type, Accept", - "content-type": "text/html; charset=utf-8", + "connection": "keep-alive", "content-length": "15", + "content-type": "text/html; charset=utf-8", + "date": "Tue, 18 Mar 2025 20:55:58 GMT", "etag": "W/\"f-V9sBEzo+y6k6sqGXsr1Ql2B0tAk\"", - "date": "Sun, 08 Mar 2020 05:33:36 GMT", - "connection": "close" - }, - "request": { - "uri": { - "protocol": "http:", - "slashes": true, - "auth": null, - "host": "localhost:23067", - "port": "23067", - "hostname": "localhost", - "hash": null, - "search": null, - "query": null, - "pathname": "/bearer", - "path": "/bearer", - "href": "http://localhost:23067/bearer" - }, - "method": "post", - "headers": { - "content-type": "application/json", - "authorization": "Bearer bearerToken", - "Content-Length": 4 - } - } + "keep-alive": "timeout=5", + "x-powered-by": "Express" + }, + "elapsedTime": 172 }, - "tests": [ - { - "name": "Body", - "valid": true, - "description": "Expected 'body' to be equal to 'responsePayload'. Received 'responsePayload'" - } - ], + "tests": [], "valid": true } }, "type": "http", - "publishTime": "2020-03-08T05:33:37.034Z" + "messageSentInstant": "2025-03-18T20:55:58.182Z" } ], "iteration": 0, @@ -1361,51 +1368,60 @@ "valid": true, "tests": [], "arguments": { - "elapsedTime": 186 + "elapsedTime": 172 } } }, "time": { - "startTime": "2020-03-08T05:33:36.848Z", - "endTime": "2020-03-08T05:33:37.034Z", - "totalTime": 186, + "startTime": "2025-03-18T20:55:58.010Z", + "endTime": "2025-03-18T20:55:58.182Z", + "totalTime": 172, "timeout": 3000 }, - "requisitions": [] + "tasks": [] }, { "valid": true, "name": "examples/http-auth-digest.yml", - "id": "0233360792_784968c016_727071", + "id": "1355570987_c58c79f583_313544", "level": 1, - "subscriptions": [ + "sensors": [ { - "id": "0233360792_b8f024a270_490320", - "name": "Subscription #0", + "id": "1355570987_caace08dbc_451494", + "name": "Sensor #0", "type": "http", "hooks": { "onInit": { "valid": true, "tests": [], - "arguments": { - "elapsedTime": 0 - } + "arguments": {} }, "onFinish": { "valid": true, "tests": [ { + "implicit": true, "valid": true, "name": "Message received", - "description": "{\n \"headers\": {\n \"content-type\": \"application/json\",\n \"authorization\": \"Digest username=\\\"guest\\\", realm=\\\"nqrRealm\\\", nonce=\\\"58bac26865505\\\", uri=\\\"/digest\\\", algorithm=\\\"MD5\\\", response=\\\"6309166f64557e9d56ffe6c34a0a6ca4\\\", opaque=\\\"opaque\\\"\",\n \"content-length\": \"4\",\n \"host\": \"localhost:23067\",\n \"connection\": \"close\"\n },\n \"params\": {},\n \"query\": {},\n \"url\": \"/digest\",\n \"body\": \"Rech\",\n \"elapsedTime\": 164\n}" + "description": "{\n \"headers\": {\n \"host\": \"localhost:23067\",\n \"connection\": \"keep-alive\",\n \"content-type\": \"application/json\",\n \"authorization\": \"Digest username=\\\"guest\\\", realm=\\\"nqrRealm\\\", nonce=\\\"58bac26865505\\\", uri=\\\"/digest\\\", algorithm=\\\"MD5\\\", response=\\\"6309166f64557e9d56ffe6c34a0a6ca4\\\", opaque=\\\"opaque\\\"\",\n \"accept\": \"*/*\",\n \"accept-language\": \"*\",\n \"sec-fetch-mode\": \"cors\",\n \"user-agent\": \"node\",\n \"accept-encoding\": \"gzip, deflate\",\n \"content-length\": \"4\"\n },\n \"params\": {},\n \"query\": {},\n \"url\": \"/digest\",\n \"body\": \"Rech\"\n}" } ], "arguments": { - "executedHooks": [ - "onInit", - "onMessageReceived", - "onFinish" - ], + "executedHooks": { + "onInit": [], + "onMessageReceived": [ + "headers", + "params", + "query", + "url", + "body", + "elapsedTime" + ], + "onFinish": [ + "executedHooks", + "elapsedTime" + ] + }, "elapsedTime": 186 } }, @@ -1420,28 +1436,33 @@ ], "arguments": { "headers": { + "host": "localhost:23067", + "connection": "keep-alive", "content-type": "application/json", "authorization": "Digest username=\"guest\", realm=\"nqrRealm\", nonce=\"58bac26865505\", uri=\"/digest\", algorithm=\"MD5\", response=\"6309166f64557e9d56ffe6c34a0a6ca4\", opaque=\"opaque\"", - "content-length": "4", - "host": "localhost:23067", - "connection": "close" + "accept": "*/*", + "accept-language": "*", + "sec-fetch-mode": "cors", + "user-agent": "node", + "accept-encoding": "gzip, deflate", + "content-length": "4" }, "params": {}, "query": {}, "url": "/digest", "body": "Rech", - "elapsedTime": 164 + "elapsedTime": 178 } } }, "valid": true, - "subscriptionTime": "2020-03-08T05:33:36.951Z" + "sensorTime": "2025-03-18T20:55:58.111Z" } ], - "publishers": [ + "actuators": [ { - "id": "0233360792_b553ccdd3b_466328", - "name": "Publisher #0", + "id": "1355570987_c0a69f13dd_953410", + "name": "Actuator #0", "valid": true, "hooks": { "onInit": { @@ -1453,112 +1474,56 @@ }, "onFinish": { "arguments": { - "executedHooks": [ - "onInit", - "onResponseReceived", - "onMessageReceived", - "onFinish" - ], + "executedHooks": { + "onInit": [], + "onResponseReceived": [ + "status", + "statusCode", + "body", + "headers", + "elapsedTime" + ], + "onFinish": [ + "executedHooks", + "elapsedTime" + ] + }, "elapsedTime": 186 }, "tests": [ { "name": "Published", "valid": true, - "description": "{\n \"statusCode\": 321,\n \"headers\": {\n \"x-powered-by\": \"Express\",\n \"access-control-allow-origin\": \"*\",\n \"access-control-allow-headers\": \"Origin, X-Requested-With, Content-Type, Accept\",\n \"content-type\": \"text/html; charset=utf-8\",\n \"content-length\": \"15\",\n \"etag\": \"W/\\\"f-V9sBEzo+y6k6sqGXsr1Ql2B0tAk\\\"\",\n \"date\": \"Sun, 08 Mar 2020 05:33:36 GMT\",\n \"connection\": \"close\"\n },\n \"body\": \"responsePayload\"\n}" + "description": "{\n \"statusCode\": 321,\n \"headers\": {\n \"access-control-allow-headers\": \"Origin, X-Requested-With, Content-Type, Accept\",\n \"access-control-allow-origin\": \"*\",\n \"connection\": \"keep-alive\",\n \"content-length\": \"15\",\n \"content-type\": \"text/html; charset=utf-8\",\n \"date\": \"Tue, 18 Mar 2025 20:55:58 GMT\",\n \"etag\": \"W/\\\"f-V9sBEzo+y6k6sqGXsr1Ql2B0tAk\\\"\",\n \"keep-alive\": \"timeout=5\",\n \"x-powered-by\": \"Express\"\n },\n \"body\": \"responsePayload\"\n}", + "implicit": true } ], "valid": true }, "onResponseReceived": { "arguments": { + "status": 321, "statusCode": 321, "body": "responsePayload", "headers": { - "x-powered-by": "Express", - "access-control-allow-origin": "*", "access-control-allow-headers": "Origin, X-Requested-With, Content-Type, Accept", - "content-type": "text/html; charset=utf-8", - "content-length": "15", - "etag": "W/\"f-V9sBEzo+y6k6sqGXsr1Ql2B0tAk\"", - "date": "Sun, 08 Mar 2020 05:33:36 GMT", - "connection": "close" - }, - "request": { - "uri": { - "protocol": "http:", - "slashes": true, - "auth": null, - "host": "localhost:23067", - "port": "23067", - "hostname": "localhost", - "hash": null, - "search": null, - "query": null, - "pathname": "/digest", - "path": "/digest", - "href": "http://localhost:23067/digest" - }, - "method": "post", - "headers": { - "content-type": "application/json", - "authorization": "Digest username=\"guest\", realm=\"nqrRealm\", nonce=\"58bac26865505\", uri=\"/digest\", algorithm=\"MD5\", response=\"6309166f64557e9d56ffe6c34a0a6ca4\", opaque=\"opaque\"", - "Content-Length": 4 - } - } - }, - "tests": [], - "valid": true - }, - "onMessageReceived": { - "arguments": { - "statusCode": 321, - "body": "responsePayload", - "headers": { - "x-powered-by": "Express", "access-control-allow-origin": "*", - "access-control-allow-headers": "Origin, X-Requested-With, Content-Type, Accept", - "content-type": "text/html; charset=utf-8", + "connection": "keep-alive", "content-length": "15", + "content-type": "text/html; charset=utf-8", + "date": "Tue, 18 Mar 2025 20:55:58 GMT", "etag": "W/\"f-V9sBEzo+y6k6sqGXsr1Ql2B0tAk\"", - "date": "Sun, 08 Mar 2020 05:33:36 GMT", - "connection": "close" - }, - "request": { - "uri": { - "protocol": "http:", - "slashes": true, - "auth": null, - "host": "localhost:23067", - "port": "23067", - "hostname": "localhost", - "hash": null, - "search": null, - "query": null, - "pathname": "/digest", - "path": "/digest", - "href": "http://localhost:23067/digest" - }, - "method": "post", - "headers": { - "content-type": "application/json", - "authorization": "Digest username=\"guest\", realm=\"nqrRealm\", nonce=\"58bac26865505\", uri=\"/digest\", algorithm=\"MD5\", response=\"6309166f64557e9d56ffe6c34a0a6ca4\", opaque=\"opaque\"", - "Content-Length": 4 - } - } + "keep-alive": "timeout=5", + "x-powered-by": "Express" + }, + "elapsedTime": 185 }, - "tests": [ - { - "name": "Body", - "valid": true, - "description": "Expected 'body' to be equal to 'responsePayload'. Received 'responsePayload'" - } - ], + "tests": [], "valid": true } }, "type": "http", - "publishTime": "2020-03-08T05:33:37.035Z" + "messageSentInstant": "2025-03-18T20:55:58.195Z" } ], "iteration": 0, @@ -1580,20 +1545,20 @@ } }, "time": { - "startTime": "2020-03-08T05:33:36.849Z", - "endTime": "2020-03-08T05:33:37.035Z", + "startTime": "2025-03-18T20:55:58.010Z", + "endTime": "2025-03-18T20:55:58.196Z", "totalTime": 186, "timeout": 3000 }, - "requisitions": [] + "tasks": [] }, { "valid": true, "name": "examples/http-more-examples.yml", - "id": "0233360792_a064465f3e_506602", + "id": "1355570987_4b98d9a6b6_427274", "level": 1, - "subscriptions": [], - "publishers": [], + "sensors": [], + "actuators": [], "iteration": 0, "totalIterations": 1, "hooks": { @@ -1608,188 +1573,231 @@ "valid": true, "tests": [], "arguments": { - "elapsedTime": 2380 + "elapsedTime": 2061 } } }, "time": { - "startTime": "2020-03-08T05:33:36.854Z", - "endTime": "2020-03-08T05:33:39.234Z", - "totalTime": 2380, + "startTime": "2025-03-18T20:55:58.011Z", + "endTime": "2025-03-18T20:56:00.072Z", + "totalTime": 2061, "timeout": 5000 }, - "requisitions": [ + "tasks": [ { "valid": true, - "name": "Requisition #0", - "id": "0233360792_f13fd961e3_570327", + "name": "task 2 (port 23076)", + "id": "1355570987_1d66bb275b_19279", "level": 2, - "subscriptions": [ + "sensors": [ { - "id": "0233360792_e916596275_656262", - "name": "Subscription #0", + "id": "1355570987_a3ff87bef2_712249", + "name": "sensor description", "type": "http", "hooks": { "onInit": { "valid": true, "tests": [], - "arguments": { - "elapsedTime": 0 - } + "arguments": {} }, "onFinish": { "valid": true, "tests": [ { "valid": true, - "name": "Message received", - "description": "{\n \"headers\": {\n \"content-type\": \"application/json\",\n \"nqr\": \"publisher\",\n \"content-length\": \"20\",\n \"host\": \"localhost:23075\",\n \"connection\": \"close\"\n },\n \"params\": {\n \"firstId\": \"idStuff\"\n },\n \"query\": {\n \"query\": \"2345\"\n },\n \"url\": \"/enqueuer/idStuff?query=2345\",\n \"body\": {\n \"enqueuer\": \"virgs\"\n },\n \"elapsedTime\": 138\n}" - } - ], - "arguments": { - "executedHooks": [ - "onInit", - "onMessageReceived", - "onFinish" - ], - "elapsedTime": 330 - } - }, - "onMessageReceived": { - "valid": true, - "tests": [ - { - "name": "Payload", - "valid": true, - "description": "Expected 'JSON.parse(body).enqueuer' to be equal to 'virgs'. Received 'virgs'" - }, - { - "name": "Params", - "valid": true, - "description": "Expected 'params.firstId' to be equal to 'idStuff'. Received 'idStuff'" - }, - { - "name": "Query", - "valid": true, - "description": "Expected 'query.query' to be equal to '2345'. Received '2345'" - }, - { - "name": "Header", - "valid": true, - "description": "Expected 'headers.nqr' to be equal to 'publisher'. Received 'publisher'" + "implicit": true, + "name": "Sensor avoided", + "description": "Avoidable sensor has not received any message" } ], "arguments": { - "headers": { - "content-type": "application/json", - "nqr": "publisher", - "content-length": "20", - "host": "localhost:23075", - "connection": "close" - }, - "params": { - "firstId": "idStuff" - }, - "query": { - "query": "2345" + "executedHooks": { + "onInit": [], + "onFinish": [ + "executedHooks", + "elapsedTime" + ] }, - "url": "/enqueuer/idStuff?query=2345", - "body": "{\"enqueuer\":\"virgs\"}", - "elapsedTime": 138 + "elapsedTime": 1045 } } }, "valid": true, - "subscriptionTime": "2020-03-08T05:33:36.951Z" + "sensorTime": "2025-03-18T20:55:58.111Z" + } + ], + "actuators": [], + "iteration": 0, + "totalIterations": 1, + "hooks": { + "onInit": { + "valid": true, + "tests": [], + "arguments": { + "elapsedTime": 0 + } }, + "onFinish": { + "valid": true, + "tests": [], + "arguments": { + "elapsedTime": 1045 + } + } + }, + "time": { + "startTime": "2025-03-18T20:55:58.020Z", + "endTime": "2025-03-18T20:55:59.065Z", + "totalTime": 1045, + "timeout": 5000 + }, + "tasks": [] + }, + { + "valid": true, + "name": "check port releasing (23076)", + "id": "1355570987_86c658be6d_178684", + "level": 2, + "sensors": [ { - "id": "0233360792_ba5a509689_54847", - "name": "same port", - "type": "http", + "id": "1355570987_7c918dc838_384996", + "name": "Sensor #0", + "type": "tcp", "hooks": { "onInit": { "valid": true, "tests": [], - "arguments": { - "elapsedTime": 0 - } + "arguments": {} }, "onFinish": { "valid": true, "tests": [ { "valid": true, - "name": "Message received", - "description": "{\n \"headers\": {\n \"content-length\": \"5\",\n \"host\": \"localhost:23075\",\n \"connection\": \"close\"\n },\n \"params\": {},\n \"query\": {},\n \"url\": \"/samePort\",\n \"body\": \"virgs\",\n \"elapsedTime\": 140\n}" + "implicit": true, + "name": "Sensor avoided", + "description": "Avoidable sensor has not received any message" } ], "arguments": { - "executedHooks": [ - "onInit", - "onMessageReceived", - "onFinish" - ], - "elapsedTime": 330 - } - }, - "onMessageReceived": { - "valid": true, - "tests": [], - "arguments": { - "headers": { - "content-length": "5", - "host": "localhost:23075", - "connection": "close" + "executedHooks": { + "onInit": [], + "onFinish": [ + "executedHooks", + "elapsedTime" + ] }, - "params": {}, - "query": {}, - "url": "/samePort", - "body": "virgs", - "elapsedTime": 140 + "elapsedTime": 1000 } } }, "valid": true, - "subscriptionTime": "2020-03-08T05:33:36.951Z" + "sensorTime": "2025-03-18T20:55:59.066Z" + } + ], + "actuators": [], + "iteration": 0, + "totalIterations": 1, + "hooks": { + "onInit": { + "valid": true, + "tests": [], + "arguments": { + "elapsedTime": 0 + } }, + "onFinish": { + "valid": true, + "tests": [], + "arguments": { + "elapsedTime": 1000 + } + } + }, + "time": { + "startTime": "2025-03-18T20:55:59.065Z", + "endTime": "2025-03-18T20:56:00.065Z", + "totalTime": 1000, + "timeout": 5000 + }, + "tasks": [] + }, + { + "valid": true, + "name": "Task #2", + "id": "1355570987_30798b98b3_741181", + "level": 2, + "sensors": [ { - "id": "0233360792_42d8fc1293_552948", - "name": "yet another, but avoidable", + "id": "1355570987_69863e6e8c_857753", + "name": "Sensor #0", "type": "http", "hooks": { "onInit": { "valid": true, "tests": [], - "arguments": { - "elapsedTime": 0 - } + "arguments": {} }, "onFinish": { "valid": true, "tests": [ { + "implicit": true, "valid": true, - "name": "Subscription avoided", - "description": "Avoidable subscription has not received any message" + "name": "Message received", + "description": "{\n \"headers\": {\n \"host\": \"localhost:23095\",\n \"connection\": \"keep-alive\",\n \"content-type\": \"text/plain;charset=UTF-8\",\n \"accept\": \"*/*\",\n \"accept-language\": \"*\",\n \"sec-fetch-mode\": \"cors\",\n \"user-agent\": \"node\",\n \"accept-encoding\": \"gzip, deflate\",\n \"content-length\": \"5\"\n },\n \"params\": {},\n \"query\": {},\n \"url\": \"/number-payload\",\n \"body\": \"virgs\"\n}" } ], "arguments": { - "executedHooks": [ - "onInit", - "onFinish" - ], - "elapsedTime": 330 + "executedHooks": { + "onInit": [], + "onMessageReceived": [ + "headers", + "params", + "query", + "url", + "body", + "elapsedTime" + ], + "onFinish": [ + "executedHooks", + "elapsedTime" + ] + }, + "elapsedTime": 6 + } + }, + "onMessageReceived": { + "valid": true, + "tests": [], + "arguments": { + "headers": { + "host": "localhost:23095", + "connection": "keep-alive", + "content-type": "text/plain;charset=UTF-8", + "accept": "*/*", + "accept-language": "*", + "sec-fetch-mode": "cors", + "user-agent": "node", + "accept-encoding": "gzip, deflate", + "content-length": "5" + }, + "params": {}, + "query": {}, + "url": "/number-payload", + "body": "virgs", + "elapsedTime": 5 } } }, "valid": true, - "subscriptionTime": "2020-03-08T05:33:36.951Z" + "sensorTime": "2025-03-18T20:56:00.067Z" } ], - "publishers": [ + "actuators": [ { - "id": "0233360792_acd0fb7e94_23678", - "name": "Publisher #0", + "id": "1355570987_cfb6df28b5_923374", + "name": "Actuator #0", "valid": true, "hooks": { "onInit": { @@ -1801,250 +1809,191 @@ }, "onFinish": { "arguments": { - "executedHooks": [ - "onInit", - "onResponseReceived", - "onMessageReceived", - "onFinish" - ], - "elapsedTime": 330 + "executedHooks": { + "onInit": [], + "onResponseReceived": [ + "status", + "statusCode", + "body", + "headers", + "elapsedTime" + ], + "onFinish": [ + "executedHooks", + "elapsedTime" + ] + }, + "elapsedTime": 6 }, "tests": [ { "name": "Published", "valid": true, - "description": "{\n \"statusCode\": 321,\n \"headers\": {\n \"x-powered-by\": \"Express\",\n \"access-control-allow-origin\": \"*\",\n \"access-control-allow-headers\": \"Origin, X-Requested-With, Content-Type, Accept\",\n \"nqr\": \"subscription\",\n \"content-type\": \"text/html; charset=utf-8\",\n \"content-length\": \"27\",\n \"etag\": \"W/\\\"1b-e5esTWfu+XftewZ5g2Tclr7ClTo\\\"\",\n \"date\": \"Sun, 08 Mar 2020 05:33:36 GMT\",\n \"connection\": \"close\"\n },\n \"body\": \"dynamically changed payload\"\n}" + "description": "{\n \"statusCode\": 444,\n \"headers\": {\n \"access-control-allow-headers\": \"Origin, X-Requested-With, Content-Type, Accept\",\n \"access-control-allow-origin\": \"*\",\n \"connection\": \"keep-alive\",\n \"content-length\": \"4\",\n \"content-type\": \"text/html; charset=utf-8\",\n \"date\": \"Tue, 18 Mar 2025 20:56:00 GMT\",\n \"etag\": \"W/\\\"4-HLLQjeteIwK7Xuhlj4t8Bu7p124\\\"\",\n \"keep-alive\": \"timeout=5\",\n \"x-powered-by\": \"Express\"\n },\n \"body\": 4.45\n}", + "implicit": true } ], "valid": true }, "onResponseReceived": { "arguments": { - "statusCode": 321, - "body": "dynamically changed payload", + "status": 444, + "statusCode": 444, + "body": "4.45", "headers": { - "x-powered-by": "Express", - "access-control-allow-origin": "*", "access-control-allow-headers": "Origin, X-Requested-With, Content-Type, Accept", - "nqr": "subscription", - "content-type": "text/html; charset=utf-8", - "content-length": "27", - "etag": "W/\"1b-e5esTWfu+XftewZ5g2Tclr7ClTo\"", - "date": "Sun, 08 Mar 2020 05:33:36 GMT", - "connection": "close" - }, - "request": { - "uri": { - "protocol": "http:", - "slashes": true, - "auth": null, - "host": "localhost:23075", - "port": "23075", - "hostname": "localhost", - "hash": null, - "search": "?query=2345", - "query": "query=2345", - "pathname": "/enqueuer/idStuff", - "path": "/enqueuer/idStuff?query=2345", - "href": "http://localhost:23075/enqueuer/idStuff?query=2345" - }, - "method": "post", - "headers": { - "content-type": "application/json", - "nqr": "publisher", - "Content-Length": 20 - } - } - }, - "tests": [ - { - "name": "Status Code", - "valid": true, - "description": "Expected 'statusCode' to be equal to '321'. Received '321'" - }, - { - "name": "Body", - "valid": true, - "description": "Expected 'body' to be equal to 'dynamically changed payload'. Received 'dynamically changed payload'" - }, - { - "name": "Header", - "valid": true, - "description": "Expected 'headers.nqr' to be equal to 'subscription'. Received 'subscription'" - } - ], - "valid": true - }, - "onMessageReceived": { - "arguments": { - "statusCode": 321, - "body": "dynamically changed payload", - "headers": { - "x-powered-by": "Express", "access-control-allow-origin": "*", - "access-control-allow-headers": "Origin, X-Requested-With, Content-Type, Accept", - "nqr": "subscription", + "connection": "keep-alive", + "content-length": "4", "content-type": "text/html; charset=utf-8", - "content-length": "27", - "etag": "W/\"1b-e5esTWfu+XftewZ5g2Tclr7ClTo\"", - "date": "Sun, 08 Mar 2020 05:33:36 GMT", - "connection": "close" + "date": "Tue, 18 Mar 2025 20:56:00 GMT", + "etag": "W/\"4-HLLQjeteIwK7Xuhlj4t8Bu7p124\"", + "keep-alive": "timeout=5", + "x-powered-by": "Express" }, - "request": { - "uri": { - "protocol": "http:", - "slashes": true, - "auth": null, - "host": "localhost:23075", - "port": "23075", - "hostname": "localhost", - "hash": null, - "search": "?query=2345", - "query": "query=2345", - "pathname": "/enqueuer/idStuff", - "path": "/enqueuer/idStuff?query=2345", - "href": "http://localhost:23075/enqueuer/idStuff?query=2345" - }, - "method": "post", - "headers": { - "content-type": "application/json", - "nqr": "publisher", - "Content-Length": 20 - } - } + "elapsedTime": 6 }, "tests": [], "valid": true } }, "type": "http", - "publishTime": "2020-03-08T05:33:37.038Z" + "messageSentInstant": "2025-03-18T20:56:00.072Z" + } + ], + "iteration": 0, + "totalIterations": 1, + "hooks": { + "onInit": { + "valid": true, + "tests": [], + "arguments": { + "elapsedTime": 0 + } }, - { - "id": "0233360792_72ad57ec09_983313", - "name": "Publisher #1", + "onFinish": { "valid": true, + "tests": [], + "arguments": { + "elapsedTime": 6 + } + } + }, + "time": { + "startTime": "2025-03-18T20:56:00.066Z", + "endTime": "2025-03-18T20:56:00.072Z", + "totalTime": 6, + "timeout": 5000 + }, + "tasks": [] + } + ] + }, + { + "valid": true, + "name": "examples/http-parallel.yml", + "id": "1355570988_9706eda42d_426011", + "level": 1, + "sensors": [], + "actuators": [], + "iteration": 0, + "totalIterations": 1, + "hooks": { + "onInit": { + "valid": true, + "tests": [], + "arguments": { + "elapsedTime": 0 + } + }, + "onFinish": { + "valid": true, + "tests": [], + "arguments": { + "elapsedTime": 423 + } + } + }, + "time": { + "startTime": "2025-03-18T20:55:58.011Z", + "endTime": "2025-03-18T20:55:58.434Z", + "totalTime": 423, + "timeout": 10000 + }, + "tasks": [ + { + "valid": true, + "name": "Task #0", + "id": "1355570988_78a743ddb1_779103", + "level": 2, + "sensors": [ + { + "id": "1355570988_8e05d812cd_824573", + "name": "Sensor #0", + "type": "http", "hooks": { "onInit": { - "arguments": { - "elapsedTime": 0 - }, + "valid": true, "tests": [], - "valid": true + "arguments": {} }, "onFinish": { - "arguments": { - "executedHooks": [ - "onInit", - "onResponseReceived", - "onMessageReceived", - "onFinish" - ], - "elapsedTime": 330 - }, + "valid": true, "tests": [ { - "name": "Published", + "implicit": true, "valid": true, - "description": "{\n \"statusCode\": 444,\n \"headers\": {\n \"x-powered-by\": \"Express\",\n \"access-control-allow-origin\": \"*\",\n \"access-control-allow-headers\": \"Origin, X-Requested-With, Content-Type, Accept\",\n \"content-type\": \"application/json; charset=utf-8\",\n \"content-length\": \"17\",\n \"etag\": \"W/\\\"11-nJYBwrCE3yNNre0RUpA2SJDxlIY\\\"\",\n \"date\": \"Sun, 08 Mar 2020 05:33:36 GMT\",\n \"connection\": \"close\"\n },\n \"body\": {\n \"deep\": \"object\"\n }\n}" + "name": "Message received", + "description": "{\n \"headers\": {\n \"host\": \"localhost:23023\",\n \"connection\": \"keep-alive\",\n \"accept\": \"*/*\",\n \"accept-language\": \"*\",\n \"sec-fetch-mode\": \"cors\",\n \"user-agent\": \"node\",\n \"accept-encoding\": \"gzip, deflate\"\n },\n \"params\": {},\n \"query\": {},\n \"url\": \"/first\",\n \"body\": \"\"\n}" } ], - "valid": true - }, - "onResponseReceived": { "arguments": { - "statusCode": 444, - "body": "{\"deep\":\"object\"}", - "headers": { - "x-powered-by": "Express", - "access-control-allow-origin": "*", - "access-control-allow-headers": "Origin, X-Requested-With, Content-Type, Accept", - "content-type": "application/json; charset=utf-8", - "content-length": "17", - "etag": "W/\"11-nJYBwrCE3yNNre0RUpA2SJDxlIY\"", - "date": "Sun, 08 Mar 2020 05:33:36 GMT", - "connection": "close" - }, - "request": { - "uri": { - "protocol": "http:", - "slashes": true, - "auth": null, - "host": "localhost:23075", - "port": "23075", - "hostname": "localhost", - "hash": null, - "search": null, - "query": null, - "pathname": "/samePort", - "path": "/samePort", - "href": "http://localhost:23075/samePort" - }, - "method": "post", - "headers": { - "Content-Length": 5 - } - } - }, - "tests": [ - { - "name": "Status Code", - "valid": true, - "description": "Expected 'statusCode' to be equal to '444'. Received '444'" + "executedHooks": { + "onInit": [], + "onMessageReceived": [ + "headers", + "params", + "query", + "url", + "body", + "elapsedTime" + ], + "onFinish": [ + "executedHooks", + "elapsedTime" + ] }, - { - "name": "Assertion #1", - "valid": true, - "description": "Expected 'JSON.parse(body).deep' to be equal to 'object'. Received 'object'" - } - ], - "valid": true + "elapsedTime": 208 + } }, "onMessageReceived": { + "valid": true, + "tests": [], "arguments": { - "statusCode": 444, - "body": "{\"deep\":\"object\"}", "headers": { - "x-powered-by": "Express", - "access-control-allow-origin": "*", - "access-control-allow-headers": "Origin, X-Requested-With, Content-Type, Accept", - "content-type": "application/json; charset=utf-8", - "content-length": "17", - "etag": "W/\"11-nJYBwrCE3yNNre0RUpA2SJDxlIY\"", - "date": "Sun, 08 Mar 2020 05:33:36 GMT", - "connection": "close" + "host": "localhost:23023", + "connection": "keep-alive", + "accept": "*/*", + "accept-language": "*", + "sec-fetch-mode": "cors", + "user-agent": "node", + "accept-encoding": "gzip, deflate" }, - "request": { - "uri": { - "protocol": "http:", - "slashes": true, - "auth": null, - "host": "localhost:23075", - "port": "23075", - "hostname": "localhost", - "hash": null, - "search": null, - "query": null, - "pathname": "/samePort", - "path": "/samePort", - "href": "http://localhost:23075/samePort" - }, - "method": "post", - "headers": { - "Content-Length": 5 - } - } - }, - "tests": [], - "valid": true + "params": {}, + "query": {}, + "url": "/first", + "body": "", + "elapsedTime": 410 + } } }, - "type": "http", - "publishTime": "2020-03-08T05:33:37.038Z" + "valid": true, + "sensorTime": "2025-03-18T20:55:58.111Z" } ], + "actuators": [], "iteration": 0, - "totalIterations": 1, + "totalIterations": 2, "hooks": { "onInit": { "valid": true, @@ -2057,214 +2006,91 @@ "valid": true, "tests": [], "arguments": { - "elapsedTime": 330 + "elapsedTime": 208 } } }, "time": { - "startTime": "2020-03-08T05:33:36.883Z", - "endTime": "2020-03-08T05:33:37.213Z", - "totalTime": 330, - "timeout": 3000 + "startTime": "2025-03-18T20:55:58.021Z", + "endTime": "2025-03-18T20:55:58.229Z", + "totalTime": 208, + "timeout": 10000 }, - "requisitions": [] + "tasks": [] }, { "valid": true, - "name": "Requisition #1", - "id": "0233360792_16e7db54dd_843746", + "name": "Task #0", + "id": "1355570988_78a743ddb1_779103", "level": 2, - "subscriptions": [ + "sensors": [ { - "id": "0233360792_34a6878700_105100", - "name": "Subscription #0", + "id": "1355570988_8e05d812cd_824573", + "name": "Sensor #0", "type": "http", "hooks": { "onInit": { "valid": true, "tests": [], - "arguments": { - "elapsedTime": 0 - } + "arguments": {} }, "onFinish": { "valid": true, "tests": [ { + "implicit": true, "valid": true, "name": "Message received", - "description": "{\n \"headers\": {\n \"content-type\": \"application/json\",\n \"content-length\": \"19\",\n \"host\": \"localhost:23075\",\n \"connection\": \"close\"\n },\n \"params\": {\n \"secondId\": \"idStuff\"\n },\n \"query\": {\n \"query\": \"111\"\n },\n \"url\": \"/enqueuer/idStuff?query=111\",\n \"body\": {\n \"duplicated\": true\n },\n \"elapsedTime\": 3\n}" + "description": "{\n \"headers\": {\n \"host\": \"localhost:23023\",\n \"connection\": \"keep-alive\",\n \"accept\": \"*/*\",\n \"accept-language\": \"*\",\n \"sec-fetch-mode\": \"cors\",\n \"user-agent\": \"node\",\n \"accept-encoding\": \"gzip, deflate\"\n },\n \"params\": {},\n \"query\": {},\n \"url\": \"/first\",\n \"body\": \"\"\n}" } ], "arguments": { - "executedHooks": [ - "onInit", - "onMessageReceived", - "onFinish" - ], - "elapsedTime": 4 + "executedHooks": { + "onInit": [], + "onMessageReceived": [ + "headers", + "params", + "query", + "url", + "body", + "elapsedTime" + ], + "onFinish": [ + "executedHooks", + "elapsedTime" + ] + }, + "elapsedTime": 203 } }, "onMessageReceived": { "valid": true, - "tests": [ - { - "name": "Payload", - "valid": true, - "description": "Expecting 'JSON.parse(body).duplicated' to be true. Received: true" - } - ], + "tests": [], "arguments": { "headers": { - "content-type": "application/json", - "content-length": "19", - "host": "localhost:23075", - "connection": "close" - }, - "params": { - "secondId": "idStuff" - }, - "query": { - "query": "111" + "host": "localhost:23023", + "connection": "keep-alive", + "accept": "*/*", + "accept-language": "*", + "sec-fetch-mode": "cors", + "user-agent": "node", + "accept-encoding": "gzip, deflate" }, - "url": "/enqueuer/idStuff?query=111", - "body": "{\"duplicated\":true}", - "elapsedTime": 3 + "params": {}, + "query": {}, + "url": "/first", + "body": "", + "elapsedTime": 202 } } }, "valid": true, - "subscriptionTime": "2020-03-08T05:33:37.216Z" - } - ], - "publishers": [ - { - "id": "0233360792_45f272e419_55232", - "name": "publisher description", - "valid": true, - "hooks": { - "onInit": { - "arguments": { - "elapsedTime": 0 - }, - "tests": [], - "valid": true - }, - "onFinish": { - "arguments": { - "executedHooks": [ - "onInit", - "onResponseReceived", - "onMessageReceived", - "onFinish" - ], - "elapsedTime": 4 - }, - "tests": [ - { - "name": "Published", - "valid": true, - "description": "{\n \"statusCode\": 321,\n \"headers\": {\n \"x-powered-by\": \"Express\",\n \"access-control-allow-origin\": \"*\",\n \"access-control-allow-headers\": \"Origin, X-Requested-With, Content-Type, Accept\",\n \"content-type\": \"text/html; charset=utf-8\",\n \"content-length\": \"25\",\n \"etag\": \"W/\\\"19-yZRAgggcER0sMyRTVBBpErTPT/A\\\"\",\n \"date\": \"Sun, 08 Mar 2020 05:33:37 GMT\",\n \"connection\": \"close\"\n },\n \"body\": \"duplicatedResponsePayload\"\n}" - } - ], - "valid": true - }, - "onResponseReceived": { - "arguments": { - "statusCode": 321, - "body": "duplicatedResponsePayload", - "headers": { - "x-powered-by": "Express", - "access-control-allow-origin": "*", - "access-control-allow-headers": "Origin, X-Requested-With, Content-Type, Accept", - "content-type": "text/html; charset=utf-8", - "content-length": "25", - "etag": "W/\"19-yZRAgggcER0sMyRTVBBpErTPT/A\"", - "date": "Sun, 08 Mar 2020 05:33:37 GMT", - "connection": "close" - }, - "request": { - "uri": { - "protocol": "http:", - "slashes": true, - "auth": null, - "host": "localhost:23075", - "port": "23075", - "hostname": "localhost", - "hash": null, - "search": "?query=111", - "query": "query=111", - "pathname": "/enqueuer/idStuff", - "path": "/enqueuer/idStuff?query=111", - "href": "http://localhost:23075/enqueuer/idStuff?query=111" - }, - "method": "post", - "headers": { - "content-type": "application/json", - "Content-Length": 19 - } - } - }, - "tests": [ - { - "name": "Status Code", - "valid": true, - "description": "Expected 'statusCode' to be equal to '321'. Received '321'" - }, - { - "name": "Body", - "valid": true, - "description": "Expected 'body' to be equal to 'duplicatedResponsePayload'. Received 'duplicatedResponsePayload'" - } - ], - "valid": true - }, - "onMessageReceived": { - "arguments": { - "statusCode": 321, - "body": "duplicatedResponsePayload", - "headers": { - "x-powered-by": "Express", - "access-control-allow-origin": "*", - "access-control-allow-headers": "Origin, X-Requested-With, Content-Type, Accept", - "content-type": "text/html; charset=utf-8", - "content-length": "25", - "etag": "W/\"19-yZRAgggcER0sMyRTVBBpErTPT/A\"", - "date": "Sun, 08 Mar 2020 05:33:37 GMT", - "connection": "close" - }, - "request": { - "uri": { - "protocol": "http:", - "slashes": true, - "auth": null, - "host": "localhost:23075", - "port": "23075", - "hostname": "localhost", - "hash": null, - "search": "?query=111", - "query": "query=111", - "pathname": "/enqueuer/idStuff", - "path": "/enqueuer/idStuff?query=111", - "href": "http://localhost:23075/enqueuer/idStuff?query=111" - }, - "method": "post", - "headers": { - "content-type": "application/json", - "Content-Length": 19 - } - } - }, - "tests": [], - "valid": true - } - }, - "type": "http", - "publishTime": "2020-03-08T05:33:37.219Z" + "sensorTime": "2025-03-18T20:55:58.230Z" } ], - "iteration": 0, - "totalIterations": 1, + "actuators": [], + "iteration": 1, + "totalIterations": 2, "hooks": { "onInit": { "valid": true, @@ -2277,61 +2103,91 @@ "valid": true, "tests": [], "arguments": { - "elapsedTime": 4 + "elapsedTime": 203 } } }, "time": { - "startTime": "2020-03-08T05:33:37.215Z", - "endTime": "2020-03-08T05:33:37.219Z", - "totalTime": 4, - "timeout": 5000 + "startTime": "2025-03-18T20:55:58.229Z", + "endTime": "2025-03-18T20:55:58.432Z", + "totalTime": 203, + "timeout": 10000 }, - "requisitions": [] + "tasks": [] }, { "valid": true, - "name": "requisition 2 (port 23076)", - "id": "0233360792_eff03cbee0_317968", + "name": "Task #1", + "id": "1355570988_d5240575d8_282139", "level": 2, - "subscriptions": [ + "sensors": [ { - "id": "0233360792_e5e0d9ff8c_299307", - "name": "subscription description", + "id": "1355570988_84d15a9cc7_206121", + "name": "Sensor #0", "type": "http", "hooks": { "onInit": { "valid": true, "tests": [], - "arguments": { - "elapsedTime": 0 - } + "arguments": {} }, "onFinish": { "valid": true, "tests": [ { + "implicit": true, "valid": true, - "name": "Subscription avoided", - "description": "Avoidable subscription has not received any message" + "name": "Message received", + "description": "{\n \"headers\": {\n \"host\": \"localhost:23023\",\n \"connection\": \"keep-alive\",\n \"accept\": \"*/*\",\n \"accept-language\": \"*\",\n \"sec-fetch-mode\": \"cors\",\n \"user-agent\": \"node\",\n \"accept-encoding\": \"gzip, deflate\"\n },\n \"params\": {},\n \"query\": {},\n \"url\": \"/second\",\n \"body\": \"\"\n}" } ], "arguments": { - "executedHooks": [ - "onInit", - "onFinish" - ], - "elapsedTime": 1002 + "executedHooks": { + "onInit": [], + "onMessageReceived": [ + "headers", + "params", + "query", + "url", + "body", + "elapsedTime" + ], + "onFinish": [ + "executedHooks", + "elapsedTime" + ] + }, + "elapsedTime": 210 + } + }, + "onMessageReceived": { + "valid": true, + "tests": [], + "arguments": { + "headers": { + "host": "localhost:23023", + "connection": "keep-alive", + "accept": "*/*", + "accept-language": "*", + "sec-fetch-mode": "cors", + "user-agent": "node", + "accept-encoding": "gzip, deflate" + }, + "params": {}, + "query": {}, + "url": "/second", + "body": "", + "elapsedTime": 412 } } }, "valid": true, - "subscriptionTime": "2020-03-08T05:33:37.220Z" + "sensorTime": "2025-03-18T20:55:58.111Z" } ], - "publishers": [], + "actuators": [], "iteration": 0, - "totalIterations": 1, + "totalIterations": 2, "hooks": { "onInit": { "valid": true, @@ -2344,61 +2200,91 @@ "valid": true, "tests": [], "arguments": { - "elapsedTime": 1002 + "elapsedTime": 210 } } }, "time": { - "startTime": "2020-03-08T05:33:37.220Z", - "endTime": "2020-03-08T05:33:38.222Z", - "totalTime": 1002, - "timeout": 5000 + "startTime": "2025-03-18T20:55:58.021Z", + "endTime": "2025-03-18T20:55:58.231Z", + "totalTime": 210, + "timeout": 10000 }, - "requisitions": [] + "tasks": [] }, { "valid": true, - "name": "check port releasing (23076)", - "id": "0233360792_33f3725983_323203", + "name": "Task #1", + "id": "1355570988_d5240575d8_282139", "level": 2, - "subscriptions": [ + "sensors": [ { - "id": "0233360792_eb12b4faec_77756", - "name": "Subscription #0", - "type": "tcp", + "id": "1355570988_84d15a9cc7_206121", + "name": "Sensor #0", + "type": "http", "hooks": { "onInit": { "valid": true, "tests": [], - "arguments": { - "elapsedTime": 0 - } + "arguments": {} }, "onFinish": { "valid": true, "tests": [ { + "implicit": true, "valid": true, - "name": "Subscription avoided", - "description": "Avoidable subscription has not received any message" + "name": "Message received", + "description": "{\n \"headers\": {\n \"host\": \"localhost:23023\",\n \"connection\": \"keep-alive\",\n \"accept\": \"*/*\",\n \"accept-language\": \"*\",\n \"sec-fetch-mode\": \"cors\",\n \"user-agent\": \"node\",\n \"accept-encoding\": \"gzip, deflate\"\n },\n \"params\": {},\n \"query\": {},\n \"url\": \"/second\",\n \"body\": \"\"\n}" } ], "arguments": { - "executedHooks": [ - "onInit", - "onFinish" - ], - "elapsedTime": 1004 + "executedHooks": { + "onInit": [], + "onMessageReceived": [ + "headers", + "params", + "query", + "url", + "body", + "elapsedTime" + ], + "onFinish": [ + "executedHooks", + "elapsedTime" + ] + }, + "elapsedTime": 203 + } + }, + "onMessageReceived": { + "valid": true, + "tests": [], + "arguments": { + "headers": { + "host": "localhost:23023", + "connection": "keep-alive", + "accept": "*/*", + "accept-language": "*", + "sec-fetch-mode": "cors", + "user-agent": "node", + "accept-encoding": "gzip, deflate" + }, + "params": {}, + "query": {}, + "url": "/second", + "body": "", + "elapsedTime": 202 } } }, "valid": true, - "subscriptionTime": "2020-03-08T05:33:38.224Z" + "sensorTime": "2025-03-18T20:55:58.231Z" } ], - "publishers": [], - "iteration": 0, - "totalIterations": 1, + "actuators": [], + "iteration": 1, + "totalIterations": 2, "hooks": { "onInit": { "valid": true, @@ -2411,79 +2297,28 @@ "valid": true, "tests": [], "arguments": { - "elapsedTime": 1004 + "elapsedTime": 203 } } }, "time": { - "startTime": "2020-03-08T05:33:38.223Z", - "endTime": "2020-03-08T05:33:39.227Z", - "totalTime": 1004, - "timeout": 5000 + "startTime": "2025-03-18T20:55:58.231Z", + "endTime": "2025-03-18T20:55:58.434Z", + "totalTime": 203, + "timeout": 10000 }, - "requisitions": [] + "tasks": [] }, { "valid": true, - "name": "Requisition #4", - "id": "0233360792_33d13bbd7c_372869", + "name": "Task #2", + "id": "1355570988_9e23299bad_75822", "level": 2, - "subscriptions": [ - { - "id": "0233360792_017ca7e673_328443", - "name": "numbered payload", - "type": "http", - "hooks": { - "onInit": { - "valid": true, - "tests": [], - "arguments": { - "elapsedTime": 0 - } - }, - "onFinish": { - "valid": true, - "tests": [ - { - "valid": true, - "name": "Message received", - "description": "{\n \"headers\": {\n \"content-length\": \"5\",\n \"host\": \"localhost:23080\",\n \"connection\": \"close\"\n },\n \"params\": {},\n \"query\": {},\n \"url\": \"/number-payload\",\n \"body\": \"virgs\",\n \"elapsedTime\": 3\n}" - } - ], - "arguments": { - "executedHooks": [ - "onInit", - "onMessageReceived", - "onFinish" - ], - "elapsedTime": 5 - } - }, - "onMessageReceived": { - "valid": true, - "tests": [], - "arguments": { - "headers": { - "content-length": "5", - "host": "localhost:23080", - "connection": "close" - }, - "params": {}, - "query": {}, - "url": "/number-payload", - "body": "virgs", - "elapsedTime": 3 - } - } - }, - "valid": true, - "subscriptionTime": "2020-03-08T05:33:39.230Z" - } - ], - "publishers": [ + "sensors": [], + "actuators": [ { - "id": "0233360792_25240a3992_219992", - "name": "Publisher #0", + "id": "1355570988_ad37a06639_317985", + "name": "Actuator #0", "valid": true, "hooks": { "onInit": { @@ -2495,106 +2330,66 @@ }, "onFinish": { "arguments": { - "executedHooks": [ - "onInit", - "onResponseReceived", - "onMessageReceived", - "onFinish" - ], - "elapsedTime": 5 + "executedHooks": { + "onInit": [], + "onResponseReceived": [ + "status", + "statusCode", + "body", + "headers", + "elapsedTime" + ], + "onFinish": [ + "executedHooks", + "elapsedTime" + ] + }, + "elapsedTime": 209 }, "tests": [ { "name": "Published", "valid": true, - "description": "{\n \"statusCode\": 444,\n \"headers\": {\n \"x-powered-by\": \"Express\",\n \"access-control-allow-origin\": \"*\",\n \"access-control-allow-headers\": \"Origin, X-Requested-With, Content-Type, Accept\",\n \"content-type\": \"text/html; charset=utf-8\",\n \"content-length\": \"4\",\n \"etag\": \"W/\\\"4-HLLQjeteIwK7Xuhlj4t8Bu7p124\\\"\",\n \"date\": \"Sun, 08 Mar 2020 05:33:39 GMT\",\n \"connection\": \"close\"\n },\n \"body\": 4.45\n}" + "description": "{\n \"statusCode\": 200,\n \"headers\": {\n \"access-control-allow-headers\": \"Origin, X-Requested-With, Content-Type, Accept\",\n \"access-control-allow-origin\": \"*\",\n \"connection\": \"keep-alive\",\n \"content-length\": \"5\",\n \"content-type\": \"text/html; charset=utf-8\",\n \"date\": \"Tue, 18 Mar 2025 20:55:58 GMT\",\n \"etag\": \"W/\\\"5-4JlqN8E9RMOwYHSTnUP6N1m9MsE\\\"\",\n \"keep-alive\": \"timeout=5\",\n \"x-powered-by\": \"Express\"\n },\n \"body\": \"first\"\n}", + "implicit": true } ], "valid": true }, "onResponseReceived": { "arguments": { - "statusCode": 444, - "body": "4.45", + "status": 200, + "statusCode": 200, + "body": "first", "headers": { - "x-powered-by": "Express", - "access-control-allow-origin": "*", "access-control-allow-headers": "Origin, X-Requested-With, Content-Type, Accept", - "content-type": "text/html; charset=utf-8", - "content-length": "4", - "etag": "W/\"4-HLLQjeteIwK7Xuhlj4t8Bu7p124\"", - "date": "Sun, 08 Mar 2020 05:33:39 GMT", - "connection": "close" - }, - "request": { - "uri": { - "protocol": "http:", - "slashes": true, - "auth": null, - "host": "localhost:23080", - "port": "23080", - "hostname": "localhost", - "hash": null, - "search": null, - "query": null, - "pathname": "/number-payload", - "path": "/number-payload", - "href": "http://localhost:23080/number-payload" - }, - "method": "post", - "headers": { - "Content-Length": 5 - } - } - }, - "tests": [], - "valid": true - }, - "onMessageReceived": { - "arguments": { - "statusCode": 444, - "body": "4.45", - "headers": { - "x-powered-by": "Express", "access-control-allow-origin": "*", - "access-control-allow-headers": "Origin, X-Requested-With, Content-Type, Accept", + "connection": "keep-alive", + "content-length": "5", "content-type": "text/html; charset=utf-8", - "content-length": "4", - "etag": "W/\"4-HLLQjeteIwK7Xuhlj4t8Bu7p124\"", - "date": "Sun, 08 Mar 2020 05:33:39 GMT", - "connection": "close" + "date": "Tue, 18 Mar 2025 20:55:58 GMT", + "etag": "W/\"5-4JlqN8E9RMOwYHSTnUP6N1m9MsE\"", + "keep-alive": "timeout=5", + "x-powered-by": "Express" }, - "request": { - "uri": { - "protocol": "http:", - "slashes": true, - "auth": null, - "host": "localhost:23080", - "port": "23080", - "hostname": "localhost", - "hash": null, - "search": null, - "query": null, - "pathname": "/number-payload", - "path": "/number-payload", - "href": "http://localhost:23080/number-payload" - }, - "method": "post", - "headers": { - "Content-Length": 5 - } - } + "elapsedTime": 209 }, - "tests": [], + "tests": [ + { + "name": "Assertion #0", + "valid": true, + "description": "Expected 'statusCode' to be equal to '200'. Received '200'" + } + ], "valid": true } }, "type": "http", - "publishTime": "2020-03-08T05:33:39.233Z" + "messageSentInstant": "2025-03-18T20:55:58.230Z" } ], "iteration": 0, - "totalIterations": 1, + "totalIterations": 2, "hooks": { "onInit": { "valid": true, @@ -2607,194 +2402,97 @@ "valid": true, "tests": [], "arguments": { - "elapsedTime": 5 + "elapsedTime": 209 } } }, "time": { - "startTime": "2020-03-08T05:33:39.229Z", - "endTime": "2020-03-08T05:33:39.234Z", - "totalTime": 5, - "timeout": 5000 + "startTime": "2025-03-18T20:55:58.021Z", + "endTime": "2025-03-18T20:55:58.230Z", + "totalTime": 209, + "timeout": 10000 }, - "requisitions": [] - } - ] - }, - { - "valid": true, - "name": "examples/http-parallel.yml", - "id": "0233360793_ffaed7f194_617295", - "level": 1, - "subscriptions": [], - "publishers": [], - "iteration": 0, - "totalIterations": 1, - "hooks": { - "onInit": { - "valid": true, - "tests": [], - "arguments": { - "elapsedTime": 0 - } + "tasks": [] }, - "onFinish": { - "valid": true, - "tests": [], - "arguments": { - "elapsedTime": 448 - } - } - }, - "time": { - "startTime": "2020-03-08T05:33:36.856Z", - "endTime": "2020-03-08T05:33:37.304Z", - "totalTime": 448, - "timeout": 5000 - }, - "requisitions": [ { "valid": true, - "name": "Requisition #0", - "id": "0233360793_f46fd5fb16_832313", + "name": "Task #2", + "id": "1355570988_9e23299bad_75822", "level": 2, - "subscriptions": [ + "sensors": [], + "actuators": [ { - "id": "0233360793_e4de1090c9_181839", - "name": "Subscription #0", - "type": "http", + "id": "1355570988_ad37a06639_317985", + "name": "Actuator #0", + "valid": true, "hooks": { "onInit": { - "valid": true, - "tests": [], "arguments": { "elapsedTime": 0 - } + }, + "tests": [], + "valid": true }, "onFinish": { - "valid": true, + "arguments": { + "executedHooks": { + "onInit": [], + "onResponseReceived": [ + "status", + "statusCode", + "body", + "headers", + "elapsedTime" + ], + "onFinish": [ + "executedHooks", + "elapsedTime" + ] + }, + "elapsedTime": 202 + }, "tests": [ { + "name": "Published", "valid": true, - "name": "Message received", - "description": "{\n \"headers\": {\n \"host\": \"localhost:23023\",\n \"content-length\": \"0\",\n \"connection\": \"close\"\n },\n \"params\": {},\n \"query\": {},\n \"url\": \"/first\",\n \"body\": \"\",\n \"elapsedTime\": 205\n}" + "description": "{\n \"statusCode\": 200,\n \"headers\": {\n \"access-control-allow-headers\": \"Origin, X-Requested-With, Content-Type, Accept\",\n \"access-control-allow-origin\": \"*\",\n \"connection\": \"keep-alive\",\n \"content-length\": \"5\",\n \"content-type\": \"text/html; charset=utf-8\",\n \"date\": \"Tue, 18 Mar 2025 20:55:58 GMT\",\n \"etag\": \"W/\\\"5-4JlqN8E9RMOwYHSTnUP6N1m9MsE\\\"\",\n \"keep-alive\": \"timeout=5\",\n \"x-powered-by\": \"Express\"\n },\n \"body\": \"first\"\n}", + "implicit": true } ], - "arguments": { - "executedHooks": [ - "onInit", - "onMessageReceived", - "onFinish" - ], - "elapsedTime": 205 - } + "valid": true }, - "onMessageReceived": { - "valid": true, - "tests": [], + "onResponseReceived": { "arguments": { + "status": 200, + "statusCode": 200, + "body": "first", "headers": { - "host": "localhost:23023", - "content-length": "0", - "connection": "close" + "access-control-allow-headers": "Origin, X-Requested-With, Content-Type, Accept", + "access-control-allow-origin": "*", + "connection": "keep-alive", + "content-length": "5", + "content-type": "text/html; charset=utf-8", + "date": "Tue, 18 Mar 2025 20:55:58 GMT", + "etag": "W/\"5-4JlqN8E9RMOwYHSTnUP6N1m9MsE\"", + "keep-alive": "timeout=5", + "x-powered-by": "Express" }, - "params": {}, - "query": {}, - "url": "/first", - "body": "", - "elapsedTime": 414 - } - } - }, - "valid": true, - "subscriptionTime": "2020-03-08T05:33:36.951Z" - } - ], - "publishers": [], - "iteration": 0, - "totalIterations": 2, - "hooks": { - "onInit": { - "valid": true, - "tests": [], - "arguments": { - "elapsedTime": 0 - } - }, - "onFinish": { - "valid": true, - "tests": [], - "arguments": { - "elapsedTime": 205 - } - } - }, - "time": { - "startTime": "2020-03-08T05:33:36.883Z", - "endTime": "2020-03-08T05:33:37.088Z", - "totalTime": 205, - "timeout": 5000 - }, - "requisitions": [] - }, - { - "valid": true, - "name": "Requisition #0", - "id": "0233360793_f46fd5fb16_832313", - "level": 2, - "subscriptions": [ - { - "id": "0233360793_e4de1090c9_181839", - "name": "Subscription #0", - "type": "http", - "hooks": { - "onInit": { - "valid": true, - "tests": [], - "arguments": { - "elapsedTime": 0 - } - }, - "onFinish": { - "valid": true, + "elapsedTime": 202 + }, "tests": [ { + "name": "Assertion #0", "valid": true, - "name": "Message received", - "description": "{\n \"headers\": {\n \"host\": \"localhost:23023\",\n \"content-length\": \"0\",\n \"connection\": \"close\"\n },\n \"params\": {},\n \"query\": {},\n \"url\": \"/first\",\n \"body\": \"\",\n \"elapsedTime\": 208\n}" + "description": "Expected 'statusCode' to be equal to '200'. Received '200'" } ], - "arguments": { - "executedHooks": [ - "onInit", - "onMessageReceived", - "onFinish" - ], - "elapsedTime": 209 - } - }, - "onMessageReceived": { - "valid": true, - "tests": [], - "arguments": { - "headers": { - "host": "localhost:23023", - "content-length": "0", - "connection": "close" - }, - "params": {}, - "query": {}, - "url": "/first", - "body": "", - "elapsedTime": 208 - } + "valid": true } }, - "valid": true, - "subscriptionTime": "2020-03-08T05:33:37.089Z" + "type": "http", + "messageSentInstant": "2025-03-18T20:55:58.433Z" } ], - "publishers": [], "iteration": 1, "totalIterations": 2, "hooks": { @@ -2809,161 +2507,98 @@ "valid": true, "tests": [], "arguments": { - "elapsedTime": 209 + "elapsedTime": 202 } } }, "time": { - "startTime": "2020-03-08T05:33:37.089Z", - "endTime": "2020-03-08T05:33:37.298Z", - "totalTime": 209, - "timeout": 5000 + "startTime": "2025-03-18T20:55:58.231Z", + "endTime": "2025-03-18T20:55:58.433Z", + "totalTime": 202, + "timeout": 10000 }, - "requisitions": [] + "tasks": [] }, { "valid": true, - "name": "Requisition #1", - "id": "0233360793_4788339776_407161", + "name": "Task #3", + "id": "1355570988_ab6beec7d0_125382", "level": 2, - "subscriptions": [ + "sensors": [], + "actuators": [ { - "id": "0233360793_16f5b5fa56_314693", - "name": "Subscription #0", - "type": "http", + "id": "1355570988_09d1baed2f_574714", + "name": "Actuator #0", + "valid": true, "hooks": { "onInit": { - "valid": true, - "tests": [], "arguments": { "elapsedTime": 0 - } + }, + "tests": [], + "valid": true }, "onFinish": { - "valid": true, + "arguments": { + "executedHooks": { + "onInit": [], + "onResponseReceived": [ + "status", + "statusCode", + "body", + "headers", + "elapsedTime" + ], + "onFinish": [ + "executedHooks", + "elapsedTime" + ] + }, + "elapsedTime": 211 + }, "tests": [ { + "name": "Published", "valid": true, - "name": "Message received", - "description": "{\n \"headers\": {\n \"host\": \"localhost:23023\",\n \"content-length\": \"0\",\n \"connection\": \"close\"\n },\n \"params\": {},\n \"query\": {},\n \"url\": \"/second\",\n \"body\": \"\",\n \"elapsedTime\": 207\n}" + "description": "{\n \"statusCode\": 200,\n \"headers\": {\n \"access-control-allow-headers\": \"Origin, X-Requested-With, Content-Type, Accept\",\n \"access-control-allow-origin\": \"*\",\n \"connection\": \"keep-alive\",\n \"content-length\": \"6\",\n \"content-type\": \"text/html; charset=utf-8\",\n \"date\": \"Tue, 18 Mar 2025 20:55:58 GMT\",\n \"etag\": \"W/\\\"6-NS94KaI4SwAcwSsMJhPHVkVKH2o\\\"\",\n \"keep-alive\": \"timeout=5\",\n \"x-powered-by\": \"Express\"\n },\n \"body\": \"second\"\n}", + "implicit": true } ], - "arguments": { - "executedHooks": [ - "onInit", - "onMessageReceived", - "onFinish" - ], - "elapsedTime": 208 - } + "valid": true }, - "onMessageReceived": { - "valid": true, - "tests": [], + "onResponseReceived": { "arguments": { + "status": 200, + "statusCode": 200, + "body": "second", "headers": { - "host": "localhost:23023", - "content-length": "0", - "connection": "close" + "access-control-allow-headers": "Origin, X-Requested-With, Content-Type, Accept", + "access-control-allow-origin": "*", + "connection": "keep-alive", + "content-length": "6", + "content-type": "text/html; charset=utf-8", + "date": "Tue, 18 Mar 2025 20:55:58 GMT", + "etag": "W/\"6-NS94KaI4SwAcwSsMJhPHVkVKH2o\"", + "keep-alive": "timeout=5", + "x-powered-by": "Express" }, - "params": {}, - "query": {}, - "url": "/second", - "body": "", - "elapsedTime": 419 - } - } - }, - "valid": true, - "subscriptionTime": "2020-03-08T05:33:36.951Z" - } - ], - "publishers": [], - "iteration": 0, - "totalIterations": 2, - "hooks": { - "onInit": { - "valid": true, - "tests": [], - "arguments": { - "elapsedTime": 0 - } - }, - "onFinish": { - "valid": true, - "tests": [], - "arguments": { - "elapsedTime": 208 - } - } - }, - "time": { - "startTime": "2020-03-08T05:33:36.884Z", - "endTime": "2020-03-08T05:33:37.092Z", - "totalTime": 208, - "timeout": 5000 - }, - "requisitions": [] - }, - { - "valid": true, - "name": "Requisition #1", - "id": "0233360793_4788339776_407161", - "level": 2, - "subscriptions": [ - { - "id": "0233360793_16f5b5fa56_314693", - "name": "Subscription #0", - "type": "http", - "hooks": { - "onInit": { - "valid": true, - "tests": [], - "arguments": { - "elapsedTime": 0 - } - }, - "onFinish": { - "valid": true, + "elapsedTime": 211 + }, "tests": [ { + "name": "Assertion #0", "valid": true, - "name": "Message received", - "description": "{\n \"headers\": {\n \"host\": \"localhost:23023\",\n \"content-length\": \"0\",\n \"connection\": \"close\"\n },\n \"params\": {},\n \"query\": {},\n \"url\": \"/second\",\n \"body\": \"\",\n \"elapsedTime\": 210\n}" + "description": "Expected 'statusCode' to be equal to '200'. Received '200'" } ], - "arguments": { - "executedHooks": [ - "onInit", - "onMessageReceived", - "onFinish" - ], - "elapsedTime": 210 - } - }, - "onMessageReceived": { - "valid": true, - "tests": [], - "arguments": { - "headers": { - "host": "localhost:23023", - "content-length": "0", - "connection": "close" - }, - "params": {}, - "query": {}, - "url": "/second", - "body": "", - "elapsedTime": 210 - } + "valid": true } }, - "valid": true, - "subscriptionTime": "2020-03-08T05:33:37.093Z" + "type": "http", + "messageSentInstant": "2025-03-18T20:55:58.232Z" } ], - "publishers": [], - "iteration": 1, + "iteration": 0, "totalIterations": 2, "hooks": { "onInit": { @@ -2977,28 +2612,28 @@ "valid": true, "tests": [], "arguments": { - "elapsedTime": 210 + "elapsedTime": 211 } } }, "time": { - "startTime": "2020-03-08T05:33:37.093Z", - "endTime": "2020-03-08T05:33:37.303Z", - "totalTime": 210, - "timeout": 5000 + "startTime": "2025-03-18T20:55:58.021Z", + "endTime": "2025-03-18T20:55:58.232Z", + "totalTime": 211, + "timeout": 10000 }, - "requisitions": [] + "tasks": [] }, { "valid": true, - "name": "Requisition #2", - "id": "0233360793_26a61bc62a_64352", + "name": "Task #3", + "id": "1355570988_ab6beec7d0_125382", "level": 2, - "subscriptions": [], - "publishers": [ + "sensors": [], + "actuators": [ { - "id": "0233360793_fb73e2d9b8_394615", - "name": "Publisher #0", + "id": "1355570988_09d1baed2f_574714", + "name": "Actuator #0", "valid": true, "hooks": { "onInit": { @@ -3010,57 +2645,49 @@ }, "onFinish": { "arguments": { - "executedHooks": [ - "onInit", - "onResponseReceived", - "onMessageReceived", - "onFinish" - ], - "elapsedTime": 206 + "executedHooks": { + "onInit": [], + "onResponseReceived": [ + "status", + "statusCode", + "body", + "headers", + "elapsedTime" + ], + "onFinish": [ + "executedHooks", + "elapsedTime" + ] + }, + "elapsedTime": 202 }, "tests": [ { "name": "Published", "valid": true, - "description": "{\n \"statusCode\": 200,\n \"headers\": {\n \"x-powered-by\": \"Express\",\n \"access-control-allow-origin\": \"*\",\n \"access-control-allow-headers\": \"Origin, X-Requested-With, Content-Type, Accept\",\n \"content-type\": \"text/html; charset=utf-8\",\n \"content-length\": \"5\",\n \"etag\": \"W/\\\"5-4JlqN8E9RMOwYHSTnUP6N1m9MsE\\\"\",\n \"date\": \"Sun, 08 Mar 2020 05:33:37 GMT\",\n \"connection\": \"close\"\n },\n \"body\": \"first\"\n}" + "description": "{\n \"statusCode\": 200,\n \"headers\": {\n \"access-control-allow-headers\": \"Origin, X-Requested-With, Content-Type, Accept\",\n \"access-control-allow-origin\": \"*\",\n \"connection\": \"keep-alive\",\n \"content-length\": \"6\",\n \"content-type\": \"text/html; charset=utf-8\",\n \"date\": \"Tue, 18 Mar 2025 20:55:58 GMT\",\n \"etag\": \"W/\\\"6-NS94KaI4SwAcwSsMJhPHVkVKH2o\\\"\",\n \"keep-alive\": \"timeout=5\",\n \"x-powered-by\": \"Express\"\n },\n \"body\": \"second\"\n}", + "implicit": true } ], "valid": true }, "onResponseReceived": { "arguments": { + "status": 200, "statusCode": 200, - "body": "first", + "body": "second", "headers": { - "x-powered-by": "Express", - "access-control-allow-origin": "*", "access-control-allow-headers": "Origin, X-Requested-With, Content-Type, Accept", + "access-control-allow-origin": "*", + "connection": "keep-alive", + "content-length": "6", "content-type": "text/html; charset=utf-8", - "content-length": "5", - "etag": "W/\"5-4JlqN8E9RMOwYHSTnUP6N1m9MsE\"", - "date": "Sun, 08 Mar 2020 05:33:37 GMT", - "connection": "close" + "date": "Tue, 18 Mar 2025 20:55:58 GMT", + "etag": "W/\"6-NS94KaI4SwAcwSsMJhPHVkVKH2o\"", + "keep-alive": "timeout=5", + "x-powered-by": "Express" }, - "request": { - "uri": { - "protocol": "http:", - "slashes": true, - "auth": null, - "host": "localhost:23023", - "port": "23023", - "hostname": "localhost", - "hash": null, - "search": null, - "query": null, - "pathname": "/first", - "path": "/first", - "href": "http://localhost:23023/first" - }, - "method": "get", - "headers": { - "content-length": 0 - } - } + "elapsedTime": 202 }, "tests": [ { @@ -3070,51 +2697,13 @@ } ], "valid": true - }, - "onMessageReceived": { - "arguments": { - "statusCode": 200, - "body": "first", - "headers": { - "x-powered-by": "Express", - "access-control-allow-origin": "*", - "access-control-allow-headers": "Origin, X-Requested-With, Content-Type, Accept", - "content-type": "text/html; charset=utf-8", - "content-length": "5", - "etag": "W/\"5-4JlqN8E9RMOwYHSTnUP6N1m9MsE\"", - "date": "Sun, 08 Mar 2020 05:33:37 GMT", - "connection": "close" - }, - "request": { - "uri": { - "protocol": "http:", - "slashes": true, - "auth": null, - "host": "localhost:23023", - "port": "23023", - "hostname": "localhost", - "hash": null, - "search": null, - "query": null, - "pathname": "/first", - "path": "/first", - "href": "http://localhost:23023/first" - }, - "method": "get", - "headers": { - "content-length": 0 - } - } - }, - "tests": [], - "valid": true } }, "type": "http", - "publishTime": "2020-03-08T05:33:37.090Z" + "messageSentInstant": "2025-03-18T20:55:58.434Z" } ], - "iteration": 0, + "iteration": 1, "totalIterations": 2, "hooks": { "onInit": { @@ -3128,330 +2717,488 @@ "valid": true, "tests": [], "arguments": { - "elapsedTime": 206 + "elapsedTime": 202 } } }, "time": { - "startTime": "2020-03-08T05:33:36.884Z", - "endTime": "2020-03-08T05:33:37.090Z", - "totalTime": 206, - "timeout": 5000 + "startTime": "2025-03-18T20:55:58.232Z", + "endTime": "2025-03-18T20:55:58.434Z", + "totalTime": 202, + "timeout": 10000 }, - "requisitions": [] - }, - { - "valid": true, - "name": "Requisition #2", - "id": "0233360793_26a61bc62a_64352", - "level": 2, - "subscriptions": [], - "publishers": [ - { - "id": "0233360793_fb73e2d9b8_394615", - "name": "Publisher #0", + "tasks": [] + } + ] + }, + { + "valid": true, + "name": "examples/http-proxy.yml", + "id": "1355570988_422eeb1e6f_508871", + "level": 1, + "sensors": [ + { + "id": "1355570988_ee4f61a440_15335", + "name": "proxy sensor", + "type": "http-proxy", + "hooks": { + "onInit": { "valid": true, - "hooks": { - "onInit": { - "arguments": { - "elapsedTime": 0 - }, - "tests": [], - "valid": true - }, - "onFinish": { - "arguments": { - "executedHooks": [ - "onInit", - "onResponseReceived", - "onMessageReceived", - "onFinish" - ], - "elapsedTime": 209 - }, - "tests": [ - { - "name": "Published", - "valid": true, - "description": "{\n \"statusCode\": 200,\n \"headers\": {\n \"x-powered-by\": \"Express\",\n \"access-control-allow-origin\": \"*\",\n \"access-control-allow-headers\": \"Origin, X-Requested-With, Content-Type, Accept\",\n \"content-type\": \"text/html; charset=utf-8\",\n \"content-length\": \"5\",\n \"etag\": \"W/\\\"5-4JlqN8E9RMOwYHSTnUP6N1m9MsE\\\"\",\n \"date\": \"Sun, 08 Mar 2020 05:33:37 GMT\",\n \"connection\": \"close\"\n },\n \"body\": \"first\"\n}" - } + "tests": [], + "arguments": {} + }, + "onFinish": { + "valid": true, + "tests": [ + { + "implicit": true, + "valid": true, + "name": "Message received", + "description": "Sensor has received its message" + } + ], + "arguments": { + "executedHooks": { + "onInit": [], + "onOriginalMessageReceived": [ + "headers", + "params", + "query", + "url", + "body", + "elapsedTime" ], - "valid": true - }, - "onResponseReceived": { - "arguments": { - "statusCode": 200, - "body": "first", - "headers": { - "x-powered-by": "Express", - "access-control-allow-origin": "*", - "access-control-allow-headers": "Origin, X-Requested-With, Content-Type, Accept", - "content-type": "text/html; charset=utf-8", - "content-length": "5", - "etag": "W/\"5-4JlqN8E9RMOwYHSTnUP6N1m9MsE\"", - "date": "Sun, 08 Mar 2020 05:33:37 GMT", - "connection": "close" - }, - "request": { - "uri": { - "protocol": "http:", - "slashes": true, - "auth": null, - "host": "localhost:23023", - "port": "23023", - "hostname": "localhost", - "hash": null, - "search": null, - "query": null, - "pathname": "/first", - "path": "/first", - "href": "http://localhost:23023/first" - }, - "method": "get", - "headers": { - "content-length": 0 - } - } - }, - "tests": [ - { - "name": "Assertion #0", - "valid": true, - "description": "Expected 'statusCode' to be equal to '200'. Received '200'" - } + "onMessageReceived": [ + "status", + "statusCode", + "body", + "headers", + "elapsedTime" ], - "valid": true + "onFinish": [ + "executedHooks", + "elapsedTime" + ] }, - "onMessageReceived": { - "arguments": { - "statusCode": 200, - "body": "first", - "headers": { - "x-powered-by": "Express", - "access-control-allow-origin": "*", - "access-control-allow-headers": "Origin, X-Requested-With, Content-Type, Accept", - "content-type": "text/html; charset=utf-8", - "content-length": "5", - "etag": "W/\"5-4JlqN8E9RMOwYHSTnUP6N1m9MsE\"", - "date": "Sun, 08 Mar 2020 05:33:37 GMT", - "connection": "close" - }, - "request": { - "uri": { - "protocol": "http:", - "slashes": true, - "auth": null, - "host": "localhost:23023", - "port": "23023", - "hostname": "localhost", - "hash": null, - "search": null, - "query": null, - "pathname": "/first", - "path": "/first", - "href": "http://localhost:23023/first" - }, - "method": "get", - "headers": { - "content-length": 0 - } - } - }, - "tests": [], - "valid": true + "elapsedTime": 193 + } + }, + "onOriginalMessageReceived": { + "valid": true, + "tests": [ + { + "name": "Assertion #0", + "valid": true, + "description": "Expected 'body' to be equal to 'original'. Received 'original'" } - }, - "type": "http", - "publishTime": "2020-03-08T05:33:37.298Z" + ], + "arguments": { + "headers": { + "host": "localhost:23085", + "connection": "keep-alive", + "content-type": "text/plain;charset=UTF-8", + "accept": "*/*", + "accept-language": "*", + "sec-fetch-mode": "cors", + "user-agent": "node", + "accept-encoding": "gzip, deflate", + "content-length": "8" + }, + "params": { + "id": "123456" + }, + "query": { + "query": "proxied" + }, + "url": "/proxy/enqueuer/123456?query=proxied", + "body": "original", + "elapsedTime": 157 + } + }, + "onMessageReceived": { + "valid": true, + "tests": [ + { + "name": "Assertion #0", + "valid": true, + "description": "Expected 'body' to be equal to 'original -> proxy -> real'. Received 'original -> proxy -> real'" + }, + { + "name": "Assertion #1", + "valid": true, + "description": "Expected 'statusCode' to be equal to '200'. Received '200'" + } + ], + "arguments": { + "status": 200, + "statusCode": 200, + "body": "original -> proxy -> real", + "headers": { + "access-control-allow-headers": "Origin, X-Requested-With, Content-Type, Accept", + "access-control-allow-origin": "*", + "connection": "keep-alive", + "content-length": "25", + "content-type": "text/html; charset=utf-8", + "date": "Tue, 18 Mar 2025 20:55:58 GMT", + "etag": "W/\"19-t4VpDbTMvQQ876rQU32330fLSVI\"", + "keep-alive": "timeout=5", + "x-powered-by": "Express" + }, + "elapsedTime": 191 + } } - ], - "iteration": 1, - "totalIterations": 2, + }, + "valid": true, + "sensorTime": "2025-03-18T20:55:58.111Z" + }, + { + "id": "1355570988_f53ce68398_803950", + "name": "real", + "type": "http", "hooks": { "onInit": { "valid": true, "tests": [], + "arguments": {} + }, + "onFinish": { + "valid": true, + "tests": [ + { + "implicit": true, + "valid": true, + "name": "Message received", + "description": "{\n \"headers\": {\n \"host\": \"localhost:23086\",\n \"connection\": \"keep-alive\",\n \"content-type\": \"text/plain;charset=UTF-8\",\n \"accept\": \"*/*\",\n \"accept-language\": \"*\",\n \"sec-fetch-mode\": \"cors\",\n \"user-agent\": \"node\",\n \"accept-encoding\": \"gzip, deflate\",\n \"content-length\": \"17\"\n },\n \"params\": {\n \"id\": \"999\"\n },\n \"query\": {\n \"query\": \"proxied\"\n },\n \"url\": \"/real/enqueuer/999?query=proxied\",\n \"body\": \"original -> proxy\"\n}" + } + ], "arguments": { - "elapsedTime": 0 + "executedHooks": { + "onInit": [], + "onMessageReceived": [ + "headers", + "params", + "query", + "url", + "body", + "elapsedTime" + ], + "onFinish": [ + "executedHooks", + "elapsedTime" + ] + }, + "elapsedTime": 194 } }, - "onFinish": { + "onMessageReceived": { "valid": true, - "tests": [], + "tests": [ + { + "name": "Assertion #0", + "valid": true, + "description": "Expected 'body' to be equal to 'original -> proxy'. Received 'original -> proxy'" + }, + { + "name": "Assertion #1", + "valid": true, + "description": "Expected 'params.id' to be equal to '999'. Received '999'" + }, + { + "name": "Assertion #2", + "valid": true, + "description": "Expected 'query.query' to be equal to 'proxied'. Received 'proxied'" + } + ], "arguments": { - "elapsedTime": 209 + "headers": { + "host": "localhost:23086", + "connection": "keep-alive", + "content-type": "text/plain;charset=UTF-8", + "accept": "*/*", + "accept-language": "*", + "sec-fetch-mode": "cors", + "user-agent": "node", + "accept-encoding": "gzip, deflate", + "content-length": "17" + }, + "params": { + "id": "999" + }, + "query": { + "query": "proxied" + }, + "url": "/real/enqueuer/999?query=proxied", + "body": "original -> proxy", + "elapsedTime": 190 } } }, - "time": { - "startTime": "2020-03-08T05:33:37.090Z", - "endTime": "2020-03-08T05:33:37.299Z", - "totalTime": 209, - "timeout": 5000 + "valid": true, + "sensorTime": "2025-03-18T20:55:58.111Z" + } + ], + "actuators": [ + { + "id": "1355570988_b3425b4e2f_670785", + "name": "actuator proxy", + "valid": true, + "hooks": { + "onInit": { + "arguments": { + "elapsedTime": 0 + }, + "tests": [], + "valid": true + }, + "onFinish": { + "arguments": { + "executedHooks": { + "onInit": [], + "onResponseReceived": [ + "status", + "statusCode", + "body", + "headers", + "elapsedTime" + ], + "onFinish": [ + "executedHooks", + "elapsedTime" + ] + }, + "elapsedTime": 194 + }, + "tests": [ + { + "name": "Published", + "valid": true, + "description": "{\n \"statusCode\": 400,\n \"headers\": {\n \"access-control-allow-headers\": \"Origin, X-Requested-With, Content-Type, Accept\",\n \"access-control-allow-origin\": \"*\",\n \"connection\": \"keep-alive\",\n \"content-length\": \"44\",\n \"content-type\": \"text/html; charset=utf-8\",\n \"date\": \"Tue, 18 Mar 2025 20:55:58 GMT\",\n \"etag\": \"W/\\\"19-t4VpDbTMvQQ876rQU32330fLSVI\\\"\",\n \"keep-alive\": \"timeout=5\",\n \"x-powered-by\": \"Express\"\n },\n \"body\": \"original -> proxy -> real' -> proxied again'\"\n}", + "implicit": true + } + ], + "valid": true + }, + "onResponseReceived": { + "arguments": { + "status": 400, + "statusCode": 400, + "body": "original -> proxy -> real' -> proxied again'", + "headers": { + "access-control-allow-headers": "Origin, X-Requested-With, Content-Type, Accept", + "access-control-allow-origin": "*", + "connection": "keep-alive", + "content-length": "44", + "content-type": "text/html; charset=utf-8", + "date": "Tue, 18 Mar 2025 20:55:58 GMT", + "etag": "W/\"19-t4VpDbTMvQQ876rQU32330fLSVI\"", + "keep-alive": "timeout=5", + "x-powered-by": "Express" + }, + "elapsedTime": 193 + }, + "tests": [], + "valid": true + } }, - "requisitions": [] + "type": "http", + "messageSentInstant": "2025-03-18T20:55:58.205Z" + } + ], + "iteration": 0, + "totalIterations": 1, + "hooks": { + "onInit": { + "valid": true, + "tests": [], + "arguments": { + "elapsedTime": 0 + } }, + "onFinish": { + "valid": true, + "tests": [], + "arguments": { + "elapsedTime": 194 + } + } + }, + "time": { + "startTime": "2025-03-18T20:55:58.012Z", + "endTime": "2025-03-18T20:55:58.206Z", + "totalTime": 194, + "timeout": 5000 + }, + "tasks": [] + }, + { + "valid": true, + "name": "examples/http-same-port.yml", + "id": "1355570988_7e4e495f2f_113776", + "level": 1, + "sensors": [], + "actuators": [], + "iteration": 0, + "totalIterations": 1, + "hooks": { + "onInit": { + "valid": true, + "tests": [], + "arguments": { + "elapsedTime": 0 + } + }, + "onFinish": { + "valid": true, + "tests": [], + "arguments": { + "elapsedTime": 257 + } + } + }, + "time": { + "startTime": "2025-03-18T20:55:58.012Z", + "endTime": "2025-03-18T20:55:58.269Z", + "totalTime": 257, + "timeout": 5000 + }, + "tasks": [ { "valid": true, - "name": "Requisition #3", - "id": "0233360793_6099fb1229_611959", + "name": "Task #0", + "id": "1355570988_be96ed2f95_247344", "level": 2, - "subscriptions": [], - "publishers": [ + "sensors": [ { - "id": "0233360793_7d016346aa_595254", - "name": "Publisher #0", - "valid": true, + "id": "1355570988_8438322211_729194", + "name": "Sensor #0", + "type": "http", "hooks": { "onInit": { - "arguments": { - "elapsedTime": 0 - }, + "valid": true, "tests": [], - "valid": true + "arguments": {} }, "onFinish": { - "arguments": { - "executedHooks": [ - "onInit", - "onResponseReceived", - "onMessageReceived", - "onFinish" - ], - "elapsedTime": 213 - }, + "valid": true, "tests": [ { - "name": "Published", + "implicit": true, "valid": true, - "description": "{\n \"statusCode\": 200,\n \"headers\": {\n \"x-powered-by\": \"Express\",\n \"access-control-allow-origin\": \"*\",\n \"access-control-allow-headers\": \"Origin, X-Requested-With, Content-Type, Accept\",\n \"content-type\": \"text/html; charset=utf-8\",\n \"content-length\": \"6\",\n \"etag\": \"W/\\\"6-NS94KaI4SwAcwSsMJhPHVkVKH2o\\\"\",\n \"date\": \"Sun, 08 Mar 2020 05:33:37 GMT\",\n \"connection\": \"close\"\n },\n \"body\": \"second\"\n}" + "name": "Message received", + "description": "{\n \"headers\": {\n \"host\": \"localhost:23065\",\n \"connection\": \"keep-alive\",\n \"content-type\": \"application/json\",\n \"nqr\": \"actuator\",\n \"accept\": \"*/*\",\n \"accept-language\": \"*\",\n \"sec-fetch-mode\": \"cors\",\n \"user-agent\": \"node\",\n \"accept-encoding\": \"gzip, deflate\",\n \"content-length\": \"20\"\n },\n \"params\": {\n \"firstId\": \"idStuff\"\n },\n \"query\": {\n \"query\": \"2345\"\n },\n \"url\": \"/enqueuer/idStuff?query=2345\",\n \"body\": {\n \"enqueuer\": \"virgs\"\n }\n}" } ], - "valid": true - }, - "onResponseReceived": { "arguments": { - "statusCode": 200, - "body": "second", - "headers": { - "x-powered-by": "Express", - "access-control-allow-origin": "*", - "access-control-allow-headers": "Origin, X-Requested-With, Content-Type, Accept", - "content-type": "text/html; charset=utf-8", - "content-length": "6", - "etag": "W/\"6-NS94KaI4SwAcwSsMJhPHVkVKH2o\"", - "date": "Sun, 08 Mar 2020 05:33:37 GMT", - "connection": "close" + "executedHooks": { + "onInit": [], + "onMessageReceived": [ + "headers", + "params", + "query", + "url", + "body", + "elapsedTime" + ], + "onFinish": [ + "executedHooks", + "elapsedTime" + ] }, - "request": { - "uri": { - "protocol": "http:", - "slashes": true, - "auth": null, - "host": "localhost:23023", - "port": "23023", - "hostname": "localhost", - "hash": null, - "search": null, - "query": null, - "pathname": "/second", - "path": "/second", - "href": "http://localhost:23023/second" - }, - "method": "get", - "headers": { - "content-length": 0 - } - } - }, + "elapsedTime": 244 + } + }, + "onMessageReceived": { + "valid": true, "tests": [ { - "name": "Assertion #0", + "name": "Payload", "valid": true, - "description": "Expected 'statusCode' to be equal to '200'. Received '200'" + "description": "Expected 'JSON.parse(body).enqueuer' to be equal to 'virgs'. Received 'virgs'" + }, + { + "name": "Params", + "valid": true, + "description": "Expected 'params.firstId' to be equal to 'idStuff'. Received 'idStuff'" + }, + { + "name": "Query", + "valid": true, + "description": "Expected 'query.query' to be equal to '2345'. Received '2345'" + }, + { + "name": "Header", + "valid": true, + "description": "Expected 'headers.nqr' to be equal to 'actuator'. Received 'actuator'" } ], - "valid": true - }, - "onMessageReceived": { "arguments": { - "statusCode": 200, - "body": "second", "headers": { - "x-powered-by": "Express", - "access-control-allow-origin": "*", - "access-control-allow-headers": "Origin, X-Requested-With, Content-Type, Accept", - "content-type": "text/html; charset=utf-8", - "content-length": "6", - "etag": "W/\"6-NS94KaI4SwAcwSsMJhPHVkVKH2o\"", - "date": "Sun, 08 Mar 2020 05:33:37 GMT", - "connection": "close" + "host": "localhost:23065", + "connection": "keep-alive", + "content-type": "application/json", + "nqr": "actuator", + "accept": "*/*", + "accept-language": "*", + "sec-fetch-mode": "cors", + "user-agent": "node", + "accept-encoding": "gzip, deflate", + "content-length": "20" }, - "request": { - "uri": { - "protocol": "http:", - "slashes": true, - "auth": null, - "host": "localhost:23023", - "port": "23023", - "hostname": "localhost", - "hash": null, - "search": null, - "query": null, - "pathname": "/second", - "path": "/second", - "href": "http://localhost:23023/second" - }, - "method": "get", - "headers": { - "content-length": 0 - } - } - }, - "tests": [], - "valid": true + "params": { + "firstId": "idStuff" + }, + "query": { + "query": "2345" + }, + "url": "/enqueuer/idStuff?query=2345", + "body": "{\"enqueuer\":\"virgs\"}", + "elapsedTime": 150 + } } }, - "type": "http", - "publishTime": "2020-03-08T05:33:37.097Z" - } - ], - "iteration": 0, - "totalIterations": 2, - "hooks": { - "onInit": { "valid": true, - "tests": [], - "arguments": { - "elapsedTime": 0 - } + "sensorTime": "2025-03-18T20:55:58.111Z" }, - "onFinish": { + { + "id": "1355570988_bb1bad56ac_392821", + "name": "avoidable", + "type": "http", + "hooks": { + "onInit": { + "valid": true, + "tests": [], + "arguments": {} + }, + "onFinish": { + "valid": true, + "tests": [ + { + "valid": true, + "implicit": true, + "name": "Sensor avoided", + "description": "Avoidable sensor has not received any message" + } + ], + "arguments": { + "executedHooks": { + "onInit": [], + "onFinish": [ + "executedHooks", + "elapsedTime" + ] + }, + "elapsedTime": 244 + } + } + }, "valid": true, - "tests": [], - "arguments": { - "elapsedTime": 213 - } + "sensorTime": "2025-03-18T20:55:58.111Z" } - }, - "time": { - "startTime": "2020-03-08T05:33:36.884Z", - "endTime": "2020-03-08T05:33:37.097Z", - "totalTime": 213, - "timeout": 5000 - }, - "requisitions": [] - }, - { - "valid": true, - "name": "Requisition #3", - "id": "0233360793_6099fb1229_611959", - "level": 2, - "subscriptions": [], - "publishers": [ + ], + "actuators": [ { - "id": "0233360793_7d016346aa_595254", - "name": "Publisher #0", + "id": "1355570988_ea8916d8d1_755330", + "name": "Actuator #0", "valid": true, "hooks": { "onInit": { @@ -3463,112 +3210,76 @@ }, "onFinish": { "arguments": { - "executedHooks": [ - "onInit", - "onResponseReceived", - "onMessageReceived", - "onFinish" - ], - "elapsedTime": 205 + "executedHooks": { + "onInit": [], + "onResponseReceived": [ + "status", + "statusCode", + "body", + "headers", + "elapsedTime" + ], + "onFinish": [ + "executedHooks", + "elapsedTime" + ] + }, + "elapsedTime": 244 }, "tests": [ { "name": "Published", "valid": true, - "description": "{\n \"statusCode\": 200,\n \"headers\": {\n \"x-powered-by\": \"Express\",\n \"access-control-allow-origin\": \"*\",\n \"access-control-allow-headers\": \"Origin, X-Requested-With, Content-Type, Accept\",\n \"content-type\": \"text/html; charset=utf-8\",\n \"content-length\": \"6\",\n \"etag\": \"W/\\\"6-NS94KaI4SwAcwSsMJhPHVkVKH2o\\\"\",\n \"date\": \"Sun, 08 Mar 2020 05:33:37 GMT\",\n \"connection\": \"close\"\n },\n \"body\": \"second\"\n}" + "description": "{\n \"statusCode\": 321,\n \"headers\": {\n \"access-control-allow-headers\": \"Origin, X-Requested-With, Content-Type, Accept\",\n \"access-control-allow-origin\": \"*\",\n \"connection\": \"close\",\n \"content-length\": \"27\",\n \"content-type\": \"text/html; charset=utf-8\",\n \"date\": \"Tue, 18 Mar 2025 20:55:58 GMT\",\n \"etag\": \"W/\\\"1b-e5esTWfu+XftewZ5g2Tclr7ClTo\\\"\",\n \"nqr\": \"sensor\",\n \"x-powered-by\": \"Express\"\n },\n \"body\": \"dynamically changed payload\"\n}", + "implicit": true } ], "valid": true }, "onResponseReceived": { "arguments": { - "statusCode": 200, - "body": "second", + "status": 321, + "statusCode": 321, + "body": "dynamically changed payload", "headers": { - "x-powered-by": "Express", - "access-control-allow-origin": "*", "access-control-allow-headers": "Origin, X-Requested-With, Content-Type, Accept", + "access-control-allow-origin": "*", + "connection": "close", + "content-length": "27", "content-type": "text/html; charset=utf-8", - "content-length": "6", - "etag": "W/\"6-NS94KaI4SwAcwSsMJhPHVkVKH2o\"", - "date": "Sun, 08 Mar 2020 05:33:37 GMT", - "connection": "close" + "date": "Tue, 18 Mar 2025 20:55:58 GMT", + "etag": "W/\"1b-e5esTWfu+XftewZ5g2Tclr7ClTo\"", + "nqr": "sensor", + "x-powered-by": "Express" }, - "request": { - "uri": { - "protocol": "http:", - "slashes": true, - "auth": null, - "host": "localhost:23023", - "port": "23023", - "hostname": "localhost", - "hash": null, - "search": null, - "query": null, - "pathname": "/second", - "path": "/second", - "href": "http://localhost:23023/second" - }, - "method": "get", - "headers": { - "content-length": 0 - } - } + "elapsedTime": 166 }, "tests": [ { - "name": "Assertion #0", + "name": "Status Code", "valid": true, - "description": "Expected 'statusCode' to be equal to '200'. Received '200'" - } - ], - "valid": true - }, - "onMessageReceived": { - "arguments": { - "statusCode": 200, - "body": "second", - "headers": { - "x-powered-by": "Express", - "access-control-allow-origin": "*", - "access-control-allow-headers": "Origin, X-Requested-With, Content-Type, Accept", - "content-type": "text/html; charset=utf-8", - "content-length": "6", - "etag": "W/\"6-NS94KaI4SwAcwSsMJhPHVkVKH2o\"", - "date": "Sun, 08 Mar 2020 05:33:37 GMT", - "connection": "close" + "description": "Expected 'statusCode' to be equal to '321'. Received '321'" }, - "request": { - "uri": { - "protocol": "http:", - "slashes": true, - "auth": null, - "host": "localhost:23023", - "port": "23023", - "hostname": "localhost", - "hash": null, - "search": null, - "query": null, - "pathname": "/second", - "path": "/second", - "href": "http://localhost:23023/second" - }, - "method": "get", - "headers": { - "content-length": 0 - } + { + "name": "Body", + "valid": true, + "description": "Expected 'body' to be equal to 'dynamically changed payload'. Received 'dynamically changed payload'" + }, + { + "name": "Header", + "valid": true, + "description": "Expected 'headers.nqr' to be equal to 'sensor'. Received 'sensor'" } - }, - "tests": [], + ], "valid": true } }, "type": "http", - "publishTime": "2020-03-08T05:33:37.304Z" + "messageSentInstant": "2025-03-18T20:55:58.187Z" } ], - "iteration": 1, - "totalIterations": 2, + "iteration": 0, + "totalIterations": 1, "hooks": { "onInit": { "valid": true, @@ -3581,394 +3292,249 @@ "valid": true, "tests": [], "arguments": { - "elapsedTime": 205 + "elapsedTime": 244 } } }, "time": { - "startTime": "2020-03-08T05:33:37.099Z", - "endTime": "2020-03-08T05:33:37.304Z", - "totalTime": 205, - "timeout": 5000 + "startTime": "2025-03-18T20:55:58.021Z", + "endTime": "2025-03-18T20:55:58.265Z", + "totalTime": 244, + "timeout": 3000 }, - "requisitions": [] - } - ] - }, - { - "valid": true, - "name": "examples/http-proxy.yml", - "id": "0233360793_aba5464e08_110976", - "level": 1, - "subscriptions": [ + "tasks": [] + }, { - "id": "0233360793_be017cf7e2_373126", - "name": "proxy subscription", - "type": "http-proxy", - "hooks": { - "onInit": { - "valid": true, - "tests": [], - "arguments": { - "elapsedTime": 0 - } - }, - "onFinish": { - "valid": true, - "tests": [ - { - "valid": true, - "name": "Message received", - "description": "Subscription has received its message" - } - ], - "arguments": { - "executedHooks": [ - "onInit", - "onOriginalMessageReceived", - "onMessageReceived", - "onFinish" - ], - "elapsedTime": 208 - } - }, - "onOriginalMessageReceived": { - "valid": true, - "tests": [ - { - "name": "Assertion #0", + "valid": true, + "name": "Task #1", + "id": "1355570988_b7d51eb9d2_505796", + "level": 2, + "sensors": [ + { + "id": "1355570988_56d279f381_963752", + "name": "fourth", + "type": "http", + "hooks": { + "onInit": { "valid": true, - "description": "Expected 'body' to be equal to 'original'. Received 'original'" - } - ], - "arguments": { - "headers": { - "content-length": "8", - "host": "localhost:23085", - "connection": "close" - }, - "params": { - "0": "enqueuer/123456" - }, - "query": { - "query": "proxied" + "tests": [], + "arguments": {} }, - "url": "/proxy/enqueuer/123456?query=proxied", - "body": "original", - "elapsedTime": 161 - } - }, - "onMessageReceived": { - "valid": true, - "tests": [ - { - "name": "Assertion #0", + "onFinish": { "valid": true, - "description": "Expected 'body' to be equal to 'original -> proxy -> real'. Received 'original -> proxy -> real'" + "tests": [ + { + "implicit": true, + "valid": true, + "name": "Message received", + "description": "{\n \"headers\": {\n \"host\": \"localhost:23065\",\n \"connection\": \"keep-alive\",\n \"content-type\": \"application/json\",\n \"accept\": \"*/*\",\n \"accept-language\": \"*\",\n \"sec-fetch-mode\": \"cors\",\n \"user-agent\": \"node\",\n \"accept-encoding\": \"gzip, deflate\",\n \"content-length\": \"19\"\n },\n \"params\": {\n \"secondId\": \"idStuff\"\n },\n \"query\": {\n \"query\": \"111\"\n },\n \"url\": \"/enqueuer/idStuff?query=111\",\n \"body\": {\n \"duplicated\": true\n }\n}" + } + ], + "arguments": { + "executedHooks": { + "onInit": [], + "onMessageReceived": [ + "headers", + "params", + "query", + "url", + "body", + "elapsedTime" + ], + "onFinish": [ + "executedHooks", + "elapsedTime" + ] + }, + "elapsedTime": 3 + } }, - { - "name": "Assertion #1", + "onMessageReceived": { "valid": true, - "description": "Expected 'statusCode' to be equal to '200'. Received '200'" - } - ], - "arguments": { - "statusCode": 200, - "body": "original -> proxy -> real", - "headers": { - "x-powered-by": "Express", - "access-control-allow-origin": "*", - "access-control-allow-headers": "Origin, X-Requested-With, Content-Type, Accept", - "content-type": "text/html; charset=utf-8", - "content-length": "25", - "etag": "W/\"19-t4VpDbTMvQQ876rQU32330fLSVI\"", - "date": "Sun, 08 Mar 2020 05:33:37 GMT", - "connection": "close" - }, - "request": { - "uri": { - "protocol": "http:", - "slashes": true, - "auth": null, - "host": "localhost:23086", - "port": "23086", - "hostname": "localhost", - "hash": null, - "search": "?query=proxied", - "query": "query=proxied", - "pathname": "/real/enqueuer/123456", - "path": "/real/enqueuer/123456?query=proxied", - "href": "http://localhost:23086/real/enqueuer/123456?query=proxied" - }, - "method": "POST", - "headers": { - "content-length": "8", - "host": "localhost:23085", - "connection": "close", - "Content-Length": 17 + "tests": [ + { + "name": "Payload", + "valid": true, + "description": "Expecting 'JSON.parse(body).duplicated' to be true. Received: true" + } + ], + "arguments": { + "headers": { + "host": "localhost:23065", + "connection": "keep-alive", + "content-type": "application/json", + "accept": "*/*", + "accept-language": "*", + "sec-fetch-mode": "cors", + "user-agent": "node", + "accept-encoding": "gzip, deflate", + "content-length": "19" + }, + "params": { + "secondId": "idStuff" + }, + "query": { + "query": "111" + }, + "url": "/enqueuer/idStuff?query=111", + "body": "{\"duplicated\":true}", + "elapsedTime": 2 } } - } - } - }, - "valid": true, - "subscriptionTime": "2020-03-08T05:33:36.951Z" - }, - { - "id": "0233360793_9421bcf470_5622", - "name": "real", - "type": "http", - "hooks": { - "onInit": { - "valid": true, - "tests": [], - "arguments": { - "elapsedTime": 0 - } - }, - "onFinish": { + }, "valid": true, - "tests": [ - { - "valid": true, - "name": "Message received", - "description": "{\n \"headers\": {\n \"content-length\": \"17\",\n \"host\": \"localhost:23085\",\n \"connection\": \"close\"\n },\n \"params\": {\n \"id\": \"123456\"\n },\n \"query\": {\n \"query\": \"proxied\"\n },\n \"url\": \"/real/enqueuer/123456?query=proxied\",\n \"body\": \"original -> proxy\",\n \"elapsedTime\": 196\n}" - } - ], - "arguments": { - "executedHooks": [ - "onInit", - "onMessageReceived", - "onFinish" - ], - "elapsedTime": 208 - } - }, - "onMessageReceived": { + "sensorTime": "2025-03-18T20:55:58.266Z" + } + ], + "actuators": [ + { + "id": "1355570988_cdcc7b4c16_648002", + "name": "actuator description", "valid": true, - "tests": [ - { - "name": "Assertion #0", - "valid": true, - "description": "Expected 'body' to be equal to 'original -> proxy'. Received 'original -> proxy'" - }, - { - "name": "Assertion #1", - "valid": true, - "description": "Expected 'params.id' to be equal to '123456'. Received '123456'" - }, - { - "name": "Assertion #2", - "valid": true, - "description": "Expected 'query.query' to be equal to 'proxied'. Received 'proxied'" - } - ], - "arguments": { - "headers": { - "content-length": "17", - "host": "localhost:23085", - "connection": "close" - }, - "params": { - "id": "123456" + "hooks": { + "onInit": { + "arguments": { + "elapsedTime": 0 + }, + "tests": [], + "valid": true }, - "query": { - "query": "proxied" + "onFinish": { + "arguments": { + "executedHooks": { + "onInit": [], + "onResponseReceived": [ + "status", + "statusCode", + "body", + "headers", + "elapsedTime" + ], + "onFinish": [ + "executedHooks", + "elapsedTime" + ] + }, + "elapsedTime": 3 + }, + "tests": [ + { + "name": "Published", + "valid": true, + "description": "{\n \"statusCode\": 321,\n \"headers\": {\n \"access-control-allow-headers\": \"Origin, X-Requested-With, Content-Type, Accept\",\n \"access-control-allow-origin\": \"*\",\n \"connection\": \"keep-alive\",\n \"content-length\": \"25\",\n \"content-type\": \"text/html; charset=utf-8\",\n \"date\": \"Tue, 18 Mar 2025 20:55:58 GMT\",\n \"etag\": \"W/\\\"19-yZRAgggcER0sMyRTVBBpErTPT/A\\\"\",\n \"keep-alive\": \"timeout=5\",\n \"x-powered-by\": \"Express\"\n },\n \"body\": \"duplicatedResponsePayload\"\n}", + "implicit": true + } + ], + "valid": true }, - "url": "/real/enqueuer/123456?query=proxied", - "body": "original -> proxy", - "elapsedTime": 196 - } + "onResponseReceived": { + "arguments": { + "status": 321, + "statusCode": 321, + "body": "duplicatedResponsePayload", + "headers": { + "access-control-allow-headers": "Origin, X-Requested-With, Content-Type, Accept", + "access-control-allow-origin": "*", + "connection": "keep-alive", + "content-length": "25", + "content-type": "text/html; charset=utf-8", + "date": "Tue, 18 Mar 2025 20:55:58 GMT", + "etag": "W/\"19-yZRAgggcER0sMyRTVBBpErTPT/A\"", + "keep-alive": "timeout=5", + "x-powered-by": "Express" + }, + "elapsedTime": 3 + }, + "tests": [ + { + "name": "Status Code", + "valid": true, + "description": "Expected 'statusCode' to be equal to '321'. Received '321'" + }, + { + "name": "Body", + "valid": true, + "description": "Expected 'body' to be equal to 'duplicatedResponsePayload'. Received 'duplicatedResponsePayload'" + } + ], + "valid": true + } + }, + "type": "http", + "messageSentInstant": "2025-03-18T20:55:58.269Z" } - }, - "valid": true, - "subscriptionTime": "2020-03-08T05:33:36.951Z" - } - ], - "publishers": [ - { - "id": "0233360793_8e53ccc444_566876", - "name": "publisher proxy", - "valid": true, + ], + "iteration": 0, + "totalIterations": 1, "hooks": { "onInit": { + "valid": true, + "tests": [], "arguments": { "elapsedTime": 0 - }, - "tests": [], - "valid": true + } }, "onFinish": { - "arguments": { - "executedHooks": [ - "onInit", - "onResponseReceived", - "onMessageReceived", - "onFinish" - ], - "elapsedTime": 208 - }, - "tests": [ - { - "name": "Published", - "valid": true, - "description": "{\n \"statusCode\": 400,\n \"headers\": {\n \"x-powered-by\": \"Express\",\n \"access-control-allow-origin\": \"*\",\n \"access-control-allow-headers\": \"Origin, X-Requested-With, Content-Type, Accept\",\n \"content-type\": \"text/html; charset=utf-8\",\n \"content-length\": \"42\",\n \"etag\": \"W/\\\"19-t4VpDbTMvQQ876rQU32330fLSVI\\\"\",\n \"date\": \"Sun, 08 Mar 2020 05:33:37 GMT\",\n \"connection\": \"close\"\n },\n \"body\": \"original -> proxy -> real -> proxied again\"\n}" - } - ], - "valid": true - }, - "onResponseReceived": { - "arguments": { - "statusCode": 400, - "body": "original -> proxy -> real -> proxied again", - "headers": { - "x-powered-by": "Express", - "access-control-allow-origin": "*", - "access-control-allow-headers": "Origin, X-Requested-With, Content-Type, Accept", - "content-type": "text/html; charset=utf-8", - "content-length": "42", - "etag": "W/\"19-t4VpDbTMvQQ876rQU32330fLSVI\"", - "date": "Sun, 08 Mar 2020 05:33:37 GMT", - "connection": "close" - }, - "request": { - "uri": { - "protocol": "http:", - "slashes": true, - "auth": null, - "host": "localhost:23085", - "port": "23085", - "hostname": "localhost", - "hash": null, - "search": "?query=proxied", - "query": "query=proxied", - "pathname": "/proxy/enqueuer/123456", - "path": "/proxy/enqueuer/123456?query=proxied", - "href": "http://localhost:23085/proxy/enqueuer/123456?query=proxied" - }, - "method": "patch", - "headers": { - "Content-Length": 8 - } - } - }, + "valid": true, "tests": [], - "valid": true - }, - "onMessageReceived": { "arguments": { - "statusCode": 400, - "body": "original -> proxy -> real -> proxied again", - "headers": { - "x-powered-by": "Express", - "access-control-allow-origin": "*", - "access-control-allow-headers": "Origin, X-Requested-With, Content-Type, Accept", - "content-type": "text/html; charset=utf-8", - "content-length": "42", - "etag": "W/\"19-t4VpDbTMvQQ876rQU32330fLSVI\"", - "date": "Sun, 08 Mar 2020 05:33:37 GMT", - "connection": "close" - }, - "request": { - "uri": { - "protocol": "http:", - "slashes": true, - "auth": null, - "host": "localhost:23085", - "port": "23085", - "hostname": "localhost", - "hash": null, - "search": "?query=proxied", - "query": "query=proxied", - "pathname": "/proxy/enqueuer/123456", - "path": "/proxy/enqueuer/123456?query=proxied", - "href": "http://localhost:23085/proxy/enqueuer/123456?query=proxied" - }, - "method": "patch", - "headers": { - "Content-Length": 8 - } - } - }, - "tests": [ - { - "name": "Assertion #0", - "valid": true, - "description": "Expected 'statusCode' to be equal to '400'. Received '400'" - }, - { - "name": "Assertion #1", - "valid": true, - "description": "Expected 'body' to be equal to 'original -> proxy -> real -> proxied again'. Received 'original -> proxy -> real -> proxied again'" - } - ], - "valid": true + "elapsedTime": 4 + } } }, - "type": "http", - "publishTime": "2020-03-08T05:33:37.064Z" - } - ], - "iteration": 0, - "totalIterations": 1, - "hooks": { - "onInit": { - "valid": true, - "tests": [], - "arguments": { - "elapsedTime": 0 - } - }, - "onFinish": { - "valid": true, - "tests": [], - "arguments": { - "elapsedTime": 208 - } + "time": { + "startTime": "2025-03-18T20:55:58.265Z", + "endTime": "2025-03-18T20:55:58.269Z", + "totalTime": 4, + "timeout": 35000 + }, + "tasks": [] } - }, - "time": { - "startTime": "2020-03-08T05:33:36.857Z", - "endTime": "2020-03-08T05:33:37.065Z", - "totalTime": 208, - "timeout": 5000 - }, - "requisitions": [] + ] }, { "valid": true, "name": "examples/http.yml", - "id": "0233360793_07461360b5_845095", + "id": "1355570988_19a154cd66_848319", "level": 1, - "subscriptions": [ + "sensors": [ { - "id": "0233360793_a0523c01e7_926595", - "name": "Subscription #0", + "id": "1355570988_a365346815_166289", + "name": "Sensor #0", "type": "http", "hooks": { "onInit": { "valid": true, "tests": [], - "arguments": { - "elapsedTime": 0 - } + "arguments": {} }, "onFinish": { "valid": true, "tests": [ { + "implicit": true, "valid": true, "name": "Message received", - "description": "{\n \"headers\": {\n \"content-length\": \"8\",\n \"host\": \"localhost:23075\",\n \"connection\": \"close\"\n },\n \"params\": {},\n \"query\": {},\n \"url\": \"/resource\",\n \"body\": \"enqueuer\",\n \"elapsedTime\": 162\n}" + "description": "{\n \"headers\": {\n \"host\": \"localhost:23075\",\n \"connection\": \"keep-alive\",\n \"requestheaderkey\": \"requestHeaderValue\",\n \"content-type\": \"text/plain;charset=UTF-8\",\n \"accept\": \"*/*\",\n \"accept-language\": \"*\",\n \"sec-fetch-mode\": \"cors\",\n \"user-agent\": \"node\",\n \"accept-encoding\": \"gzip, deflate\",\n \"content-length\": \"8\"\n },\n \"params\": {},\n \"query\": {},\n \"url\": \"/resource\",\n \"body\": \"enqueuer\"\n}" } ], "arguments": { - "executedHooks": [ - "onInit", - "onMessageReceived", - "onFinish" - ], - "elapsedTime": 178 + "executedHooks": { + "onInit": [], + "onMessageReceived": [ + "headers", + "params", + "query", + "url", + "body", + "elapsedTime" + ], + "onFinish": [ + "executedHooks", + "elapsedTime" + ] + }, + "elapsedTime": 171 } }, "onMessageReceived": { @@ -3978,30 +3544,42 @@ "name": "Assertion #0", "valid": true, "description": "Expecting 'enqueuer' (body) to contain 'queue'" + }, + { + "name": "Assertion #1", + "valid": true, + "description": "Expected 'headers.requestheaderkey' to be equal to 'requestHeaderValue'. Received 'requestHeaderValue'" } ], "arguments": { "headers": { - "content-length": "8", "host": "localhost:23075", - "connection": "close" + "connection": "keep-alive", + "requestheaderkey": "requestHeaderValue", + "content-type": "text/plain;charset=UTF-8", + "accept": "*/*", + "accept-language": "*", + "sec-fetch-mode": "cors", + "user-agent": "node", + "accept-encoding": "gzip, deflate", + "content-length": "8" }, "params": {}, "query": {}, "url": "/resource", "body": "enqueuer", - "elapsedTime": 162 + "elapsedTime": 157 } } }, "valid": true, - "subscriptionTime": "2020-03-08T05:33:36.951Z" + "sensorTime": "2025-03-18T20:55:58.111Z" } ], - "publishers": [ + "actuators": [ { - "id": "0233360793_7b553c9aff_715465", - "name": "Publisher #0", + "id": "1355570988_bbfbaaedee_990714", + "name": "Actuator #0", "valid": true, "hooks": { "onInit": { @@ -4013,57 +3591,50 @@ }, "onFinish": { "arguments": { - "executedHooks": [ - "onInit", - "onResponseReceived", - "onMessageReceived", - "onFinish" - ], - "elapsedTime": 178 + "executedHooks": { + "onInit": [], + "onResponseReceived": [ + "status", + "statusCode", + "body", + "headers", + "elapsedTime" + ], + "onFinish": [ + "executedHooks", + "elapsedTime" + ] + }, + "elapsedTime": 171 }, "tests": [ { "name": "Published", "valid": true, - "description": "{\n \"statusCode\": 444,\n \"headers\": {\n \"x-powered-by\": \"Express\",\n \"access-control-allow-origin\": \"*\",\n \"access-control-allow-headers\": \"Origin, X-Requested-With, Content-Type, Accept\",\n \"content-type\": \"text/html; charset=utf-8\",\n \"content-length\": \"4\",\n \"etag\": \"W/\\\"4-W/H9kn37hnlJai5s8Ay+UMHIcUU\\\"\",\n \"date\": \"Sun, 08 Mar 2020 05:33:36 GMT\",\n \"connection\": \"close\"\n },\n \"body\": \"blah\"\n}" + "description": "{\n \"statusCode\": 444,\n \"headers\": {\n \"access-control-allow-headers\": \"Origin, X-Requested-With, Content-Type, Accept\",\n \"access-control-allow-origin\": \"*\",\n \"connection\": \"keep-alive\",\n \"content-length\": \"4\",\n \"content-type\": \"text/html; charset=utf-8\",\n \"date\": \"Tue, 18 Mar 2025 20:55:58 GMT\",\n \"etag\": \"W/\\\"4-W/H9kn37hnlJai5s8Ay+UMHIcUU\\\"\",\n \"keep-alive\": \"timeout=5\",\n \"responseheaderkey\": \"responseHeaderValue\",\n \"x-powered-by\": \"Express\"\n },\n \"body\": \"blah\"\n}", + "implicit": true } ], "valid": true }, "onResponseReceived": { "arguments": { + "status": 444, "statusCode": 444, "body": "blah", "headers": { - "x-powered-by": "Express", - "access-control-allow-origin": "*", "access-control-allow-headers": "Origin, X-Requested-With, Content-Type, Accept", - "content-type": "text/html; charset=utf-8", + "access-control-allow-origin": "*", + "connection": "keep-alive", "content-length": "4", + "content-type": "text/html; charset=utf-8", + "date": "Tue, 18 Mar 2025 20:55:58 GMT", "etag": "W/\"4-W/H9kn37hnlJai5s8Ay+UMHIcUU\"", - "date": "Sun, 08 Mar 2020 05:33:36 GMT", - "connection": "close" - }, - "request": { - "uri": { - "protocol": "http:", - "slashes": true, - "auth": null, - "host": "localhost:23075", - "port": "23075", - "hostname": "localhost", - "hash": null, - "search": null, - "query": null, - "pathname": "/resource", - "path": "/resource", - "href": "http://localhost:23075/resource" - }, - "method": "post", - "headers": { - "Content-Length": 8 - } - } + "keep-alive": "timeout=5", + "responseheaderkey": "responseHeaderValue", + "x-powered-by": "Express" + }, + "elapsedTime": 171 }, "tests": [ { @@ -4075,51 +3646,18 @@ "name": "Assertion #1", "valid": true, "description": "Expected 'body' to be equal to 'blah'. Received 'blah'" + }, + { + "name": "Assertion #2", + "valid": true, + "description": "Expected 'headers.responseheaderkey' to be equal to 'responseHeaderValue'. Received 'responseHeaderValue'" } ], "valid": true - }, - "onMessageReceived": { - "arguments": { - "statusCode": 444, - "body": "blah", - "headers": { - "x-powered-by": "Express", - "access-control-allow-origin": "*", - "access-control-allow-headers": "Origin, X-Requested-With, Content-Type, Accept", - "content-type": "text/html; charset=utf-8", - "content-length": "4", - "etag": "W/\"4-W/H9kn37hnlJai5s8Ay+UMHIcUU\"", - "date": "Sun, 08 Mar 2020 05:33:36 GMT", - "connection": "close" - }, - "request": { - "uri": { - "protocol": "http:", - "slashes": true, - "auth": null, - "host": "localhost:23075", - "port": "23075", - "hostname": "localhost", - "hash": null, - "search": null, - "query": null, - "pathname": "/resource", - "path": "/resource", - "href": "http://localhost:23075/resource" - }, - "method": "post", - "headers": { - "Content-Length": 8 - } - } - }, - "tests": [], - "valid": true } }, "type": "http", - "publishTime": "2020-03-08T05:33:37.036Z" + "messageSentInstant": "2025-03-18T20:55:58.184Z" } ], "iteration": 0, @@ -4136,52 +3674,61 @@ "valid": true, "tests": [], "arguments": { - "elapsedTime": 178 + "elapsedTime": 171 } } }, "time": { - "startTime": "2020-03-08T05:33:36.858Z", - "endTime": "2020-03-08T05:33:37.037Z", - "totalTime": 179, + "startTime": "2025-03-18T20:55:58.013Z", + "endTime": "2025-03-18T20:55:58.184Z", + "totalTime": 171, "timeout": 5000 }, - "requisitions": [] + "tasks": [] }, { "valid": true, "name": "examples/https.yml", - "id": "0233360793_d6075fad05_992906", + "id": "1355570988_df10003961_318866", "level": 1, - "subscriptions": [ + "sensors": [ { - "id": "0233360793_81e3a69582_423410", - "name": "Subscription #0", + "id": "1355570988_11c785964d_389003", + "name": "Sensor #0", "type": "https", "hooks": { "onInit": { "valid": true, "tests": [], - "arguments": { - "elapsedTime": 0 - } + "arguments": {} }, "onFinish": { "valid": true, "tests": [ { + "implicit": true, "valid": true, "name": "Message received", - "description": "{\n \"headers\": {\n \"content-type\": \"application/json\",\n \"content-length\": \"18\",\n \"host\": \"localhost:4430\",\n \"connection\": \"close\"\n },\n \"params\": {},\n \"query\": {},\n \"url\": \"/enqueuer\",\n \"body\": {\n \"https\": \"works!\"\n },\n \"elapsedTime\": 185\n}" + "description": "{\n \"headers\": {\n \"host\": \"127.0.0.1:4430\",\n \"connection\": \"keep-alive\",\n \"content-type\": \"application/json\",\n \"accept\": \"*/*\",\n \"accept-language\": \"*\",\n \"sec-fetch-mode\": \"cors\",\n \"user-agent\": \"node\",\n \"accept-encoding\": \"br, gzip, deflate\",\n \"content-length\": \"18\"\n },\n \"params\": {},\n \"query\": {},\n \"url\": \"/enqueuer\",\n \"body\": {\n \"https\": \"works!\"\n }\n}" } ], "arguments": { - "executedHooks": [ - "onInit", - "onMessageReceived", - "onFinish" - ], - "elapsedTime": 189 + "executedHooks": { + "onInit": [], + "onMessageReceived": [ + "headers", + "params", + "query", + "url", + "body", + "elapsedTime" + ], + "onFinish": [ + "executedHooks", + "elapsedTime" + ] + }, + "elapsedTime": 180 } }, "onMessageReceived": { @@ -4195,27 +3742,32 @@ ], "arguments": { "headers": { + "host": "127.0.0.1:4430", + "connection": "keep-alive", "content-type": "application/json", - "content-length": "18", - "host": "localhost:4430", - "connection": "close" + "accept": "*/*", + "accept-language": "*", + "sec-fetch-mode": "cors", + "user-agent": "node", + "accept-encoding": "br, gzip, deflate", + "content-length": "18" }, "params": {}, "query": {}, "url": "/enqueuer", "body": "{\"https\":\"works!\"}", - "elapsedTime": 185 + "elapsedTime": 159 } } }, "valid": true, - "subscriptionTime": "2020-03-08T05:33:36.951Z" + "sensorTime": "2025-03-18T20:55:58.111Z" } ], - "publishers": [ + "actuators": [ { - "id": "0233360793_5cd9b40d14_659209", - "name": "Publisher #0", + "id": "1355570988_54fca16b67_23795", + "name": "Actuator #0", "valid": true, "hooks": { "onInit": { @@ -4227,115 +3779,56 @@ }, "onFinish": { "arguments": { - "executedHooks": [ - "onInit", - "onResponseReceived", - "onMessageReceived", - "onFinish" - ], - "elapsedTime": 189 + "executedHooks": { + "onInit": [], + "onResponseReceived": [ + "status", + "statusCode", + "body", + "headers", + "elapsedTime" + ], + "onFinish": [ + "executedHooks", + "elapsedTime" + ] + }, + "elapsedTime": 180 }, "tests": [ { "name": "Published", "valid": true, - "description": "{\n \"statusCode\": 200,\n \"headers\": {\n \"x-powered-by\": \"Express\",\n \"access-control-allow-origin\": \"*\",\n \"access-control-allow-headers\": \"Origin, X-Requested-With, Content-Type, Accept\",\n \"content-type\": \"text/html; charset=utf-8\",\n \"content-length\": \"5\",\n \"etag\": \"W/\\\"5-w0N9vHwSVdOiHURNhuvy6SNMIr0\\\"\",\n \"date\": \"Sun, 08 Mar 2020 05:33:37 GMT\",\n \"connection\": \"close\"\n },\n \"body\": \"https\"\n}" + "description": "{\n \"statusCode\": 200,\n \"headers\": {\n \"access-control-allow-headers\": \"Origin, X-Requested-With, Content-Type, Accept\",\n \"access-control-allow-origin\": \"*\",\n \"connection\": \"keep-alive\",\n \"content-length\": \"5\",\n \"content-type\": \"text/html; charset=utf-8\",\n \"date\": \"Tue, 18 Mar 2025 20:55:58 GMT\",\n \"etag\": \"W/\\\"5-w0N9vHwSVdOiHURNhuvy6SNMIr0\\\"\",\n \"keep-alive\": \"timeout=5\",\n \"x-powered-by\": \"Express\"\n },\n \"body\": \"https\"\n}", + "implicit": true } ], "valid": true }, "onResponseReceived": { "arguments": { + "status": 200, "statusCode": 200, "body": "https", "headers": { - "x-powered-by": "Express", - "access-control-allow-origin": "*", "access-control-allow-headers": "Origin, X-Requested-With, Content-Type, Accept", - "content-type": "text/html; charset=utf-8", - "content-length": "5", - "etag": "W/\"5-w0N9vHwSVdOiHURNhuvy6SNMIr0\"", - "date": "Sun, 08 Mar 2020 05:33:37 GMT", - "connection": "close" - }, - "request": { - "uri": { - "protocol": "https:", - "slashes": true, - "auth": null, - "host": "localhost:4430", - "port": "4430", - "hostname": "localhost", - "hash": null, - "search": null, - "query": null, - "pathname": "/enqueuer", - "path": "/enqueuer", - "href": "https://localhost:4430/enqueuer" - }, - "method": "post", - "headers": { - "content-type": "application/json", - "Content-Length": 18 - } - } - }, - "tests": [], - "valid": true - }, - "onMessageReceived": { - "arguments": { - "statusCode": 200, - "body": "https", - "headers": { - "x-powered-by": "Express", "access-control-allow-origin": "*", - "access-control-allow-headers": "Origin, X-Requested-With, Content-Type, Accept", - "content-type": "text/html; charset=utf-8", + "connection": "keep-alive", "content-length": "5", + "content-type": "text/html; charset=utf-8", + "date": "Tue, 18 Mar 2025 20:55:58 GMT", "etag": "W/\"5-w0N9vHwSVdOiHURNhuvy6SNMIr0\"", - "date": "Sun, 08 Mar 2020 05:33:37 GMT", - "connection": "close" - }, - "request": { - "uri": { - "protocol": "https:", - "slashes": true, - "auth": null, - "host": "localhost:4430", - "port": "4430", - "hostname": "localhost", - "hash": null, - "search": null, - "query": null, - "pathname": "/enqueuer", - "path": "/enqueuer", - "href": "https://localhost:4430/enqueuer" - }, - "method": "post", - "headers": { - "content-type": "application/json", - "Content-Length": 18 - } - } - }, - "tests": [ - { - "name": "Status Code", - "valid": true, - "description": "Expected 'statusCode' to be equal to '200'. Received '200'" + "keep-alive": "timeout=5", + "x-powered-by": "Express" }, - { - "name": "Body", - "valid": true, - "description": "Expected 'body' to be equal to 'https'. Received 'https'" - } - ], + "elapsedTime": 178 + }, + "tests": [], "valid": true } }, "type": "HTTPS", - "publishTime": "2020-03-08T05:33:37.047Z" + "messageSentInstant": "2025-03-18T20:55:58.191Z" } ], "iteration": 0, @@ -4352,25 +3845,25 @@ "valid": true, "tests": [], "arguments": { - "elapsedTime": 189 + "elapsedTime": 180 } } }, "time": { - "startTime": "2020-03-08T05:33:36.859Z", - "endTime": "2020-03-08T05:33:37.048Z", - "totalTime": 189, - "timeout": 3000 + "startTime": "2025-03-18T20:55:58.013Z", + "endTime": "2025-03-18T20:55:58.193Z", + "totalTime": 180, + "timeout": 5000 }, - "requisitions": [] + "tasks": [] }, { "valid": true, "name": "examples/ignore.yml", - "id": "0233360793_8ea767da7b_677632", + "id": "1355570988_462ea965fc_88424", "level": 1, - "subscriptions": [], - "publishers": [], + "sensors": [], + "actuators": [], "iteration": 0, "totalIterations": 1, "hooks": { @@ -4385,27 +3878,27 @@ "valid": true, "tests": [], "arguments": { - "elapsedTime": 69 + "elapsedTime": 87 } } }, "time": { - "startTime": "2020-03-08T05:33:36.860Z", - "endTime": "2020-03-08T05:33:36.929Z", - "totalTime": 69, + "startTime": "2025-03-18T20:55:58.014Z", + "endTime": "2025-03-18T20:55:58.101Z", + "totalTime": 87, "timeout": 5000 }, - "requisitions": [ + "tasks": [ { "valid": true, - "name": "Requisition #0", - "id": "0233360793_cbabde97ea_466080", + "name": "Task #0", + "id": "1355570988_a55bebc0fd_853628", "level": 2, - "subscriptions": [], - "publishers": [ + "sensors": [], + "actuators": [ { - "id": "0233360794_c325787526_709001", - "name": "Publisher #0", + "id": "1355570988_1f5a8da9cd_492843", + "name": "Actuator #0", "ignored": true, "valid": true, "hooks": { @@ -4435,27 +3928,27 @@ "valid": true, "tests": [], "arguments": { - "elapsedTime": 32 + "elapsedTime": 74 } } }, "time": { - "startTime": "2020-03-08T05:33:36.885Z", - "endTime": "2020-03-08T05:33:36.918Z", - "totalTime": 33, + "startTime": "2025-03-18T20:55:58.021Z", + "endTime": "2025-03-18T20:55:58.095Z", + "totalTime": 74, "timeout": 5000 }, - "requisitions": [] + "tasks": [] }, { "valid": true, - "name": "Requisition #1", - "id": "0233360794_24ec47c05e_220425", + "name": "Task #1", + "id": "1355570988_4c097977ac_495643", "level": 2, - "subscriptions": [ + "sensors": [ { - "id": "0233360794_41bbcab029_693948", - "name": "Subscription #0", + "id": "1355570988_ce955c26fb_854214", + "name": "Sensor #0", "ignored": true, "type": "file", "hooks": { @@ -4471,7 +3964,7 @@ "valid": true } ], - "publishers": [], + "actuators": [], "iteration": 0, "totalIterations": 1, "hooks": { @@ -4486,26 +3979,26 @@ "valid": true, "tests": [], "arguments": { - "elapsedTime": 2 + "elapsedTime": 1 } } }, "time": { - "startTime": "2020-03-08T05:33:36.923Z", - "endTime": "2020-03-08T05:33:36.925Z", - "totalTime": 2, + "startTime": "2025-03-18T20:55:58.098Z", + "endTime": "2025-03-18T20:55:58.099Z", + "totalTime": 1, "timeout": 5000 }, - "requisitions": [] + "tasks": [] }, { "valid": true, - "name": "Requisition #2", - "id": "0233360794_73301c2bc1_824458", + "name": "Task #2", + "id": "1355570988_f21399a597_212699", "ignored": true, "level": 2, - "subscriptions": [], - "publishers": [], + "sensors": [], + "actuators": [], "hooks": { "onInit": { "arguments": {}, @@ -4519,21 +4012,21 @@ } }, "time": { - "startTime": "2020-03-08T05:33:36.927Z", - "endTime": "2020-03-08T05:33:36.927Z", + "startTime": "2025-03-18T20:55:58.100Z", + "endTime": "2025-03-18T20:55:58.100Z", "totalTime": 0 }, - "requisitions": [] + "tasks": [] } ] }, { "valid": true, "name": "examples/import.yml", - "id": "0233360794_11d1ffbd36_804", + "id": "1355570988_671d019c4a_586338", "level": 1, - "subscriptions": [], - "publishers": [], + "sensors": [], + "actuators": [], "iteration": 0, "totalIterations": 1, "hooks": { @@ -4548,24 +4041,24 @@ "valid": true, "tests": [], "arguments": { - "elapsedTime": 178 + "elapsedTime": 183 } } }, "time": { - "startTime": "2020-03-08T05:33:36.862Z", - "endTime": "2020-03-08T05:33:37.040Z", - "totalTime": 178, + "startTime": "2025-03-18T20:55:58.014Z", + "endTime": "2025-03-18T20:55:58.197Z", + "totalTime": 183, "timeout": 5000 }, - "requisitions": [ + "tasks": [ { "valid": true, - "name": "static importRequisition", - "id": "0233360794_0c14282f98_513588", + "name": "static importTask", + "id": "1355570988_7bed35a763_356445", "level": 2, - "subscriptions": [], - "publishers": [], + "sensors": [], + "actuators": [], "iteration": 0, "totalIterations": 1, "hooks": { @@ -4582,29 +4075,29 @@ { "name": "Assertion #0", "valid": true, - "description": "Expecting 'requisition.imported' to be true. Received: true" + "description": "Expecting 'task.imported' to be true. Received: true" } ], "arguments": { - "elapsedTime": 32 + "elapsedTime": 72 } } }, "time": { - "startTime": "2020-03-08T05:33:36.885Z", - "endTime": "2020-03-08T05:33:36.917Z", - "totalTime": 32, + "startTime": "2025-03-18T20:55:58.022Z", + "endTime": "2025-03-18T20:55:58.095Z", + "totalTime": 73, "timeout": 5000 }, - "requisitions": [] + "tasks": [] }, { "valid": true, - "name": "dynamic importRequisition", - "id": "0233360794_6857a59a1d_946774", + "name": "dynamic importTask", + "id": "1355570988_c497197b78_349904", "level": 2, - "subscriptions": [], - "publishers": [], + "sensors": [], + "actuators": [], "iteration": 0, "totalIterations": 1, "hooks": { @@ -4621,44 +4114,42 @@ { "name": "Assertion #0", "valid": true, - "description": "Expecting 'requisition.dynamicallyImported' to be true. Received: true" + "description": "Expecting 'task.dynamicallyImported' to be true. Received: true" }, { "name": "Assertion #1", "valid": true, - "description": "Expected 'requisition.priority' to be equal to 'higher'. Received 'higher'" + "description": "Expected 'task.priority' to be equal to 'higher'. Received 'higher'" } ], "arguments": { - "elapsedTime": 3 + "elapsedTime": 2 } } }, "time": { - "startTime": "2020-03-08T05:33:36.921Z", - "endTime": "2020-03-08T05:33:36.924Z", - "totalTime": 3, + "startTime": "2025-03-18T20:55:58.097Z", + "endTime": "2025-03-18T20:55:58.099Z", + "totalTime": 2, "timeout": 5000 }, - "requisitions": [] + "tasks": [] }, { "valid": true, - "name": "Requisition #2", - "id": "0233360794_28f1b301b5_208127", + "name": "Task #2", + "id": "1355570988_5f88266801_796789", "level": 2, - "subscriptions": [ + "sensors": [ { - "id": "0233360794_a7a23228b4_464151", - "name": "Subscription #0", + "id": "1355570988_2fa2706f5b_562816", + "name": "Sensor #0", "type": "http", "hooks": { "onInit": { "valid": true, "tests": [], - "arguments": { - "elapsedTime": 0 - } + "arguments": {} }, "onFinish": { "valid": true, @@ -4666,26 +4157,37 @@ { "name": "Assertion #0", "valid": true, - "description": "Expected 'subscription.type' to be equal to 'http'. Received 'http'" + "description": "Expected 'sensor.type' to be equal to 'http'. Received 'http'" }, { "name": "Assertion #1", "valid": true, - "description": "Expected 'subscription.endpoint' to be equal to '/subscription-reuse'. Received '/subscription-reuse'" + "description": "Expected 'sensor.endpoint' to be equal to '/sensor-reuse'. Received '/sensor-reuse'" }, { + "implicit": true, "valid": true, "name": "Message received", - "description": "{\n \"headers\": {\n \"content-length\": \"5\",\n \"host\": \"localhost:23075\",\n \"connection\": \"close\"\n },\n \"params\": {},\n \"query\": {},\n \"url\": \"/subscription-reuse\",\n \"body\": \"virgs\",\n \"elapsedTime\": 98\n}" + "description": "{\n \"headers\": {\n \"host\": \"localhost:23075\",\n \"connection\": \"keep-alive\",\n \"content-type\": \"text/plain;charset=UTF-8\",\n \"accept\": \"*/*\",\n \"accept-language\": \"*\",\n \"sec-fetch-mode\": \"cors\",\n \"user-agent\": \"node\",\n \"accept-encoding\": \"gzip, deflate\",\n \"content-length\": \"5\"\n },\n \"params\": {},\n \"query\": {},\n \"url\": \"/sensor-reuse\",\n \"body\": \"virgs\"\n}" } ], "arguments": { - "executedHooks": [ - "onInit", - "onMessageReceived", - "onFinish" - ], - "elapsedTime": 113 + "executedHooks": { + "onInit": [], + "onMessageReceived": [ + "headers", + "params", + "query", + "url", + "body", + "elapsedTime" + ], + "onFinish": [ + "executedHooks", + "elapsedTime" + ] + }, + "elapsedTime": 98 } }, "onMessageReceived": { @@ -4693,26 +4195,32 @@ "tests": [], "arguments": { "headers": { - "content-length": "5", "host": "localhost:23075", - "connection": "close" + "connection": "keep-alive", + "content-type": "text/plain;charset=UTF-8", + "accept": "*/*", + "accept-language": "*", + "sec-fetch-mode": "cors", + "user-agent": "node", + "accept-encoding": "gzip, deflate", + "content-length": "5" }, "params": {}, "query": {}, - "url": "/subscription-reuse", + "url": "/sensor-reuse", "body": "virgs", - "elapsedTime": 98 + "elapsedTime": 90 } } }, "valid": true, - "subscriptionTime": "2020-03-08T05:33:36.951Z" + "sensorTime": "2025-03-18T20:55:58.111Z" } ], - "publishers": [ + "actuators": [ { - "id": "0233360794_5163661566_941755", - "name": "Publisher #0", + "id": "1355570988_a0d38bedc4_300445", + "name": "Actuator #0", "valid": true, "hooks": { "onInit": { @@ -4724,118 +4232,66 @@ }, "onFinish": { "arguments": { - "executedHooks": [ - "onInit", - "onResponseReceived", - "onMessageReceived", - "onFinish" - ], - "elapsedTime": 113 + "executedHooks": { + "onInit": [], + "onResponseReceived": [ + "status", + "statusCode", + "body", + "headers", + "elapsedTime" + ], + "onFinish": [ + "executedHooks", + "elapsedTime" + ] + }, + "elapsedTime": 97 }, "tests": [ { "name": "Published", "valid": true, - "description": "{\n \"statusCode\": 444,\n \"headers\": {\n \"x-powered-by\": \"Express\",\n \"access-control-allow-origin\": \"*\",\n \"access-control-allow-headers\": \"Origin, X-Requested-With, Content-Type, Accept\",\n \"content-type\": \"text/html; charset=utf-8\",\n \"content-length\": \"4\",\n \"etag\": \"W/\\\"4-W/H9kn37hnlJai5s8Ay+UMHIcUU\\\"\",\n \"date\": \"Sun, 08 Mar 2020 05:33:36 GMT\",\n \"connection\": \"close\"\n },\n \"body\": \"blah\"\n}" + "description": "{\n \"statusCode\": 444,\n \"headers\": {\n \"access-control-allow-headers\": \"Origin, X-Requested-With, Content-Type, Accept\",\n \"access-control-allow-origin\": \"*\",\n \"connection\": \"keep-alive\",\n \"content-length\": \"4\",\n \"content-type\": \"text/html; charset=utf-8\",\n \"date\": \"Tue, 18 Mar 2025 20:55:58 GMT\",\n \"etag\": \"W/\\\"4-W/H9kn37hnlJai5s8Ay+UMHIcUU\\\"\",\n \"keep-alive\": \"timeout=5\",\n \"x-powered-by\": \"Express\"\n },\n \"body\": \"blah\"\n}", + "implicit": true }, { "name": "Assertion #0", "valid": true, - "description": "Expected 'publisher.type' to be equal to 'http'. Received 'http'" + "description": "Expected 'actuator.type' to be equal to 'http'. Received 'http'" }, { "name": "Assertion #1", "valid": true, - "description": "Expected 'publisher.method' to be equal to 'POST'. Received 'POST'" + "description": "Expected 'actuator.method' to be equal to 'POST'. Received 'POST'" } ], "valid": true }, "onResponseReceived": { "arguments": { + "status": 444, "statusCode": 444, "body": "blah", "headers": { - "x-powered-by": "Express", - "access-control-allow-origin": "*", "access-control-allow-headers": "Origin, X-Requested-With, Content-Type, Accept", - "content-type": "text/html; charset=utf-8", - "content-length": "4", - "etag": "W/\"4-W/H9kn37hnlJai5s8Ay+UMHIcUU\"", - "date": "Sun, 08 Mar 2020 05:33:36 GMT", - "connection": "close" - }, - "request": { - "uri": { - "protocol": "http:", - "slashes": true, - "auth": null, - "host": "localhost:23075", - "port": "23075", - "hostname": "localhost", - "hash": null, - "search": null, - "query": null, - "pathname": "/subscription-reuse", - "path": "/subscription-reuse", - "href": "http://localhost:23075/subscription-reuse" - }, - "method": "post", - "headers": { - "Content-Length": 5 - } - } - }, - "tests": [], - "valid": true - }, - "onMessageReceived": { - "arguments": { - "statusCode": 444, - "body": "blah", - "headers": { - "x-powered-by": "Express", "access-control-allow-origin": "*", - "access-control-allow-headers": "Origin, X-Requested-With, Content-Type, Accept", - "content-type": "text/html; charset=utf-8", + "connection": "keep-alive", "content-length": "4", + "content-type": "text/html; charset=utf-8", + "date": "Tue, 18 Mar 2025 20:55:58 GMT", "etag": "W/\"4-W/H9kn37hnlJai5s8Ay+UMHIcUU\"", - "date": "Sun, 08 Mar 2020 05:33:36 GMT", - "connection": "close" + "keep-alive": "timeout=5", + "x-powered-by": "Express" }, - "request": { - "uri": { - "protocol": "http:", - "slashes": true, - "auth": null, - "host": "localhost:23075", - "port": "23075", - "hostname": "localhost", - "hash": null, - "search": null, - "query": null, - "pathname": "/subscription-reuse", - "path": "/subscription-reuse", - "href": "http://localhost:23075/subscription-reuse" - }, - "method": "post", - "headers": { - "Content-Length": 5 - } - } + "elapsedTime": 97 }, - "tests": [ - { - "name": "Assertion #0", - "valid": true, - "description": "Expected 'statusCode' to be equal to '444'. Received '444'" - } - ], + "tests": [], "valid": true } }, "type": "http", - "publishTime": "2020-03-08T05:33:37.039Z" + "messageSentInstant": "2025-03-18T20:55:58.197Z" } ], "iteration": 0, @@ -4852,27 +4308,27 @@ "valid": true, "tests": [], "arguments": { - "elapsedTime": 113 + "elapsedTime": 98 } } }, "time": { - "startTime": "2020-03-08T05:33:36.926Z", - "endTime": "2020-03-08T05:33:37.040Z", - "totalTime": 114, + "startTime": "2025-03-18T20:55:58.099Z", + "endTime": "2025-03-18T20:55:58.197Z", + "totalTime": 98, "timeout": 5000 }, - "requisitions": [] + "tasks": [] } ] }, { "valid": true, "name": "examples/no-tests.yml", - "id": "0233360794_92334e1554_934193", + "id": "1355570988_1fd3fcba18_616845", "level": 1, - "subscriptions": [], - "publishers": [], + "sensors": [], + "actuators": [], "iteration": 0, "totalIterations": 1, "hooks": { @@ -4887,24 +4343,24 @@ "valid": true, "tests": [], "arguments": { - "elapsedTime": 218 + "elapsedTime": 210 } } }, "time": { - "startTime": "2020-03-08T05:33:36.862Z", - "endTime": "2020-03-08T05:33:37.081Z", - "totalTime": 219, + "startTime": "2025-03-18T20:55:58.014Z", + "endTime": "2025-03-18T20:55:58.224Z", + "totalTime": 210, "timeout": 5000 }, - "requisitions": [ + "tasks": [ { "valid": true, - "name": "Requisition #0", - "id": "0233360794_35cb1b4ae8_838119", + "name": "Task #0", + "id": "1355570988_01ce9f0a63_6425", "level": 2, - "subscriptions": [], - "publishers": [], + "sensors": [], + "actuators": [], "iteration": 0, "totalIterations": 1, "hooks": { @@ -4924,22 +4380,22 @@ } }, "time": { - "startTime": "2020-03-08T05:33:36.980Z", - "endTime": "2020-03-08T05:33:37.080Z", + "startTime": "2025-03-18T20:55:58.124Z", + "endTime": "2025-03-18T20:55:58.224Z", "totalTime": 100, "timeout": 5000 }, - "requisitions": [] + "tasks": [] } ] }, { "valid": true, - "name": "examples/parallel-requisition.yml", - "id": "0233360794_e05b5acab1_820854", + "name": "examples/parallel-task.yml", + "id": "1355570988_1cb4ee5875_216905", "level": 1, - "subscriptions": [], - "publishers": [], + "sensors": [], + "actuators": [], "iteration": 0, "totalIterations": 1, "hooks": { @@ -4954,24 +4410,24 @@ "valid": true, "tests": [], "arguments": { - "elapsedTime": 858 + "elapsedTime": 853 } } }, "time": { - "startTime": "2020-03-08T05:33:36.864Z", - "endTime": "2020-03-08T05:33:37.722Z", - "totalTime": 858, + "startTime": "2025-03-18T20:55:58.015Z", + "endTime": "2025-03-18T20:55:58.868Z", + "totalTime": 853, "timeout": 5000 }, - "requisitions": [ + "tasks": [ { "valid": true, - "name": "Requisition #0", - "id": "0233360794_e2f0694b46_166090", + "name": "Task #0", + "id": "1355570988_7fe57f520c_649907", "level": 2, - "subscriptions": [], - "publishers": [], + "sensors": [], + "actuators": [], "iteration": 0, "totalIterations": 1, "hooks": { @@ -4988,33 +4444,33 @@ { "name": "started at the same time", "valid": true, - "description": "Expected 'Math.abs(requisition.requisitions[1].startTime.getTime() - requisition.requisitions[0].startTime.getTime())' to be less than or equal to '100'. Received '1'" + "description": "Expected 'Math.abs(task.tasks[1].startTime.getTime() - task.tasks[0].startTime.getTime())' to be less than or equal to '100'. Received '0'" }, { "name": "Assertion #1", "valid": true, - "description": "Expected 'elapsedTime' to be less than or equal to '400'. Received '324'" + "description": "Expected 'elapsedTime' to be less than or equal to '400'. Received '341'" } ], "arguments": { - "elapsedTime": 324 + "elapsedTime": 341 } } }, "time": { - "startTime": "2020-03-08T05:33:36.886Z", - "endTime": "2020-03-08T05:33:37.211Z", - "totalTime": 325, + "startTime": "2025-03-18T20:55:58.022Z", + "endTime": "2025-03-18T20:55:58.364Z", + "totalTime": 342, "timeout": 5000 }, - "requisitions": [ + "tasks": [ { "valid": true, - "name": "Requisition #0", - "id": "0233360794_913ffe0794_861814", + "name": "Task #0", + "id": "1355570988_5db8f2c023_14140", "level": 3, - "subscriptions": [], - "publishers": [], + "sensors": [], + "actuators": [], "iteration": 0, "totalIterations": 1, "hooks": { @@ -5029,25 +4485,25 @@ "valid": true, "tests": [], "arguments": { - "elapsedTime": 201 + "elapsedTime": 202 } } }, "time": { - "startTime": "2020-03-08T05:33:36.908Z", - "endTime": "2020-03-08T05:33:37.110Z", + "startTime": "2025-03-18T20:55:58.063Z", + "endTime": "2025-03-18T20:55:58.265Z", "totalTime": 202, "timeout": 5000 }, - "requisitions": [] + "tasks": [] }, { "valid": true, - "name": "Requisition #1", - "id": "0233360794_fdabeaad92_305365", + "name": "Task #1", + "id": "1355570988_45d0cee5e7_398896", "level": 3, - "subscriptions": [], - "publishers": [], + "sensors": [], + "actuators": [], "iteration": 0, "totalIterations": 1, "hooks": { @@ -5062,27 +4518,27 @@ "valid": true, "tests": [], "arguments": { - "elapsedTime": 301 + "elapsedTime": 300 } } }, "time": { - "startTime": "2020-03-08T05:33:36.909Z", - "endTime": "2020-03-08T05:33:37.210Z", - "totalTime": 301, + "startTime": "2025-03-18T20:55:58.063Z", + "endTime": "2025-03-18T20:55:58.363Z", + "totalTime": 300, "timeout": 5000 }, - "requisitions": [] + "tasks": [] } ] }, { "valid": true, - "name": "Requisition #1", - "id": "0233360794_30bc9c1ac3_177900", + "name": "Task #1", + "id": "1355570988_d3366a8bad_567254", "level": 2, - "subscriptions": [], - "publishers": [], + "sensors": [], + "actuators": [], "iteration": 0, "totalIterations": 1, "hooks": { @@ -5099,33 +4555,33 @@ { "name": "started after the other", "valid": true, - "description": "Expected 'requisition.requisitions[1].startTime.getTime() - requisition.requisitions[0].startTime.getTime()' to be greater than or equal to '200'. Received '204'" + "description": "Expected 'task.tasks[1].startTime.getTime() - task.tasks[0].startTime.getTime()' to be greater than or equal to '200'. Received '202'" }, { "name": "Assertion #1", "valid": true, - "description": "Expected 'elapsedTime' to be less than or equal to '600'. Received '510'" + "description": "Expected 'elapsedTime' to be less than or equal to '600'. Received '503'" } ], "arguments": { - "elapsedTime": 510 + "elapsedTime": 503 } } }, "time": { - "startTime": "2020-03-08T05:33:37.212Z", - "endTime": "2020-03-08T05:33:37.722Z", - "totalTime": 510, + "startTime": "2025-03-18T20:55:58.364Z", + "endTime": "2025-03-18T20:55:58.867Z", + "totalTime": 503, "timeout": 5000 }, - "requisitions": [ + "tasks": [ { "valid": true, - "name": "Requisition #0", - "id": "0233360794_913ffe0794_479661", + "name": "Task #0", + "id": "1355570988_5db8f2c023_860781", "level": 3, - "subscriptions": [], - "publishers": [], + "sensors": [], + "actuators": [], "iteration": 0, "totalIterations": 1, "hooks": { @@ -5140,25 +4596,25 @@ "valid": true, "tests": [], "arguments": { - "elapsedTime": 204 + "elapsedTime": 202 } } }, "time": { - "startTime": "2020-03-08T05:33:37.212Z", - "endTime": "2020-03-08T05:33:37.416Z", - "totalTime": 204, + "startTime": "2025-03-18T20:55:58.364Z", + "endTime": "2025-03-18T20:55:58.566Z", + "totalTime": 202, "timeout": 5000 }, - "requisitions": [] + "tasks": [] }, { "valid": true, - "name": "Requisition #1", - "id": "0233360794_463f29d694_315020", + "name": "Task #1", + "id": "1355570988_0c768854c4_835800", "level": 3, - "subscriptions": [], - "publishers": [], + "sensors": [], + "actuators": [], "iteration": 0, "totalIterations": 1, "hooks": { @@ -5173,17 +4629,17 @@ "valid": true, "tests": [], "arguments": { - "elapsedTime": 305 + "elapsedTime": 301 } } }, "time": { - "startTime": "2020-03-08T05:33:37.416Z", - "endTime": "2020-03-08T05:33:37.721Z", - "totalTime": 305, + "startTime": "2025-03-18T20:55:58.566Z", + "endTime": "2025-03-18T20:55:58.867Z", + "totalTime": 301, "timeout": 5000 }, - "requisitions": [] + "tasks": [] } ] } @@ -5191,14 +4647,14 @@ }, { "valid": true, - "name": "examples/parallel-test-publisher.yml", - "id": "0233360794_98a37b717a_67806", + "name": "examples/parallel-test-actuator.yml", + "id": "1355570988_6cafd01191_792511", "level": 1, - "subscriptions": [], - "publishers": [ + "sensors": [], + "actuators": [ { - "id": "0233360794_2955c0f42c_251125", - "name": "Publisher #0", + "id": "1355570988_b0c204f8d7_305811", + "name": "Actuator #0", "valid": true, "hooks": { "onInit": { @@ -5210,18 +4666,26 @@ }, "onFinish": { "arguments": { - "executedHooks": [ - "onInit", - "onMessageReceived", - "onFinish" - ], - "elapsedTime": 508 + "executedHooks": { + "onInit": [], + "onMessageReceived": [ + "payload", + "stream", + "elapsedTime" + ], + "onFinish": [ + "executedHooks", + "elapsedTime" + ] + }, + "elapsedTime": 1004 }, "tests": [ { "name": "Published", "valid": true, - "description": "Published successfully" + "description": "Published successfully", + "implicit": true } ], "valid": true @@ -5232,9 +4696,9 @@ "stream": { "address": "127.0.0.1", "family": "IPv4", - "port": 61981 + "port": 57120 }, - "elapsedTime": 1509 + "elapsedTime": 2003 }, "tests": [ { @@ -5252,7 +4716,7 @@ } }, "type": "tcp", - "publishTime": "2020-03-08T05:33:37.371Z" + "messageSentInstant": "2025-03-18T20:55:59.019Z" } ], "iteration": 0, @@ -5269,52 +4733,59 @@ "valid": true, "tests": [], "arguments": { - "elapsedTime": 508 + "elapsedTime": 1004 } } }, "time": { - "startTime": "2020-03-08T05:33:36.864Z", - "endTime": "2020-03-08T05:33:37.372Z", - "totalTime": 508, - "timeout": 3000 + "startTime": "2025-03-18T20:55:58.015Z", + "endTime": "2025-03-18T20:55:59.019Z", + "totalTime": 1004, + "timeout": 10000 }, - "requisitions": [] + "tasks": [] }, { "valid": true, - "name": "examples/parallel-test-subscription.yml", - "id": "0233360794_eb9c2c8618_255594", + "name": "examples/parallel-test-sensor.yml", + "id": "1355570988_fe639b1e21_752481", "level": 1, - "subscriptions": [ + "sensors": [ { - "id": "0233360795_b419ff22d8_113724", - "name": "Subscription #0", + "id": "1355570988_ae5a5d0b7b_108571", + "name": "Sensor #0", "type": "tcp", "hooks": { "onInit": { "valid": true, "tests": [], - "arguments": { - "elapsedTime": 0 - } + "arguments": {} }, "onFinish": { "valid": true, "tests": [ { + "implicit": true, "valid": true, "name": "Message received", - "description": "Subscription has received its message" + "description": "Sensor has received its message" } ], "arguments": { - "executedHooks": [ - "onInit", - "onMessageReceived", - "onFinish" - ], - "elapsedTime": 505 + "executedHooks": { + "onInit": [], + "onMessageReceived": [ + "payload", + "stream", + "path", + "elapsedTime" + ], + "onFinish": [ + "executedHooks", + "elapsedTime" + ] + }, + "elapsedTime": 1003 } }, "onMessageReceived": { @@ -5333,15 +4804,15 @@ "family": "IPv6", "port": 23081 }, - "elapsedTime": 504 + "elapsedTime": 1003 } } }, "valid": true, - "subscriptionTime": "2020-03-08T05:33:36.951Z" + "sensorTime": "2025-03-18T20:55:58.111Z" } ], - "publishers": [], + "actuators": [], "iteration": 0, "totalIterations": 1, "hooks": { @@ -5356,25 +4827,25 @@ "valid": true, "tests": [], "arguments": { - "elapsedTime": 505 + "elapsedTime": 1003 } } }, "time": { - "startTime": "2020-03-08T05:33:36.865Z", - "endTime": "2020-03-08T05:33:37.370Z", - "totalTime": 505, - "timeout": 3000 + "startTime": "2025-03-18T20:55:58.015Z", + "endTime": "2025-03-18T20:55:59.018Z", + "totalTime": 1003, + "timeout": 10000 }, - "requisitions": [] + "tasks": [] }, { "valid": true, "name": "examples/parent.yml", - "id": "0233360795_b79240b3fa_96730", + "id": "1355570989_da63bddcca_944032", "level": 1, - "subscriptions": [], - "publishers": [], + "sensors": [], + "actuators": [], "iteration": 0, "totalIterations": 1, "hooks": { @@ -5389,24 +4860,24 @@ "valid": true, "tests": [], "arguments": { - "elapsedTime": 164 + "elapsedTime": 185 } } }, "time": { - "startTime": "2020-03-08T05:33:36.865Z", - "endTime": "2020-03-08T05:33:37.029Z", - "totalTime": 164, + "startTime": "2025-03-18T20:55:58.015Z", + "endTime": "2025-03-18T20:55:58.200Z", + "totalTime": 185, "timeout": 5000 }, - "requisitions": [ + "tasks": [ { "valid": true, - "name": "Requisition #0", - "id": "0233360795_c28c57bc75_142157", + "name": "Task #0", + "id": "1355570989_3b15b37229_512254", "level": 2, - "subscriptions": [], - "publishers": [], + "sensors": [], + "actuators": [], "iteration": 0, "totalIterations": 1, "hooks": { @@ -5421,27 +4892,27 @@ "valid": true, "tests": [], "arguments": { - "elapsedTime": 31 + "elapsedTime": 72 } } }, "time": { - "startTime": "2020-03-08T05:33:36.886Z", - "endTime": "2020-03-08T05:33:36.917Z", - "totalTime": 31, + "startTime": "2025-03-18T20:55:58.022Z", + "endTime": "2025-03-18T20:55:58.095Z", + "totalTime": 73, "timeout": 5000 }, - "requisitions": [] + "tasks": [] }, { "valid": true, - "name": "Requisition #1", - "id": "0233360795_52aeba2265_976420", + "name": "Task #1", + "id": "1355570989_75be7f29de_679568", "level": 2, - "subscriptions": [ + "sensors": [ { - "id": "0233360795_f3eb57f16a_27206", - "name": "Subscription #0", + "id": "1355570989_4219491ee0_369260", + "name": "Sensor #0", "type": "tcp", "hooks": { "onInit": { @@ -5450,36 +4921,38 @@ { "name": "Assertion #0", "valid": true, - "description": "Expected 'subscription.parent.parent.requisitions[0].file.length' to be equal to '10'. Received '10'" + "description": "Expected 'sensor.parent.parent.tasks[0].file.length' to be equal to '10'. Received '10'" } ], - "arguments": { - "elapsedTime": 0 - } + "arguments": {} }, "onFinish": { "valid": true, "tests": [ { "valid": true, - "name": "Subscription avoided", - "description": "Avoidable subscription has not received any message" + "implicit": true, + "name": "Sensor avoided", + "description": "Avoidable sensor has not received any message" } ], "arguments": { - "executedHooks": [ - "onInit", - "onFinish" - ], - "elapsedTime": 107 + "executedHooks": { + "onInit": [], + "onFinish": [ + "executedHooks", + "elapsedTime" + ] + }, + "elapsedTime": 102 } } }, "valid": true, - "subscriptionTime": "2020-03-08T05:33:36.951Z" + "sensorTime": "2025-03-18T20:55:58.111Z" } ], - "publishers": [], + "actuators": [], "iteration": 0, "totalIterations": 1, "hooks": { @@ -5494,30 +4967,30 @@ "valid": true, "tests": [], "arguments": { - "elapsedTime": 107 + "elapsedTime": 103 } } }, "time": { - "startTime": "2020-03-08T05:33:36.921Z", - "endTime": "2020-03-08T05:33:37.029Z", - "totalTime": 108, + "startTime": "2025-03-18T20:55:58.097Z", + "endTime": "2025-03-18T20:55:58.200Z", + "totalTime": 103, "timeout": 5000 }, - "requisitions": [] + "tasks": [] } ] }, { "valid": true, "name": "examples/readme-enqueuer-repo-hit.yml", - "id": "0233360795_5edd709fc3_130557", + "id": "1355570989_80bd223bd7_760004", "level": 1, - "subscriptions": [], - "publishers": [ + "sensors": [], + "actuators": [ { - "id": "0233360795_cf5343e68d_486656", - "name": "Publisher #0", + "id": "1355570989_4d2d1f7e97_684906", + "name": "Actuator #0", "valid": true, "hooks": { "onInit": { @@ -5529,72 +5002,57 @@ }, "onFinish": { "arguments": { - "executedHooks": [ - "onInit", - "onResponseReceived", - "onMessageReceived", - "onFinish" - ], - "elapsedTime": 762 + "executedHooks": { + "onInit": [], + "onResponseReceived": [ + "status", + "statusCode", + "body", + "headers", + "elapsedTime" + ], + "onFinish": [ + "executedHooks", + "elapsedTime" + ] + }, + "elapsedTime": 985 }, "tests": [ { "name": "Published", "valid": true, - "description": "{\n \"statusCode\": 200,\n \"headers\": {\n \"date\": \"Sun, 08 Mar 2020 05:33:37 GMT\",\n \"content-type\": \"text/html; charset=utf-8\",\n \"server\": \"GitHub.com\",\n \"status\": \"200 OK\",\n \"vary\": \"X-PJAX, Accept-Encoding, Accept, X-Requested-With\",\n \"etag\": \"W/\\\"e488a776a390ca1e6112af885e19a43b\\\"\",\n \"cache-control\": \"max-age=0, private, must-revalidate\",\n \"strict-transport-security\": \"max-age=31536000; includeSubdomains; preload\",\n \"x-frame-options\": \"deny\",\n \"x-content-type-options\": \"nosniff\",\n \"x-xss-protection\": \"1; mode=block\",\n \"expect-ct\": \"max-age=2592000, report-uri=\\\"https://api.github.com/_private/browser/errors\\\"\",\n \"content-security-policy\": \"default-src 'none'; base-uri 'self'; block-all-mixed-content; connect-src 'self' uploads.github.com www.githubstatus.com collector.githubapp.com api.github.com www.google-analytics.com github-cloud.s3.amazonaws.com github-production-repository-file-5c1aeb.s3.amazonaws.com github-production-upload-manifest-file-7fdce7.s3.amazonaws.com github-production-user-asset-6210df.s3.amazonaws.com wss://live.github.com; font-src github.githubassets.com; form-action 'self' github.com gist.github.com; frame-ancestors 'none'; frame-src render.githubusercontent.com; img-src 'self' data: github.githubassets.com identicons.github.com collector.githubapp.com github-cloud.s3.amazonaws.com *.githubusercontent.com; manifest-src 'self'; media-src 'none'; script-src github.githubassets.com; style-src 'unsafe-inline' github.githubassets.com\",\n \"age\": \"0\",\n \"set-cookie\": [\n \"_gh_sess=NIjkGD7YYWQ115S4OHG6JQUkfOOgoFL39LjAItk%2BgABxY%2BDMA6dABKH6c%2B9c5eybzagK4jOboXCIoGV5%2BvY2sqWsSyM1a4L%2F3uk3okn2u%2FrYzgP2tHeaEcS89Qvu0v2UOmBp%2BDU0qQvjPTkNvjIfVtZnF9vUoPOWysJ4IUxXaqjkFFS4YWSWD7bqfpxCE6sXU74oFbMVpIQ6pd01yuO1W0ufWGTAYorrAiRqeOZ9%2Fgly%2BtJJTO7in8mfPyFhdnz930P0z8URtENxAMb9d6bBxQ%3D%3D--b4cNsBtPqvjmWGKH--dFzHLmLlq3thQfie%2FC2N4A%3D%3D; Path=/; HttpOnly; Secure\",\n \"_octo=GH1.1.471616304.1583645617; Path=/; Domain=github.com; Expires=Mon, 08 Mar 2021 05:33:37 GMT; Secure\",\n \"logged_in=no; Path=/; Domain=github.com; Expires=Mon, 08 Mar 2021 05:33:37 GMT; HttpOnly; Secure\"\n ],\n \"accept-ranges\": \"bytes\",\n \"transfer-encoding\": \"chunked\",\n \"connection\": \"close\",\n \"x-github-request-id\": \"F216:085B:70FC9:9E247:5E6483B1\"\n },\n \"body\": \"\\n\\n\\n\\n\\n\\n\\n\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n\\n\\n\\n \\n \\n \\n \\n \\n \\n \\n\\n\\n \\n \\n GitHub - enqueuer-land/enqueuer: Multi protocol microservice testing tool\\n \\n \\n \\n \\n\\n \\n \\n\\n \\n \\n \\n\\n \\n\\n\\n\\n \\n\\n \\n\\n \\n\\n \\n \\n \\n\\n \\n\\n\\n\\n\\n \\n\\n\\n\\n\\n\\n\\n \\n\\n \\n \\n\\n \\n\\n\\n \\n\\n \\n \\n\\n \\n\\n \\n\\n \\n\\n\\n \\n\\n\\n \\n\\n \\n\\n \\n \\n\\n\\n\\n\\n \\n\\n \\n\\n \\n \\n\\n
\\n Skip to content\\n \\n \\n \\n\\n \\n \\n\\n\\n\\n
\\n
\\n
\\n \\n \\n \\n
\\n\\n
\\n
\\n \\n
\\n\\n \\n\\n \\n
\\n
\\n
\\n\\n
\\n\\n
\\n\\n\\n
\\n\\n
\\n\\n\\n \\n\\n \\n\\n\\n\\n\\n
\\n
\\n
\\n \\n \\n\\n\\n\\n\\n \\n\\n\\n\\n\\n\\n\\n\\n\\n\\n
\\n\\n
\\n\\n
\\n

\\n \\n \\n enqueuer-land\\n \\n /\\n \\n enqueuer\\n \\n \\n

\\n\\n\\n
\\n\\n \\n\\n
\\n \\n\\n\\n\\n
\\n\\n\\n
\\n
\\n\\n \\n \\n \\n\\n\\n
\\n \\n Multi protocol microservice testing tool\\n \\n https://enqueuer.com\\n
\\n
\\n\\n \\n\\n\\n\\n \\n\\n
\\n \\n
\\n TypeScript\\n JavaScript\\n HTML\\n Shell\\n
\\n
\\n \\n
\\n\\n\\n\\n\\n\\n\\n
\\n \\n
\\n \\n Branch:\\n master\\n \\n \\n\\n \\n
\\n \\n \\n \\n
\\n
\\n
\\n\\n\\n \\n\\n
\\n \\n
\\n\\n
\\n\\n Find file\\n
\\n\\n \\n\\n\\n \\n\\n
\\n \\n Clone or download\\n \\n
\\n
\\n\\n
\\n
\\n\\n

\\n Clone with HTTPS\\n \\n \\n \\n

\\n

\\n Use Git or checkout with SVN using the web URL.\\n

\\n\\n
\\n \\n
\\n \\n
\\n
\\n\\n
\\n\\n \\n
\\n\\n \\n\\n
\\n

Launching GitHub Desktop

\\n

If nothing happens, download GitHub Desktop and try again.

\\n

\\n
\\n\\n
\\n

Launching GitHub Desktop

\\n

If nothing happens, download GitHub Desktop and try again.

\\n

\\n
\\n\\n
\\n

Launching Xcode

\\n

If nothing happens, download Xcode and try again.

\\n

\\n
\\n\\n
\\n

Launching Visual Studio

\\n

If nothing happens, download the GitHub extension for Visual Studio and try again.

\\n

\\n
\\n\\n
\\n
\\n
\\n\\n\\n
\\n\\n\\n\\n
\\n
\\n

Latest commit

\\n
\\n \\n
\\n \\\"\\\"\\n Fetching latest commitâ€Ļ\\n
\\n
\\n Cannot retrieve the latest commit at this time.\\n
\\n
\\n
\\n

Files

\\n \\n\\n\\n Permalink\\n\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n\\n\\n \\n \\n \\n \\n \\n\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n
TypeNameLatest commit messageCommit time
Failed to load latest commit information.
\\n \\n \\\"\\\"\\n \\n .github/ISSUE_TEMPLATE\\n \\n \\n \\n \\n \\n
\\n \\n \\\"\\\"\\n \\n conf\\n \\n \\n \\n \\n \\n
\\n \\n \\\"\\\"\\n \\n docs\\n \\n \\n \\n \\n \\n
\\n \\n \\\"\\\"\\n \\n examples\\n \\n \\n \\n \\n \\n
\\n \\n \\\"\\\"\\n \\n https-cert\\n \\n \\n \\n \\n \\n
\\n \\n \\\"\\\"\\n \\n misc\\n \\n \\n \\n \\n \\n
\\n \\n \\\"\\\"\\n \\n output\\n \\n \\n \\n \\n \\n
\\n \\n \\\"\\\"\\n \\n src\\n \\n \\n \\n \\n \\n
\\n \\n \\\"\\\"\\n \\n temp\\n \\n \\n \\n \\n \\n
\\n \\n \\\"\\\"\\n \\n .codeclimate.yml\\n \\n \\n \\n \\n \\n
\\n \\n \\\"\\\"\\n \\n .gitignore\\n \\n \\n \\n \\n \\n
\\n \\n \\\"\\\"\\n \\n .npmignore\\n \\n \\n \\n \\n \\n
\\n \\n \\\"\\\"\\n \\n .travis.yml\\n \\n \\n \\n \\n \\n
\\n \\n \\\"\\\"\\n \\n CNAME\\n \\n \\n \\n \\n \\n
\\n \\n \\\"\\\"\\n \\n CONTRIBUTING.md\\n \\n \\n \\n \\n \\n
\\n \\n \\\"\\\"\\n \\n License\\n \\n \\n \\n \\n \\n
\\n \\n \\\"\\\"\\n \\n README.md\\n \\n \\n \\n \\n \\n
\\n \\n \\\"\\\"\\n \\n package-lock.json\\n \\n \\n \\n \\n \\n
\\n \\n \\\"\\\"\\n \\n package.json\\n \\n \\n \\n \\n \\n
\\n \\n \\\"\\\"\\n \\n tsconfig.json\\n \\n \\n \\n \\n \\n
\\n \\n \\\"\\\"\\n \\n tslint.json\\n \\n \\n \\n \\n \\n
\\n\\n
\\n\\n\\n\\n
\\n\\n
\\n
\\n

\\n \\n README.md\\n

\\n
\\n
\\n
\\n
\\n
\\n
\\n
\\n\\n
\\n

\\\"npm\\\"\\n\\\"Build\\n\\\"Greenkeeper\\n\\\"Known\\n\\\"License:

\\n

\\\"enqueuerlogo\\\"

\\n

Why

\\n

Make sure your set of services is working properly by mocking its inputs and asserting against\\nits outputs

\\n

What

\\n

Enqueuer is a platform that provides the following capabilities:

\\n
    \\n
  • Support for multi protocols
  • \\n
  • Chainable message flows
  • \\n
  • Friendly for developers and non developers
  • \\n
  • Easily extensible through third party plugins, including yours
  • \\n
  • Automated end-to-end testing
  • \\n
  • Place tests front and center
  • \\n
\\n

Official page

\\n

https://enqueuer.com

\\n

Docs

\\n

Check out the full documentation

\\n
\\n
\\n
\\n\\n\\n\\n\\n
\\n
\\n\\n
\\n
\\n \\n\\n
\\n\\n \\n
\\n
\\n \\n\\n \\n \\n\\n \\n
\\n
\\n \\n
\\n
\\n\\n\\n\\n
\\n \\n \\n You can’t perform that action at this time.\\n
\\n\\n\\n \\n \\n \\n \\n \\n \\n \\n \\n
\\n \\n \\n \\n
\\n \\n\\n
\\n
\\n
\\n
\\n\\n
\\n\\n \\n\\n\\n\"\n}" + "description": "{\n \"statusCode\": 200,\n \"headers\": {\n \"accept-ranges\": \"bytes\",\n \"cache-control\": \"max-age=0, private, must-revalidate\",\n \"content-encoding\": \"gzip\",\n \"content-security-policy\": \"default-src 'none'; base-uri 'self'; child-src github.githubassets.com github.com/assets-cdn/worker/ github.com/assets/ gist.github.com/assets-cdn/worker/; connect-src 'self' uploads.github.com www.githubstatus.com collector.github.com raw.githubusercontent.com api.github.com github-cloud.s3.amazonaws.com github-production-repository-file-5c1aeb.s3.amazonaws.com github-production-upload-manifest-file-7fdce7.s3.amazonaws.com github-production-user-asset-6210df.s3.amazonaws.com *.rel.tunnels.api.visualstudio.com wss://*.rel.tunnels.api.visualstudio.com objects-origin.githubusercontent.com copilot-proxy.githubusercontent.com proxy.individual.githubcopilot.com proxy.business.githubcopilot.com proxy.enterprise.githubcopilot.com *.actions.githubusercontent.com wss://*.actions.githubusercontent.com productionresultssa0.blob.core.windows.net/ productionresultssa1.blob.core.windows.net/ productionresultssa2.blob.core.windows.net/ productionresultssa3.blob.core.windows.net/ productionresultssa4.blob.core.windows.net/ productionresultssa5.blob.core.windows.net/ productionresultssa6.blob.core.windows.net/ productionresultssa7.blob.core.windows.net/ productionresultssa8.blob.core.windows.net/ productionresultssa9.blob.core.windows.net/ productionresultssa10.blob.core.windows.net/ productionresultssa11.blob.core.windows.net/ productionresultssa12.blob.core.windows.net/ productionresultssa13.blob.core.windows.net/ productionresultssa14.blob.core.windows.net/ productionresultssa15.blob.core.windows.net/ productionresultssa16.blob.core.windows.net/ productionresultssa17.blob.core.windows.net/ productionresultssa18.blob.core.windows.net/ productionresultssa19.blob.core.windows.net/ github-production-repository-image-32fea6.s3.amazonaws.com github-production-release-asset-2e65be.s3.amazonaws.com insights.github.com wss://alive.github.com api.githubcopilot.com api.individual.githubcopilot.com api.business.githubcopilot.com api.enterprise.githubcopilot.com; font-src github.githubassets.com; form-action 'self' github.com gist.github.com copilot-workspace.githubnext.com objects-origin.githubusercontent.com; frame-ancestors 'none'; frame-src viewscreen.githubusercontent.com notebooks.githubusercontent.com; img-src 'self' data: blob: github.githubassets.com media.githubusercontent.com camo.githubusercontent.com identicons.github.com avatars.githubusercontent.com private-avatars.githubusercontent.com github-cloud.s3.amazonaws.com objects.githubusercontent.com secured-user-images.githubusercontent.com/ user-images.githubusercontent.com/ private-user-images.githubusercontent.com opengraph.githubassets.com github-production-user-asset-6210df.s3.amazonaws.com customer-stories-feed.github.com spotlights-feed.github.com objects-origin.githubusercontent.com *.githubusercontent.com; manifest-src 'self'; media-src github.com user-images.githubusercontent.com/ secured-user-images.githubusercontent.com/ private-user-images.githubusercontent.com github-production-user-asset-6210df.s3.amazonaws.com gist.github.com; script-src github.githubassets.com; style-src 'unsafe-inline' github.githubassets.com; upgrade-insecure-requests; worker-src github.githubassets.com github.com/assets-cdn/worker/ github.com/assets/ gist.github.com/assets-cdn/worker/\",\n \"content-type\": \"text/html; charset=utf-8\",\n \"date\": \"Tue, 18 Mar 2025 20:55:58 GMT\",\n \"etag\": \"W/\\\"f9511f46ff50ad1d216094797be8b550\\\"\",\n \"referrer-policy\": \"no-referrer-when-downgrade\",\n \"server\": \"GitHub.com\",\n \"set-cookie\": \"logged_in=no; Path=/; Domain=github.com; Expires=Wed, 18 Mar 2026 20:55:58 GMT; HttpOnly; Secure; SameSite=Lax\",\n \"strict-transport-security\": \"max-age=31536000; includeSubdomains; preload\",\n \"transfer-encoding\": \"chunked\",\n \"vary\": \"X-PJAX, X-PJAX-Container, Turbo-Visit, Turbo-Frame, Accept-Encoding, Accept, X-Requested-With\",\n \"x-content-type-options\": \"nosniff\",\n \"x-frame-options\": \"deny\",\n \"x-github-request-id\": \"DF11:3659:2CD6D40:3F8465E:67D9DDDE\",\n \"x-xss-protection\": \"0\"\n },\n \"body\": \"\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n \\n \\n \\n \\n \\n \\n \\n \\n\\n \\n\\n \\n\\n \\n \\n \\n \\n \\n\\n\\n \\n\\n\\n \\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n \\n \\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n \\n\\n\\n\\n GitHub - enqueuer-land/enqueuer: Polyglot flow testing tool\\n\\n\\n\\n \\n \\n \\n\\n \\n \\n\\n\\n \\n\\n\\n \\n\\n\\n \\n \\n\\n \\n \\n\\n \\n\\n\\n\\n \\n\\n \\n\\n\\n\\n\\n \\n\\n \\n\\n \\n\\n \\n\\n \\n\\n \\n\\n \\n \\n \\n\\n \\n \\n \\n\\n\\n\\n\\n \\n\\n\\n\\n \\n\\n\\n \\n \\n \\n \\n\\n \\n\\n \\n \\n\\n \\n\\n\\n\\n \\n\\n\\n \\n\\n\\n \\n\\n \\n\\n \\n \\n \\n\\n\\n\\n\\n\\n \\n\\n \\n\\n \\n
\\n \\n\\n\\n
\\n Skip to content\\n\\n \\n \\n \\n \\n \\n\\n\\n\\n\\n \\n \\n
\\n\\n\\n\\n\\n\\n \\n\\n \\n\\n \\n\\n\\n
\\n

Navigation Menu

\\n\\n \\n\\n
\\n
\\n
\\n \\n
\\n\\n \\n \\n \\n\\n \\n\\n
\\n \\n Sign in\\n \\n
\\n
\\n\\n\\n
\\n
\\n \\n\\n
\\n \\n\\n\\n\\n \\n \\n
\\n \\n \\n\\n
\\n Search or jump to...\\n
\\n \\n\\n
\\n \\n\\n \\n\\n \\n
\\n \\n

Search code, repositories, users, issues, pull requests...

\\n
\\n \\n
\\n
\\n \\n
\\n \\n \\n \\n \\n \\n\\n \\n
\\n
\\n
\\n
\\n \\n
\\n
\\n Clear\\n \\n\\n
\\n \\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n
\\n \\n
\\n
\\n
\\n\\n \\n
\\n
\\n\\n
\\n
\\n
\\n \\n
\\n \\n\\n \\n
\\n
\\n
\\n

\\n Provide feedback\\n

\\n \\n
\\n
\\n \\n
\\n
\\n \\n
\\n \\n
\\n

We read every piece of feedback, and take your input very seriously.

\\n \\n \\n \\n
\\n
\\n \\n
\\n\\n \\n \\n\\n \\n
\\n
\\n
\\n

\\n Saved searches\\n

\\n

Use saved searches to filter your results more quickly

\\n
\\n
\\n \\n
\\n
\\n \\n
\\n \\n
\\n\\n \\n\\n
\\n
\\n
\\n\\n
\\n
\\n \\n
\\n
\\n
\\n\\n\\n\\n
\\n \\n Sign in\\n \\n
\\n\\n \\n Sign up\\n \\n \\n
\\n
\\n
\\n \\n\\n\\n \\n \\n\\n
\\n\\n\\n\\n\\n\\n\\n\\n\\n
\\n\\n\\n\\n\\n \\n
\\n\\n\\n \\n\\n\\n\\n\\n\\n\\n \\n
\\n
\\n \\n \\n\\n\\n\\n\\n\\n \\n \\n\\n \\n\\n\\n\\n\\n\\n\\n \\n
\\n\\n
\\n\\n
\\n \\n
\\n \\n \\n\\n \\n \\n \\n enqueuer-land\\n \\n /\\n \\n enqueuer\\n \\n\\n Public\\n
\\n\\n\\n
\\n\\n
\\n \\n\\n
\\n
\\n\\n
\\n
\\n

\\n Polyglot flow testing tool\\n

\\n
\\n \\n \\n\\n \\n enqueuer.com\\n \\n
\\n\\n \\n

License

\\n \\n\\n\\n \\n\\n
\\n \\n
\\n \\n \\nNotifications\\n You must be signed in to change notification settings\\n\\n
\\n \\n \\n\\n \\n
\\n
\\n\\n
\\n\\n\\n \\n\\n
\\n\\n \\n\\n\\n\\n\\n
\\n \\n\\n\\n\\n \\n \\n

enqueuer-land/enqueuer

\\n
\\n
\\n\\n
\\n
\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n \\n \\n

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Enqueuer

\\n

\\\"npm\\\"\\n\\\"Build\\n\\\"Greenkeeper\\n\\\"Known\\n\\\"License:

\\n

\\\"enqueuerlogo\\\"

\\n

Why

\\n

Make sure your set of services is working properly by mocking its inputs and asserting against\\nits outputs

\\n

What

\\n

Enqueuer is a platform that provides the following capabilities:

\\n
    \\n
  • Support for multi protocols
  • \\n
  • Chainable message flows
  • \\n
  • Friendly for developers and non developers
  • \\n
  • Easily extensible through third party plugins, including yours
  • \\n
  • Automated end-to-end testing
  • \\n
  • Place tests front and center
  • \\n
\\n

Official page

\\n

https://enqueuer.com

\\n

Docs

\\n

Check out the full documentation

\\n
\\n\\n\\n \\n
\\n
\\n\\n
\\n
\\n
\\n
\\n

About

\\n\\n

\\n Polyglot flow testing tool\\n

\\n
\\n \\n \\n\\n \\n enqueuer.com\\n \\n
\\n\\n

Topics

\\n \\n\\n

Resources

\\n \\n\\n \\n

License

\\n \\n\\n\\n

Code of conduct

\\n \\n\\n

Security policy

\\n \\n\\n \\n \\n\\n \\n\\n \\n\\n

Stars

\\n \\n\\n

Watchers

\\n \\n\\n

Forks

\\n \\n\\n \\n
\\n\\n
\\n
\\n\\n \\n \\n\\n \\n \\n
\\n
\\n \\n

\\n Packages\\n

\\n\\n\\n
\\n No packages published
\\n
\\n\\n\\n\\n
\\n
\\n\\n \\n \\n\\n \\n
\\n
\\n

\\n Contributors\\n 5

\\n\\n\\n \\n
    \\n
  • \\n
    \\n
  • \\n
  • \\n
    \\n
  • \\n
  • \\n
    \\n
  • \\n
  • \\n
    \\n
  • \\n
  • \\n
    \\n
  • \\n
\\n
\\n\\n\\n
\\n
\\n\\n \\n \\n
\\n
\\n

Languages

\\n
\\n \\n \\n \\n \\n \\n
\\n\\n\\n
\\n
\\n\\n
\\n
\\n \\n
\\n\\n
\\n\\n\\n
\\n\\n
\\n\\n\\n
\\n
\\n\\n \\n\\n
\\n

Footer

\\n\\n \\n\\n\\n
\\n
\\n \\n \\n \\n\\n\\n \\n © 2025 GitHub, Inc.\\n \\n
\\n\\n \\n
\\n
\\n\\n\\n\\n \\n\\n\\n\\n \\n\\n \\n\\n
\\n
\\n
\\n
\\n\\n \\n\\n\\n\\n\\n\\n \\n\\n
\\n
\\n \\n\\n\\n\"\n}", + "implicit": true } ], "valid": true }, "onResponseReceived": { "arguments": { + "status": 200, "statusCode": 200, - "body": "\n\n\n\n\n\n\n\n \n \n \n \n \n \n \n \n \n\n\n\n \n \n \n \n \n \n \n\n\n \n \n GitHub - enqueuer-land/enqueuer: Multi protocol microservice testing tool\n \n \n \n \n\n \n \n\n \n \n \n\n \n\n\n\n \n\n \n\n \n\n \n \n \n\n \n\n\n\n\n \n\n\n\n\n\n\n \n\n \n \n\n \n\n\n \n\n \n \n\n \n\n \n\n \n\n\n \n\n\n \n\n \n\n \n \n\n\n\n\n \n\n \n\n \n \n\n
\n Skip to content\n \n \n \n\n \n \n\n\n\n
\n
\n
\n \n \n \n
\n\n
\n
\n \n
\n\n \n\n \n
\n
\n
\n\n
\n\n
\n\n\n
\n\n
\n\n\n \n\n \n\n\n\n\n
\n
\n
\n \n \n\n\n\n\n \n\n\n\n\n\n\n\n\n\n
\n\n
\n\n
\n

\n \n \n enqueuer-land\n \n /\n \n enqueuer\n \n \n

\n\n\n
\n\n \n\n
\n \n\n\n\n
\n\n\n
\n
\n\n \n \n \n\n\n
\n \n Multi protocol microservice testing tool\n \n https://enqueuer.com\n
\n
\n\n \n\n\n\n \n\n
\n \n
\n TypeScript\n JavaScript\n HTML\n Shell\n
\n
\n \n
\n\n\n\n\n\n\n
\n \n
\n \n Branch:\n master\n \n \n\n \n
\n \n \n \n
\n
\n
\n\n\n \n\n
\n \n
\n\n
\n\n Find file\n
\n\n \n\n\n \n\n
\n \n Clone or download\n \n
\n
\n\n
\n
\n\n

\n Clone with HTTPS\n \n \n \n

\n

\n Use Git or checkout with SVN using the web URL.\n

\n\n
\n \n
\n \n
\n
\n\n
\n\n \n
\n\n \n\n
\n

Launching GitHub Desktop

\n

If nothing happens, download GitHub Desktop and try again.

\n

\n
\n\n
\n

Launching GitHub Desktop

\n

If nothing happens, download GitHub Desktop and try again.

\n

\n
\n\n
\n

Launching Xcode

\n

If nothing happens, download Xcode and try again.

\n

\n
\n\n
\n

Launching Visual Studio

\n

If nothing happens, download the GitHub extension for Visual Studio and try again.

\n

\n
\n\n
\n
\n
\n\n\n
\n\n\n\n
\n
\n

Latest commit

\n
\n \n
\n \"\"\n Fetching latest commitâ€Ļ\n
\n
\n Cannot retrieve the latest commit at this time.\n
\n
\n
\n

Files

\n \n\n\n Permalink\n\n \n \n \n \n \n \n \n \n \n\n\n \n \n \n \n \n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
TypeNameLatest commit messageCommit time
Failed to load latest commit information.
\n \n \"\"\n \n .github/ISSUE_TEMPLATE\n \n \n \n \n \n
\n \n \"\"\n \n conf\n \n \n \n \n \n
\n \n \"\"\n \n docs\n \n \n \n \n \n
\n \n \"\"\n \n examples\n \n \n \n \n \n
\n \n \"\"\n \n https-cert\n \n \n \n \n \n
\n \n \"\"\n \n misc\n \n \n \n \n \n
\n \n \"\"\n \n output\n \n \n \n \n \n
\n \n \"\"\n \n src\n \n \n \n \n \n
\n \n \"\"\n \n temp\n \n \n \n \n \n
\n \n \"\"\n \n .codeclimate.yml\n \n \n \n \n \n
\n \n \"\"\n \n .gitignore\n \n \n \n \n \n
\n \n \"\"\n \n .npmignore\n \n \n \n \n \n
\n \n \"\"\n \n .travis.yml\n \n \n \n \n \n
\n \n \"\"\n \n CNAME\n \n \n \n \n \n
\n \n \"\"\n \n CONTRIBUTING.md\n \n \n \n \n \n
\n \n \"\"\n \n License\n \n \n \n \n \n
\n \n \"\"\n \n README.md\n \n \n \n \n \n
\n \n \"\"\n \n package-lock.json\n \n \n \n \n \n
\n \n \"\"\n \n package.json\n \n \n \n \n \n
\n \n \"\"\n \n tsconfig.json\n \n \n \n \n \n
\n \n \"\"\n \n tslint.json\n \n \n \n \n \n
\n\n
\n\n\n\n
\n\n
\n
\n

\n \n README.md\n

\n
\n
\n
\n
\n
\n
\n
\n\n
\n

\"npm\"\n\"Build\n\"Greenkeeper\n\"Known\n\"License:

\n

\"enqueuerlogo\"

\n

Why

\n

Make sure your set of services is working properly by mocking its inputs and asserting against\nits outputs

\n

What

\n

Enqueuer is a platform that provides the following capabilities:

\n
    \n
  • Support for multi protocols
  • \n
  • Chainable message flows
  • \n
  • Friendly for developers and non developers
  • \n
  • Easily extensible through third party plugins, including yours
  • \n
  • Automated end-to-end testing
  • \n
  • Place tests front and center
  • \n
\n

Official page

\n

https://enqueuer.com

\n

Docs

\n

Check out the full documentation

\n
\n
\n
\n\n\n\n\n
\n
\n\n
\n
\n \n\n
\n\n \n
\n
\n \n\n \n \n\n \n
\n
\n \n
\n
\n\n\n\n
\n \n \n You can’t perform that action at this time.\n
\n\n\n \n \n \n \n \n \n \n \n
\n \n \n \n
\n \n\n
\n
\n
\n
\n\n
\n\n \n\n\n", + "body": "\n\n\n\n\n\n\n\n\n\n\n\n \n \n \n \n \n \n \n \n\n \n\n \n\n \n \n \n \n \n\n\n \n\n\n \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n \n \n\n\n\n\n\n\n\n\n\n\n \n\n\n\n GitHub - enqueuer-land/enqueuer: Polyglot flow testing tool\n\n\n\n \n \n \n\n \n \n\n\n \n\n\n \n\n\n \n \n\n \n \n\n \n\n\n\n \n\n \n\n\n\n\n \n\n \n\n \n\n \n\n \n\n \n\n \n \n \n\n \n \n \n\n\n\n\n \n\n\n\n \n\n\n \n \n \n \n\n \n\n \n \n\n \n\n\n\n \n\n\n \n\n\n \n\n \n\n \n \n \n\n\n\n\n\n \n\n \n\n \n
\n \n\n\n
\n Skip to content\n\n \n \n \n \n \n\n\n\n\n \n \n
\n\n\n\n\n\n \n\n \n\n \n\n\n
\n

Navigation Menu

\n\n \n\n
\n
\n
\n \n
\n\n \n \n \n\n \n\n
\n \n Sign in\n \n
\n
\n\n\n
\n
\n \n\n
\n \n\n\n\n \n \n
\n \n \n\n
\n Search or jump to...\n
\n \n\n
\n \n\n \n\n \n
\n \n

Search code, repositories, users, issues, pull requests...

\n
\n \n
\n
\n \n
\n \n \n \n \n \n\n \n
\n
\n
\n
\n \n
\n
\n Clear\n \n\n
\n \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
\n \n
\n
\n
\n\n \n
\n
\n\n
\n
\n
\n \n
\n \n\n \n
\n
\n
\n

\n Provide feedback\n

\n \n
\n
\n \n
\n
\n \n
\n \n
\n

We read every piece of feedback, and take your input very seriously.

\n \n \n \n
\n
\n \n
\n\n \n \n\n \n
\n
\n
\n

\n Saved searches\n

\n

Use saved searches to filter your results more quickly

\n
\n
\n \n
\n
\n \n
\n \n
\n\n \n\n
\n
\n
\n\n
\n
\n \n
\n
\n
\n\n\n\n
\n \n Sign in\n \n
\n\n \n Sign up\n \n \n
\n
\n
\n \n\n\n \n \n\n
\n\n\n\n\n\n\n\n\n
\n\n\n\n\n \n
\n\n\n \n\n\n\n\n\n\n \n
\n
\n \n \n\n\n\n\n\n \n \n\n \n\n\n\n\n\n\n \n
\n\n
\n\n
\n \n
\n \n \n\n \n \n \n enqueuer-land\n \n /\n \n enqueuer\n \n\n Public\n
\n\n\n
\n\n
\n \n\n
\n
\n\n
\n
\n

\n Polyglot flow testing tool\n

\n
\n \n \n\n \n enqueuer.com\n \n
\n\n \n

License

\n \n\n\n \n\n
\n \n
\n \n \nNotifications\n You must be signed in to change notification settings\n\n
\n \n \n\n \n
\n
\n\n
\n\n\n \n\n
\n\n \n\n\n\n\n
\n \n\n\n\n \n \n

enqueuer-land/enqueuer

\n
\n
\n\n
\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n \n \n

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Enqueuer

\n

\"npm\"\n\"Build\n\"Greenkeeper\n\"Known\n\"License:

\n

\"enqueuerlogo\"

\n

Why

\n

Make sure your set of services is working properly by mocking its inputs and asserting against\nits outputs

\n

What

\n

Enqueuer is a platform that provides the following capabilities:

\n
    \n
  • Support for multi protocols
  • \n
  • Chainable message flows
  • \n
  • Friendly for developers and non developers
  • \n
  • Easily extensible through third party plugins, including yours
  • \n
  • Automated end-to-end testing
  • \n
  • Place tests front and center
  • \n
\n

Official page

\n

https://enqueuer.com

\n

Docs

\n

Check out the full documentation

\n
\n\n\n \n
\n
\n\n
\n
\n
\n
\n

About

\n\n

\n Polyglot flow testing tool\n

\n
\n \n \n\n \n enqueuer.com\n \n
\n\n

Topics

\n \n\n

Resources

\n \n\n \n

License

\n \n\n\n

Code of conduct

\n \n\n

Security policy

\n \n\n \n \n\n \n\n \n\n

Stars

\n \n\n

Watchers

\n \n\n

Forks

\n \n\n \n
\n\n
\n
\n\n \n \n\n \n \n
\n
\n \n

\n Packages\n

\n\n\n
\n No packages published
\n
\n\n\n\n
\n
\n\n \n \n\n \n
\n
\n

\n Contributors\n 5

\n\n\n \n
    \n
  • \n
    \n
  • \n
  • \n
    \n
  • \n
  • \n
    \n
  • \n
  • \n
    \n
  • \n
  • \n
    \n
  • \n
\n
\n\n\n
\n
\n\n \n \n
\n
\n

Languages

\n
\n \n \n \n \n \n
\n\n\n
\n
\n\n
\n
\n \n
\n\n
\n\n\n
\n\n
\n\n\n
\n
\n\n \n\n
\n

Footer

\n\n \n\n\n
\n
\n \n \n \n\n\n \n © 2025 GitHub, Inc.\n \n
\n\n \n
\n
\n\n\n\n \n\n\n\n \n\n \n\n
\n
\n
\n
\n\n \n\n\n\n\n\n \n\n
\n
\n \n\n\n", "headers": { - "date": "Sun, 08 Mar 2020 05:33:37 GMT", + "accept-ranges": "bytes", + "cache-control": "max-age=0, private, must-revalidate", + "content-encoding": "gzip", + "content-security-policy": "default-src 'none'; base-uri 'self'; child-src github.githubassets.com github.com/assets-cdn/worker/ github.com/assets/ gist.github.com/assets-cdn/worker/; connect-src 'self' uploads.github.com www.githubstatus.com collector.github.com raw.githubusercontent.com api.github.com github-cloud.s3.amazonaws.com github-production-repository-file-5c1aeb.s3.amazonaws.com github-production-upload-manifest-file-7fdce7.s3.amazonaws.com github-production-user-asset-6210df.s3.amazonaws.com *.rel.tunnels.api.visualstudio.com wss://*.rel.tunnels.api.visualstudio.com objects-origin.githubusercontent.com copilot-proxy.githubusercontent.com proxy.individual.githubcopilot.com proxy.business.githubcopilot.com proxy.enterprise.githubcopilot.com *.actions.githubusercontent.com wss://*.actions.githubusercontent.com productionresultssa0.blob.core.windows.net/ productionresultssa1.blob.core.windows.net/ productionresultssa2.blob.core.windows.net/ productionresultssa3.blob.core.windows.net/ productionresultssa4.blob.core.windows.net/ productionresultssa5.blob.core.windows.net/ productionresultssa6.blob.core.windows.net/ productionresultssa7.blob.core.windows.net/ productionresultssa8.blob.core.windows.net/ productionresultssa9.blob.core.windows.net/ productionresultssa10.blob.core.windows.net/ productionresultssa11.blob.core.windows.net/ productionresultssa12.blob.core.windows.net/ productionresultssa13.blob.core.windows.net/ productionresultssa14.blob.core.windows.net/ productionresultssa15.blob.core.windows.net/ productionresultssa16.blob.core.windows.net/ productionresultssa17.blob.core.windows.net/ productionresultssa18.blob.core.windows.net/ productionresultssa19.blob.core.windows.net/ github-production-repository-image-32fea6.s3.amazonaws.com github-production-release-asset-2e65be.s3.amazonaws.com insights.github.com wss://alive.github.com api.githubcopilot.com api.individual.githubcopilot.com api.business.githubcopilot.com api.enterprise.githubcopilot.com; font-src github.githubassets.com; form-action 'self' github.com gist.github.com copilot-workspace.githubnext.com objects-origin.githubusercontent.com; frame-ancestors 'none'; frame-src viewscreen.githubusercontent.com notebooks.githubusercontent.com; img-src 'self' data: blob: github.githubassets.com media.githubusercontent.com camo.githubusercontent.com identicons.github.com avatars.githubusercontent.com private-avatars.githubusercontent.com github-cloud.s3.amazonaws.com objects.githubusercontent.com secured-user-images.githubusercontent.com/ user-images.githubusercontent.com/ private-user-images.githubusercontent.com opengraph.githubassets.com github-production-user-asset-6210df.s3.amazonaws.com customer-stories-feed.github.com spotlights-feed.github.com objects-origin.githubusercontent.com *.githubusercontent.com; manifest-src 'self'; media-src github.com user-images.githubusercontent.com/ secured-user-images.githubusercontent.com/ private-user-images.githubusercontent.com github-production-user-asset-6210df.s3.amazonaws.com gist.github.com; script-src github.githubassets.com; style-src 'unsafe-inline' github.githubassets.com; upgrade-insecure-requests; worker-src github.githubassets.com github.com/assets-cdn/worker/ github.com/assets/ gist.github.com/assets-cdn/worker/", "content-type": "text/html; charset=utf-8", + "date": "Tue, 18 Mar 2025 20:55:58 GMT", + "etag": "W/\"f9511f46ff50ad1d216094797be8b550\"", + "referrer-policy": "no-referrer-when-downgrade", "server": "GitHub.com", - "status": "200 OK", - "vary": "X-PJAX, Accept-Encoding, Accept, X-Requested-With", - "etag": "W/\"e488a776a390ca1e6112af885e19a43b\"", - "cache-control": "max-age=0, private, must-revalidate", + "set-cookie": "logged_in=no; Path=/; Domain=github.com; Expires=Wed, 18 Mar 2026 20:55:58 GMT; HttpOnly; Secure; SameSite=Lax", "strict-transport-security": "max-age=31536000; includeSubdomains; preload", - "x-frame-options": "deny", - "x-content-type-options": "nosniff", - "x-xss-protection": "1; mode=block", - "expect-ct": "max-age=2592000, report-uri=\"https://api.github.com/_private/browser/errors\"", - "content-security-policy": "default-src 'none'; base-uri 'self'; block-all-mixed-content; connect-src 'self' uploads.github.com www.githubstatus.com collector.githubapp.com api.github.com www.google-analytics.com github-cloud.s3.amazonaws.com github-production-repository-file-5c1aeb.s3.amazonaws.com github-production-upload-manifest-file-7fdce7.s3.amazonaws.com github-production-user-asset-6210df.s3.amazonaws.com wss://live.github.com; font-src github.githubassets.com; form-action 'self' github.com gist.github.com; frame-ancestors 'none'; frame-src render.githubusercontent.com; img-src 'self' data: github.githubassets.com identicons.github.com collector.githubapp.com github-cloud.s3.amazonaws.com *.githubusercontent.com; manifest-src 'self'; media-src 'none'; script-src github.githubassets.com; style-src 'unsafe-inline' github.githubassets.com", - "age": "0", - "set-cookie": [ - "_gh_sess=NIjkGD7YYWQ115S4OHG6JQUkfOOgoFL39LjAItk%2BgABxY%2BDMA6dABKH6c%2B9c5eybzagK4jOboXCIoGV5%2BvY2sqWsSyM1a4L%2F3uk3okn2u%2FrYzgP2tHeaEcS89Qvu0v2UOmBp%2BDU0qQvjPTkNvjIfVtZnF9vUoPOWysJ4IUxXaqjkFFS4YWSWD7bqfpxCE6sXU74oFbMVpIQ6pd01yuO1W0ufWGTAYorrAiRqeOZ9%2Fgly%2BtJJTO7in8mfPyFhdnz930P0z8URtENxAMb9d6bBxQ%3D%3D--b4cNsBtPqvjmWGKH--dFzHLmLlq3thQfie%2FC2N4A%3D%3D; Path=/; HttpOnly; Secure", - "_octo=GH1.1.471616304.1583645617; Path=/; Domain=github.com; Expires=Mon, 08 Mar 2021 05:33:37 GMT; Secure", - "logged_in=no; Path=/; Domain=github.com; Expires=Mon, 08 Mar 2021 05:33:37 GMT; HttpOnly; Secure" - ], - "accept-ranges": "bytes", "transfer-encoding": "chunked", - "connection": "close", - "x-github-request-id": "F216:085B:70FC9:9E247:5E6483B1" - }, - "request": { - "uri": { - "protocol": "https:", - "slashes": true, - "auth": null, - "host": "github.com", - "port": 443, - "hostname": "github.com", - "hash": null, - "search": null, - "query": null, - "pathname": "/enqueuer-land/enqueuer", - "path": "/enqueuer-land/enqueuer", - "href": "https://github.com/enqueuer-land/enqueuer" - }, - "method": "get", - "headers": { - "content-length": 0 - } - } + "vary": "X-PJAX, X-PJAX-Container, Turbo-Visit, Turbo-Frame, Accept-Encoding, Accept, X-Requested-With", + "x-content-type-options": "nosniff", + "x-frame-options": "deny", + "x-github-request-id": "DF11:3659:2CD6D40:3F8465E:67D9DDDE", + "x-xss-protection": "0" + }, + "elapsedTime": 977 }, "tests": [ { @@ -5604,63 +5062,10 @@ } ], "valid": true - }, - "onMessageReceived": { - "arguments": { - "statusCode": 200, - "body": "\n\n\n\n\n\n\n\n \n \n \n \n \n \n \n \n \n\n\n\n \n \n \n \n \n \n \n\n\n \n \n GitHub - enqueuer-land/enqueuer: Multi protocol microservice testing tool\n \n \n \n \n\n \n \n\n \n \n \n\n \n\n\n\n \n\n \n\n \n\n \n \n \n\n \n\n\n\n\n \n\n\n\n\n\n\n \n\n \n \n\n \n\n\n \n\n \n \n\n \n\n \n\n \n\n\n \n\n\n \n\n \n\n \n \n\n\n\n\n \n\n \n\n \n \n\n
\n Skip to content\n \n \n \n\n \n \n\n\n\n
\n
\n
\n \n \n \n
\n\n
\n
\n \n
\n\n \n\n \n
\n
\n
\n\n
\n\n
\n\n\n
\n\n
\n\n\n \n\n \n\n\n\n\n
\n
\n
\n \n \n\n\n\n\n \n\n\n\n\n\n\n\n\n\n
\n\n
\n\n
\n

\n \n \n enqueuer-land\n \n /\n \n enqueuer\n \n \n

\n\n\n
\n\n \n\n
\n \n\n\n\n
\n\n\n
\n
\n\n \n \n \n\n\n
\n \n Multi protocol microservice testing tool\n \n https://enqueuer.com\n
\n
\n\n \n\n\n\n \n\n
\n \n
\n TypeScript\n JavaScript\n HTML\n Shell\n
\n
\n \n
\n\n\n\n\n\n\n
\n \n
\n \n Branch:\n master\n \n \n\n \n
\n \n \n \n
\n
\n
\n\n\n \n\n
\n \n
\n\n
\n\n Find file\n
\n\n \n\n\n \n\n
\n \n Clone or download\n \n
\n
\n\n
\n
\n\n

\n Clone with HTTPS\n \n \n \n

\n

\n Use Git or checkout with SVN using the web URL.\n

\n\n
\n \n
\n \n
\n
\n\n
\n\n \n
\n\n \n\n
\n

Launching GitHub Desktop

\n

If nothing happens, download GitHub Desktop and try again.

\n

\n
\n\n
\n

Launching GitHub Desktop

\n

If nothing happens, download GitHub Desktop and try again.

\n

\n
\n\n
\n

Launching Xcode

\n

If nothing happens, download Xcode and try again.

\n

\n
\n\n
\n

Launching Visual Studio

\n

If nothing happens, download the GitHub extension for Visual Studio and try again.

\n

\n
\n\n
\n
\n
\n\n\n
\n\n\n\n
\n
\n

Latest commit

\n
\n \n
\n \"\"\n Fetching latest commitâ€Ļ\n
\n
\n Cannot retrieve the latest commit at this time.\n
\n
\n
\n

Files

\n \n\n\n Permalink\n\n \n \n \n \n \n \n \n \n \n\n\n \n \n \n \n \n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
TypeNameLatest commit messageCommit time
Failed to load latest commit information.
\n \n \"\"\n \n .github/ISSUE_TEMPLATE\n \n \n \n \n \n
\n \n \"\"\n \n conf\n \n \n \n \n \n
\n \n \"\"\n \n docs\n \n \n \n \n \n
\n \n \"\"\n \n examples\n \n \n \n \n \n
\n \n \"\"\n \n https-cert\n \n \n \n \n \n
\n \n \"\"\n \n misc\n \n \n \n \n \n
\n \n \"\"\n \n output\n \n \n \n \n \n
\n \n \"\"\n \n src\n \n \n \n \n \n
\n \n \"\"\n \n temp\n \n \n \n \n \n
\n \n \"\"\n \n .codeclimate.yml\n \n \n \n \n \n
\n \n \"\"\n \n .gitignore\n \n \n \n \n \n
\n \n \"\"\n \n .npmignore\n \n \n \n \n \n
\n \n \"\"\n \n .travis.yml\n \n \n \n \n \n
\n \n \"\"\n \n CNAME\n \n \n \n \n \n
\n \n \"\"\n \n CONTRIBUTING.md\n \n \n \n \n \n
\n \n \"\"\n \n License\n \n \n \n \n \n
\n \n \"\"\n \n README.md\n \n \n \n \n \n
\n \n \"\"\n \n package-lock.json\n \n \n \n \n \n
\n \n \"\"\n \n package.json\n \n \n \n \n \n
\n \n \"\"\n \n tsconfig.json\n \n \n \n \n \n
\n \n \"\"\n \n tslint.json\n \n \n \n \n \n
\n\n
\n\n\n\n
\n\n
\n
\n

\n \n README.md\n

\n
\n
\n
\n
\n
\n
\n
\n\n
\n

\"npm\"\n\"Build\n\"Greenkeeper\n\"Known\n\"License:

\n

\"enqueuerlogo\"

\n

Why

\n

Make sure your set of services is working properly by mocking its inputs and asserting against\nits outputs

\n

What

\n

Enqueuer is a platform that provides the following capabilities:

\n
    \n
  • Support for multi protocols
  • \n
  • Chainable message flows
  • \n
  • Friendly for developers and non developers
  • \n
  • Easily extensible through third party plugins, including yours
  • \n
  • Automated end-to-end testing
  • \n
  • Place tests front and center
  • \n
\n

Official page

\n

https://enqueuer.com

\n

Docs

\n

Check out the full documentation

\n
\n
\n
\n\n\n\n\n
\n
\n\n
\n
\n \n\n
\n\n \n
\n
\n \n\n \n \n\n \n
\n
\n \n
\n
\n\n\n\n
\n \n \n You can’t perform that action at this time.\n
\n\n\n \n \n \n \n \n \n \n \n
\n \n \n \n
\n \n\n
\n
\n
\n
\n\n
\n\n \n\n\n", - "headers": { - "date": "Sun, 08 Mar 2020 05:33:37 GMT", - "content-type": "text/html; charset=utf-8", - "server": "GitHub.com", - "status": "200 OK", - "vary": "X-PJAX, Accept-Encoding, Accept, X-Requested-With", - "etag": "W/\"e488a776a390ca1e6112af885e19a43b\"", - "cache-control": "max-age=0, private, must-revalidate", - "strict-transport-security": "max-age=31536000; includeSubdomains; preload", - "x-frame-options": "deny", - "x-content-type-options": "nosniff", - "x-xss-protection": "1; mode=block", - "expect-ct": "max-age=2592000, report-uri=\"https://api.github.com/_private/browser/errors\"", - "content-security-policy": "default-src 'none'; base-uri 'self'; block-all-mixed-content; connect-src 'self' uploads.github.com www.githubstatus.com collector.githubapp.com api.github.com www.google-analytics.com github-cloud.s3.amazonaws.com github-production-repository-file-5c1aeb.s3.amazonaws.com github-production-upload-manifest-file-7fdce7.s3.amazonaws.com github-production-user-asset-6210df.s3.amazonaws.com wss://live.github.com; font-src github.githubassets.com; form-action 'self' github.com gist.github.com; frame-ancestors 'none'; frame-src render.githubusercontent.com; img-src 'self' data: github.githubassets.com identicons.github.com collector.githubapp.com github-cloud.s3.amazonaws.com *.githubusercontent.com; manifest-src 'self'; media-src 'none'; script-src github.githubassets.com; style-src 'unsafe-inline' github.githubassets.com", - "age": "0", - "set-cookie": [ - "_gh_sess=NIjkGD7YYWQ115S4OHG6JQUkfOOgoFL39LjAItk%2BgABxY%2BDMA6dABKH6c%2B9c5eybzagK4jOboXCIoGV5%2BvY2sqWsSyM1a4L%2F3uk3okn2u%2FrYzgP2tHeaEcS89Qvu0v2UOmBp%2BDU0qQvjPTkNvjIfVtZnF9vUoPOWysJ4IUxXaqjkFFS4YWSWD7bqfpxCE6sXU74oFbMVpIQ6pd01yuO1W0ufWGTAYorrAiRqeOZ9%2Fgly%2BtJJTO7in8mfPyFhdnz930P0z8URtENxAMb9d6bBxQ%3D%3D--b4cNsBtPqvjmWGKH--dFzHLmLlq3thQfie%2FC2N4A%3D%3D; Path=/; HttpOnly; Secure", - "_octo=GH1.1.471616304.1583645617; Path=/; Domain=github.com; Expires=Mon, 08 Mar 2021 05:33:37 GMT; Secure", - "logged_in=no; Path=/; Domain=github.com; Expires=Mon, 08 Mar 2021 05:33:37 GMT; HttpOnly; Secure" - ], - "accept-ranges": "bytes", - "transfer-encoding": "chunked", - "connection": "close", - "x-github-request-id": "F216:085B:70FC9:9E247:5E6483B1" - }, - "request": { - "uri": { - "protocol": "https:", - "slashes": true, - "auth": null, - "host": "github.com", - "port": 443, - "hostname": "github.com", - "hash": null, - "search": null, - "query": null, - "pathname": "/enqueuer-land/enqueuer", - "path": "/enqueuer-land/enqueuer", - "href": "https://github.com/enqueuer-land/enqueuer" - }, - "method": "get", - "headers": { - "content-length": 0 - } - } - }, - "tests": [], - "valid": true } }, "type": "http", - "publishTime": "2020-03-08T05:33:37.626Z" + "messageSentInstant": "2025-03-18T20:55:58.995Z" } ], "iteration": 0, @@ -5677,25 +5082,25 @@ "valid": true, "tests": [], "arguments": { - "elapsedTime": 762 + "elapsedTime": 985 } } }, "time": { - "startTime": "2020-03-08T05:33:36.866Z", - "endTime": "2020-03-08T05:33:37.628Z", - "totalTime": 762, + "startTime": "2025-03-18T20:55:58.015Z", + "endTime": "2025-03-18T20:55:59.000Z", + "totalTime": 985, "timeout": 5000 }, - "requisitions": [] + "tasks": [] }, { "valid": true, "name": "examples/recursion.yml", - "id": "0233360795_00da84a874_805085", + "id": "1355570989_82eed4bb8e_179234", "level": 1, - "subscriptions": [], - "publishers": [], + "sensors": [], + "actuators": [], "iteration": 0, "totalIterations": 1, "hooks": { @@ -5710,24 +5115,24 @@ "valid": true, "tests": [], "arguments": { - "elapsedTime": 82 + "elapsedTime": 93 } } }, "time": { - "startTime": "2020-03-08T05:33:36.866Z", - "endTime": "2020-03-08T05:33:36.948Z", - "totalTime": 82, + "startTime": "2025-03-18T20:55:58.016Z", + "endTime": "2025-03-18T20:55:58.109Z", + "totalTime": 93, "timeout": 5000 }, - "requisitions": [ + "tasks": [ { "valid": true, - "name": "Requisition #0", - "id": "0233360795_75977bc4c1_982184", + "name": "Task #0", + "id": "1355570989_0c7f0fb7f7_171606", "level": 2, - "subscriptions": [], - "publishers": [], + "sensors": [], + "actuators": [], "iteration": 0, "totalIterations": 1, "hooks": { @@ -5742,25 +5147,25 @@ "valid": true, "tests": [], "arguments": { - "elapsedTime": 31 + "elapsedTime": 72 } } }, "time": { - "startTime": "2020-03-08T05:33:36.886Z", - "endTime": "2020-03-08T05:33:36.917Z", - "totalTime": 31, + "startTime": "2025-03-18T20:55:58.022Z", + "endTime": "2025-03-18T20:55:58.095Z", + "totalTime": 73, "timeout": 5000 }, - "requisitions": [] + "tasks": [] }, { "valid": true, - "name": "Requisition #1", - "id": "0233360795_e87d17a22e_21789", + "name": "Task #1", + "id": "1355570989_57cf642fae_901943", "level": 2, - "subscriptions": [], - "publishers": [], + "sensors": [], + "actuators": [], "iteration": 0, "totalIterations": 3, "hooks": { @@ -5775,24 +5180,24 @@ "valid": true, "tests": [], "arguments": { - "elapsedTime": 16 + "elapsedTime": 9 } } }, "time": { - "startTime": "2020-03-08T05:33:36.921Z", - "endTime": "2020-03-08T05:33:36.937Z", - "totalTime": 16, + "startTime": "2025-03-18T20:55:58.097Z", + "endTime": "2025-03-18T20:55:58.106Z", + "totalTime": 9, "timeout": 5000 }, - "requisitions": [ + "tasks": [ { "valid": true, - "name": "Requisition #0", - "id": "0233360795_55233c1965_344410", + "name": "Task #0", + "id": "1355570989_a83fe935c1_633469", "level": 3, - "subscriptions": [], - "publishers": [], + "sensors": [], + "actuators": [], "iteration": 0, "totalIterations": 3, "hooks": { @@ -5807,24 +5212,24 @@ "valid": true, "tests": [], "arguments": { - "elapsedTime": 7 + "elapsedTime": 5 } } }, "time": { - "startTime": "2020-03-08T05:33:36.923Z", - "endTime": "2020-03-08T05:33:36.930Z", - "totalTime": 7, + "startTime": "2025-03-18T20:55:58.098Z", + "endTime": "2025-03-18T20:55:58.103Z", + "totalTime": 5, "timeout": 5000 }, - "requisitions": [ + "tasks": [ { "valid": true, - "name": "Requisition #0", - "id": "0233360795_5e02f9345e_207197", + "name": "Task #0", + "id": "1355570989_7ffa627c18_29061", "level": 4, - "subscriptions": [], - "publishers": [], + "sensors": [], + "actuators": [], "iteration": 0, "totalIterations": 3, "hooks": { @@ -5839,25 +5244,25 @@ "valid": true, "tests": [], "arguments": { - "elapsedTime": 0 + "elapsedTime": 1 } } }, "time": { - "startTime": "2020-03-08T05:33:36.924Z", - "endTime": "2020-03-08T05:33:36.925Z", + "startTime": "2025-03-18T20:55:58.098Z", + "endTime": "2025-03-18T20:55:58.099Z", "totalTime": 1, "timeout": 5000 }, - "requisitions": [] + "tasks": [] }, { "valid": true, - "name": "Requisition #0", - "id": "0233360795_5e02f9345e_207197", + "name": "Task #0", + "id": "1355570989_7ffa627c18_29061", "level": 4, - "subscriptions": [], - "publishers": [], + "sensors": [], + "actuators": [], "iteration": 1, "totalIterations": 3, "hooks": { @@ -5877,20 +5282,20 @@ } }, "time": { - "startTime": "2020-03-08T05:33:36.927Z", - "endTime": "2020-03-08T05:33:36.928Z", + "startTime": "2025-03-18T20:55:58.099Z", + "endTime": "2025-03-18T20:55:58.100Z", "totalTime": 1, "timeout": 5000 }, - "requisitions": [] + "tasks": [] }, { "valid": true, - "name": "Requisition #0", - "id": "0233360795_5e02f9345e_207197", + "name": "Task #0", + "id": "1355570989_7ffa627c18_29061", "level": 4, - "subscriptions": [], - "publishers": [], + "sensors": [], + "actuators": [], "iteration": 2, "totalIterations": 3, "hooks": { @@ -5905,27 +5310,27 @@ "valid": true, "tests": [], "arguments": { - "elapsedTime": 0 + "elapsedTime": 2 } } }, "time": { - "startTime": "2020-03-08T05:33:36.929Z", - "endTime": "2020-03-08T05:33:36.930Z", - "totalTime": 1, + "startTime": "2025-03-18T20:55:58.100Z", + "endTime": "2025-03-18T20:55:58.102Z", + "totalTime": 2, "timeout": 5000 }, - "requisitions": [] + "tasks": [] } ] }, { "valid": true, - "name": "Requisition #0", - "id": "0233360795_55233c1965_344410", + "name": "Task #0", + "id": "1355570989_a83fe935c1_633469", "level": 3, - "subscriptions": [], - "publishers": [], + "sensors": [], + "actuators": [], "iteration": 1, "totalIterations": 3, "hooks": { @@ -5940,24 +5345,24 @@ "valid": true, "tests": [], "arguments": { - "elapsedTime": 4 + "elapsedTime": 2 } } }, "time": { - "startTime": "2020-03-08T05:33:36.931Z", - "endTime": "2020-03-08T05:33:36.935Z", - "totalTime": 4, + "startTime": "2025-03-18T20:55:58.103Z", + "endTime": "2025-03-18T20:55:58.105Z", + "totalTime": 2, "timeout": 5000 }, - "requisitions": [ + "tasks": [ { "valid": true, - "name": "Requisition #0", - "id": "0233360795_5e02f9345e_207197", + "name": "Task #0", + "id": "1355570989_7ffa627c18_29061", "level": 4, - "subscriptions": [], - "publishers": [], + "sensors": [], + "actuators": [], "iteration": 0, "totalIterations": 3, "hooks": { @@ -5972,25 +5377,25 @@ "valid": true, "tests": [], "arguments": { - "elapsedTime": 1 + "elapsedTime": 0 } } }, "time": { - "startTime": "2020-03-08T05:33:36.931Z", - "endTime": "2020-03-08T05:33:36.932Z", - "totalTime": 1, + "startTime": "2025-03-18T20:55:58.103Z", + "endTime": "2025-03-18T20:55:58.103Z", + "totalTime": 0, "timeout": 5000 }, - "requisitions": [] + "tasks": [] }, { "valid": true, - "name": "Requisition #0", - "id": "0233360795_5e02f9345e_207197", + "name": "Task #0", + "id": "1355570989_7ffa627c18_29061", "level": 4, - "subscriptions": [], - "publishers": [], + "sensors": [], + "actuators": [], "iteration": 1, "totalIterations": 3, "hooks": { @@ -6010,20 +5415,20 @@ } }, "time": { - "startTime": "2020-03-08T05:33:36.932Z", - "endTime": "2020-03-08T05:33:36.933Z", + "startTime": "2025-03-18T20:55:58.103Z", + "endTime": "2025-03-18T20:55:58.104Z", "totalTime": 1, "timeout": 5000 }, - "requisitions": [] + "tasks": [] }, { "valid": true, - "name": "Requisition #0", - "id": "0233360795_5e02f9345e_207197", + "name": "Task #0", + "id": "1355570989_7ffa627c18_29061", "level": 4, - "subscriptions": [], - "publishers": [], + "sensors": [], + "actuators": [], "iteration": 2, "totalIterations": 3, "hooks": { @@ -6038,27 +5443,27 @@ "valid": true, "tests": [], "arguments": { - "elapsedTime": 1 + "elapsedTime": 0 } } }, "time": { - "startTime": "2020-03-08T05:33:36.933Z", - "endTime": "2020-03-08T05:33:36.934Z", - "totalTime": 1, + "startTime": "2025-03-18T20:55:58.104Z", + "endTime": "2025-03-18T20:55:58.104Z", + "totalTime": 0, "timeout": 5000 }, - "requisitions": [] + "tasks": [] } ] }, { "valid": true, - "name": "Requisition #0", - "id": "0233360795_55233c1965_344410", + "name": "Task #0", + "id": "1355570989_a83fe935c1_633469", "level": 3, - "subscriptions": [], - "publishers": [], + "sensors": [], + "actuators": [], "iteration": 2, "totalIterations": 3, "hooks": { @@ -6073,24 +5478,24 @@ "valid": true, "tests": [], "arguments": { - "elapsedTime": 2 + "elapsedTime": 1 } } }, "time": { - "startTime": "2020-03-08T05:33:36.935Z", - "endTime": "2020-03-08T05:33:36.937Z", - "totalTime": 2, + "startTime": "2025-03-18T20:55:58.105Z", + "endTime": "2025-03-18T20:55:58.106Z", + "totalTime": 1, "timeout": 5000 }, - "requisitions": [ + "tasks": [ { "valid": true, - "name": "Requisition #0", - "id": "0233360795_5e02f9345e_207197", + "name": "Task #0", + "id": "1355570989_7ffa627c18_29061", "level": 4, - "subscriptions": [], - "publishers": [], + "sensors": [], + "actuators": [], "iteration": 0, "totalIterations": 3, "hooks": { @@ -6105,25 +5510,25 @@ "valid": true, "tests": [], "arguments": { - "elapsedTime": 1 + "elapsedTime": 0 } } }, "time": { - "startTime": "2020-03-08T05:33:36.935Z", - "endTime": "2020-03-08T05:33:36.936Z", - "totalTime": 1, + "startTime": "2025-03-18T20:55:58.105Z", + "endTime": "2025-03-18T20:55:58.105Z", + "totalTime": 0, "timeout": 5000 }, - "requisitions": [] + "tasks": [] }, { "valid": true, - "name": "Requisition #0", - "id": "0233360795_5e02f9345e_207197", + "name": "Task #0", + "id": "1355570989_7ffa627c18_29061", "level": 4, - "subscriptions": [], - "publishers": [], + "sensors": [], + "actuators": [], "iteration": 1, "totalIterations": 3, "hooks": { @@ -6138,25 +5543,25 @@ "valid": true, "tests": [], "arguments": { - "elapsedTime": 1 + "elapsedTime": 0 } } }, "time": { - "startTime": "2020-03-08T05:33:36.936Z", - "endTime": "2020-03-08T05:33:36.937Z", + "startTime": "2025-03-18T20:55:58.105Z", + "endTime": "2025-03-18T20:55:58.106Z", "totalTime": 1, "timeout": 5000 }, - "requisitions": [] + "tasks": [] }, { "valid": true, - "name": "Requisition #0", - "id": "0233360795_5e02f9345e_207197", + "name": "Task #0", + "id": "1355570989_7ffa627c18_29061", "level": 4, - "subscriptions": [], - "publishers": [], + "sensors": [], + "actuators": [], "iteration": 2, "totalIterations": 3, "hooks": { @@ -6176,12 +5581,12 @@ } }, "time": { - "startTime": "2020-03-08T05:33:36.937Z", - "endTime": "2020-03-08T05:33:36.937Z", + "startTime": "2025-03-18T20:55:58.106Z", + "endTime": "2025-03-18T20:55:58.106Z", "totalTime": 0, "timeout": 5000 }, - "requisitions": [] + "tasks": [] } ] } @@ -6189,11 +5594,11 @@ }, { "valid": true, - "name": "Requisition #1", - "id": "0233360795_e87d17a22e_21789", + "name": "Task #1", + "id": "1355570989_57cf642fae_901943", "level": 2, - "subscriptions": [], - "publishers": [], + "sensors": [], + "actuators": [], "iteration": 1, "totalIterations": 3, "hooks": { @@ -6208,24 +5613,24 @@ "valid": true, "tests": [], "arguments": { - "elapsedTime": 4 + "elapsedTime": 1 } } }, "time": { - "startTime": "2020-03-08T05:33:36.938Z", - "endTime": "2020-03-08T05:33:36.942Z", - "totalTime": 4, + "startTime": "2025-03-18T20:55:58.106Z", + "endTime": "2025-03-18T20:55:58.107Z", + "totalTime": 1, "timeout": 5000 }, - "requisitions": [ + "tasks": [ { "valid": true, - "name": "Requisition #0", - "id": "0233360795_55233c1965_344410", + "name": "Task #0", + "id": "1355570989_a83fe935c1_633469", "level": 3, - "subscriptions": [], - "publishers": [], + "sensors": [], + "actuators": [], "iteration": 0, "totalIterations": 3, "hooks": { @@ -6240,24 +5645,24 @@ "valid": true, "tests": [], "arguments": { - "elapsedTime": 1 + "elapsedTime": 0 } } }, "time": { - "startTime": "2020-03-08T05:33:36.938Z", - "endTime": "2020-03-08T05:33:36.939Z", - "totalTime": 1, + "startTime": "2025-03-18T20:55:58.106Z", + "endTime": "2025-03-18T20:55:58.106Z", + "totalTime": 0, "timeout": 5000 }, - "requisitions": [ + "tasks": [ { "valid": true, - "name": "Requisition #0", - "id": "0233360795_5e02f9345e_207197", + "name": "Task #0", + "id": "1355570989_7ffa627c18_29061", "level": 4, - "subscriptions": [], - "publishers": [], + "sensors": [], + "actuators": [], "iteration": 0, "totalIterations": 3, "hooks": { @@ -6277,20 +5682,20 @@ } }, "time": { - "startTime": "2020-03-08T05:33:36.938Z", - "endTime": "2020-03-08T05:33:36.938Z", + "startTime": "2025-03-18T20:55:58.106Z", + "endTime": "2025-03-18T20:55:58.106Z", "totalTime": 0, "timeout": 5000 }, - "requisitions": [] + "tasks": [] }, { "valid": true, - "name": "Requisition #0", - "id": "0233360795_5e02f9345e_207197", + "name": "Task #0", + "id": "1355570989_7ffa627c18_29061", "level": 4, - "subscriptions": [], - "publishers": [], + "sensors": [], + "actuators": [], "iteration": 1, "totalIterations": 3, "hooks": { @@ -6310,20 +5715,20 @@ } }, "time": { - "startTime": "2020-03-08T05:33:36.939Z", - "endTime": "2020-03-08T05:33:36.939Z", + "startTime": "2025-03-18T20:55:58.106Z", + "endTime": "2025-03-18T20:55:58.106Z", "totalTime": 0, "timeout": 5000 }, - "requisitions": [] + "tasks": [] }, { "valid": true, - "name": "Requisition #0", - "id": "0233360795_5e02f9345e_207197", + "name": "Task #0", + "id": "1355570989_7ffa627c18_29061", "level": 4, - "subscriptions": [], - "publishers": [], + "sensors": [], + "actuators": [], "iteration": 2, "totalIterations": 3, "hooks": { @@ -6343,22 +5748,22 @@ } }, "time": { - "startTime": "2020-03-08T05:33:36.939Z", - "endTime": "2020-03-08T05:33:36.939Z", + "startTime": "2025-03-18T20:55:58.106Z", + "endTime": "2025-03-18T20:55:58.106Z", "totalTime": 0, "timeout": 5000 }, - "requisitions": [] + "tasks": [] } ] }, { "valid": true, - "name": "Requisition #0", - "id": "0233360795_55233c1965_344410", + "name": "Task #0", + "id": "1355570989_a83fe935c1_633469", "level": 3, - "subscriptions": [], - "publishers": [], + "sensors": [], + "actuators": [], "iteration": 1, "totalIterations": 3, "hooks": { @@ -6378,19 +5783,19 @@ } }, "time": { - "startTime": "2020-03-08T05:33:36.939Z", - "endTime": "2020-03-08T05:33:36.940Z", + "startTime": "2025-03-18T20:55:58.106Z", + "endTime": "2025-03-18T20:55:58.107Z", "totalTime": 1, "timeout": 5000 }, - "requisitions": [ + "tasks": [ { "valid": true, - "name": "Requisition #0", - "id": "0233360795_5e02f9345e_207197", + "name": "Task #0", + "id": "1355570989_7ffa627c18_29061", "level": 4, - "subscriptions": [], - "publishers": [], + "sensors": [], + "actuators": [], "iteration": 0, "totalIterations": 3, "hooks": { @@ -6410,20 +5815,20 @@ } }, "time": { - "startTime": "2020-03-08T05:33:36.940Z", - "endTime": "2020-03-08T05:33:36.940Z", + "startTime": "2025-03-18T20:55:58.106Z", + "endTime": "2025-03-18T20:55:58.106Z", "totalTime": 0, "timeout": 5000 }, - "requisitions": [] + "tasks": [] }, { "valid": true, - "name": "Requisition #0", - "id": "0233360795_5e02f9345e_207197", + "name": "Task #0", + "id": "1355570989_7ffa627c18_29061", "level": 4, - "subscriptions": [], - "publishers": [], + "sensors": [], + "actuators": [], "iteration": 1, "totalIterations": 3, "hooks": { @@ -6443,20 +5848,20 @@ } }, "time": { - "startTime": "2020-03-08T05:33:36.940Z", - "endTime": "2020-03-08T05:33:36.940Z", - "totalTime": 0, + "startTime": "2025-03-18T20:55:58.106Z", + "endTime": "2025-03-18T20:55:58.107Z", + "totalTime": 1, "timeout": 5000 }, - "requisitions": [] + "tasks": [] }, { "valid": true, - "name": "Requisition #0", - "id": "0233360795_5e02f9345e_207197", + "name": "Task #0", + "id": "1355570989_7ffa627c18_29061", "level": 4, - "subscriptions": [], - "publishers": [], + "sensors": [], + "actuators": [], "iteration": 2, "totalIterations": 3, "hooks": { @@ -6476,22 +5881,22 @@ } }, "time": { - "startTime": "2020-03-08T05:33:36.940Z", - "endTime": "2020-03-08T05:33:36.940Z", + "startTime": "2025-03-18T20:55:58.107Z", + "endTime": "2025-03-18T20:55:58.107Z", "totalTime": 0, "timeout": 5000 }, - "requisitions": [] + "tasks": [] } ] }, { "valid": true, - "name": "Requisition #0", - "id": "0233360795_55233c1965_344410", + "name": "Task #0", + "id": "1355570989_a83fe935c1_633469", "level": 3, - "subscriptions": [], - "publishers": [], + "sensors": [], + "actuators": [], "iteration": 2, "totalIterations": 3, "hooks": { @@ -6506,24 +5911,24 @@ "valid": true, "tests": [], "arguments": { - "elapsedTime": 1 + "elapsedTime": 0 } } }, "time": { - "startTime": "2020-03-08T05:33:36.941Z", - "endTime": "2020-03-08T05:33:36.942Z", - "totalTime": 1, + "startTime": "2025-03-18T20:55:58.107Z", + "endTime": "2025-03-18T20:55:58.107Z", + "totalTime": 0, "timeout": 5000 }, - "requisitions": [ + "tasks": [ { "valid": true, - "name": "Requisition #0", - "id": "0233360795_5e02f9345e_207197", + "name": "Task #0", + "id": "1355570989_7ffa627c18_29061", "level": 4, - "subscriptions": [], - "publishers": [], + "sensors": [], + "actuators": [], "iteration": 0, "totalIterations": 3, "hooks": { @@ -6543,20 +5948,20 @@ } }, "time": { - "startTime": "2020-03-08T05:33:36.941Z", - "endTime": "2020-03-08T05:33:36.941Z", + "startTime": "2025-03-18T20:55:58.107Z", + "endTime": "2025-03-18T20:55:58.107Z", "totalTime": 0, "timeout": 5000 }, - "requisitions": [] + "tasks": [] }, { "valid": true, - "name": "Requisition #0", - "id": "0233360795_5e02f9345e_207197", + "name": "Task #0", + "id": "1355570989_7ffa627c18_29061", "level": 4, - "subscriptions": [], - "publishers": [], + "sensors": [], + "actuators": [], "iteration": 1, "totalIterations": 3, "hooks": { @@ -6576,20 +5981,20 @@ } }, "time": { - "startTime": "2020-03-08T05:33:36.941Z", - "endTime": "2020-03-08T05:33:36.941Z", + "startTime": "2025-03-18T20:55:58.107Z", + "endTime": "2025-03-18T20:55:58.107Z", "totalTime": 0, "timeout": 5000 }, - "requisitions": [] + "tasks": [] }, { "valid": true, - "name": "Requisition #0", - "id": "0233360795_5e02f9345e_207197", + "name": "Task #0", + "id": "1355570989_7ffa627c18_29061", "level": 4, - "subscriptions": [], - "publishers": [], + "sensors": [], + "actuators": [], "iteration": 2, "totalIterations": 3, "hooks": { @@ -6604,17 +6009,17 @@ "valid": true, "tests": [], "arguments": { - "elapsedTime": 1 + "elapsedTime": 0 } } }, "time": { - "startTime": "2020-03-08T05:33:36.941Z", - "endTime": "2020-03-08T05:33:36.942Z", - "totalTime": 1, + "startTime": "2025-03-18T20:55:58.107Z", + "endTime": "2025-03-18T20:55:58.107Z", + "totalTime": 0, "timeout": 5000 }, - "requisitions": [] + "tasks": [] } ] } @@ -6622,11 +6027,11 @@ }, { "valid": true, - "name": "Requisition #1", - "id": "0233360795_e87d17a22e_21789", + "name": "Task #1", + "id": "1355570989_57cf642fae_901943", "level": 2, - "subscriptions": [], - "publishers": [], + "sensors": [], + "actuators": [], "iteration": 2, "totalIterations": 3, "hooks": { @@ -6641,24 +6046,24 @@ "valid": true, "tests": [], "arguments": { - "elapsedTime": 5 + "elapsedTime": 2 } } }, "time": { - "startTime": "2020-03-08T05:33:36.942Z", - "endTime": "2020-03-08T05:33:36.947Z", - "totalTime": 5, + "startTime": "2025-03-18T20:55:58.107Z", + "endTime": "2025-03-18T20:55:58.109Z", + "totalTime": 2, "timeout": 5000 }, - "requisitions": [ + "tasks": [ { "valid": true, - "name": "Requisition #0", - "id": "0233360795_55233c1965_344410", + "name": "Task #0", + "id": "1355570989_a83fe935c1_633469", "level": 3, - "subscriptions": [], - "publishers": [], + "sensors": [], + "actuators": [], "iteration": 0, "totalIterations": 3, "hooks": { @@ -6673,24 +6078,24 @@ "valid": true, "tests": [], "arguments": { - "elapsedTime": 1 + "elapsedTime": 0 } } }, "time": { - "startTime": "2020-03-08T05:33:36.943Z", - "endTime": "2020-03-08T05:33:36.944Z", - "totalTime": 1, + "startTime": "2025-03-18T20:55:58.108Z", + "endTime": "2025-03-18T20:55:58.108Z", + "totalTime": 0, "timeout": 5000 }, - "requisitions": [ + "tasks": [ { "valid": true, - "name": "Requisition #0", - "id": "0233360795_5e02f9345e_207197", + "name": "Task #0", + "id": "1355570989_7ffa627c18_29061", "level": 4, - "subscriptions": [], - "publishers": [], + "sensors": [], + "actuators": [], "iteration": 0, "totalIterations": 3, "hooks": { @@ -6710,20 +6115,20 @@ } }, "time": { - "startTime": "2020-03-08T05:33:36.943Z", - "endTime": "2020-03-08T05:33:36.943Z", + "startTime": "2025-03-18T20:55:58.108Z", + "endTime": "2025-03-18T20:55:58.108Z", "totalTime": 0, "timeout": 5000 }, - "requisitions": [] + "tasks": [] }, { "valid": true, - "name": "Requisition #0", - "id": "0233360795_5e02f9345e_207197", + "name": "Task #0", + "id": "1355570989_7ffa627c18_29061", "level": 4, - "subscriptions": [], - "publishers": [], + "sensors": [], + "actuators": [], "iteration": 1, "totalIterations": 3, "hooks": { @@ -6743,20 +6148,20 @@ } }, "time": { - "startTime": "2020-03-08T05:33:36.943Z", - "endTime": "2020-03-08T05:33:36.943Z", + "startTime": "2025-03-18T20:55:58.108Z", + "endTime": "2025-03-18T20:55:58.108Z", "totalTime": 0, "timeout": 5000 }, - "requisitions": [] + "tasks": [] }, { "valid": true, - "name": "Requisition #0", - "id": "0233360795_5e02f9345e_207197", + "name": "Task #0", + "id": "1355570989_7ffa627c18_29061", "level": 4, - "subscriptions": [], - "publishers": [], + "sensors": [], + "actuators": [], "iteration": 2, "totalIterations": 3, "hooks": { @@ -6771,27 +6176,27 @@ "valid": true, "tests": [], "arguments": { - "elapsedTime": 1 + "elapsedTime": 0 } } }, "time": { - "startTime": "2020-03-08T05:33:36.943Z", - "endTime": "2020-03-08T05:33:36.944Z", - "totalTime": 1, + "startTime": "2025-03-18T20:55:58.108Z", + "endTime": "2025-03-18T20:55:58.108Z", + "totalTime": 0, "timeout": 5000 }, - "requisitions": [] + "tasks": [] } ] }, { "valid": true, - "name": "Requisition #0", - "id": "0233360795_55233c1965_344410", + "name": "Task #0", + "id": "1355570989_a83fe935c1_633469", "level": 3, - "subscriptions": [], - "publishers": [], + "sensors": [], + "actuators": [], "iteration": 1, "totalIterations": 3, "hooks": { @@ -6806,24 +6211,24 @@ "valid": true, "tests": [], "arguments": { - "elapsedTime": 1 + "elapsedTime": 0 } } }, "time": { - "startTime": "2020-03-08T05:33:36.945Z", - "endTime": "2020-03-08T05:33:36.946Z", - "totalTime": 1, + "startTime": "2025-03-18T20:55:58.108Z", + "endTime": "2025-03-18T20:55:58.108Z", + "totalTime": 0, "timeout": 5000 }, - "requisitions": [ + "tasks": [ { "valid": true, - "name": "Requisition #0", - "id": "0233360795_5e02f9345e_207197", + "name": "Task #0", + "id": "1355570989_7ffa627c18_29061", "level": 4, - "subscriptions": [], - "publishers": [], + "sensors": [], + "actuators": [], "iteration": 0, "totalIterations": 3, "hooks": { @@ -6843,20 +6248,20 @@ } }, "time": { - "startTime": "2020-03-08T05:33:36.945Z", - "endTime": "2020-03-08T05:33:36.945Z", + "startTime": "2025-03-18T20:55:58.108Z", + "endTime": "2025-03-18T20:55:58.108Z", "totalTime": 0, "timeout": 5000 }, - "requisitions": [] + "tasks": [] }, { "valid": true, - "name": "Requisition #0", - "id": "0233360795_5e02f9345e_207197", + "name": "Task #0", + "id": "1355570989_7ffa627c18_29061", "level": 4, - "subscriptions": [], - "publishers": [], + "sensors": [], + "actuators": [], "iteration": 1, "totalIterations": 3, "hooks": { @@ -6876,20 +6281,20 @@ } }, "time": { - "startTime": "2020-03-08T05:33:36.945Z", - "endTime": "2020-03-08T05:33:36.945Z", + "startTime": "2025-03-18T20:55:58.108Z", + "endTime": "2025-03-18T20:55:58.108Z", "totalTime": 0, "timeout": 5000 }, - "requisitions": [] + "tasks": [] }, { "valid": true, - "name": "Requisition #0", - "id": "0233360795_5e02f9345e_207197", + "name": "Task #0", + "id": "1355570989_7ffa627c18_29061", "level": 4, - "subscriptions": [], - "publishers": [], + "sensors": [], + "actuators": [], "iteration": 2, "totalIterations": 3, "hooks": { @@ -6904,27 +6309,27 @@ "valid": true, "tests": [], "arguments": { - "elapsedTime": 1 + "elapsedTime": 0 } } }, "time": { - "startTime": "2020-03-08T05:33:36.945Z", - "endTime": "2020-03-08T05:33:36.946Z", - "totalTime": 1, + "startTime": "2025-03-18T20:55:58.108Z", + "endTime": "2025-03-18T20:55:58.108Z", + "totalTime": 0, "timeout": 5000 }, - "requisitions": [] + "tasks": [] } ] }, { "valid": true, - "name": "Requisition #0", - "id": "0233360795_55233c1965_344410", + "name": "Task #0", + "id": "1355570989_a83fe935c1_633469", "level": 3, - "subscriptions": [], - "publishers": [], + "sensors": [], + "actuators": [], "iteration": 2, "totalIterations": 3, "hooks": { @@ -6944,19 +6349,19 @@ } }, "time": { - "startTime": "2020-03-08T05:33:36.946Z", - "endTime": "2020-03-08T05:33:36.947Z", + "startTime": "2025-03-18T20:55:58.108Z", + "endTime": "2025-03-18T20:55:58.109Z", "totalTime": 1, "timeout": 5000 }, - "requisitions": [ + "tasks": [ { "valid": true, - "name": "Requisition #0", - "id": "0233360795_5e02f9345e_207197", + "name": "Task #0", + "id": "1355570989_7ffa627c18_29061", "level": 4, - "subscriptions": [], - "publishers": [], + "sensors": [], + "actuators": [], "iteration": 0, "totalIterations": 3, "hooks": { @@ -6976,20 +6381,20 @@ } }, "time": { - "startTime": "2020-03-08T05:33:36.946Z", - "endTime": "2020-03-08T05:33:36.946Z", + "startTime": "2025-03-18T20:55:58.108Z", + "endTime": "2025-03-18T20:55:58.108Z", "totalTime": 0, "timeout": 5000 }, - "requisitions": [] + "tasks": [] }, { "valid": true, - "name": "Requisition #0", - "id": "0233360795_5e02f9345e_207197", + "name": "Task #0", + "id": "1355570989_7ffa627c18_29061", "level": 4, - "subscriptions": [], - "publishers": [], + "sensors": [], + "actuators": [], "iteration": 1, "totalIterations": 3, "hooks": { @@ -7004,25 +6409,25 @@ "valid": true, "tests": [], "arguments": { - "elapsedTime": 1 + "elapsedTime": 0 } } }, "time": { - "startTime": "2020-03-08T05:33:36.946Z", - "endTime": "2020-03-08T05:33:36.947Z", - "totalTime": 1, + "startTime": "2025-03-18T20:55:58.109Z", + "endTime": "2025-03-18T20:55:58.109Z", + "totalTime": 0, "timeout": 5000 }, - "requisitions": [] + "tasks": [] }, { "valid": true, - "name": "Requisition #0", - "id": "0233360795_5e02f9345e_207197", + "name": "Task #0", + "id": "1355570989_7ffa627c18_29061", "level": 4, - "subscriptions": [], - "publishers": [], + "sensors": [], + "actuators": [], "iteration": 2, "totalIterations": 3, "hooks": { @@ -7042,12 +6447,12 @@ } }, "time": { - "startTime": "2020-03-08T05:33:36.947Z", - "endTime": "2020-03-08T05:33:36.947Z", + "startTime": "2025-03-18T20:55:58.109Z", + "endTime": "2025-03-18T20:55:58.109Z", "totalTime": 0, "timeout": 5000 }, - "requisitions": [] + "tasks": [] } ] } @@ -7055,11 +6460,11 @@ }, { "valid": true, - "name": "Requisition #2", - "id": "0233360795_aef615856f_583792", + "name": "Task #2", + "id": "1355570989_ab1ea5077a_118527", "level": 2, - "subscriptions": [], - "publishers": [], + "sensors": [], + "actuators": [], "iteration": 0, "totalIterations": 1, "hooks": { @@ -7080,27 +6485,27 @@ "valid": true, "tests": [], "arguments": { - "elapsedTime": 1 + "elapsedTime": 0 } } }, "time": { - "startTime": "2020-03-08T05:33:36.947Z", - "endTime": "2020-03-08T05:33:36.948Z", - "totalTime": 1, + "startTime": "2025-03-18T20:55:58.109Z", + "endTime": "2025-03-18T20:55:58.109Z", + "totalTime": 0, "timeout": 5000 }, - "requisitions": [] + "tasks": [] } ] }, { "valid": true, - "name": "examples/requisition-delay-iterations.yml", - "id": "0233360795_8e102bb4f5_510219", + "name": "examples/skipped.yml", + "id": "1355570989_f01dfa0923_153087", "level": 1, - "subscriptions": [], - "publishers": [], + "sensors": [], + "actuators": [], "iteration": 0, "totalIterations": 1, "hooks": { @@ -7115,26 +6520,26 @@ "valid": true, "tests": [], "arguments": { - "elapsedTime": 3071 + "elapsedTime": 88 } } }, "time": { - "startTime": "2020-03-08T05:33:36.867Z", - "endTime": "2020-03-08T05:33:39.938Z", - "totalTime": 3071, + "startTime": "2025-03-18T20:55:58.017Z", + "endTime": "2025-03-18T20:55:58.105Z", + "totalTime": 88, "timeout": 5000 }, - "requisitions": [ + "tasks": [ { "valid": true, - "name": "iterations", - "id": "0233360795_b4f515b70d_757234", + "name": "Task #0", + "id": "1355570989_d0e9827c8e_627996", "level": 2, - "subscriptions": [], - "publishers": [], + "sensors": [], + "actuators": [], "iteration": 0, - "totalIterations": 10, + "totalIterations": 5, "hooks": { "onInit": { "valid": true, @@ -7147,27 +6552,27 @@ "valid": true, "tests": [], "arguments": { - "elapsedTime": 30 + "elapsedTime": 72 } } }, "time": { - "startTime": "2020-03-08T05:33:36.887Z", - "endTime": "2020-03-08T05:33:36.917Z", - "totalTime": 30, + "startTime": "2025-03-18T20:55:58.022Z", + "endTime": "2025-03-18T20:55:58.095Z", + "totalTime": 73, "timeout": 5000 }, - "requisitions": [] + "tasks": [] }, { "valid": true, - "name": "iterations", - "id": "0233360795_b4f515b70d_757234", + "name": "Task #0", + "id": "1355570989_d0e9827c8e_627996", "level": 2, - "subscriptions": [], - "publishers": [], + "sensors": [], + "actuators": [], "iteration": 1, - "totalIterations": 10, + "totalIterations": 5, "hooks": { "onInit": { "valid": true, @@ -7180,27 +6585,27 @@ "valid": true, "tests": [], "arguments": { - "elapsedTime": 5 + "elapsedTime": 3 } } }, "time": { - "startTime": "2020-03-08T05:33:36.919Z", - "endTime": "2020-03-08T05:33:36.924Z", - "totalTime": 5, + "startTime": "2025-03-18T20:55:58.096Z", + "endTime": "2025-03-18T20:55:58.099Z", + "totalTime": 3, "timeout": 5000 }, - "requisitions": [] + "tasks": [] }, { "valid": true, - "name": "iterations", - "id": "0233360795_b4f515b70d_757234", + "name": "Task #0", + "id": "1355570989_d0e9827c8e_627996", "level": 2, - "subscriptions": [], - "publishers": [], + "sensors": [], + "actuators": [], "iteration": 2, - "totalIterations": 10, + "totalIterations": 5, "hooks": { "onInit": { "valid": true, @@ -7213,27 +6618,27 @@ "valid": true, "tests": [], "arguments": { - "elapsedTime": 2 + "elapsedTime": 1 } } }, "time": { - "startTime": "2020-03-08T05:33:36.925Z", - "endTime": "2020-03-08T05:33:36.927Z", - "totalTime": 2, + "startTime": "2025-03-18T20:55:58.099Z", + "endTime": "2025-03-18T20:55:58.100Z", + "totalTime": 1, "timeout": 5000 }, - "requisitions": [] + "tasks": [] }, { "valid": true, - "name": "iterations", - "id": "0233360795_b4f515b70d_757234", + "name": "Task #0", + "id": "1355570989_d0e9827c8e_627996", "level": 2, - "subscriptions": [], - "publishers": [], + "sensors": [], + "actuators": [], "iteration": 3, - "totalIterations": 10, + "totalIterations": 5, "hooks": { "onInit": { "valid": true, @@ -7251,22 +6656,22 @@ } }, "time": { - "startTime": "2020-03-08T05:33:36.928Z", - "endTime": "2020-03-08T05:33:36.929Z", + "startTime": "2025-03-18T20:55:58.100Z", + "endTime": "2025-03-18T20:55:58.101Z", "totalTime": 1, "timeout": 5000 }, - "requisitions": [] + "tasks": [] }, { "valid": true, - "name": "iterations", - "id": "0233360795_b4f515b70d_757234", + "name": "Task #0", + "id": "1355570989_d0e9827c8e_627996", "level": 2, - "subscriptions": [], - "publishers": [], + "sensors": [], + "actuators": [], "iteration": 4, - "totalIterations": 10, + "totalIterations": 5, "hooks": { "onInit": { "valid": true, @@ -7284,29 +6689,170 @@ } }, "time": { - "startTime": "2020-03-08T05:33:36.930Z", - "endTime": "2020-03-08T05:33:36.930Z", + "startTime": "2025-03-18T20:55:58.102Z", + "endTime": "2025-03-18T20:55:58.102Z", "totalTime": 0, "timeout": 5000 }, - "requisitions": [] + "tasks": [] }, { "valid": true, - "name": "iterations", - "id": "0233360795_b4f515b70d_757234", + "name": "Task #1", + "id": "1355570989_0217f12f55_728788", "level": 2, - "subscriptions": [], - "publishers": [], - "iteration": 5, - "totalIterations": 10, + "sensors": [], + "actuators": [], + "iteration": 0, + "totalIterations": 1, "hooks": { "onInit": { + "valid": true, + "tests": [ + { + "name": "Assertion #0", + "valid": true, + "description": "Expected 'store.skippedExecutions' to be equal to '5'. Received '5'" + } + ], + "arguments": { + "elapsedTime": 0 + } + }, + "onFinish": { "valid": true, "tests": [], "arguments": { "elapsedTime": 0 } + } + }, + "time": { + "startTime": "2025-03-18T20:55:58.103Z", + "endTime": "2025-03-18T20:55:58.103Z", + "totalTime": 0, + "timeout": 5000 + }, + "tasks": [] + }, + { + "valid": true, + "name": "Task #2", + "id": "1355570989_44a821ab65_176262", + "level": 2, + "sensors": [], + "actuators": [], + "hooks": { + "onInit": { + "arguments": {}, + "valid": true, + "tests": [] + }, + "onFinish": { + "arguments": {}, + "valid": true, + "tests": [ + { + "valid": true, + "name": "Task skipped", + "description": "There is no iterations set to this task" + } + ] + } + }, + "time": { + "startTime": "2025-03-18T20:55:58.103Z", + "endTime": "2025-03-18T20:55:58.103Z", + "totalTime": 0 + }, + "tasks": [] + }, + { + "valid": true, + "name": "Task #3", + "id": "1355570989_e5e2d3b9a7_77304", + "level": 2, + "sensors": [], + "actuators": [], + "hooks": { + "onInit": { + "arguments": {}, + "valid": true, + "tests": [] + }, + "onFinish": { + "arguments": {}, + "valid": true, + "tests": [ + { + "valid": true, + "name": "Task skipped", + "description": "There is no iterations set to this task" + } + ] + } + }, + "time": { + "startTime": "2025-03-18T20:55:58.103Z", + "endTime": "2025-03-18T20:55:58.103Z", + "totalTime": 0 + }, + "tasks": [] + }, + { + "valid": true, + "name": "Task #4", + "id": "1355570989_5c1186cc83_316216", + "level": 2, + "sensors": [], + "actuators": [], + "hooks": { + "onInit": { + "arguments": {}, + "valid": true, + "tests": [] + }, + "onFinish": { + "arguments": {}, + "valid": true, + "tests": [ + { + "valid": true, + "name": "Task skipped", + "description": "There is no iterations set to this task" + } + ] + } + }, + "time": { + "startTime": "2025-03-18T20:55:58.103Z", + "endTime": "2025-03-18T20:55:58.103Z", + "totalTime": 0 + }, + "tasks": [] + }, + { + "valid": true, + "name": "Task #5", + "id": "1355570989_ae9d70b300_323170", + "level": 2, + "sensors": [], + "actuators": [], + "iteration": 0, + "totalIterations": "1", + "hooks": { + "onInit": { + "valid": true, + "tests": [ + { + "name": "Assertion #0", + "valid": true, + "description": "Expecting 'true' to be true. Received: true" + } + ], + "arguments": { + "elapsedTime": 0 + } }, "onFinish": { "valid": true, @@ -7317,22 +6863,22 @@ } }, "time": { - "startTime": "2020-03-08T05:33:36.930Z", - "endTime": "2020-03-08T05:33:36.931Z", + "startTime": "2025-03-18T20:55:58.103Z", + "endTime": "2025-03-18T20:55:58.104Z", "totalTime": 1, "timeout": 5000 }, - "requisitions": [] + "tasks": [] }, { "valid": true, - "name": "iterations", - "id": "0233360795_b4f515b70d_757234", + "name": "Task #6", + "id": "1355570989_7aca67c473_982740", "level": 2, - "subscriptions": [], - "publishers": [], - "iteration": 6, - "totalIterations": 10, + "sensors": [], + "actuators": [], + "iteration": 0, + "totalIterations": 1, "hooks": { "onInit": { "valid": true, @@ -7345,31 +6891,37 @@ "valid": true, "tests": [], "arguments": { - "elapsedTime": 1 + "elapsedTime": 0 } } }, "time": { - "startTime": "2020-03-08T05:33:36.931Z", - "endTime": "2020-03-08T05:33:36.932Z", + "startTime": "2025-03-18T20:55:58.104Z", + "endTime": "2025-03-18T20:55:58.105Z", "totalTime": 1, "timeout": 5000 }, - "requisitions": [] + "tasks": [] }, { "valid": true, - "name": "iterations", - "id": "0233360795_b4f515b70d_757234", + "name": "Task #7", + "id": "1355570989_f081cebcb2_508868", "level": 2, - "subscriptions": [], - "publishers": [], - "iteration": 7, - "totalIterations": 10, + "sensors": [], + "actuators": [], + "iteration": 0, + "totalIterations": 1, "hooks": { "onInit": { "valid": true, - "tests": [], + "tests": [ + { + "name": "Assertion #0", + "valid": true, + "description": "Expecting 'store.executed' to be defined" + } + ], "arguments": { "elapsedTime": 0 } @@ -7383,22 +6935,184 @@ } }, "time": { - "startTime": "2020-03-08T05:33:36.933Z", - "endTime": "2020-03-08T05:33:36.933Z", + "startTime": "2025-03-18T20:55:58.105Z", + "endTime": "2025-03-18T20:55:58.105Z", "totalTime": 0, "timeout": 5000 }, - "requisitions": [] + "tasks": [] + } + ] + }, + { + "valid": true, + "name": "examples/ssl.yml", + "id": "1355570989_b924ef6cfd_213651", + "level": 1, + "sensors": [], + "actuators": [], + "iteration": 0, + "totalIterations": 1, + "hooks": { + "onInit": { + "valid": true, + "tests": [], + "arguments": { + "elapsedTime": 0 + } }, + "onFinish": { + "valid": true, + "tests": [], + "arguments": { + "elapsedTime": 4179 + } + } + }, + "time": { + "startTime": "2025-03-18T20:55:58.017Z", + "endTime": "2025-03-18T20:56:02.196Z", + "totalTime": 4179, + "timeout": 5000 + }, + "tasks": [ { "valid": true, - "name": "iterations", - "id": "0233360795_b4f515b70d_757234", + "name": "Task #0", + "id": "1355570989_bdc6ef7e9a_982863", "level": 2, - "subscriptions": [], - "publishers": [], - "iteration": 8, - "totalIterations": 10, + "sensors": [ + { + "id": "1355570989_bcb968e58f_445880", + "name": "Sensor #0", + "type": "ssl", + "hooks": { + "onInit": { + "valid": true, + "tests": [], + "arguments": {} + }, + "onFinish": { + "valid": true, + "tests": [ + { + "implicit": true, + "valid": true, + "name": "Message received", + "description": "Sensor has received its message" + } + ], + "arguments": { + "executedHooks": { + "onInit": [], + "onMessageReceived": [ + "payload", + "stream", + "path", + "elapsedTime" + ], + "onFinish": [ + "executedHooks", + "elapsedTime" + ] + }, + "elapsedTime": 2168 + } + }, + "onMessageReceived": { + "valid": true, + "tests": [ + { + "name": "Assertion #0", + "valid": true, + "description": "Expected 'payload' to be equal to 'secureMessage'. Received 'secureMessage'" + } + ], + "arguments": { + "payload": "secureMessage", + "stream": { + "address": "::1", + "family": "IPv6", + "port": 23082 + }, + "elapsedTime": 171 + } + } + }, + "valid": true, + "sensorTime": "2025-03-18T20:55:58.111Z" + } + ], + "actuators": [ + { + "id": "1355570989_759a24aece_76421", + "name": "Actuator #0", + "valid": true, + "hooks": { + "onInit": { + "arguments": { + "elapsedTime": 0 + }, + "tests": [], + "valid": true + }, + "onFinish": { + "arguments": { + "executedHooks": { + "onInit": [], + "onMessageReceived": [ + "payload", + "stream", + "elapsedTime" + ], + "onFinish": [ + "executedHooks", + "elapsedTime" + ] + }, + "elapsedTime": 2168 + }, + "tests": [ + { + "name": "Published", + "valid": true, + "description": "Published successfully", + "implicit": true + } + ], + "valid": true + }, + "onMessageReceived": { + "arguments": { + "payload": "hisecureResponse", + "stream": { + "address": "::1", + "family": "IPv6", + "port": 57108 + }, + "elapsedTime": 4174 + }, + "tests": [ + { + "name": "Assertion #0", + "valid": true, + "description": "Expected 'payload' to be equal to 'hisecureResponse'. Received 'hisecureResponse'" + }, + { + "name": "Assertion #0", + "valid": true, + "description": "Expected 'payload' to be equal to 'hisecureResponse'. Received 'hisecureResponse'" + } + ], + "valid": true + } + }, + "type": "ssl", + "messageSentInstant": "2025-03-18T20:56:00.191Z" + } + ], + "iteration": 0, + "totalIterations": 1, "hooks": { "onInit": { "valid": true, @@ -7411,27 +7125,155 @@ "valid": true, "tests": [], "arguments": { - "elapsedTime": 1 + "elapsedTime": 2168 } } }, "time": { - "startTime": "2020-03-08T05:33:36.933Z", - "endTime": "2020-03-08T05:33:36.934Z", - "totalTime": 1, + "startTime": "2025-03-18T20:55:58.023Z", + "endTime": "2025-03-18T20:56:00.191Z", + "totalTime": 2168, "timeout": 5000 }, - "requisitions": [] + "tasks": [] }, { "valid": true, - "name": "iterations", - "id": "0233360795_b4f515b70d_757234", + "name": "Task #1", + "id": "1355570989_6eba96f32f_961750", "level": 2, - "subscriptions": [], - "publishers": [], - "iteration": 9, - "totalIterations": 10, + "sensors": [ + { + "id": "1355570989_7a4ecedb6b_51124", + "name": "Sensor #0", + "type": "ssl", + "hooks": { + "onInit": { + "valid": true, + "tests": [], + "arguments": {} + }, + "onFinish": { + "valid": true, + "tests": [ + { + "implicit": true, + "valid": true, + "name": "Message received", + "description": "Sensor has received its message" + } + ], + "arguments": { + "executedHooks": { + "onInit": [], + "onMessageReceived": [ + "payload", + "stream", + "path", + "elapsedTime" + ], + "onFinish": [ + "executedHooks", + "elapsedTime" + ] + }, + "elapsedTime": 2004 + } + }, + "onMessageReceived": { + "valid": true, + "tests": [ + { + "name": "Assertion #0", + "valid": true, + "description": "Expected 'payload' to be equal to 'reusingSecureMessage'. Received 'reusingSecureMessage'" + } + ], + "arguments": { + "payload": "reusingSecureMessage", + "stream": { + "address": "::1", + "family": "IPv6", + "port": 23082 + }, + "elapsedTime": 0 + } + } + }, + "valid": true, + "sensorTime": "2025-03-18T20:56:00.192Z" + } + ], + "actuators": [ + { + "id": "1355570989_450b411eba_257685", + "name": "Actuator #0", + "valid": true, + "hooks": { + "onInit": { + "arguments": { + "elapsedTime": 0 + }, + "tests": [], + "valid": true + }, + "onFinish": { + "arguments": { + "executedHooks": { + "onInit": [], + "onMessageReceived": [ + "payload", + "stream", + "elapsedTime" + ], + "onFinish": [ + "executedHooks", + "elapsedTime" + ] + }, + "elapsedTime": 2004 + }, + "tests": [ + { + "name": "Published", + "valid": true, + "description": "Published successfully", + "implicit": true + } + ], + "valid": true + }, + "onMessageReceived": { + "arguments": { + "payload": "reusingSecureResponse", + "stream": { + "address": "::1", + "family": "IPv6", + "port": 57108 + }, + "elapsedTime": 2006 + }, + "tests": [ + { + "name": "Assertion #0", + "valid": true, + "description": "Expected 'payload' to be equal to 'reusingSecureResponse'. Received 'reusingSecureResponse'" + }, + { + "name": "Assertion #0", + "valid": true, + "description": "Expected 'payload' to be equal to 'reusingSecureResponse'. Received 'reusingSecureResponse'" + } + ], + "valid": true + } + }, + "type": "ssl", + "messageSentInstant": "2025-03-18T20:56:02.195Z" + } + ], + "iteration": 0, + "totalIterations": 1, "hooks": { "onInit": { "valid": true, @@ -7444,76 +7286,114 @@ "valid": true, "tests": [], "arguments": { - "elapsedTime": 1 + "elapsedTime": 2004 } } }, "time": { - "startTime": "2020-03-08T05:33:36.934Z", - "endTime": "2020-03-08T05:33:36.935Z", - "totalTime": 1, + "startTime": "2025-03-18T20:56:00.192Z", + "endTime": "2025-03-18T20:56:02.196Z", + "totalTime": 2004, "timeout": 5000 }, - "requisitions": [] - }, + "tasks": [] + } + ] + }, + { + "valid": true, + "name": "examples/stdin.yml", + "id": "1355570989_357accc01a_960750", + "level": 1, + "sensors": [ { - "valid": true, - "name": "delayed", - "id": "0233360795_ce1438bac6_499948", - "level": 2, - "subscriptions": [], - "publishers": [], - "iteration": 0, - "totalIterations": 1, + "id": "anyIdTest", + "name": "sensor description", + "type": "standard-input", "hooks": { "onInit": { "valid": true, "tests": [], + "arguments": {} + }, + "onFinish": { + "valid": true, + "tests": [ + { + "implicit": true, + "valid": true, + "name": "Message received", + "description": "Sensor has received its message" + } + ], "arguments": { - "elapsedTime": 0 + "executedHooks": { + "onInit": [], + "onMessageReceived": [ + "message", + "elapsedTime" + ], + "onFinish": [ + "executedHooks", + "elapsedTime" + ] + }, + "elapsedTime": 111 } }, - "onFinish": { + "onMessageReceived": { "valid": true, "tests": [ { - "name": "It was executed 10 times", - "valid": true, - "description": "Expected 'requisition.parent.requisitions[0].counter' to be equal to '10'. Received '10'" - }, - { - "name": "Elapsed time", - "valid": true, - "description": "Expected 'elapsedTime' to be greater than or equal to '2950'. Received '3001'" - }, - { - "name": "Assertion #2", + "name": "payload", "valid": true, - "description": "Expected 'new Date().getTime() - requisition.startTime.getTime()' to be greater than or equal to '2950'. Received '3002'" + "description": "Expected 'message' to be equal to 'enqueuer standard-input payload'. Received 'enqueuer standard-input payload'" } ], "arguments": { - "elapsedTime": 3001 + "message": "enqueuer standard-input payload", + "elapsedTime": 111 } } }, - "time": { - "startTime": "2020-03-08T05:33:36.936Z", - "endTime": "2020-03-08T05:33:39.938Z", - "totalTime": 3002, - "timeout": 5000 - }, - "requisitions": [] + "valid": true, + "sensorTime": "2025-03-18T20:55:58.066Z" } - ] + ], + "actuators": [], + "iteration": 0, + "totalIterations": 1, + "hooks": { + "onInit": { + "valid": true, + "tests": [], + "arguments": { + "elapsedTime": 0 + } + }, + "onFinish": { + "valid": true, + "tests": [], + "arguments": { + "elapsedTime": 111 + } + } + }, + "time": { + "startTime": "2025-03-18T20:55:58.017Z", + "endTime": "2025-03-18T20:55:58.128Z", + "totalTime": 111, + "timeout": 5000 + }, + "tasks": [] }, { "valid": true, - "name": "examples/skipped.yml", - "id": "0233360795_bb333e1dde_738635", + "name": "examples/store.yml", + "id": "1355570989_d97c8c8411_325820", "level": 1, - "subscriptions": [], - "publishers": [], + "sensors": [], + "actuators": [], "iteration": 0, "totalIterations": 1, "hooks": { @@ -7528,92 +7408,26 @@ "valid": true, "tests": [], "arguments": { - "elapsedTime": 68 + "elapsedTime": 84 } } }, "time": { - "startTime": "2020-03-08T05:33:36.868Z", - "endTime": "2020-03-08T05:33:36.936Z", - "totalTime": 68, + "startTime": "2025-03-18T20:55:58.018Z", + "endTime": "2025-03-18T20:55:58.102Z", + "totalTime": 84, "timeout": 5000 }, - "requisitions": [ + "tasks": [ { "valid": true, - "name": "Requisition #0", - "id": "0233360795_fc4b0751bf_854712", + "name": "Task #0", + "id": "1355570989_ebc76a078d_270083", "level": 2, - "subscriptions": [], - "publishers": [], + "sensors": [], + "actuators": [], "iteration": 0, - "totalIterations": 5, - "hooks": { - "onInit": { - "valid": true, - "tests": [], - "arguments": { - "elapsedTime": 0 - } - }, - "onFinish": { - "valid": true, - "tests": [], - "arguments": { - "elapsedTime": 30 - } - } - }, - "time": { - "startTime": "2020-03-08T05:33:36.887Z", - "endTime": "2020-03-08T05:33:36.917Z", - "totalTime": 30, - "timeout": 5000 - }, - "requisitions": [] - }, - { - "valid": true, - "name": "Requisition #0", - "id": "0233360795_fc4b0751bf_854712", - "level": 2, - "subscriptions": [], - "publishers": [], - "iteration": 1, - "totalIterations": 5, - "hooks": { - "onInit": { - "valid": true, - "tests": [], - "arguments": { - "elapsedTime": 0 - } - }, - "onFinish": { - "valid": true, - "tests": [], - "arguments": { - "elapsedTime": 4 - } - } - }, - "time": { - "startTime": "2020-03-08T05:33:36.920Z", - "endTime": "2020-03-08T05:33:36.924Z", - "totalTime": 4, - "timeout": 5000 - }, - "requisitions": [] - }, - { - "valid": true, - "name": "Requisition #0", - "id": "0233360795_fc4b0751bf_854712", - "level": 2, - "subscriptions": [], - "publishers": [], - "iteration": 2, - "totalIterations": 5, + "totalIterations": 1, "hooks": { "onInit": { "valid": true, @@ -7626,27 +7440,27 @@ "valid": true, "tests": [], "arguments": { - "elapsedTime": 2 + "elapsedTime": 71 } } }, "time": { - "startTime": "2020-03-08T05:33:36.925Z", - "endTime": "2020-03-08T05:33:36.927Z", - "totalTime": 2, + "startTime": "2025-03-18T20:55:58.023Z", + "endTime": "2025-03-18T20:55:58.095Z", + "totalTime": 72, "timeout": 5000 }, - "requisitions": [] + "tasks": [] }, { "valid": true, - "name": "Requisition #0", - "id": "0233360795_fc4b0751bf_854712", + "name": "Task #1", + "id": "1355570989_c6f4e581d6_650969", "level": 2, - "subscriptions": [], - "publishers": [], - "iteration": 3, - "totalIterations": 5, + "sensors": [], + "actuators": [], + "iteration": 0, + "totalIterations": 1, "hooks": { "onInit": { "valid": true, @@ -7657,29 +7471,40 @@ }, "onFinish": { "valid": true, - "tests": [], + "tests": [ + { + "name": "Payload", + "valid": true, + "description": "Expected '123' to be equal to '123'. Received '123'" + }, + { + "name": "Environment Variables added", + "valid": true, + "description": "Expecting 'store.PATH' to be defined" + } + ], "arguments": { "elapsedTime": 1 } } }, "time": { - "startTime": "2020-03-08T05:33:36.928Z", - "endTime": "2020-03-08T05:33:36.929Z", + "startTime": "2025-03-18T20:55:58.098Z", + "endTime": "2025-03-18T20:55:58.099Z", "totalTime": 1, "timeout": 5000 }, - "requisitions": [] + "tasks": [] }, { "valid": true, - "name": "Requisition #0", - "id": "0233360795_fc4b0751bf_854712", + "name": "Task #2", + "id": "1355570989_681da84562_828504", "level": 2, - "subscriptions": [], - "publishers": [], - "iteration": 4, - "totalIterations": 5, + "sensors": [], + "actuators": [], + "iteration": 0, + "totalIterations": 1, "hooks": { "onInit": { "valid": true, @@ -7689,175 +7514,74 @@ } }, "onFinish": { - "valid": true, - "tests": [], - "arguments": { - "elapsedTime": 0 - } - } - }, - "time": { - "startTime": "2020-03-08T05:33:36.930Z", - "endTime": "2020-03-08T05:33:36.930Z", - "totalTime": 0, - "timeout": 5000 - }, - "requisitions": [] - }, - { - "valid": true, - "name": "Requisition #1", - "id": "0233360795_0813922659_753665", - "level": 2, - "subscriptions": [], - "publishers": [], - "iteration": 0, - "totalIterations": 1, - "hooks": { - "onInit": { "valid": true, "tests": [ { - "name": "Assertion #0", + "name": "Payload", "valid": true, - "description": "Expected 'store.skippedExecutions' to be equal to '5'. Received '5'" + "description": "Expected '124' to be equal to '124'. Received '124'" } ], "arguments": { "elapsedTime": 0 } - }, - "onFinish": { - "valid": true, - "tests": [], - "arguments": { - "elapsedTime": 0 - } } }, "time": { - "startTime": "2020-03-08T05:33:36.931Z", - "endTime": "2020-03-08T05:33:36.931Z", + "startTime": "2025-03-18T20:55:58.100Z", + "endTime": "2025-03-18T20:55:58.100Z", "totalTime": 0, "timeout": 5000 }, - "requisitions": [] - }, - { - "valid": true, - "name": "Requisition #2", - "id": "0233360795_38e558917b_848570", - "level": 2, - "subscriptions": [], - "publishers": [], - "hooks": { - "onInit": { - "arguments": {}, - "valid": true, - "tests": [] - }, - "onFinish": { - "arguments": {}, - "valid": true, - "tests": [ - { - "valid": true, - "name": "Requisition skipped", - "description": "There is no iterations set to this requisition" - } - ] - } - }, - "time": { - "startTime": "2020-03-08T05:33:36.932Z", - "endTime": "2020-03-08T05:33:36.932Z", - "totalTime": 0 - }, - "requisitions": [] - }, - { + "tasks": [] + } + ] + }, + { + "valid": true, + "name": "examples/task-delay-iterations.yml", + "id": "1355570989_f867b9467c_128265", + "level": 1, + "sensors": [], + "actuators": [], + "iteration": 0, + "totalIterations": 1, + "hooks": { + "onInit": { "valid": true, - "name": "Requisition #3", - "id": "0233360795_4aa5c78448_51228", - "level": 2, - "subscriptions": [], - "publishers": [], - "hooks": { - "onInit": { - "arguments": {}, - "valid": true, - "tests": [] - }, - "onFinish": { - "arguments": {}, - "valid": true, - "tests": [ - { - "valid": true, - "name": "Requisition skipped", - "description": "There is no iterations set to this requisition" - } - ] - } - }, - "time": { - "startTime": "2020-03-08T05:33:36.932Z", - "endTime": "2020-03-08T05:33:36.932Z", - "totalTime": 0 - }, - "requisitions": [] + "tests": [], + "arguments": { + "elapsedTime": 0 + } }, - { + "onFinish": { "valid": true, - "name": "Requisition #4", - "id": "0233360795_6ac450666c_171747", - "level": 2, - "subscriptions": [], - "publishers": [], - "hooks": { - "onInit": { - "arguments": {}, - "valid": true, - "tests": [] - }, - "onFinish": { - "arguments": {}, - "valid": true, - "tests": [ - { - "valid": true, - "name": "Requisition skipped", - "description": "There is no iterations set to this requisition" - } - ] - } - }, - "time": { - "startTime": "2020-03-08T05:33:36.932Z", - "endTime": "2020-03-08T05:33:36.932Z", - "totalTime": 0 - }, - "requisitions": [] - }, + "tests": [], + "arguments": { + "elapsedTime": 3088 + } + } + }, + "time": { + "startTime": "2025-03-18T20:55:58.018Z", + "endTime": "2025-03-18T20:56:01.107Z", + "totalTime": 3089, + "timeout": 5000 + }, + "tasks": [ { "valid": true, - "name": "Requisition #5", - "id": "0233360795_23f493719b_809056", + "name": "iterations", + "id": "1355570989_98e6c02ed8_808806", "level": 2, - "subscriptions": [], - "publishers": [], - "iteration": 0, - "totalIterations": "1", - "hooks": { - "onInit": { - "valid": true, - "tests": [ - { - "name": "Assertion #0", - "valid": true, - "description": "Expecting 'true' to be true. Received: true" - } - ], + "sensors": [], + "actuators": [], + "iteration": 0, + "totalIterations": 10, + "hooks": { + "onInit": { + "valid": true, + "tests": [], "arguments": { "elapsedTime": 0 } @@ -7866,27 +7590,27 @@ "valid": true, "tests": [], "arguments": { - "elapsedTime": 0 + "elapsedTime": 71 } } }, "time": { - "startTime": "2020-03-08T05:33:36.933Z", - "endTime": "2020-03-08T05:33:36.933Z", - "totalTime": 0, + "startTime": "2025-03-18T20:55:58.023Z", + "endTime": "2025-03-18T20:55:58.095Z", + "totalTime": 72, "timeout": 5000 }, - "requisitions": [] + "tasks": [] }, { "valid": true, - "name": "Requisition #6", - "id": "0233360795_f9b72d5147_592534", + "name": "iterations", + "id": "1355570989_98e6c02ed8_808806", "level": 2, - "subscriptions": [], - "publishers": [], - "iteration": 0, - "totalIterations": 1, + "sensors": [], + "actuators": [], + "iteration": 1, + "totalIterations": 10, "hooks": { "onInit": { "valid": true, @@ -7899,37 +7623,31 @@ "valid": true, "tests": [], "arguments": { - "elapsedTime": 0 + "elapsedTime": 2 } } }, "time": { - "startTime": "2020-03-08T05:33:36.934Z", - "endTime": "2020-03-08T05:33:36.934Z", - "totalTime": 0, + "startTime": "2025-03-18T20:55:58.097Z", + "endTime": "2025-03-18T20:55:58.099Z", + "totalTime": 2, "timeout": 5000 }, - "requisitions": [] + "tasks": [] }, { "valid": true, - "name": "Requisition #7", - "id": "0233360795_b41f5d915d_934112", + "name": "iterations", + "id": "1355570989_98e6c02ed8_808806", "level": 2, - "subscriptions": [], - "publishers": [], - "iteration": 0, - "totalIterations": 1, + "sensors": [], + "actuators": [], + "iteration": 2, + "totalIterations": 10, "hooks": { "onInit": { "valid": true, - "tests": [ - { - "name": "Assertion #0", - "valid": true, - "description": "Expecting 'store.executed' to be defined" - } - ], + "tests": [], "arguments": { "elapsedTime": 0 } @@ -7938,174 +7656,27 @@ "valid": true, "tests": [], "arguments": { - "elapsedTime": 0 + "elapsedTime": 1 } } }, "time": { - "startTime": "2020-03-08T05:33:36.935Z", - "endTime": "2020-03-08T05:33:36.935Z", - "totalTime": 0, + "startTime": "2025-03-18T20:55:58.099Z", + "endTime": "2025-03-18T20:55:58.100Z", + "totalTime": 1, "timeout": 5000 }, - "requisitions": [] - } - ] - }, - { - "valid": true, - "name": "examples/ssl.yml", - "id": "0233360795_fbc833de59_897737", - "level": 1, - "subscriptions": [], - "publishers": [], - "iteration": 0, - "totalIterations": 1, - "hooks": { - "onInit": { - "valid": true, - "tests": [], - "arguments": { - "elapsedTime": 0 - } + "tasks": [] }, - "onFinish": { - "valid": true, - "tests": [], - "arguments": { - "elapsedTime": 4164 - } - } - }, - "time": { - "startTime": "2020-03-08T05:33:36.870Z", - "endTime": "2020-03-08T05:33:41.034Z", - "totalTime": 4164, - "timeout": 5000 - }, - "requisitions": [ { "valid": true, - "name": "Requisition #0", - "id": "0233360795_ec6b6289cc_859238", + "name": "iterations", + "id": "1355570989_98e6c02ed8_808806", "level": 2, - "subscriptions": [ - { - "id": "0233360795_d5884fd0ec_878362", - "name": "Subscription #0", - "type": "ssl", - "hooks": { - "onInit": { - "valid": true, - "tests": [], - "arguments": { - "elapsedTime": 0 - } - }, - "onFinish": { - "valid": true, - "tests": [ - { - "valid": true, - "name": "Message received", - "description": "Subscription has received its message" - } - ], - "arguments": { - "executedHooks": [ - "onInit", - "onMessageReceived", - "onFinish" - ], - "elapsedTime": 2142 - } - }, - "onMessageReceived": { - "valid": true, - "tests": [ - { - "name": "Assertion #0", - "valid": true, - "description": "Expected 'payload' to be equal to 'secureMessage'. Received 'secureMessage'" - } - ], - "arguments": { - "payload": "secureMessage", - "stream": { - "address": "::ffff:127.0.0.1", - "family": "IPv6", - "port": 23082 - }, - "elapsedTime": 153 - } - } - }, - "valid": true, - "subscriptionTime": "2020-03-08T05:33:36.951Z" - } - ], - "publishers": [ - { - "id": "0233360795_e063362e26_185358", - "name": "Publisher #0", - "valid": true, - "hooks": { - "onInit": { - "arguments": { - "elapsedTime": 0 - }, - "tests": [], - "valid": true - }, - "onFinish": { - "arguments": { - "executedHooks": [ - "onInit", - "onMessageReceived", - "onFinish" - ], - "elapsedTime": 2142 - }, - "tests": [ - { - "name": "Published", - "valid": true, - "description": "Published successfully" - } - ], - "valid": true - }, - "onMessageReceived": { - "arguments": { - "payload": "hisecureResponse", - "stream": { - "address": "127.0.0.1", - "family": "IPv4", - "port": 61964 - }, - "elapsedTime": 4147 - }, - "tests": [ - { - "name": "Assertion #0", - "valid": true, - "description": "Expected 'payload' to be equal to 'hisecureResponse'. Received 'hisecureResponse'" - }, - { - "name": "Assertion #0", - "valid": true, - "description": "Expected 'payload' to be equal to 'hisecureResponse'. Received 'hisecureResponse'" - } - ], - "valid": true - } - }, - "type": "ssl", - "publishTime": "2020-03-08T05:33:39.029Z" - } - ], - "iteration": 0, - "totalIterations": 1, + "sensors": [], + "actuators": [], + "iteration": 3, + "totalIterations": 10, "hooks": { "onInit": { "valid": true, @@ -8118,140 +7689,60 @@ "valid": true, "tests": [], "arguments": { - "elapsedTime": 2142 + "elapsedTime": 1 } } }, "time": { - "startTime": "2020-03-08T05:33:36.888Z", - "endTime": "2020-03-08T05:33:39.030Z", - "totalTime": 2142, + "startTime": "2025-03-18T20:55:58.100Z", + "endTime": "2025-03-18T20:55:58.101Z", + "totalTime": 1, "timeout": 5000 }, - "requisitions": [] + "tasks": [] }, { "valid": true, - "name": "Requisition #1", - "id": "0233360796_c8e796a6f7_691554", + "name": "iterations", + "id": "1355570989_98e6c02ed8_808806", "level": 2, - "subscriptions": [ - { - "id": "0233360796_464eaccac3_710694", - "name": "Subscription #0", - "type": "ssl", - "hooks": { - "onInit": { - "valid": true, - "tests": [], - "arguments": { - "elapsedTime": 0 - } - }, - "onFinish": { - "valid": true, - "tests": [ - { - "valid": true, - "name": "Message received", - "description": "Subscription has received its message" - } - ], - "arguments": { - "executedHooks": [ - "onInit", - "onMessageReceived", - "onFinish" - ], - "elapsedTime": 2002 - } - }, - "onMessageReceived": { - "valid": true, - "tests": [ - { - "name": "Assertion #0", - "valid": true, - "description": "Expected 'payload' to be equal to 'reusingSecureMessage'. Received 'reusingSecureMessage'" - } - ], - "arguments": { - "payload": "reusingSecureMessage", - "stream": { - "address": "::ffff:127.0.0.1", - "family": "IPv6", - "port": 23082 - }, - "elapsedTime": 0 - } - } - }, + "sensors": [], + "actuators": [], + "iteration": 4, + "totalIterations": 10, + "hooks": { + "onInit": { "valid": true, - "subscriptionTime": "2020-03-08T05:33:39.031Z" - } - ], - "publishers": [ - { - "id": "0233360796_26f6887fe6_16302", - "name": "Publisher #0", + "tests": [], + "arguments": { + "elapsedTime": 0 + } + }, + "onFinish": { "valid": true, - "hooks": { - "onInit": { - "arguments": { - "elapsedTime": 0 - }, - "tests": [], - "valid": true - }, - "onFinish": { - "arguments": { - "executedHooks": [ - "onInit", - "onMessageReceived", - "onFinish" - ], - "elapsedTime": 2002 - }, - "tests": [ - { - "name": "Published", - "valid": true, - "description": "Published successfully" - } - ], - "valid": true - }, - "onMessageReceived": { - "arguments": { - "payload": "reusingSecureResponse", - "stream": { - "address": "127.0.0.1", - "family": "IPv4", - "port": 61964 - }, - "elapsedTime": 2004 - }, - "tests": [ - { - "name": "Assertion #0", - "valid": true, - "description": "Expected 'payload' to be equal to 'reusingSecureResponse'. Received 'reusingSecureResponse'" - }, - { - "name": "Assertion #0", - "valid": true, - "description": "Expected 'payload' to be equal to 'reusingSecureResponse'. Received 'reusingSecureResponse'" - } - ], - "valid": true - } - }, - "type": "ssl", - "publishTime": "2020-03-08T05:33:41.033Z" + "tests": [], + "arguments": { + "elapsedTime": 0 + } } - ], - "iteration": 0, - "totalIterations": 1, + }, + "time": { + "startTime": "2025-03-18T20:55:58.102Z", + "endTime": "2025-03-18T20:55:58.102Z", + "totalTime": 0, + "timeout": 5000 + }, + "tasks": [] + }, + { + "valid": true, + "name": "iterations", + "id": "1355570989_98e6c02ed8_808806", + "level": 2, + "sensors": [], + "actuators": [], + "iteration": 5, + "totalIterations": 10, "hooks": { "onInit": { "valid": true, @@ -8264,30 +7755,27 @@ "valid": true, "tests": [], "arguments": { - "elapsedTime": 2002 + "elapsedTime": 1 } } }, "time": { - "startTime": "2020-03-08T05:33:39.031Z", - "endTime": "2020-03-08T05:33:41.033Z", - "totalTime": 2002, + "startTime": "2025-03-18T20:55:58.102Z", + "endTime": "2025-03-18T20:55:58.103Z", + "totalTime": 1, "timeout": 5000 }, - "requisitions": [] - } - ] - }, - { - "valid": true, - "name": "examples/stdin.yml", - "id": "0233360796_ae6c8a1714_155639", - "level": 1, - "subscriptions": [ + "tasks": [] + }, { - "id": "anyIdTest", - "name": "subscription description", - "type": "standard-input", + "valid": true, + "name": "iterations", + "id": "1355570989_98e6c02ed8_808806", + "level": 2, + "sensors": [], + "actuators": [], + "iteration": 6, + "totalIterations": 10, "hooks": { "onInit": { "valid": true, @@ -8298,109 +7786,62 @@ }, "onFinish": { "valid": true, - "tests": [ - { - "valid": true, - "name": "Message received", - "description": "Subscription has received its message" - } - ], + "tests": [], + "arguments": { + "elapsedTime": 0 + } + } + }, + "time": { + "startTime": "2025-03-18T20:55:58.103Z", + "endTime": "2025-03-18T20:55:58.104Z", + "totalTime": 1, + "timeout": 5000 + }, + "tasks": [] + }, + { + "valid": true, + "name": "iterations", + "id": "1355570989_98e6c02ed8_808806", + "level": 2, + "sensors": [], + "actuators": [], + "iteration": 7, + "totalIterations": 10, + "hooks": { + "onInit": { + "valid": true, + "tests": [], "arguments": { - "executedHooks": [ - "onInit", - "onMessageReceived", - "onFinish" - ], - "elapsedTime": 115 + "elapsedTime": 0 } }, - "onMessageReceived": { + "onFinish": { "valid": true, - "tests": [ - { - "name": "payload", - "valid": true, - "description": "Expected 'message' to be equal to 'enqueuer standard-input payload'. Received 'enqueuer standard-input payload'" - } - ], + "tests": [], "arguments": { - "message": "enqueuer standard-input payload", - "elapsedTime": 115 + "elapsedTime": 0 } } }, - "valid": true, - "subscriptionTime": "2020-03-08T05:33:36.911Z" - } - ], - "publishers": [], - "iteration": 0, - "totalIterations": 1, - "hooks": { - "onInit": { - "valid": true, - "tests": [], - "arguments": { - "elapsedTime": 0 - } - }, - "onFinish": { - "valid": true, - "tests": [], - "arguments": { - "elapsedTime": 115 - } - } - }, - "time": { - "startTime": "2020-03-08T05:33:36.870Z", - "endTime": "2020-03-08T05:33:36.985Z", - "totalTime": 115, - "timeout": 5000 - }, - "requisitions": [] - }, - { - "valid": true, - "name": "examples/store.yml", - "id": "0233360796_7b9aa96592_15280", - "level": 1, - "subscriptions": [], - "publishers": [], - "iteration": 0, - "totalIterations": 1, - "hooks": { - "onInit": { - "valid": true, - "tests": [], - "arguments": { - "elapsedTime": 0 - } + "time": { + "startTime": "2025-03-18T20:55:58.104Z", + "endTime": "2025-03-18T20:55:58.104Z", + "totalTime": 0, + "timeout": 5000 + }, + "tasks": [] }, - "onFinish": { - "valid": true, - "tests": [], - "arguments": { - "elapsedTime": 59 - } - } - }, - "time": { - "startTime": "2020-03-08T05:33:36.871Z", - "endTime": "2020-03-08T05:33:36.930Z", - "totalTime": 59, - "timeout": 5000 - }, - "requisitions": [ { "valid": true, - "name": "Requisition #0", - "id": "0233360796_478dfcaed5_446468", + "name": "iterations", + "id": "1355570989_98e6c02ed8_808806", "level": 2, - "subscriptions": [], - "publishers": [], - "iteration": 0, - "totalIterations": 1, + "sensors": [], + "actuators": [], + "iteration": 8, + "totalIterations": 10, "hooks": { "onInit": { "valid": true, @@ -8413,27 +7854,27 @@ "valid": true, "tests": [], "arguments": { - "elapsedTime": 29 + "elapsedTime": 1 } } }, "time": { - "startTime": "2020-03-08T05:33:36.888Z", - "endTime": "2020-03-08T05:33:36.917Z", - "totalTime": 29, + "startTime": "2025-03-18T20:55:58.104Z", + "endTime": "2025-03-18T20:55:58.105Z", + "totalTime": 1, "timeout": 5000 }, - "requisitions": [] + "tasks": [] }, { "valid": true, - "name": "Requisition #1", - "id": "0233360796_c96621cca9_556304", + "name": "iterations", + "id": "1355570989_98e6c02ed8_808806", "level": 2, - "subscriptions": [], - "publishers": [], - "iteration": 0, - "totalIterations": 1, + "sensors": [], + "actuators": [], + "iteration": 9, + "totalIterations": 10, "hooks": { "onInit": { "valid": true, @@ -8444,38 +7885,27 @@ }, "onFinish": { "valid": true, - "tests": [ - { - "name": "Payload", - "valid": true, - "description": "Expected '123' to be equal to '123'. Received '123'" - }, - { - "name": "Environment Variables added", - "valid": true, - "description": "Expecting 'store.PATH' to be defined" - } - ], + "tests": [], "arguments": { - "elapsedTime": 2 + "elapsedTime": 0 } } }, "time": { - "startTime": "2020-03-08T05:33:36.922Z", - "endTime": "2020-03-08T05:33:36.924Z", - "totalTime": 2, + "startTime": "2025-03-18T20:55:58.105Z", + "endTime": "2025-03-18T20:55:58.105Z", + "totalTime": 0, "timeout": 5000 }, - "requisitions": [] + "tasks": [] }, { "valid": true, - "name": "Requisition #2", - "id": "0233360796_05a830b0a8_964737", + "name": "delayed", + "id": "1355570989_612a4aab97_201415", "level": 2, - "subscriptions": [], - "publishers": [], + "sensors": [], + "actuators": [], "iteration": 0, "totalIterations": 1, "hooks": { @@ -8490,33 +7920,43 @@ "valid": true, "tests": [ { - "name": "Payload", + "name": "It was executed 10 times", "valid": true, - "description": "Expected '124' to be equal to '124'. Received '124'" + "description": "Expected 'task.parent.tasks[0].counter' to be equal to '10'. Received '10'" + }, + { + "name": "Elapsed time", + "valid": true, + "description": "Expected 'elapsedTime' to be greater than or equal to '2950'. Received '3001'" + }, + { + "name": "Assertion #2", + "valid": true, + "description": "Expected 'new Date().getTime() - task.startTime.getTime()' to be greater than or equal to '2950'. Received '3001'" } ], "arguments": { - "elapsedTime": 1 + "elapsedTime": 3001 } } }, "time": { - "startTime": "2020-03-08T05:33:36.927Z", - "endTime": "2020-03-08T05:33:36.928Z", - "totalTime": 1, + "startTime": "2025-03-18T20:55:58.105Z", + "endTime": "2025-03-18T20:56:01.106Z", + "totalTime": 3001, "timeout": 5000 }, - "requisitions": [] + "tasks": [] } ] }, { "valid": true, "name": "examples/tcp.yml", - "id": "0233360796_bf35f3e127_429250", + "id": "1355570989_e7b866b1d6_400567", "level": 1, - "subscriptions": [], - "publishers": [], + "sensors": [], + "actuators": [], "iteration": 0, "totalIterations": 1, "hooks": { @@ -8531,51 +7971,58 @@ "valid": true, "tests": [], "arguments": { - "elapsedTime": 2934 + "elapsedTime": 2925 } } }, "time": { - "startTime": "2020-03-08T05:33:36.873Z", - "endTime": "2020-03-08T05:33:39.807Z", - "totalTime": 2934, + "startTime": "2025-03-18T20:55:58.018Z", + "endTime": "2025-03-18T20:56:00.943Z", + "totalTime": 2925, "timeout": 5000 }, - "requisitions": [ + "tasks": [ { "valid": true, - "name": "Requisition #0", - "id": "0233360796_595f6dfba1_826051", + "name": "Task #0", + "id": "1355570989_aa0a05d694_902568", "level": 2, - "subscriptions": [ + "sensors": [ { - "id": "0233360796_4e5617f719_679309", - "name": "Subscription #0", + "id": "1355570989_4e1b8c622b_900053", + "name": "Sensor #0", "type": "tcp", "hooks": { "onInit": { "valid": true, "tests": [], - "arguments": { - "elapsedTime": 0 - } + "arguments": {} }, "onFinish": { "valid": true, "tests": [ { + "implicit": true, "valid": true, "name": "Message received", - "description": "Subscription has received its message" + "description": "Sensor has received its message" } ], "arguments": { - "executedHooks": [ - "onInit", - "onMessageReceived", - "onFinish" - ], - "elapsedTime": 1105 + "executedHooks": { + "onInit": [], + "onMessageReceived": [ + "payload", + "stream", + "path", + "elapsedTime" + ], + "onFinish": [ + "executedHooks", + "elapsedTime" + ] + }, + "elapsedTime": 1111 } }, "onMessageReceived": { @@ -8589,28 +8036,28 @@ { "name": "Assertion #1", "valid": true, - "description": "Expecting '::ffff:127.0.0.1' (stream.address) to contain '127.0.0.1'" + "description": "Expected 'stream.address' to be any of '['127.0.0.1', ::1]'. Received '::1'" } ], "arguments": { "payload": "Hey Jude", "stream": { - "address": "::ffff:127.0.0.1", + "address": "::1", "family": "IPv6", "port": 23069 }, - "elapsedTime": 106 + "elapsedTime": 130 } } }, "valid": true, - "subscriptionTime": "2020-03-08T05:33:36.951Z" + "sensorTime": "2025-03-18T20:55:58.111Z" } ], - "publishers": [ + "actuators": [ { - "id": "0233360796_3b7a9e3645_11917", - "name": "Publisher #0", + "id": "1355570989_a89f8d87b3_639140", + "name": "Actuator #0", "valid": true, "hooks": { "onInit": { @@ -8622,18 +8069,26 @@ }, "onFinish": { "arguments": { - "executedHooks": [ - "onInit", - "onMessageReceived", - "onFinish" - ], - "elapsedTime": 1105 + "executedHooks": { + "onInit": [], + "onMessageReceived": [ + "payload", + "stream", + "elapsedTime" + ], + "onFinish": [ + "executedHooks", + "elapsedTime" + ] + }, + "elapsedTime": 1111 }, "tests": [ { "name": "Published", "valid": true, - "description": "Published successfully" + "description": "Published successfully", + "implicit": true } ], "valid": true @@ -8642,11 +8097,11 @@ "arguments": { "payload": "Do not make it bad", "stream": { - "address": "127.0.0.1", - "family": "IPv4", - "port": 61963 + "address": "::1", + "family": "IPv6", + "port": 57107 }, - "elapsedTime": 1108 + "elapsedTime": 1114 }, "tests": [ { @@ -8664,7 +8119,7 @@ } }, "type": "tcp", - "publishTime": "2020-03-08T05:33:37.994Z" + "messageSentInstant": "2025-03-18T20:55:59.133Z" } ], "iteration": 0, @@ -8681,51 +8136,58 @@ "valid": true, "tests": [], "arguments": { - "elapsedTime": 1105 + "elapsedTime": 1111 } } }, "time": { - "startTime": "2020-03-08T05:33:36.889Z", - "endTime": "2020-03-08T05:33:37.994Z", - "totalTime": 1105, + "startTime": "2025-03-18T20:55:58.023Z", + "endTime": "2025-03-18T20:55:59.134Z", + "totalTime": 1111, "timeout": 3000 }, - "requisitions": [] + "tasks": [] }, { "valid": true, - "name": "Requisition #1", - "id": "0233360796_3f1b829a68_117288", + "name": "Task #1", + "id": "1355570989_006b92378b_129582", "level": 2, - "subscriptions": [ + "sensors": [ { - "id": "0233360796_944c1488d1_831955", - "name": "Subscription #0", + "id": "1355570989_b4295abd6f_184933", + "name": "Sensor #0", "type": "tcp", "hooks": { "onInit": { "valid": true, "tests": [], - "arguments": { - "elapsedTime": 0 - } + "arguments": {} }, "onFinish": { "valid": true, "tests": [ { + "implicit": true, "valid": true, "name": "Message received", - "description": "Subscription has received its message" + "description": "Sensor has received its message" } ], "arguments": { - "executedHooks": [ - "onInit", - "onMessageReceived", - "onFinish" - ], + "executedHooks": { + "onInit": [], + "onMessageReceived": [ + "payload", + "stream", + "path", + "elapsedTime" + ], + "onFinish": [ + "executedHooks", + "elapsedTime" + ] + }, "elapsedTime": 805 } }, @@ -8741,22 +8203,22 @@ "arguments": { "payload": "I do not care", "stream": { - "address": "::ffff:127.0.0.1", + "address": "::1", "family": "IPv6", "port": 23070 }, - "elapsedTime": 2 + "elapsedTime": 4 } } }, "valid": true, - "subscriptionTime": "2020-03-08T05:33:37.997Z" + "sensorTime": "2025-03-18T20:55:59.136Z" } ], - "publishers": [ + "actuators": [ { - "id": "0233360796_369e07d49e_615425", - "name": "Publisher #0", + "id": "1355570989_cb6fdf14a5_788185", + "name": "Actuator #0", "valid": true, "hooks": { "onInit": { @@ -8768,18 +8230,26 @@ }, "onFinish": { "arguments": { - "executedHooks": [ - "onInit", - "onMessageReceived", - "onFinish" - ], + "executedHooks": { + "onInit": [], + "onMessageReceived": [ + "payload", + "stream", + "elapsedTime" + ], + "onFinish": [ + "executedHooks", + "elapsedTime" + ] + }, "elapsedTime": 805 }, "tests": [ { "name": "Published", "valid": true, - "description": "Published successfully" + "description": "Published successfully", + "implicit": true } ], "valid": true @@ -8788,11 +8258,11 @@ "arguments": { "payload": "EnqueuerRocks", "stream": { - "address": "127.0.0.1", - "family": "IPv4", - "port": 61982 + "address": "::1", + "family": "IPv6", + "port": 57121 }, - "elapsedTime": 1812 + "elapsedTime": 1809 }, "tests": [ { @@ -8810,7 +8280,7 @@ } }, "type": "tcp", - "publishTime": "2020-03-08T05:33:38.801Z" + "messageSentInstant": "2025-03-18T20:55:59.940Z" } ], "iteration": 0, @@ -8832,47 +8302,54 @@ } }, "time": { - "startTime": "2020-03-08T05:33:37.996Z", - "endTime": "2020-03-08T05:33:38.801Z", + "startTime": "2025-03-18T20:55:59.135Z", + "endTime": "2025-03-18T20:55:59.940Z", "totalTime": 805, "timeout": 3000 }, - "requisitions": [] + "tasks": [] }, { "valid": true, - "name": "Requisition #2", - "id": "0233360796_7b3d80f664_265369", + "name": "Task #2", + "id": "1355570989_df4c8be8cb_933160", "level": 2, - "subscriptions": [ + "sensors": [ { - "id": "0233360796_a5c6b9dc6a_622561", - "name": "Subscription #0", + "id": "1355570989_2c1d8ad513_787867", + "name": "Sensor #0", "type": "tcp", "hooks": { "onInit": { "valid": true, "tests": [], - "arguments": { - "elapsedTime": 0 - } + "arguments": {} }, "onFinish": { "valid": true, "tests": [ { + "implicit": true, "valid": true, "name": "Message received", - "description": "Subscription has received its message" + "description": "Sensor has received its message" } ], "arguments": { - "executedHooks": [ - "onInit", - "onMessageReceived", - "onFinish" - ], - "elapsedTime": 1003 + "executedHooks": { + "onInit": [], + "onMessageReceived": [ + "payload", + "stream", + "path", + "elapsedTime" + ], + "onFinish": [ + "executedHooks", + "elapsedTime" + ] + }, + "elapsedTime": 1002 } }, "onMessageReceived": { @@ -8887,7 +8364,7 @@ "arguments": { "payload": "The socket is still open", "stream": { - "address": "::ffff:127.0.0.1", + "address": "::1", "family": "IPv6", "port": 23070 }, @@ -8896,13 +8373,13 @@ } }, "valid": true, - "subscriptionTime": "2020-03-08T05:33:38.804Z" + "sensorTime": "2025-03-18T20:55:59.940Z" } ], - "publishers": [ + "actuators": [ { - "id": "0233360796_c04ec1e8aa_940022", - "name": "Publisher #0", + "id": "1355570989_cce053e941_842824", + "name": "Actuator #0", "valid": true, "hooks": { "onInit": { @@ -8914,18 +8391,26 @@ }, "onFinish": { "arguments": { - "executedHooks": [ - "onInit", - "onMessageReceived", - "onFinish" - ], - "elapsedTime": 1004 + "executedHooks": { + "onInit": [], + "onMessageReceived": [ + "payload", + "stream", + "elapsedTime" + ], + "onFinish": [ + "executedHooks", + "elapsedTime" + ] + }, + "elapsedTime": 1002 }, "tests": [ { "name": "Published", "valid": true, - "description": "Published successfully" + "description": "Published successfully", + "implicit": true } ], "valid": true @@ -8934,11 +8419,11 @@ "arguments": { "payload": "enqueuer Rocks", "stream": { - "address": "127.0.0.1", - "family": "IPv4", - "port": 61982 + "address": "::1", + "family": "IPv6", + "port": 57121 }, - "elapsedTime": 1005 + "elapsedTime": 1004 }, "tests": [ { @@ -8956,7 +8441,7 @@ } }, "type": "tcp", - "publishTime": "2020-03-08T05:33:39.806Z" + "messageSentInstant": "2025-03-18T20:56:00.942Z" } ], "iteration": 0, @@ -8973,27 +8458,27 @@ "valid": true, "tests": [], "arguments": { - "elapsedTime": 1003 + "elapsedTime": 1002 } } }, "time": { - "startTime": "2020-03-08T05:33:38.803Z", - "endTime": "2020-03-08T05:33:39.807Z", - "totalTime": 1004, + "startTime": "2025-03-18T20:55:59.940Z", + "endTime": "2025-03-18T20:56:00.942Z", + "totalTime": 1002, "timeout": 3000 }, - "requisitions": [] + "tasks": [] } ] }, { "valid": true, "name": "examples/udp.yml", - "id": "0233360796_f69fa0cdb2_712296", + "id": "1355570989_84d5864ece_310413", "level": 1, - "subscriptions": [], - "publishers": [], + "sensors": [], + "actuators": [], "iteration": 0, "totalIterations": 1, "hooks": { @@ -9008,51 +8493,57 @@ "valid": true, "tests": [], "arguments": { - "elapsedTime": 115 + "elapsedTime": 111 } } }, "time": { - "startTime": "2020-03-08T05:33:36.874Z", - "endTime": "2020-03-08T05:33:36.989Z", - "totalTime": 115, + "startTime": "2025-03-18T20:55:58.019Z", + "endTime": "2025-03-18T20:55:58.130Z", + "totalTime": 111, "timeout": 5000 }, - "requisitions": [ + "tasks": [ { "valid": true, - "name": "Requisition #0", - "id": "0233360796_ac96cfadf9_902215", + "name": "Task #0", + "id": "1355570989_801e85b90d_697242", "level": 2, - "subscriptions": [ + "sensors": [ { - "id": "0233360796_6e62597dc8_377983", - "name": "subscription description", + "id": "1355570989_a8c5612df5_48918", + "name": "sensor description", "type": "udp", "hooks": { "onInit": { "valid": true, "tests": [], - "arguments": { - "elapsedTime": 0 - } + "arguments": {} }, "onFinish": { "valid": true, "tests": [ { + "implicit": true, "valid": true, "name": "Message received", - "description": "Subscription has received its message" + "description": "Sensor has received its message" } ], "arguments": { - "executedHooks": [ - "onInit", - "onMessageReceived", - "onFinish" - ], - "elapsedTime": 99 + "executedHooks": { + "onInit": [], + "onMessageReceived": [ + "payload", + "remoteInfo", + "elapsedTime" + ], + "onFinish": [ + "executedHooks", + "elapsedTime" + ] + }, + "elapsedTime": 107 } }, "onMessageReceived": { @@ -9080,21 +8571,21 @@ "remoteInfo": { "address": "127.0.0.1", "family": "IPv4", - "port": 64082, + "port": 57685, "size": 2 }, - "elapsedTime": 98 + "elapsedTime": 106 } } }, "valid": true, - "subscriptionTime": "2020-03-08T05:33:36.914Z" + "sensorTime": "2025-03-18T20:55:58.067Z" } ], - "publishers": [ + "actuators": [ { - "id": "0233360796_f1c5db9ba9_424777", - "name": "publisher description", + "id": "1355570989_2f217fdcff_677702", + "name": "actuator description", "valid": true, "hooks": { "onInit": { @@ -9106,24 +8597,28 @@ }, "onFinish": { "arguments": { - "executedHooks": [ - "onInit", - "onFinish" - ], - "elapsedTime": 99 + "executedHooks": { + "onInit": [], + "onFinish": [ + "executedHooks", + "elapsedTime" + ] + }, + "elapsedTime": 107 }, "tests": [ { "name": "Published", "valid": true, - "description": "Published successfully" + "description": "Published successfully", + "implicit": true } ], "valid": true } }, "type": "udp", - "publishTime": "2020-03-08T05:33:36.960Z" + "messageSentInstant": "2025-03-18T20:55:58.125Z" } ], "iteration": 0, @@ -9140,27 +8635,27 @@ "valid": true, "tests": [], "arguments": { - "elapsedTime": 99 + "elapsedTime": 107 } } }, "time": { - "startTime": "2020-03-08T05:33:36.890Z", - "endTime": "2020-03-08T05:33:36.989Z", - "totalTime": 99, + "startTime": "2025-03-18T20:55:58.023Z", + "endTime": "2025-03-18T20:55:58.130Z", + "totalTime": 107, "timeout": 5000 }, - "requisitions": [] + "tasks": [] } ] }, { "valid": true, "name": "examples/uds.yml", - "id": "0233360796_c449648554_690772", + "id": "1355570989_8525788e48_714984", "level": 1, - "subscriptions": [], - "publishers": [], + "sensors": [], + "actuators": [], "iteration": 0, "totalIterations": 1, "hooks": { @@ -9175,51 +8670,58 @@ "valid": true, "tests": [], "arguments": { - "elapsedTime": 5107 + "elapsedTime": 7118 } } }, "time": { - "startTime": "2020-03-08T05:33:36.877Z", - "endTime": "2020-03-08T05:33:41.984Z", - "totalTime": 5107, - "timeout": 7000 + "startTime": "2025-03-18T20:55:58.019Z", + "endTime": "2025-03-18T20:56:05.137Z", + "totalTime": 7118, + "timeout": 10000 }, - "requisitions": [ + "tasks": [ { "valid": true, - "name": "Requisition #0", - "id": "0233360796_7acc07c435_931555", + "name": "Task #0", + "id": "1355570989_f6ae2177d8_761854", "level": 2, - "subscriptions": [ + "sensors": [ { - "id": "0233360797_5e03c0017a_298828", - "name": "Subscription #0", + "id": "1355570989_fbae96be75_499663", + "name": "Sensor #0", "type": "uds", "hooks": { "onInit": { "valid": true, "tests": [], - "arguments": { - "elapsedTime": 0 - } + "arguments": {} }, "onFinish": { "valid": true, "tests": [ { + "implicit": true, "valid": true, "name": "Message received", - "description": "Subscription has received its message" + "description": "Sensor has received its message" } ], "arguments": { - "executedHooks": [ - "onInit", - "onMessageReceived", - "onFinish" - ], - "elapsedTime": 2074 + "executedHooks": { + "onInit": [], + "onMessageReceived": [ + "payload", + "stream", + "path", + "elapsedTime" + ], + "onFinish": [ + "executedHooks", + "elapsedTime" + ] + }, + "elapsedTime": 3106 } }, "onMessageReceived": { @@ -9240,18 +8742,18 @@ "payload": "enqueuer", "stream": {}, "path": "/tmp/unix.sock", - "elapsedTime": 100 + "elapsedTime": 124 } } }, "valid": true, - "subscriptionTime": "2020-03-08T05:33:36.951Z" + "sensorTime": "2025-03-18T20:55:58.111Z" } ], - "publishers": [ + "actuators": [ { - "id": "0233360796_b14ca3892b_776328", - "name": "Publisher #0", + "id": "1355570989_b345b00bc1_962895", + "name": "Actuator #0", "valid": true, "hooks": { "onInit": { @@ -9263,18 +8765,26 @@ }, "onFinish": { "arguments": { - "executedHooks": [ - "onInit", - "onMessageReceived", - "onFinish" - ], - "elapsedTime": 2074 + "executedHooks": { + "onInit": [], + "onMessageReceived": [ + "payload", + "stream", + "elapsedTime" + ], + "onFinish": [ + "executedHooks", + "elapsedTime" + ] + }, + "elapsedTime": 3106 }, "tests": [ { "name": "Published", "valid": true, - "description": "Published successfully" + "description": "Published successfully", + "implicit": true } ], "valid": true @@ -9283,7 +8793,7 @@ "arguments": { "payload": "responsePayload", "stream": {}, - "elapsedTime": 2077 + "elapsedTime": 3107 }, "tests": [ { @@ -9301,7 +8811,7 @@ } }, "type": "uds", - "publishTime": "2020-03-08T05:33:38.964Z" + "messageSentInstant": "2025-03-18T20:56:01.129Z" } ], "iteration": 0, @@ -9318,52 +8828,59 @@ "valid": true, "tests": [], "arguments": { - "elapsedTime": 2074 + "elapsedTime": 3106 } } }, "time": { - "startTime": "2020-03-08T05:33:36.891Z", - "endTime": "2020-03-08T05:33:38.965Z", - "totalTime": 2074, + "startTime": "2025-03-18T20:55:58.024Z", + "endTime": "2025-03-18T20:56:01.130Z", + "totalTime": 3106, "timeout": 5000 }, - "requisitions": [] + "tasks": [] }, { "valid": true, - "name": "Requisition #1", - "id": "0233360797_591e70c91e_492945", + "name": "Task #1", + "id": "1355570989_d7d08996c9_271155", "level": 2, - "subscriptions": [ + "sensors": [ { - "id": "0233360797_81ccf3e7f8_794394", - "name": "Subscription #0", + "id": "1355570989_70b5db124e_836654", + "name": "Sensor #0", "type": "uds", "hooks": { "onInit": { "valid": true, "tests": [], - "arguments": { - "elapsedTime": 0 - } + "arguments": {} }, "onFinish": { "valid": true, "tests": [ { + "implicit": true, "valid": true, "name": "Message received", - "description": "Subscription has received its message" + "description": "Sensor has received its message" } ], "arguments": { - "executedHooks": [ - "onInit", - "onMessageReceived", - "onFinish" - ], - "elapsedTime": 1005 + "executedHooks": { + "onInit": [], + "onMessageReceived": [ + "payload", + "stream", + "path", + "elapsedTime" + ], + "onFinish": [ + "executedHooks", + "elapsedTime" + ] + }, + "elapsedTime": 1004 } }, "onMessageReceived": { @@ -9384,13 +8901,13 @@ } }, "valid": true, - "subscriptionTime": "2020-03-08T05:33:38.967Z" + "sensorTime": "2025-03-18T20:56:01.131Z" } ], - "publishers": [ + "actuators": [ { - "id": "0233360797_297aae1eb3_710383", - "name": "Publisher #0", + "id": "1355570989_bfdf7072a9_421839", + "name": "Actuator #0", "valid": true, "hooks": { "onInit": { @@ -9401,31 +8918,35 @@ { "name": "Assertion #0", "valid": true, - "description": "Expected 'publisher.payload' to be equal to 'enqueuer'. Received 'enqueuer'" + "description": "Expected 'actuator.payload' to be equal to 'enqueuer'. Received 'enqueuer'" } ], "valid": true }, "onFinish": { "arguments": { - "executedHooks": [ - "onInit", - "onFinish" - ], - "elapsedTime": 1005 + "executedHooks": { + "onInit": [], + "onFinish": [ + "executedHooks", + "elapsedTime" + ] + }, + "elapsedTime": 1004 }, "tests": [ { "name": "Published", "valid": true, - "description": "Published successfully" + "description": "Published successfully", + "implicit": true } ], "valid": true } }, "type": "uds", - "publishTime": "2020-03-08T05:33:39.972Z" + "messageSentInstant": "2025-03-18T20:56:02.133Z" } ], "iteration": 0, @@ -9442,52 +8963,59 @@ "valid": true, "tests": [], "arguments": { - "elapsedTime": 1005 + "elapsedTime": 1004 } } }, "time": { - "startTime": "2020-03-08T05:33:38.967Z", - "endTime": "2020-03-08T05:33:39.972Z", - "totalTime": 1005, + "startTime": "2025-03-18T20:56:01.130Z", + "endTime": "2025-03-18T20:56:02.134Z", + "totalTime": 1004, "timeout": 5000 }, - "requisitions": [] + "tasks": [] }, { "valid": true, - "name": "Requisition #2", - "id": "0233360797_3321951a43_659411", + "name": "Task #2", + "id": "1355570989_419ce8cd57_868213", "level": 2, - "subscriptions": [ + "sensors": [ { - "id": "0233360797_d308bbf817_967145", - "name": "Subscription #0", + "id": "1355570989_a7b5fb04f1_894816", + "name": "Sensor #0", "type": "uds", "hooks": { "onInit": { "valid": true, "tests": [], - "arguments": { - "elapsedTime": 0 - } + "arguments": {} }, "onFinish": { "valid": true, "tests": [ { + "implicit": true, "valid": true, "name": "Message received", - "description": "Subscription has received its message" + "description": "Sensor has received its message" } ], "arguments": { - "executedHooks": [ - "onInit", - "onMessageReceived", - "onFinish" - ], - "elapsedTime": 2010 + "executedHooks": { + "onInit": [], + "onMessageReceived": [ + "payload", + "stream", + "path", + "elapsedTime" + ], + "onFinish": [ + "executedHooks", + "elapsedTime" + ] + }, + "elapsedTime": 3002 } }, "onMessageReceived": { @@ -9502,18 +9030,18 @@ "arguments": { "payload": "I am still opened", "stream": {}, - "elapsedTime": 3 + "elapsedTime": 1 } } }, "valid": true, - "subscriptionTime": "2020-03-08T05:33:39.975Z" + "sensorTime": "2025-03-18T20:56:02.135Z" } ], - "publishers": [ + "actuators": [ { - "id": "0233360797_b6920facd0_475742", - "name": "Publisher #0", + "id": "1355570989_97331480ef_87367", + "name": "Actuator #0", "valid": true, "hooks": { "onInit": { @@ -9525,18 +9053,26 @@ }, "onFinish": { "arguments": { - "executedHooks": [ - "onInit", - "onMessageReceived", - "onFinish" - ], - "elapsedTime": 2010 + "executedHooks": { + "onInit": [], + "onMessageReceived": [ + "payload", + "stream", + "elapsedTime" + ], + "onFinish": [ + "executedHooks", + "elapsedTime" + ] + }, + "elapsedTime": 3002 }, "tests": [ { "name": "Published", "valid": true, - "description": "Published successfully" + "description": "Published successfully", + "implicit": true } ], "valid": true @@ -9545,7 +9081,7 @@ "arguments": { "payload": "I am still bidirectional", "stream": {}, - "elapsedTime": 2008 + "elapsedTime": 3001 }, "tests": [ { @@ -9558,7 +9094,7 @@ } }, "type": "uds", - "publishTime": "2020-03-08T05:33:41.982Z" + "messageSentInstant": "2025-03-18T20:56:05.137Z" } ], "iteration": 0, @@ -9575,27 +9111,27 @@ "valid": true, "tests": [], "arguments": { - "elapsedTime": 2010 + "elapsedTime": 3002 } } }, "time": { - "startTime": "2020-03-08T05:33:39.974Z", - "endTime": "2020-03-08T05:33:41.984Z", - "totalTime": 2010, + "startTime": "2025-03-18T20:56:02.135Z", + "endTime": "2025-03-18T20:56:05.137Z", + "totalTime": 3002, "timeout": 5000 }, - "requisitions": [] + "tasks": [] } ] }, { "valid": true, "name": "examples/variables.yml", - "id": "0233360797_3a6eb18d6d_491636", + "id": "1355570989_a267a90add_578473", "level": 1, - "subscriptions": [], - "publishers": [], + "sensors": [], + "actuators": [], "iteration": 0, "totalIterations": 1, "hooks": { @@ -9610,24 +9146,24 @@ "valid": true, "tests": [], "arguments": { - "elapsedTime": 50 + "elapsedTime": 81 } } }, "time": { - "startTime": "2020-03-08T05:33:36.877Z", - "endTime": "2020-03-08T05:33:36.928Z", - "totalTime": 51, + "startTime": "2025-03-18T20:55:58.019Z", + "endTime": "2025-03-18T20:55:58.100Z", + "totalTime": 81, "timeout": 5000 }, - "requisitions": [ + "tasks": [ { "valid": true, - "name": "Requisition #0", - "id": "0233360797_bdf0ac0681_548015", + "name": "Task #0", + "id": "1355570989_1d45118746_81375", "level": 2, - "subscriptions": [], - "publishers": [], + "sensors": [], + "actuators": [], "iteration": 0, "totalIterations": 1, "hooks": { @@ -9653,25 +9189,25 @@ } ], "arguments": { - "elapsedTime": 26 + "elapsedTime": 70 } } }, "time": { - "startTime": "2020-03-08T05:33:36.891Z", - "endTime": "2020-03-08T05:33:36.917Z", - "totalTime": 26, + "startTime": "2025-03-18T20:55:58.024Z", + "endTime": "2025-03-18T20:55:58.095Z", + "totalTime": 71, "timeout": 5000 }, - "requisitions": [] + "tasks": [] }, { "valid": true, - "name": "Requisition #1", - "id": "0233360797_5153bc0c75_721312", + "name": "Task #1", + "id": "1355570989_3baa6689ee_968319", "level": 2, - "subscriptions": [], - "publishers": [], + "sensors": [], + "actuators": [], "iteration": 0, "totalIterations": 1, "hooks": { @@ -9681,7 +9217,7 @@ { "name": "Assertion #0", "valid": true, - "description": "Expected 'requisition.anyName' to be equal to '10'. Received '10'" + "description": "Expected 'task.anyName' to be equal to '10'. Received '10'" }, { "name": "Assertion #1", @@ -9702,27 +9238,27 @@ "valid": true, "tests": [], "arguments": { - "elapsedTime": 2 + "elapsedTime": 1 } } }, "time": { - "startTime": "2020-03-08T05:33:36.922Z", - "endTime": "2020-03-08T05:33:36.924Z", - "totalTime": 2, + "startTime": "2025-03-18T20:55:58.098Z", + "endTime": "2025-03-18T20:55:58.099Z", + "totalTime": 1, "timeout": 5000 }, - "requisitions": [] + "tasks": [] } ] }, { "valid": true, "name": "examples/number.json", - "id": "0233360797_1925b227c5_645737", + "id": "1355570989_fa8fbbf66f_919281", "level": 1, - "subscriptions": [], - "publishers": [], + "sensors": [], + "actuators": [], "iteration": 0, "totalIterations": 1, "hooks": { @@ -9732,12 +9268,12 @@ { "name": "Assertion #0", "valid": true, - "description": "Expected 'typeof requisition.number' to be equal to 'number'. Received 'number'" + "description": "Expected 'typeof task.number' to be equal to 'number'. Received 'number'" }, { "name": "Assertion #1", "valid": true, - "description": "Expected 'requisition.number' to be equal to '123'. Received '123'" + "description": "Expected 'task.number' to be equal to '123'. Received '123'" } ], "arguments": { @@ -9748,25 +9284,25 @@ "valid": true, "tests": [], "arguments": { - "elapsedTime": 39 + "elapsedTime": 75 } } }, "time": { - "startTime": "2020-03-08T05:33:36.878Z", - "endTime": "2020-03-08T05:33:36.917Z", - "totalTime": 39, + "startTime": "2025-03-18T20:55:58.019Z", + "endTime": "2025-03-18T20:55:58.095Z", + "totalTime": 76, "timeout": 5000 }, - "requisitions": [] + "tasks": [] }, { "valid": true, - "name": "examples/requisition-navigation.yaml", - "id": "0233360797_e848774ac1_305346", + "name": "examples/task-navigation.yaml", + "id": "1355570989_006e246507_557339", "level": 1, - "subscriptions": [], - "publishers": [], + "sensors": [], + "actuators": [], "iteration": 0, "totalIterations": 1, "hooks": { @@ -9781,24 +9317,24 @@ "valid": true, "tests": [], "arguments": { - "elapsedTime": 50 + "elapsedTime": 80 } } }, "time": { - "startTime": "2020-03-08T05:33:36.878Z", - "endTime": "2020-03-08T05:33:36.928Z", - "totalTime": 50, + "startTime": "2025-03-18T20:55:58.020Z", + "endTime": "2025-03-18T20:55:58.100Z", + "totalTime": 80, "timeout": 5000 }, - "requisitions": [ + "tasks": [ { "valid": true, "name": "first", - "id": "0233360797_542da8f206_750326", + "id": "1355570990_dba1d7182d_939313", "level": 2, - "subscriptions": [], - "publishers": [], + "sensors": [], + "actuators": [], "iteration": 0, "totalIterations": 1, "hooks": { @@ -9808,7 +9344,7 @@ { "name": "Assertion #0", "valid": true, - "description": "Expected 'requisition.parent.requisitions[1].name' to be equal to 'second'. Received 'second'" + "description": "Expected 'task.parent.tasks[1].name' to be equal to 'second'. Received 'second'" } ], "arguments": { @@ -9819,25 +9355,25 @@ "valid": true, "tests": [], "arguments": { - "elapsedTime": 25 + "elapsedTime": 71 } } }, "time": { - "startTime": "2020-03-08T05:33:36.892Z", - "endTime": "2020-03-08T05:33:36.917Z", - "totalTime": 25, + "startTime": "2025-03-18T20:55:58.024Z", + "endTime": "2025-03-18T20:55:58.095Z", + "totalTime": 71, "timeout": 5000 }, - "requisitions": [] + "tasks": [] }, { "valid": true, "name": "second", - "id": "0233360797_d7c80e2b4e_252247", + "id": "1355570990_8b8ad27229_816266", "level": 2, - "subscriptions": [], - "publishers": [], + "sensors": [], + "actuators": [], "iteration": 0, "totalIterations": 1, "hooks": { @@ -9847,7 +9383,7 @@ { "name": "Assertion #0", "valid": true, - "description": "Expected 'requisition.parent.requisitions[0].name' to be equal to 'first'. Received 'first'" + "description": "Expected 'task.parent.tasks[0].name' to be equal to 'first'. Received 'first'" } ], "arguments": { @@ -9863,12 +9399,12 @@ } }, "time": { - "startTime": "2020-03-08T05:33:36.923Z", - "endTime": "2020-03-08T05:33:36.924Z", + "startTime": "2025-03-18T20:55:58.098Z", + "endTime": "2025-03-18T20:55:58.099Z", "totalTime": 1, "timeout": 5000 }, - "requisitions": [] + "tasks": [] } ] } diff --git a/output/examples.yml b/output/examples.yml index 01077867..25432ac6 100644 --- a/output/examples.yml +++ b/output/examples.yml @@ -1,9 +1,9 @@ valid: true name: enqueuer -id: 0233360790_44deaa57a3_629459 +id: 1355570986_3b99cdfebb_733514 level: 0 -subscriptions: [] -publishers: [] +sensors: [] +actuators: [] iteration: 0 totalIterations: 1 hooks: @@ -16,22 +16,52 @@ hooks: valid: true tests: [] arguments: - elapsedTime: 5156 + elapsedTime: 7136 onParsed: valid: true tests: [] time: - startTime: '2020-03-08T05:33:36.829Z' - endTime: '2020-03-08T05:33:41.985Z' - totalTime: 5156 -requisitions: + startTime: '2025-03-18T20:55:58.002Z' + endTime: '2025-03-18T20:56:05.138Z' + totalTime: 7136 +tasks: + - + valid: true + name: examples/argumentNames.yml + id: 1355570986_bd8a0b0ab4_928299 + level: 1 + sensors: [] + actuators: [] + iteration: 0 + totalIterations: 1 + hooks: + onInit: + valid: true + tests: [] + arguments: + elapsedTime: 0 + onFinish: + valid: true + tests: + - + name: 'Assertion #0' + valid: true + description: "Expected '\"argumentNames\"' to be equal to '[\n \"task\",\n \"elapsedTime\"\n]'. Received '[\n \"task\",\n \"elapsedTime\"\n]'" + arguments: + elapsedTime: 90 + time: + startTime: '2025-03-18T20:55:58.003Z' + endTime: '2025-03-18T20:55:58.094Z' + totalTime: 91 + timeout: 5000 + tasks: [] - valid: true name: examples/assertions.yml - id: 0233360790_2ca609e341_792411 + id: 1355570986_38d38742c0_542950 level: 1 - subscriptions: [] - publishers: [] + sensors: [] + actuators: [] iteration: 0 totalIterations: 1 hooks: @@ -41,11 +71,11 @@ requisitions: - name: 'Assertion #0' valid: true - description: 'Expecting ''requisition.definedVariable'' to be defined' + description: 'Expecting ''task.definedVariable'' to be defined' - name: 'Assertion #1' valid: true - description: 'Expecting ''requisition.undefinedVariable'' to be undefined. Received: undefined' + description: 'Expecting ''task.undefinedVariable'' to be undefined. Received: undefined' - name: 'Assertion #2' valid: true @@ -89,11 +119,11 @@ requisitions: - name: 'Assertion #12' valid: true - description: 'Expecting ''enqueuer'' (`enqueuer`) to contain ''queue''' + description: 'Expecting ''enqueuer'' (''enqueuer'') to contain ''queue''' - name: 'Assertion #13' valid: true - description: 'Expecting ''enqueuer'' (`enqueuer`) not to contain ''nqr''' + description: 'Expecting ''enqueuer'' (''enqueuer'') not to contain ''nqr''' - name: 'Assertion #14' valid: true @@ -114,74 +144,86 @@ requisitions: name: 'Assertion #18' valid: true description: 'Expected ''1'' not to be equal to ''0''. Received ''1''' + - + name: 'Assertion #19' + valid: true + description: 'Expected ''''blue'''' to be any of ''[red, green, blue]''. Received ''blue''' + - + name: 'Assertion #20' + valid: true + description: 'Expected ''''yellow'''' not to be any of ''[red, green, blue]''. Received ''yellow''' arguments: elapsedTime: 0 onFinish: valid: true tests: [] arguments: - elapsedTime: 85 + elapsedTime: 90 time: - startTime: '2020-03-08T05:33:36.831Z' - endTime: '2020-03-08T05:33:36.917Z' - totalTime: 86 + startTime: '2025-03-18T20:55:58.004Z' + endTime: '2025-03-18T20:55:58.094Z' + totalTime: 90 timeout: 5000 - requisitions: [] + tasks: [] - valid: true name: examples/avoid.yml - id: 0233360790_852d538668_983762 + id: 1355570987_4f0a9a3a43_363429 level: 1 - subscriptions: + sensors: - - id: 0233360790_2e0cc1fd7c_358902 - name: 'Subscription #0' + id: 1355570987_cd969fe2c2_864102 + name: 'Sensor #0' type: tcp hooks: onInit: valid: true tests: [] - arguments: - elapsedTime: 0 + arguments: {} onFinish: valid: true tests: - valid: true - name: 'Subscription avoided' - description: 'Avoidable subscription has not received any message' + implicit: true + name: 'Sensor avoided' + description: 'Avoidable sensor has not received any message' arguments: executedHooks: - - onInit - - onFinish - elapsedTime: 3059 + onInit: [] + onFinish: + - executedHooks + - elapsedTime + elapsedTime: 3020 valid: true - subscriptionTime: '2020-03-08T05:33:36.951Z' + sensorTime: '2025-03-18T20:55:58.111Z' - - id: 0233360791_8444956d6a_667353 - name: 'Subscription #1' + id: 1355570987_955bbbf611_959597 + name: 'Sensor #1' type: HTTP hooks: onInit: valid: true tests: [] - arguments: - elapsedTime: 0 + arguments: {} onFinish: valid: true tests: - valid: true - name: 'Subscription avoided' - description: 'Avoidable subscription has not received any message' + implicit: true + name: 'Sensor avoided' + description: 'Avoidable sensor has not received any message' arguments: executedHooks: - - onInit - - onFinish - elapsedTime: 3058 - valid: true - subscriptionTime: '2020-03-08T05:33:36.951Z' - publishers: [] + onInit: [] + onFinish: + - executedHooks + - elapsedTime + elapsedTime: 3019 + valid: true + sensorTime: '2025-03-18T20:55:58.111Z' + actuators: [] iteration: 0 totalIterations: 1 hooks: @@ -194,20 +236,20 @@ requisitions: valid: true tests: [] arguments: - elapsedTime: 3059 + elapsedTime: 3020 time: - startTime: '2020-03-08T05:33:36.834Z' - endTime: '2020-03-08T05:33:39.894Z' - totalTime: 3060 + startTime: '2025-03-18T20:55:58.005Z' + endTime: '2025-03-18T20:56:01.025Z' + totalTime: 3020 timeout: 5000 - requisitions: [] + tasks: [] - valid: true name: examples/crypto-require.yml - id: 0233360791_1d33e31a27_738675 + id: 1355570987_56a8860916_14740 level: 1 - subscriptions: [] - publishers: [] + sensors: [] + actuators: [] iteration: 0 totalIterations: 1 hooks: @@ -222,44 +264,49 @@ requisitions: - name: 'Assertion #0' valid: true - description: 'Expected ''requisition.toEncrypt'' to be equal to ''7aad9a1a6a91e0f18c417cb3aa0e0217b283778e636c580509e494eeec1472e0''. Received ''7aad9a1a6a91e0f18c417cb3aa0e0217b283778e636c580509e494eeec1472e0''' + description: 'Expected ''task.toEncrypt'' to be equal to ''68fa8a566bf6c20d2c1f93ab4c71c224032850e7a6fd4c29e59681b3f7937c2f''. Received ''68fa8a566bf6c20d2c1f93ab4c71c224032850e7a6fd4c29e59681b3f7937c2f''' arguments: - elapsedTime: 81 + elapsedTime: 88 time: - startTime: '2020-03-08T05:33:36.835Z' - endTime: '2020-03-08T05:33:36.917Z' - totalTime: 82 + startTime: '2025-03-18T20:55:58.006Z' + endTime: '2025-03-18T20:55:58.094Z' + totalTime: 88 timeout: 5000 - requisitions: [] + tasks: [] - valid: true name: examples/custom.yml - id: 0233360791_6da5232086_556677 + id: 1355570987_4c3cd94026_865882 level: 1 - subscriptions: + sensors: - - id: 0233360791_f6315a7fbc_215435 - name: 'subscription description' + id: 1355570987_3ec24b875c_165075 + name: 'sensor description' type: custom hooks: onInit: valid: true tests: [] - arguments: - elapsedTime: 0 + arguments: {} onFinish: valid: true tests: - + implicit: true valid: true name: 'Message received' - description: 'Subscription has received its message' + description: 'Sensor has received its message' arguments: executedHooks: - - onInit - - onMessageReceived - - onFinish - elapsedTime: 151 + onInit: [] + onMessageReceived: + - payload + - remoteInfo + - elapsedTime + onFinish: + - executedHooks + - elapsedTime + elapsedTime: 123 onMessageReceived: valid: true tests: @@ -280,15 +327,15 @@ requisitions: remoteInfo: address: 127.0.0.1 family: IPv4 - port: 63327 + port: 61322 size: 2 - elapsedTime: 150 + elapsedTime: 123 valid: true - subscriptionTime: '2020-03-08T05:33:36.912Z' - publishers: + sensorTime: '2025-03-18T20:55:58.067Z' + actuators: - - id: 0233360791_816cec1424_302873 - name: 'publisher description' + id: 1355570987_235ae67dd8_750350 + name: 'actuator description' valid: true hooks: onInit: @@ -299,17 +346,20 @@ requisitions: onFinish: arguments: executedHooks: - - onInit - - onFinish - elapsedTime: 151 + onInit: [] + onFinish: + - executedHooks + - elapsedTime + elapsedTime: 122 tests: - name: Published valid: true description: 'Published successfully' + implicit: true valid: true type: custom - publishTime: '2020-03-08T05:33:36.960Z' + messageSentInstant: '2025-03-18T20:55:58.125Z' iteration: 0 totalIterations: 1 hooks: @@ -322,20 +372,20 @@ requisitions: valid: true tests: [] arguments: - elapsedTime: 152 + elapsedTime: 123 time: - startTime: '2020-03-08T05:33:36.836Z' - endTime: '2020-03-08T05:33:36.988Z' - totalTime: 152 + startTime: '2025-03-18T20:55:58.006Z' + endTime: '2025-03-18T20:55:58.129Z' + totalTime: 123 timeout: 5000 - requisitions: [] + tasks: [] - valid: true name: examples/file-placeholder.yml - id: 0233360791_a5f62352f4_516197 + id: 1355570987_70295ec20e_421976 level: 1 - subscriptions: [] - publishers: [] + sensors: [] + actuators: [] iteration: 0 totalIterations: 1 hooks: @@ -348,20 +398,20 @@ requisitions: valid: true tests: [] arguments: - elapsedTime: 88 + elapsedTime: 92 time: - startTime: '2020-03-08T05:33:36.839Z' - endTime: '2020-03-08T05:33:36.928Z' - totalTime: 89 + startTime: '2025-03-18T20:55:58.008Z' + endTime: '2025-03-18T20:55:58.100Z' + totalTime: 92 timeout: 5000 - requisitions: + tasks: - valid: true - name: 'Requisition #0' - id: 0233360791_374c18eaf8_811103 + name: 'Task #0' + id: 1355570987_6cb2ec3367_261969 level: 2 - subscriptions: [] - publishers: [] + sensors: [] + actuators: [] iteration: 0 totalIterations: 1 hooks: @@ -371,39 +421,39 @@ requisitions: - name: 'fileRead (yml)' valid: true - description: 'Expected ''requisition.yml.key'' to be equal to ''I''m persisted in config file''. Received ''I''m persisted in config file''' + description: 'Expected ''task.yml.key'' to be equal to ''ymlValue''. Received ''ymlValue''' - name: 'fileRead (.json)' valid: true - description: 'Expected ''requisition.json.key'' to be equal to ''value''. Received ''value''' + description: 'Expected ''task.json.key'' to be equal to ''value''. Received ''value''' - name: 'fileRead (.csv)' valid: true - description: 'Expected ''requisition.csv[1].title'' to be equal to ''secondRow''. Received ''secondRow''' + description: 'Expected ''task.csv[1].title'' to be equal to ''secondRow''. Received ''secondRow''' - name: 'fileRead (.js)' valid: true - description: 'Expected ''requisition.javascript(20)'' to be equal to ''40''. Received ''40''' + description: 'Expected ''task.javascript(20)'' to be equal to ''40''. Received ''40''' arguments: elapsedTime: 0 onFinish: valid: true tests: [] arguments: - elapsedTime: 38 + elapsedTime: 74 time: - startTime: '2020-03-08T05:33:36.879Z' - endTime: '2020-03-08T05:33:36.917Z' - totalTime: 38 + startTime: '2025-03-18T20:55:58.020Z' + endTime: '2025-03-18T20:55:58.095Z' + totalTime: 75 timeout: 5000 - requisitions: [] + tasks: [] - valid: true - name: 'Requisition #1' - id: 0233360791_8067e3ed86_612706 + name: 'Task #1' + id: 1355570987_c473d27cb3_918157 level: 2 - subscriptions: [] - publishers: [] + sensors: [] + actuators: [] iteration: 0 totalIterations: 1 hooks: @@ -413,60 +463,68 @@ requisitions: - name: 'it''s not stored' valid: true - description: 'Expecting ''requisition.yml'' to be undefined. Received: undefined' + description: 'Expecting ''task.yml'' to be undefined. Received: undefined' arguments: elapsedTime: 0 onFinish: valid: true tests: [] arguments: - elapsedTime: 4 + elapsedTime: 2 time: - startTime: '2020-03-08T05:33:36.920Z' - endTime: '2020-03-08T05:33:36.924Z' - totalTime: 4 + startTime: '2025-03-18T20:55:58.097Z' + endTime: '2025-03-18T20:55:58.099Z' + totalTime: 2 timeout: 5000 - requisitions: [] + tasks: [] - valid: true name: examples/file.yml - id: 0233360791_d7a8d31f32_110495 + id: 1355570987_b3282d2997_469801 level: 1 - subscriptions: + sensors: - - id: 0233360791_3dd85e61fc_645581 - name: 'subscription description' + id: 1355570987_c27b643ad5_221057 + name: 'sensor description' type: file-system-watcher hooks: onInit: valid: true tests: [] - arguments: - elapsedTime: 1 + arguments: {} onFinish: valid: true tests: - + implicit: true valid: true name: 'Message received' - description: 'Subscription has received its message' + description: 'Sensor has received its message' arguments: executedHooks: - - onInit - - onMessageReceived - - onFinish - elapsedTime: 143 + onInit: [] + onMessageReceived: + - content + - name + - size + - modified + - created + - elapsedTime + onFinish: + - executedHooks + - elapsedTime + elapsedTime: 142 onMessageReceived: valid: true tests: - name: 'Some time has passed' valid: true - description: 'Expected ''now'' to be greater than or equal to ''1583645616841''. Received ''1583645616982''' + description: 'Expected ''now'' to be greater than or equal to ''1724908054863''. Received ''1742331358150''' - name: Filename valid: true - description: 'Expecting ''temp/fileTest0233360915_5a0f35cf7e_347122.file'' (name) to contain ''temp/''' + description: 'Expecting ''temp/fileTest2207340943_2b3564f8c7_709692.file'' (name) to contain ''temp/''' - name: Content valid: true @@ -484,18 +542,18 @@ requisitions: valid: true description: 'Expected ''size'' not to be greater than ''0''. Received ''13''' arguments: - content: '1583645616841' - name: temp/fileTest0233360915_5a0f35cf7e_347122.file + content: '1724908054863' + name: temp/fileTest2207340943_2b3564f8c7_709692.file size: 13 - modified: '2020-03-08T05:33:36.916Z' - created: '2020-03-08T05:33:36.916Z' - elapsedTime: 141 + modified: '2024-08-29T05:07:34.944Z' + created: '2024-08-29T05:07:34.944Z' + elapsedTime: 142 valid: true - subscriptionTime: '2020-03-08T05:33:36.910Z' - publishers: + sensorTime: '2025-03-18T20:55:58.064Z' + actuators: - - id: 0233360791_4b5a457015_558435 - name: 'publisher description' + id: 1355570987_2fbdddbfc9_782373 + name: 'actuator description' valid: true hooks: onInit: @@ -506,17 +564,20 @@ requisitions: onFinish: arguments: executedHooks: - - onInit - - onFinish + onInit: [] + onFinish: + - executedHooks + - elapsedTime elapsedTime: 142 tests: - name: Published valid: true description: 'Published successfully' + implicit: true valid: true type: file - publishTime: '2020-03-08T05:33:36.917Z' + messageSentInstant: '2025-03-18T20:55:58.091Z' iteration: 0 totalIterations: 1 hooks: @@ -529,82 +590,88 @@ requisitions: valid: true tests: [] arguments: - elapsedTime: 143 + elapsedTime: 142 time: - startTime: '2020-03-08T05:33:36.840Z' - endTime: '2020-03-08T05:33:36.984Z' - totalTime: 144 + startTime: '2025-03-18T20:55:58.008Z' + endTime: '2025-03-18T20:55:58.150Z' + totalTime: 142 timeout: 3000 - requisitions: [] + tasks: [] - valid: true name: examples/hooks.yml - id: 0233360791_75b8872357_380660 + id: 1355570987_4671381b7a_13072 level: 1 - subscriptions: + sensors: - - id: 0233360791_fbe2d8d804_24917 - name: 'Subscription #0' + id: 1355570987_c997e9cb93_291457 + name: 'Sensor #0' type: tcp hooks: onInit: valid: true tests: - - name: 'Subscription exists onInit' + name: 'Sensor exists onInit' valid: true - description: 'Expecting ''subscription'' to be defined' + description: 'Expecting ''sensor'' to be defined' - name: 'Assertion #1' valid: true description: 'Expected ''this.name'' to be equal to ''changed''. Received ''changed''' - - name: 'Subscription name' + name: 'Sensor name' valid: true - description: 'Expected ''subscription.name'' to be equal to ''changed''. Received ''changed''' - arguments: - elapsedTime: 0 + description: 'Expected ''sensor.name'' to be equal to ''changed''. Received ''changed''' + arguments: {} onFinish: valid: true tests: - + implicit: true valid: true name: 'Message received' - description: 'Subscription has received its message' + description: 'Sensor has received its message' arguments: executedHooks: - - onInit - - onMessageReceived - - onFinish - elapsedTime: 2152 + onInit: [] + onMessageReceived: + - payload + - stream + - path + - elapsedTime + onFinish: + - executedHooks + - elapsedTime + elapsedTime: 2125 onMessageReceived: valid: true tests: - - name: 'Subscription exists onMessageReceived' + name: 'Sensor exists onMessageReceived' valid: true - description: 'Expecting ''subscription'' to be defined' + description: 'Expecting ''sensor'' to be defined' - - name: 'Subscription name changed' + name: 'Sensor name changed' valid: true - description: 'Expected ''subscription.name'' to be equal to ''changed''. Received ''changed''' + description: 'Expected ''sensor.name'' to be equal to ''changed''. Received ''changed''' - - name: 'Subscription onInit executed' + name: 'Sensor onInit executed' valid: true - description: 'Expecting ''requisition::onInit-> subscription::onInit-> publisher::onInit'' (store.sequence) to contain ''subscription::onInit''' + description: 'Expecting ''task::onInit''-> sensor::onInit''''-> actuator::onInit'''' (store.sequence) to contain ''sensor::onInit''' arguments: payload: it stream: - address: '::ffff:127.0.0.1' + address: '::1' family: IPv6 port: 23080 - elapsedTime: 151 + elapsedTime: 143 valid: true - subscriptionTime: '2020-03-08T05:33:36.951Z' - publishers: + sensorTime: '2025-03-18T20:55:58.111Z' + actuators: - - id: 0233360791_ca0fe1e8c5_215816 - name: 'Publisher #0' + id: 1355570987_9390463906_407947 + name: 'Actuator #0' valid: true hooks: onInit: @@ -612,17 +679,17 @@ requisitions: elapsedTime: 0 tests: - - name: 'Publisher exists onInit' + name: 'Actuator exists onInit' valid: true - description: 'Expecting ''publisher'' to be defined' + description: 'Expecting ''actuator'' to be defined' - - name: 'Publisher name' + name: 'Actuator name' valid: true - description: 'Expecting ''publisher.name'' to be defined' + description: 'Expecting ''actuator.name'' to be defined' - name: 'Assertion #2' valid: true - description: 'Expecting ''publisher'' to be defined' + description: 'Expecting ''actuator'' to be defined' - name: 'Assertion #3' valid: true @@ -635,60 +702,66 @@ requisitions: onFinish: arguments: executedHooks: - - onInit - - onMessageReceived - - onFinish - elapsedTime: 2152 + onInit: [] + onMessageReceived: + - payload + - stream + - elapsedTime + onFinish: + - executedHooks + - elapsedTime + elapsedTime: 2125 tests: - name: Published valid: true description: 'Published successfully' + implicit: true valid: true onMessageReceived: arguments: payload: 'hook response' stream: - address: 127.0.0.1 - family: IPv4 - port: 61962 - elapsedTime: 2153 + address: '::1' + family: IPv6 + port: 57106 + elapsedTime: 2126 tests: - - name: 'Publisher exists onMessageReceived' + name: 'Actuator exists onMessageReceived' valid: true - description: 'Expecting ''publisher'' to be defined' + description: 'Expecting ''actuator'' to be defined' - - name: 'Requisition onInit' + name: 'Task onInit' valid: true - description: 'Expecting ''requisition::onInit-> subscription::onInit-> publisher::onInit-> publisher::onMessageReceived'' (store.sequence) to contain ''requisition::onInit''' + description: 'Expecting ''task::onInit''-> sensor::onInit''''-> actuator::onInit''''-> actuator::onMessageReceived'''' (store.sequence) to contain ''task::onInit''' - - name: 'Publisher name changed' + name: 'Actuator name changed' valid: true - description: 'Expected ''publisher.name'' to be equal to ''changed''. Received ''changed''' + description: 'Expected ''actuator.name'' to be equal to ''changed''. Received ''changed''' - - name: 'Publisher onInit executed' + name: 'Actuator onInit executed' valid: true - description: 'Expecting ''requisition::onInit-> subscription::onInit-> publisher::onInit-> publisher::onMessageReceived'' (store.sequence) to contain ''publisher::onInit''' + description: 'Expecting ''task::onInit''-> sensor::onInit''''-> actuator::onInit''''-> actuator::onMessageReceived'''' (store.sequence) to contain ''actuator::onInit''' - - name: 'Publisher exists onMessageReceived' + name: 'Actuator exists onMessageReceived' valid: true - description: 'Expecting ''publisher'' to be defined' + description: 'Expecting ''actuator'' to be defined' - - name: 'Requisition onInit' + name: 'Task onInit' valid: true - description: 'Expecting ''requisition::onInit-> subscription::onInit-> publisher::onInit-> publisher::onMessageReceived-> publisher::onMessageReceived'' (store.sequence) to contain ''requisition::onInit''' + description: 'Expecting ''task::onInit''-> sensor::onInit''''-> actuator::onInit''''-> actuator::onMessageReceived''''-> actuator::onMessageReceived'''' (store.sequence) to contain ''task::onInit''' - - name: 'Publisher name changed' + name: 'Actuator name changed' valid: true - description: 'Expected ''publisher.name'' to be equal to ''changed''. Received ''changed''' + description: 'Expected ''actuator.name'' to be equal to ''changed''. Received ''changed''' - - name: 'Publisher onInit executed' + name: 'Actuator onInit executed' valid: true - description: 'Expecting ''requisition::onInit-> subscription::onInit-> publisher::onInit-> publisher::onMessageReceived-> publisher::onMessageReceived'' (store.sequence) to contain ''publisher::onInit''' + description: 'Expecting ''task::onInit''-> sensor::onInit''''-> actuator::onInit''''-> actuator::onMessageReceived''''-> actuator::onMessageReceived'''' (store.sequence) to contain ''actuator::onInit''' valid: true type: tcp - publishTime: '2020-03-08T05:33:38.996Z' + messageSentInstant: '2025-03-18T20:56:00.134Z' iteration: 0 totalIterations: 1 hooks: @@ -696,9 +769,9 @@ requisitions: valid: true tests: - - name: 'Requisitions exists onInit' + name: 'Tasks exists onInit' valid: true - description: 'Expecting ''requisition.name'' to be defined' + description: 'Expecting ''task.name'' to be defined' - name: 'Assertion #1' valid: true @@ -709,42 +782,50 @@ requisitions: valid: true tests: [] arguments: - elapsedTime: 2153 + elapsedTime: 2126 time: - startTime: '2020-03-08T05:33:36.843Z' - endTime: '2020-03-08T05:33:38.996Z' - totalTime: 2153 + startTime: '2025-03-18T20:55:58.008Z' + endTime: '2025-03-18T20:56:00.134Z' + totalTime: 2126 timeout: 3000 - requisitions: [] + tasks: [] - valid: true name: examples/http-auth-basic.yml - id: 0233360791_6420f75659_500201 + id: 1355570987_91ac9852fd_972995 level: 1 - subscriptions: + sensors: - - id: 0233360791_94e20b540b_615297 - name: 'Subscription #0' + id: 1355570987_27bcb0751a_80934 + name: 'Sensor #0' type: http hooks: onInit: valid: true tests: [] - arguments: - elapsedTime: 0 + arguments: {} onFinish: valid: true tests: - + implicit: true valid: true name: 'Message received' - description: "{\n \"headers\": {\n \"content-type\": \"application/json\",\n \"authorization\": \"Basic dXNlcjpwYXNzd29yZA==\",\n \"content-length\": \"10\",\n \"host\": \"localhost:23068\",\n \"connection\": \"close\"\n },\n \"params\": {},\n \"query\": {},\n \"url\": \"/basic\",\n \"body\": \"basic auth\",\n \"elapsedTime\": 155\n}" + description: "{\n \"headers\": {\n \"host\": \"localhost:23068\",\n \"connection\": \"keep-alive\",\n \"content-type\": \"application/json\",\n \"authorization\": \"Basic dXNlcjpwYXNzd29yZA==\",\n \"accept\": \"*/*\",\n \"accept-language\": \"*\",\n \"sec-fetch-mode\": \"cors\",\n \"user-agent\": \"node\",\n \"accept-encoding\": \"gzip, deflate\",\n \"content-length\": \"10\"\n },\n \"params\": {},\n \"query\": {},\n \"url\": \"/basic\",\n \"body\": \"basic auth\"\n}" arguments: executedHooks: - - onInit - - onMessageReceived - - onFinish - elapsedTime: 186 + onInit: [] + onMessageReceived: + - headers + - params + - query + - url + - body + - elapsedTime + onFinish: + - executedHooks + - elapsedTime + elapsedTime: 170 onMessageReceived: valid: true tests: @@ -754,22 +835,27 @@ requisitions: description: 'Expected ''body'' to be equal to ''basic auth''. Received ''basic auth''' arguments: headers: + host: 'localhost:23068' + connection: keep-alive content-type: application/json authorization: 'Basic dXNlcjpwYXNzd29yZA==' + accept: '*/*' + accept-language: '*' + sec-fetch-mode: cors + user-agent: node + accept-encoding: 'gzip, deflate' content-length: '10' - host: 'localhost:23068' - connection: close params: {} query: {} url: /basic body: 'basic auth' - elapsedTime: 155 + elapsedTime: 153 valid: true - subscriptionTime: '2020-03-08T05:33:36.951Z' - publishers: + sensorTime: '2025-03-18T20:55:58.111Z' + actuators: - - id: 0233360791_f4551bd7af_263114 - name: 'Publisher #0' + id: 1355570987_b0b95487e7_157099 + name: 'Actuator #0' valid: true hooks: onInit: @@ -780,91 +866,44 @@ requisitions: onFinish: arguments: executedHooks: - - onInit - - onResponseReceived - - onMessageReceived - - onFinish - elapsedTime: 186 + onInit: [] + onResponseReceived: + - status + - statusCode + - body + - headers + - elapsedTime + onFinish: + - executedHooks + - elapsedTime + elapsedTime: 170 tests: - name: Published valid: true - description: "{\n \"statusCode\": 200,\n \"headers\": {\n \"x-powered-by\": \"Express\",\n \"access-control-allow-origin\": \"*\",\n \"access-control-allow-headers\": \"Origin, X-Requested-With, Content-Type, Accept\",\n \"content-type\": \"text/html; charset=utf-8\",\n \"content-length\": \"19\",\n \"etag\": \"W/\\\"13-nGN7LEwCUKXpQoLxNqKZXSglYNc\\\"\",\n \"date\": \"Sun, 08 Mar 2020 05:33:36 GMT\",\n \"connection\": \"close\"\n },\n \"body\": \"basic auth response\"\n}" + description: "{\n \"statusCode\": 200,\n \"headers\": {\n \"access-control-allow-headers\": \"Origin, X-Requested-With, Content-Type, Accept\",\n \"access-control-allow-origin\": \"*\",\n \"connection\": \"keep-alive\",\n \"content-length\": \"19\",\n \"content-type\": \"text/html; charset=utf-8\",\n \"date\": \"Tue, 18 Mar 2025 20:55:58 GMT\",\n \"etag\": \"W/\\\"13-nGN7LEwCUKXpQoLxNqKZXSglYNc\\\"\",\n \"keep-alive\": \"timeout=5\",\n \"x-powered-by\": \"Express\"\n },\n \"body\": \"basic auth response\"\n}" + implicit: true valid: true onResponseReceived: arguments: + status: 200 statusCode: 200 body: 'basic auth response' headers: - x-powered-by: Express - access-control-allow-origin: '*' access-control-allow-headers: 'Origin, X-Requested-With, Content-Type, Accept' - content-type: 'text/html; charset=utf-8' - content-length: '19' - etag: 'W/"13-nGN7LEwCUKXpQoLxNqKZXSglYNc"' - date: 'Sun, 08 Mar 2020 05:33:36 GMT' - connection: close - request: - uri: - protocol: 'http:' - slashes: true - auth: null - host: 'localhost:23068' - port: '23068' - hostname: localhost - hash: null - search: null - query: null - pathname: /basic - path: /basic - href: 'http://localhost:23068/basic' - method: post - headers: - content-type: application/json - authorization: 'Basic dXNlcjpwYXNzd29yZA==' - Content-Length: 10 - tests: [] - valid: true - onMessageReceived: - arguments: - statusCode: 200 - body: 'basic auth response' - headers: - x-powered-by: Express access-control-allow-origin: '*' - access-control-allow-headers: 'Origin, X-Requested-With, Content-Type, Accept' - content-type: 'text/html; charset=utf-8' + connection: keep-alive content-length: '19' + content-type: 'text/html; charset=utf-8' + date: 'Tue, 18 Mar 2025 20:55:58 GMT' etag: 'W/"13-nGN7LEwCUKXpQoLxNqKZXSglYNc"' - date: 'Sun, 08 Mar 2020 05:33:36 GMT' - connection: close - request: - uri: - protocol: 'http:' - slashes: true - auth: null - host: 'localhost:23068' - port: '23068' - hostname: localhost - hash: null - search: null - query: null - pathname: /basic - path: /basic - href: 'http://localhost:23068/basic' - method: post - headers: - content-type: application/json - authorization: 'Basic dXNlcjpwYXNzd29yZA==' - Content-Length: 10 - tests: - - - name: 'Assertion #0' - valid: true - description: 'Expected ''body'' to be equal to ''basic auth response''. Received ''basic auth response''' + keep-alive: timeout=5 + x-powered-by: Express + elapsedTime: 169 + tests: [] valid: true type: http - publishTime: '2020-03-08T05:33:37.032Z' + messageSentInstant: '2025-03-18T20:55:58.179Z' iteration: 0 totalIterations: 1 hooks: @@ -877,42 +916,50 @@ requisitions: valid: true tests: [] arguments: - elapsedTime: 187 + elapsedTime: 170 time: - startTime: '2020-03-08T05:33:36.846Z' - endTime: '2020-03-08T05:33:37.033Z' - totalTime: 187 + startTime: '2025-03-18T20:55:58.010Z' + endTime: '2025-03-18T20:55:58.180Z' + totalTime: 170 timeout: 3000 - requisitions: [] + tasks: [] - valid: true name: examples/http-auth-bearer.yml - id: 0233360791_5e821857a3_700740 + id: 1355570987_4ea6593e25_828153 level: 1 - subscriptions: + sensors: - - id: 0233360792_dab3c9a05e_724388 - name: 'Subscription #0' + id: 1355570987_66a2a1123e_112700 + name: 'Sensor #0' type: http hooks: onInit: valid: true tests: [] - arguments: - elapsedTime: 0 + arguments: {} onFinish: valid: true tests: - + implicit: true valid: true name: 'Message received' - description: "{\n \"headers\": {\n \"content-type\": \"application/json\",\n \"authorization\": \"Bearer bearerToken\",\n \"content-length\": \"4\",\n \"host\": \"localhost:23067\",\n \"connection\": \"close\"\n },\n \"params\": {},\n \"query\": {},\n \"url\": \"/bearer\",\n \"body\": \"Rech\",\n \"elapsedTime\": 157\n}" + description: "{\n \"headers\": {\n \"host\": \"localhost:23067\",\n \"connection\": \"keep-alive\",\n \"content-type\": \"application/json\",\n \"authorization\": \"Bearer bearerToken\",\n \"accept\": \"*/*\",\n \"accept-language\": \"*\",\n \"sec-fetch-mode\": \"cors\",\n \"user-agent\": \"node\",\n \"accept-encoding\": \"gzip, deflate\",\n \"content-length\": \"4\"\n },\n \"params\": {},\n \"query\": {},\n \"url\": \"/bearer\",\n \"body\": \"Rech\"\n}" arguments: executedHooks: - - onInit - - onMessageReceived - - onFinish - elapsedTime: 186 + onInit: [] + onMessageReceived: + - headers + - params + - query + - url + - body + - elapsedTime + onFinish: + - executedHooks + - elapsedTime + elapsedTime: 172 onMessageReceived: valid: true tests: @@ -922,22 +969,27 @@ requisitions: description: 'Expected ''body'' to be equal to ''Rech''. Received ''Rech''' arguments: headers: + host: 'localhost:23067' + connection: keep-alive content-type: application/json authorization: 'Bearer bearerToken' + accept: '*/*' + accept-language: '*' + sec-fetch-mode: cors + user-agent: node + accept-encoding: 'gzip, deflate' content-length: '4' - host: 'localhost:23067' - connection: close params: {} query: {} url: /bearer body: Rech elapsedTime: 157 valid: true - subscriptionTime: '2020-03-08T05:33:36.951Z' - publishers: + sensorTime: '2025-03-18T20:55:58.111Z' + actuators: - - id: 0233360791_388c1593e3_970276 - name: 'Publisher #0' + id: 1355570987_e17058e556_620375 + name: 'Actuator #0' valid: true hooks: onInit: @@ -948,91 +1000,44 @@ requisitions: onFinish: arguments: executedHooks: - - onInit - - onResponseReceived - - onMessageReceived - - onFinish - elapsedTime: 186 + onInit: [] + onResponseReceived: + - status + - statusCode + - body + - headers + - elapsedTime + onFinish: + - executedHooks + - elapsedTime + elapsedTime: 172 tests: - name: Published valid: true - description: "{\n \"statusCode\": 321,\n \"headers\": {\n \"x-powered-by\": \"Express\",\n \"access-control-allow-origin\": \"*\",\n \"access-control-allow-headers\": \"Origin, X-Requested-With, Content-Type, Accept\",\n \"content-type\": \"text/html; charset=utf-8\",\n \"content-length\": \"15\",\n \"etag\": \"W/\\\"f-V9sBEzo+y6k6sqGXsr1Ql2B0tAk\\\"\",\n \"date\": \"Sun, 08 Mar 2020 05:33:36 GMT\",\n \"connection\": \"close\"\n },\n \"body\": \"responsePayload\"\n}" + description: "{\n \"statusCode\": 321,\n \"headers\": {\n \"access-control-allow-headers\": \"Origin, X-Requested-With, Content-Type, Accept\",\n \"access-control-allow-origin\": \"*\",\n \"connection\": \"keep-alive\",\n \"content-length\": \"15\",\n \"content-type\": \"text/html; charset=utf-8\",\n \"date\": \"Tue, 18 Mar 2025 20:55:58 GMT\",\n \"etag\": \"W/\\\"f-V9sBEzo+y6k6sqGXsr1Ql2B0tAk\\\"\",\n \"keep-alive\": \"timeout=5\",\n \"x-powered-by\": \"Express\"\n },\n \"body\": \"responsePayload\"\n}" + implicit: true valid: true onResponseReceived: arguments: + status: 321 statusCode: 321 body: responsePayload headers: - x-powered-by: Express - access-control-allow-origin: '*' access-control-allow-headers: 'Origin, X-Requested-With, Content-Type, Accept' - content-type: 'text/html; charset=utf-8' - content-length: '15' - etag: 'W/"f-V9sBEzo+y6k6sqGXsr1Ql2B0tAk"' - date: 'Sun, 08 Mar 2020 05:33:36 GMT' - connection: close - request: - uri: - protocol: 'http:' - slashes: true - auth: null - host: 'localhost:23067' - port: '23067' - hostname: localhost - hash: null - search: null - query: null - pathname: /bearer - path: /bearer - href: 'http://localhost:23067/bearer' - method: post - headers: - content-type: application/json - authorization: 'Bearer bearerToken' - Content-Length: 4 - tests: [] - valid: true - onMessageReceived: - arguments: - statusCode: 321 - body: responsePayload - headers: - x-powered-by: Express access-control-allow-origin: '*' - access-control-allow-headers: 'Origin, X-Requested-With, Content-Type, Accept' - content-type: 'text/html; charset=utf-8' + connection: keep-alive content-length: '15' + content-type: 'text/html; charset=utf-8' + date: 'Tue, 18 Mar 2025 20:55:58 GMT' etag: 'W/"f-V9sBEzo+y6k6sqGXsr1Ql2B0tAk"' - date: 'Sun, 08 Mar 2020 05:33:36 GMT' - connection: close - request: - uri: - protocol: 'http:' - slashes: true - auth: null - host: 'localhost:23067' - port: '23067' - hostname: localhost - hash: null - search: null - query: null - pathname: /bearer - path: /bearer - href: 'http://localhost:23067/bearer' - method: post - headers: - content-type: application/json - authorization: 'Bearer bearerToken' - Content-Length: 4 - tests: - - - name: Body - valid: true - description: 'Expected ''body'' to be equal to ''responsePayload''. Received ''responsePayload''' + keep-alive: timeout=5 + x-powered-by: Express + elapsedTime: 172 + tests: [] valid: true type: http - publishTime: '2020-03-08T05:33:37.034Z' + messageSentInstant: '2025-03-18T20:55:58.182Z' iteration: 0 totalIterations: 1 hooks: @@ -1045,41 +1050,49 @@ requisitions: valid: true tests: [] arguments: - elapsedTime: 186 + elapsedTime: 172 time: - startTime: '2020-03-08T05:33:36.848Z' - endTime: '2020-03-08T05:33:37.034Z' - totalTime: 186 + startTime: '2025-03-18T20:55:58.010Z' + endTime: '2025-03-18T20:55:58.182Z' + totalTime: 172 timeout: 3000 - requisitions: [] + tasks: [] - valid: true name: examples/http-auth-digest.yml - id: 0233360792_784968c016_727071 + id: 1355570987_c58c79f583_313544 level: 1 - subscriptions: + sensors: - - id: 0233360792_b8f024a270_490320 - name: 'Subscription #0' + id: 1355570987_caace08dbc_451494 + name: 'Sensor #0' type: http hooks: onInit: valid: true tests: [] - arguments: - elapsedTime: 0 + arguments: {} onFinish: valid: true tests: - + implicit: true valid: true name: 'Message received' - description: "{\n \"headers\": {\n \"content-type\": \"application/json\",\n \"authorization\": \"Digest username=\\\"guest\\\", realm=\\\"nqrRealm\\\", nonce=\\\"58bac26865505\\\", uri=\\\"/digest\\\", algorithm=\\\"MD5\\\", response=\\\"6309166f64557e9d56ffe6c34a0a6ca4\\\", opaque=\\\"opaque\\\"\",\n \"content-length\": \"4\",\n \"host\": \"localhost:23067\",\n \"connection\": \"close\"\n },\n \"params\": {},\n \"query\": {},\n \"url\": \"/digest\",\n \"body\": \"Rech\",\n \"elapsedTime\": 164\n}" + description: "{\n \"headers\": {\n \"host\": \"localhost:23067\",\n \"connection\": \"keep-alive\",\n \"content-type\": \"application/json\",\n \"authorization\": \"Digest username=\\\"guest\\\", realm=\\\"nqrRealm\\\", nonce=\\\"58bac26865505\\\", uri=\\\"/digest\\\", algorithm=\\\"MD5\\\", response=\\\"6309166f64557e9d56ffe6c34a0a6ca4\\\", opaque=\\\"opaque\\\"\",\n \"accept\": \"*/*\",\n \"accept-language\": \"*\",\n \"sec-fetch-mode\": \"cors\",\n \"user-agent\": \"node\",\n \"accept-encoding\": \"gzip, deflate\",\n \"content-length\": \"4\"\n },\n \"params\": {},\n \"query\": {},\n \"url\": \"/digest\",\n \"body\": \"Rech\"\n}" arguments: executedHooks: - - onInit - - onMessageReceived - - onFinish + onInit: [] + onMessageReceived: + - headers + - params + - query + - url + - body + - elapsedTime + onFinish: + - executedHooks + - elapsedTime elapsedTime: 186 onMessageReceived: valid: true @@ -1090,22 +1103,27 @@ requisitions: description: 'Expected ''body'' to be equal to ''Rech''. Received ''Rech''' arguments: headers: + host: 'localhost:23067' + connection: keep-alive content-type: application/json authorization: 'Digest username="guest", realm="nqrRealm", nonce="58bac26865505", uri="/digest", algorithm="MD5", response="6309166f64557e9d56ffe6c34a0a6ca4", opaque="opaque"' + accept: '*/*' + accept-language: '*' + sec-fetch-mode: cors + user-agent: node + accept-encoding: 'gzip, deflate' content-length: '4' - host: 'localhost:23067' - connection: close params: {} query: {} url: /digest body: Rech - elapsedTime: 164 + elapsedTime: 178 valid: true - subscriptionTime: '2020-03-08T05:33:36.951Z' - publishers: + sensorTime: '2025-03-18T20:55:58.111Z' + actuators: - - id: 0233360792_b553ccdd3b_466328 - name: 'Publisher #0' + id: 1355570987_c0a69f13dd_953410 + name: 'Actuator #0' valid: true hooks: onInit: @@ -1116,91 +1134,44 @@ requisitions: onFinish: arguments: executedHooks: - - onInit - - onResponseReceived - - onMessageReceived - - onFinish + onInit: [] + onResponseReceived: + - status + - statusCode + - body + - headers + - elapsedTime + onFinish: + - executedHooks + - elapsedTime elapsedTime: 186 tests: - name: Published valid: true - description: "{\n \"statusCode\": 321,\n \"headers\": {\n \"x-powered-by\": \"Express\",\n \"access-control-allow-origin\": \"*\",\n \"access-control-allow-headers\": \"Origin, X-Requested-With, Content-Type, Accept\",\n \"content-type\": \"text/html; charset=utf-8\",\n \"content-length\": \"15\",\n \"etag\": \"W/\\\"f-V9sBEzo+y6k6sqGXsr1Ql2B0tAk\\\"\",\n \"date\": \"Sun, 08 Mar 2020 05:33:36 GMT\",\n \"connection\": \"close\"\n },\n \"body\": \"responsePayload\"\n}" + description: "{\n \"statusCode\": 321,\n \"headers\": {\n \"access-control-allow-headers\": \"Origin, X-Requested-With, Content-Type, Accept\",\n \"access-control-allow-origin\": \"*\",\n \"connection\": \"keep-alive\",\n \"content-length\": \"15\",\n \"content-type\": \"text/html; charset=utf-8\",\n \"date\": \"Tue, 18 Mar 2025 20:55:58 GMT\",\n \"etag\": \"W/\\\"f-V9sBEzo+y6k6sqGXsr1Ql2B0tAk\\\"\",\n \"keep-alive\": \"timeout=5\",\n \"x-powered-by\": \"Express\"\n },\n \"body\": \"responsePayload\"\n}" + implicit: true valid: true onResponseReceived: arguments: + status: 321 statusCode: 321 body: responsePayload headers: - x-powered-by: Express - access-control-allow-origin: '*' access-control-allow-headers: 'Origin, X-Requested-With, Content-Type, Accept' - content-type: 'text/html; charset=utf-8' - content-length: '15' - etag: 'W/"f-V9sBEzo+y6k6sqGXsr1Ql2B0tAk"' - date: 'Sun, 08 Mar 2020 05:33:36 GMT' - connection: close - request: - uri: - protocol: 'http:' - slashes: true - auth: null - host: 'localhost:23067' - port: '23067' - hostname: localhost - hash: null - search: null - query: null - pathname: /digest - path: /digest - href: 'http://localhost:23067/digest' - method: post - headers: - content-type: application/json - authorization: 'Digest username="guest", realm="nqrRealm", nonce="58bac26865505", uri="/digest", algorithm="MD5", response="6309166f64557e9d56ffe6c34a0a6ca4", opaque="opaque"' - Content-Length: 4 - tests: [] - valid: true - onMessageReceived: - arguments: - statusCode: 321 - body: responsePayload - headers: - x-powered-by: Express access-control-allow-origin: '*' - access-control-allow-headers: 'Origin, X-Requested-With, Content-Type, Accept' - content-type: 'text/html; charset=utf-8' + connection: keep-alive content-length: '15' + content-type: 'text/html; charset=utf-8' + date: 'Tue, 18 Mar 2025 20:55:58 GMT' etag: 'W/"f-V9sBEzo+y6k6sqGXsr1Ql2B0tAk"' - date: 'Sun, 08 Mar 2020 05:33:36 GMT' - connection: close - request: - uri: - protocol: 'http:' - slashes: true - auth: null - host: 'localhost:23067' - port: '23067' - hostname: localhost - hash: null - search: null - query: null - pathname: /digest - path: /digest - href: 'http://localhost:23067/digest' - method: post - headers: - content-type: application/json - authorization: 'Digest username="guest", realm="nqrRealm", nonce="58bac26865505", uri="/digest", algorithm="MD5", response="6309166f64557e9d56ffe6c34a0a6ca4", opaque="opaque"' - Content-Length: 4 - tests: - - - name: Body - valid: true - description: 'Expected ''body'' to be equal to ''responsePayload''. Received ''responsePayload''' + keep-alive: timeout=5 + x-powered-by: Express + elapsedTime: 185 + tests: [] valid: true type: http - publishTime: '2020-03-08T05:33:37.035Z' + messageSentInstant: '2025-03-18T20:55:58.195Z' iteration: 0 totalIterations: 1 hooks: @@ -1215,18 +1186,18 @@ requisitions: arguments: elapsedTime: 186 time: - startTime: '2020-03-08T05:33:36.849Z' - endTime: '2020-03-08T05:33:37.035Z' + startTime: '2025-03-18T20:55:58.010Z' + endTime: '2025-03-18T20:55:58.196Z' totalTime: 186 timeout: 3000 - requisitions: [] + tasks: [] - valid: true name: examples/http-more-examples.yml - id: 0233360792_a064465f3e_506602 + id: 1355570987_4b98d9a6b6_427274 level: 1 - subscriptions: [] - publishers: [] + sensors: [] + actuators: [] iteration: 0 totalIterations: 1 hooks: @@ -1239,143 +1210,179 @@ requisitions: valid: true tests: [] arguments: - elapsedTime: 2380 + elapsedTime: 2061 time: - startTime: '2020-03-08T05:33:36.854Z' - endTime: '2020-03-08T05:33:39.234Z' - totalTime: 2380 + startTime: '2025-03-18T20:55:58.011Z' + endTime: '2025-03-18T20:56:00.072Z' + totalTime: 2061 timeout: 5000 - requisitions: + tasks: - valid: true - name: 'Requisition #0' - id: 0233360792_f13fd961e3_570327 + name: 'task 2 (port 23076)' + id: 1355570987_1d66bb275b_19279 level: 2 - subscriptions: + sensors: - - id: 0233360792_e916596275_656262 - name: 'Subscription #0' + id: 1355570987_a3ff87bef2_712249 + name: 'sensor description' type: http hooks: onInit: valid: true tests: [] - arguments: - elapsedTime: 0 + arguments: {} onFinish: valid: true tests: - valid: true - name: 'Message received' - description: "{\n \"headers\": {\n \"content-type\": \"application/json\",\n \"nqr\": \"publisher\",\n \"content-length\": \"20\",\n \"host\": \"localhost:23075\",\n \"connection\": \"close\"\n },\n \"params\": {\n \"firstId\": \"idStuff\"\n },\n \"query\": {\n \"query\": \"2345\"\n },\n \"url\": \"/enqueuer/idStuff?query=2345\",\n \"body\": {\n \"enqueuer\": \"virgs\"\n },\n \"elapsedTime\": 138\n}" + implicit: true + name: 'Sensor avoided' + description: 'Avoidable sensor has not received any message' arguments: executedHooks: - - onInit - - onMessageReceived - - onFinish - elapsedTime: 330 - onMessageReceived: + onInit: [] + onFinish: + - executedHooks + - elapsedTime + elapsedTime: 1045 + valid: true + sensorTime: '2025-03-18T20:55:58.111Z' + actuators: [] + iteration: 0 + totalIterations: 1 + hooks: + onInit: + valid: true + tests: [] + arguments: + elapsedTime: 0 + onFinish: + valid: true + tests: [] + arguments: + elapsedTime: 1045 + time: + startTime: '2025-03-18T20:55:58.020Z' + endTime: '2025-03-18T20:55:59.065Z' + totalTime: 1045 + timeout: 5000 + tasks: [] + - + valid: true + name: 'check port releasing (23076)' + id: 1355570987_86c658be6d_178684 + level: 2 + sensors: + - + id: 1355570987_7c918dc838_384996 + name: 'Sensor #0' + type: tcp + hooks: + onInit: + valid: true + tests: [] + arguments: {} + onFinish: valid: true tests: - - name: Payload - valid: true - description: 'Expected ''JSON.parse(body).enqueuer'' to be equal to ''virgs''. Received ''virgs''' - - - name: Params - valid: true - description: 'Expected ''params.firstId'' to be equal to ''idStuff''. Received ''idStuff''' - - - name: Query - valid: true - description: 'Expected ''query.query'' to be equal to ''2345''. Received ''2345''' - - - name: Header valid: true - description: 'Expected ''headers.nqr'' to be equal to ''publisher''. Received ''publisher''' + implicit: true + name: 'Sensor avoided' + description: 'Avoidable sensor has not received any message' arguments: - headers: - content-type: application/json - nqr: publisher - content-length: '20' - host: 'localhost:23075' - connection: close - params: - firstId: idStuff - query: - query: '2345' - url: '/enqueuer/idStuff?query=2345' - body: '{"enqueuer":"virgs"}' - elapsedTime: 138 + executedHooks: + onInit: [] + onFinish: + - executedHooks + - elapsedTime + elapsedTime: 1000 + valid: true + sensorTime: '2025-03-18T20:55:59.066Z' + actuators: [] + iteration: 0 + totalIterations: 1 + hooks: + onInit: valid: true - subscriptionTime: '2020-03-08T05:33:36.951Z' + tests: [] + arguments: + elapsedTime: 0 + onFinish: + valid: true + tests: [] + arguments: + elapsedTime: 1000 + time: + startTime: '2025-03-18T20:55:59.065Z' + endTime: '2025-03-18T20:56:00.065Z' + totalTime: 1000 + timeout: 5000 + tasks: [] + - + valid: true + name: 'Task #2' + id: 1355570987_30798b98b3_741181 + level: 2 + sensors: - - id: 0233360792_ba5a509689_54847 - name: 'same port' + id: 1355570987_69863e6e8c_857753 + name: 'Sensor #0' type: http hooks: onInit: valid: true tests: [] - arguments: - elapsedTime: 0 + arguments: {} onFinish: valid: true tests: - + implicit: true valid: true name: 'Message received' - description: "{\n \"headers\": {\n \"content-length\": \"5\",\n \"host\": \"localhost:23075\",\n \"connection\": \"close\"\n },\n \"params\": {},\n \"query\": {},\n \"url\": \"/samePort\",\n \"body\": \"virgs\",\n \"elapsedTime\": 140\n}" + description: "{\n \"headers\": {\n \"host\": \"localhost:23095\",\n \"connection\": \"keep-alive\",\n \"content-type\": \"text/plain;charset=UTF-8\",\n \"accept\": \"*/*\",\n \"accept-language\": \"*\",\n \"sec-fetch-mode\": \"cors\",\n \"user-agent\": \"node\",\n \"accept-encoding\": \"gzip, deflate\",\n \"content-length\": \"5\"\n },\n \"params\": {},\n \"query\": {},\n \"url\": \"/number-payload\",\n \"body\": \"virgs\"\n}" arguments: executedHooks: - - onInit - - onMessageReceived - - onFinish - elapsedTime: 330 + onInit: [] + onMessageReceived: + - headers + - params + - query + - url + - body + - elapsedTime + onFinish: + - executedHooks + - elapsedTime + elapsedTime: 6 onMessageReceived: valid: true tests: [] arguments: headers: + host: 'localhost:23095' + connection: keep-alive + content-type: text/plain;charset=UTF-8 + accept: '*/*' + accept-language: '*' + sec-fetch-mode: cors + user-agent: node + accept-encoding: 'gzip, deflate' content-length: '5' - host: 'localhost:23075' - connection: close params: {} query: {} - url: /samePort + url: /number-payload body: virgs - elapsedTime: 140 - valid: true - subscriptionTime: '2020-03-08T05:33:36.951Z' - - - id: 0233360792_42d8fc1293_552948 - name: 'yet another, but avoidable' - type: http - hooks: - onInit: - valid: true - tests: [] - arguments: - elapsedTime: 0 - onFinish: - valid: true - tests: - - - valid: true - name: 'Subscription avoided' - description: 'Avoidable subscription has not received any message' - arguments: - executedHooks: - - onInit - - onFinish - elapsedTime: 330 + elapsedTime: 5 valid: true - subscriptionTime: '2020-03-08T05:33:36.951Z' - publishers: + sensorTime: '2025-03-18T20:56:00.067Z' + actuators: - - id: 0233360792_acd0fb7e94_23678 - name: 'Publisher #0' + id: 1355570987_cfb6df28b5_923374 + name: 'Actuator #0' valid: true hooks: onInit: @@ -1386,199 +1393,44 @@ requisitions: onFinish: arguments: executedHooks: - - onInit - - onResponseReceived - - onMessageReceived - - onFinish - elapsedTime: 330 + onInit: [] + onResponseReceived: + - status + - statusCode + - body + - headers + - elapsedTime + onFinish: + - executedHooks + - elapsedTime + elapsedTime: 6 tests: - name: Published valid: true - description: "{\n \"statusCode\": 321,\n \"headers\": {\n \"x-powered-by\": \"Express\",\n \"access-control-allow-origin\": \"*\",\n \"access-control-allow-headers\": \"Origin, X-Requested-With, Content-Type, Accept\",\n \"nqr\": \"subscription\",\n \"content-type\": \"text/html; charset=utf-8\",\n \"content-length\": \"27\",\n \"etag\": \"W/\\\"1b-e5esTWfu+XftewZ5g2Tclr7ClTo\\\"\",\n \"date\": \"Sun, 08 Mar 2020 05:33:36 GMT\",\n \"connection\": \"close\"\n },\n \"body\": \"dynamically changed payload\"\n}" + description: "{\n \"statusCode\": 444,\n \"headers\": {\n \"access-control-allow-headers\": \"Origin, X-Requested-With, Content-Type, Accept\",\n \"access-control-allow-origin\": \"*\",\n \"connection\": \"keep-alive\",\n \"content-length\": \"4\",\n \"content-type\": \"text/html; charset=utf-8\",\n \"date\": \"Tue, 18 Mar 2025 20:56:00 GMT\",\n \"etag\": \"W/\\\"4-HLLQjeteIwK7Xuhlj4t8Bu7p124\\\"\",\n \"keep-alive\": \"timeout=5\",\n \"x-powered-by\": \"Express\"\n },\n \"body\": 4.45\n}" + implicit: true valid: true onResponseReceived: arguments: - statusCode: 321 - body: 'dynamically changed payload' + status: 444 + statusCode: 444 + body: '4.45' headers: - x-powered-by: Express - access-control-allow-origin: '*' access-control-allow-headers: 'Origin, X-Requested-With, Content-Type, Accept' - nqr: subscription + access-control-allow-origin: '*' + connection: keep-alive + content-length: '4' content-type: 'text/html; charset=utf-8' - content-length: '27' - etag: 'W/"1b-e5esTWfu+XftewZ5g2Tclr7ClTo"' - date: 'Sun, 08 Mar 2020 05:33:36 GMT' - connection: close - request: - uri: - protocol: 'http:' - slashes: true - auth: null - host: 'localhost:23075' - port: '23075' - hostname: localhost - hash: null - search: '?query=2345' - query: query=2345 - pathname: /enqueuer/idStuff - path: '/enqueuer/idStuff?query=2345' - href: 'http://localhost:23075/enqueuer/idStuff?query=2345' - method: post - headers: - content-type: application/json - nqr: publisher - Content-Length: 20 - tests: - - - name: 'Status Code' - valid: true - description: 'Expected ''statusCode'' to be equal to ''321''. Received ''321''' - - - name: Body - valid: true - description: 'Expected ''body'' to be equal to ''dynamically changed payload''. Received ''dynamically changed payload''' - - - name: Header - valid: true - description: 'Expected ''headers.nqr'' to be equal to ''subscription''. Received ''subscription''' - valid: true - onMessageReceived: - arguments: - statusCode: 321 - body: 'dynamically changed payload' - headers: - x-powered-by: Express - access-control-allow-origin: '*' - access-control-allow-headers: 'Origin, X-Requested-With, Content-Type, Accept' - nqr: subscription - content-type: 'text/html; charset=utf-8' - content-length: '27' - etag: 'W/"1b-e5esTWfu+XftewZ5g2Tclr7ClTo"' - date: 'Sun, 08 Mar 2020 05:33:36 GMT' - connection: close - request: - uri: - protocol: 'http:' - slashes: true - auth: null - host: 'localhost:23075' - port: '23075' - hostname: localhost - hash: null - search: '?query=2345' - query: query=2345 - pathname: /enqueuer/idStuff - path: '/enqueuer/idStuff?query=2345' - href: 'http://localhost:23075/enqueuer/idStuff?query=2345' - method: post - headers: - content-type: application/json - nqr: publisher - Content-Length: 20 - tests: [] - valid: true - type: http - publishTime: '2020-03-08T05:33:37.038Z' - - - id: 0233360792_72ad57ec09_983313 - name: 'Publisher #1' - valid: true - hooks: - onInit: - arguments: - elapsedTime: 0 - tests: [] - valid: true - onFinish: - arguments: - executedHooks: - - onInit - - onResponseReceived - - onMessageReceived - - onFinish - elapsedTime: 330 - tests: - - - name: Published - valid: true - description: "{\n \"statusCode\": 444,\n \"headers\": {\n \"x-powered-by\": \"Express\",\n \"access-control-allow-origin\": \"*\",\n \"access-control-allow-headers\": \"Origin, X-Requested-With, Content-Type, Accept\",\n \"content-type\": \"application/json; charset=utf-8\",\n \"content-length\": \"17\",\n \"etag\": \"W/\\\"11-nJYBwrCE3yNNre0RUpA2SJDxlIY\\\"\",\n \"date\": \"Sun, 08 Mar 2020 05:33:36 GMT\",\n \"connection\": \"close\"\n },\n \"body\": {\n \"deep\": \"object\"\n }\n}" - valid: true - onResponseReceived: - arguments: - statusCode: 444 - body: '{"deep":"object"}' - headers: - x-powered-by: Express - access-control-allow-origin: '*' - access-control-allow-headers: 'Origin, X-Requested-With, Content-Type, Accept' - content-type: 'application/json; charset=utf-8' - content-length: '17' - etag: 'W/"11-nJYBwrCE3yNNre0RUpA2SJDxlIY"' - date: 'Sun, 08 Mar 2020 05:33:36 GMT' - connection: close - request: - uri: - protocol: 'http:' - slashes: true - auth: null - host: 'localhost:23075' - port: '23075' - hostname: localhost - hash: null - search: null - query: null - pathname: /samePort - path: /samePort - href: 'http://localhost:23075/samePort' - method: post - headers: - Content-Length: 5 - tests: - - - name: 'Status Code' - valid: true - description: 'Expected ''statusCode'' to be equal to ''444''. Received ''444''' - - - name: 'Assertion #1' - valid: true - description: 'Expected ''JSON.parse(body).deep'' to be equal to ''object''. Received ''object''' - valid: true - onMessageReceived: - arguments: - statusCode: 444 - body: '{"deep":"object"}' - headers: + date: 'Tue, 18 Mar 2025 20:56:00 GMT' + etag: 'W/"4-HLLQjeteIwK7Xuhlj4t8Bu7p124"' + keep-alive: timeout=5 x-powered-by: Express - access-control-allow-origin: '*' - access-control-allow-headers: 'Origin, X-Requested-With, Content-Type, Accept' - content-type: 'application/json; charset=utf-8' - content-length: '17' - etag: 'W/"11-nJYBwrCE3yNNre0RUpA2SJDxlIY"' - date: 'Sun, 08 Mar 2020 05:33:36 GMT' - connection: close - request: - uri: - protocol: 'http:' - slashes: true - auth: null - host: 'localhost:23075' - port: '23075' - hostname: localhost - hash: null - search: null - query: null - pathname: /samePort - path: /samePort - href: 'http://localhost:23075/samePort' - method: post - headers: - Content-Length: 5 + elapsedTime: 6 tests: [] valid: true type: http - publishTime: '2020-03-08T05:33:37.038Z' + messageSentInstant: '2025-03-18T20:56:00.072Z' iteration: 0 totalIterations: 1 hooks: @@ -1591,167 +1443,98 @@ requisitions: valid: true tests: [] arguments: - elapsedTime: 330 + elapsedTime: 6 time: - startTime: '2020-03-08T05:33:36.883Z' - endTime: '2020-03-08T05:33:37.213Z' - totalTime: 330 - timeout: 3000 - requisitions: [] + startTime: '2025-03-18T20:56:00.066Z' + endTime: '2025-03-18T20:56:00.072Z' + totalTime: 6 + timeout: 5000 + tasks: [] + - + valid: true + name: examples/http-parallel.yml + id: 1355570988_9706eda42d_426011 + level: 1 + sensors: [] + actuators: [] + iteration: 0 + totalIterations: 1 + hooks: + onInit: + valid: true + tests: [] + arguments: + elapsedTime: 0 + onFinish: + valid: true + tests: [] + arguments: + elapsedTime: 423 + time: + startTime: '2025-03-18T20:55:58.011Z' + endTime: '2025-03-18T20:55:58.434Z' + totalTime: 423 + timeout: 10000 + tasks: - valid: true - name: 'Requisition #1' - id: 0233360792_16e7db54dd_843746 + name: 'Task #0' + id: 1355570988_78a743ddb1_779103 level: 2 - subscriptions: + sensors: - - id: 0233360792_34a6878700_105100 - name: 'Subscription #0' + id: 1355570988_8e05d812cd_824573 + name: 'Sensor #0' type: http hooks: onInit: valid: true tests: [] - arguments: - elapsedTime: 0 + arguments: {} onFinish: valid: true tests: - + implicit: true valid: true name: 'Message received' - description: "{\n \"headers\": {\n \"content-type\": \"application/json\",\n \"content-length\": \"19\",\n \"host\": \"localhost:23075\",\n \"connection\": \"close\"\n },\n \"params\": {\n \"secondId\": \"idStuff\"\n },\n \"query\": {\n \"query\": \"111\"\n },\n \"url\": \"/enqueuer/idStuff?query=111\",\n \"body\": {\n \"duplicated\": true\n },\n \"elapsedTime\": 3\n}" + description: "{\n \"headers\": {\n \"host\": \"localhost:23023\",\n \"connection\": \"keep-alive\",\n \"accept\": \"*/*\",\n \"accept-language\": \"*\",\n \"sec-fetch-mode\": \"cors\",\n \"user-agent\": \"node\",\n \"accept-encoding\": \"gzip, deflate\"\n },\n \"params\": {},\n \"query\": {},\n \"url\": \"/first\",\n \"body\": \"\"\n}" arguments: executedHooks: - - onInit - - onMessageReceived - - onFinish - elapsedTime: 4 + onInit: [] + onMessageReceived: + - headers + - params + - query + - url + - body + - elapsedTime + onFinish: + - executedHooks + - elapsedTime + elapsedTime: 208 onMessageReceived: valid: true - tests: - - - name: Payload - valid: true - description: 'Expecting ''JSON.parse(body).duplicated'' to be true. Received: true' - arguments: - headers: - content-type: application/json - content-length: '19' - host: 'localhost:23075' - connection: close - params: - secondId: idStuff - query: - query: '111' - url: '/enqueuer/idStuff?query=111' - body: '{"duplicated":true}' - elapsedTime: 3 - valid: true - subscriptionTime: '2020-03-08T05:33:37.216Z' - publishers: - - - id: 0233360792_45f272e419_55232 - name: 'publisher description' - valid: true - hooks: - onInit: - arguments: - elapsedTime: 0 tests: [] - valid: true - onFinish: - arguments: - executedHooks: - - onInit - - onResponseReceived - - onMessageReceived - - onFinish - elapsedTime: 4 - tests: - - - name: Published - valid: true - description: "{\n \"statusCode\": 321,\n \"headers\": {\n \"x-powered-by\": \"Express\",\n \"access-control-allow-origin\": \"*\",\n \"access-control-allow-headers\": \"Origin, X-Requested-With, Content-Type, Accept\",\n \"content-type\": \"text/html; charset=utf-8\",\n \"content-length\": \"25\",\n \"etag\": \"W/\\\"19-yZRAgggcER0sMyRTVBBpErTPT/A\\\"\",\n \"date\": \"Sun, 08 Mar 2020 05:33:37 GMT\",\n \"connection\": \"close\"\n },\n \"body\": \"duplicatedResponsePayload\"\n}" - valid: true - onResponseReceived: - arguments: - statusCode: 321 - body: duplicatedResponsePayload - headers: - x-powered-by: Express - access-control-allow-origin: '*' - access-control-allow-headers: 'Origin, X-Requested-With, Content-Type, Accept' - content-type: 'text/html; charset=utf-8' - content-length: '25' - etag: 'W/"19-yZRAgggcER0sMyRTVBBpErTPT/A"' - date: 'Sun, 08 Mar 2020 05:33:37 GMT' - connection: close - request: - uri: - protocol: 'http:' - slashes: true - auth: null - host: 'localhost:23075' - port: '23075' - hostname: localhost - hash: null - search: '?query=111' - query: query=111 - pathname: /enqueuer/idStuff - path: '/enqueuer/idStuff?query=111' - href: 'http://localhost:23075/enqueuer/idStuff?query=111' - method: post - headers: - content-type: application/json - Content-Length: 19 - tests: - - - name: 'Status Code' - valid: true - description: 'Expected ''statusCode'' to be equal to ''321''. Received ''321''' - - - name: Body - valid: true - description: 'Expected ''body'' to be equal to ''duplicatedResponsePayload''. Received ''duplicatedResponsePayload''' - valid: true - onMessageReceived: arguments: - statusCode: 321 - body: duplicatedResponsePayload headers: - x-powered-by: Express - access-control-allow-origin: '*' - access-control-allow-headers: 'Origin, X-Requested-With, Content-Type, Accept' - content-type: 'text/html; charset=utf-8' - content-length: '25' - etag: 'W/"19-yZRAgggcER0sMyRTVBBpErTPT/A"' - date: 'Sun, 08 Mar 2020 05:33:37 GMT' - connection: close - request: - uri: - protocol: 'http:' - slashes: true - auth: null - host: 'localhost:23075' - port: '23075' - hostname: localhost - hash: null - search: '?query=111' - query: query=111 - pathname: /enqueuer/idStuff - path: '/enqueuer/idStuff?query=111' - href: 'http://localhost:23075/enqueuer/idStuff?query=111' - method: post - headers: - content-type: application/json - Content-Length: 19 - tests: [] - valid: true - type: http - publishTime: '2020-03-08T05:33:37.219Z' + host: 'localhost:23023' + connection: keep-alive + accept: '*/*' + accept-language: '*' + sec-fetch-mode: cors + user-agent: node + accept-encoding: 'gzip, deflate' + params: {} + query: {} + url: /first + body: "" + elapsedTime: 410 + valid: true + sensorTime: '2025-03-18T20:55:58.111Z' + actuators: [] iteration: 0 - totalIterations: 1 + totalIterations: 2 hooks: onInit: valid: true @@ -1762,46 +1545,72 @@ requisitions: valid: true tests: [] arguments: - elapsedTime: 4 + elapsedTime: 208 time: - startTime: '2020-03-08T05:33:37.215Z' - endTime: '2020-03-08T05:33:37.219Z' - totalTime: 4 - timeout: 5000 - requisitions: [] + startTime: '2025-03-18T20:55:58.021Z' + endTime: '2025-03-18T20:55:58.229Z' + totalTime: 208 + timeout: 10000 + tasks: [] - valid: true - name: 'requisition 2 (port 23076)' - id: 0233360792_eff03cbee0_317968 + name: 'Task #0' + id: 1355570988_78a743ddb1_779103 level: 2 - subscriptions: + sensors: - - id: 0233360792_e5e0d9ff8c_299307 - name: 'subscription description' + id: 1355570988_8e05d812cd_824573 + name: 'Sensor #0' type: http hooks: onInit: valid: true tests: [] - arguments: - elapsedTime: 0 + arguments: {} onFinish: valid: true tests: - + implicit: true valid: true - name: 'Subscription avoided' - description: 'Avoidable subscription has not received any message' + name: 'Message received' + description: "{\n \"headers\": {\n \"host\": \"localhost:23023\",\n \"connection\": \"keep-alive\",\n \"accept\": \"*/*\",\n \"accept-language\": \"*\",\n \"sec-fetch-mode\": \"cors\",\n \"user-agent\": \"node\",\n \"accept-encoding\": \"gzip, deflate\"\n },\n \"params\": {},\n \"query\": {},\n \"url\": \"/first\",\n \"body\": \"\"\n}" arguments: executedHooks: - - onInit - - onFinish - elapsedTime: 1002 + onInit: [] + onMessageReceived: + - headers + - params + - query + - url + - body + - elapsedTime + onFinish: + - executedHooks + - elapsedTime + elapsedTime: 203 + onMessageReceived: + valid: true + tests: [] + arguments: + headers: + host: 'localhost:23023' + connection: keep-alive + accept: '*/*' + accept-language: '*' + sec-fetch-mode: cors + user-agent: node + accept-encoding: 'gzip, deflate' + params: {} + query: {} + url: /first + body: "" + elapsedTime: 202 valid: true - subscriptionTime: '2020-03-08T05:33:37.220Z' - publishers: [] - iteration: 0 - totalIterations: 1 + sensorTime: '2025-03-18T20:55:58.230Z' + actuators: [] + iteration: 1 + totalIterations: 2 hooks: onInit: valid: true @@ -1812,46 +1621,72 @@ requisitions: valid: true tests: [] arguments: - elapsedTime: 1002 + elapsedTime: 203 time: - startTime: '2020-03-08T05:33:37.220Z' - endTime: '2020-03-08T05:33:38.222Z' - totalTime: 1002 - timeout: 5000 - requisitions: [] + startTime: '2025-03-18T20:55:58.229Z' + endTime: '2025-03-18T20:55:58.432Z' + totalTime: 203 + timeout: 10000 + tasks: [] - valid: true - name: 'check port releasing (23076)' - id: 0233360792_33f3725983_323203 + name: 'Task #1' + id: 1355570988_d5240575d8_282139 level: 2 - subscriptions: + sensors: - - id: 0233360792_eb12b4faec_77756 - name: 'Subscription #0' - type: tcp + id: 1355570988_84d15a9cc7_206121 + name: 'Sensor #0' + type: http hooks: onInit: valid: true tests: [] - arguments: - elapsedTime: 0 + arguments: {} onFinish: valid: true tests: - + implicit: true valid: true - name: 'Subscription avoided' - description: 'Avoidable subscription has not received any message' + name: 'Message received' + description: "{\n \"headers\": {\n \"host\": \"localhost:23023\",\n \"connection\": \"keep-alive\",\n \"accept\": \"*/*\",\n \"accept-language\": \"*\",\n \"sec-fetch-mode\": \"cors\",\n \"user-agent\": \"node\",\n \"accept-encoding\": \"gzip, deflate\"\n },\n \"params\": {},\n \"query\": {},\n \"url\": \"/second\",\n \"body\": \"\"\n}" arguments: executedHooks: - - onInit - - onFinish - elapsedTime: 1004 + onInit: [] + onMessageReceived: + - headers + - params + - query + - url + - body + - elapsedTime + onFinish: + - executedHooks + - elapsedTime + elapsedTime: 210 + onMessageReceived: + valid: true + tests: [] + arguments: + headers: + host: 'localhost:23023' + connection: keep-alive + accept: '*/*' + accept-language: '*' + sec-fetch-mode: cors + user-agent: node + accept-encoding: 'gzip, deflate' + params: {} + query: {} + url: /second + body: "" + elapsedTime: 412 valid: true - subscriptionTime: '2020-03-08T05:33:38.224Z' - publishers: [] + sensorTime: '2025-03-18T20:55:58.111Z' + actuators: [] iteration: 0 - totalIterations: 1 + totalIterations: 2 hooks: onInit: valid: true @@ -1862,61 +1697,99 @@ requisitions: valid: true tests: [] arguments: - elapsedTime: 1004 + elapsedTime: 210 time: - startTime: '2020-03-08T05:33:38.223Z' - endTime: '2020-03-08T05:33:39.227Z' - totalTime: 1004 - timeout: 5000 - requisitions: [] + startTime: '2025-03-18T20:55:58.021Z' + endTime: '2025-03-18T20:55:58.231Z' + totalTime: 210 + timeout: 10000 + tasks: [] - valid: true - name: 'Requisition #4' - id: 0233360792_33d13bbd7c_372869 + name: 'Task #1' + id: 1355570988_d5240575d8_282139 level: 2 - subscriptions: + sensors: - - id: 0233360792_017ca7e673_328443 - name: 'numbered payload' + id: 1355570988_84d15a9cc7_206121 + name: 'Sensor #0' type: http hooks: onInit: valid: true tests: [] - arguments: - elapsedTime: 0 + arguments: {} onFinish: valid: true tests: - + implicit: true valid: true name: 'Message received' - description: "{\n \"headers\": {\n \"content-length\": \"5\",\n \"host\": \"localhost:23080\",\n \"connection\": \"close\"\n },\n \"params\": {},\n \"query\": {},\n \"url\": \"/number-payload\",\n \"body\": \"virgs\",\n \"elapsedTime\": 3\n}" + description: "{\n \"headers\": {\n \"host\": \"localhost:23023\",\n \"connection\": \"keep-alive\",\n \"accept\": \"*/*\",\n \"accept-language\": \"*\",\n \"sec-fetch-mode\": \"cors\",\n \"user-agent\": \"node\",\n \"accept-encoding\": \"gzip, deflate\"\n },\n \"params\": {},\n \"query\": {},\n \"url\": \"/second\",\n \"body\": \"\"\n}" arguments: executedHooks: - - onInit - - onMessageReceived - - onFinish - elapsedTime: 5 + onInit: [] + onMessageReceived: + - headers + - params + - query + - url + - body + - elapsedTime + onFinish: + - executedHooks + - elapsedTime + elapsedTime: 203 onMessageReceived: valid: true tests: [] arguments: headers: - content-length: '5' - host: 'localhost:23080' - connection: close + host: 'localhost:23023' + connection: keep-alive + accept: '*/*' + accept-language: '*' + sec-fetch-mode: cors + user-agent: node + accept-encoding: 'gzip, deflate' params: {} query: {} - url: /number-payload - body: virgs - elapsedTime: 3 + url: /second + body: "" + elapsedTime: 202 + valid: true + sensorTime: '2025-03-18T20:55:58.231Z' + actuators: [] + iteration: 1 + totalIterations: 2 + hooks: + onInit: + valid: true + tests: [] + arguments: + elapsedTime: 0 + onFinish: valid: true - subscriptionTime: '2020-03-08T05:33:39.230Z' - publishers: + tests: [] + arguments: + elapsedTime: 203 + time: + startTime: '2025-03-18T20:55:58.231Z' + endTime: '2025-03-18T20:55:58.434Z' + totalTime: 203 + timeout: 10000 + tasks: [] + - + valid: true + name: 'Task #2' + id: 1355570988_9e23299bad_75822 + level: 2 + sensors: [] + actuators: - - id: 0233360792_25240a3992_219992 - name: 'Publisher #0' + id: 1355570988_ad37a06639_317985 + name: 'Actuator #0' valid: true hooks: onInit: @@ -1927,238 +1800,49 @@ requisitions: onFinish: arguments: executedHooks: - - onInit - - onResponseReceived - - onMessageReceived - - onFinish - elapsedTime: 5 + onInit: [] + onResponseReceived: + - status + - statusCode + - body + - headers + - elapsedTime + onFinish: + - executedHooks + - elapsedTime + elapsedTime: 209 tests: - name: Published valid: true - description: "{\n \"statusCode\": 444,\n \"headers\": {\n \"x-powered-by\": \"Express\",\n \"access-control-allow-origin\": \"*\",\n \"access-control-allow-headers\": \"Origin, X-Requested-With, Content-Type, Accept\",\n \"content-type\": \"text/html; charset=utf-8\",\n \"content-length\": \"4\",\n \"etag\": \"W/\\\"4-HLLQjeteIwK7Xuhlj4t8Bu7p124\\\"\",\n \"date\": \"Sun, 08 Mar 2020 05:33:39 GMT\",\n \"connection\": \"close\"\n },\n \"body\": 4.45\n}" + description: "{\n \"statusCode\": 200,\n \"headers\": {\n \"access-control-allow-headers\": \"Origin, X-Requested-With, Content-Type, Accept\",\n \"access-control-allow-origin\": \"*\",\n \"connection\": \"keep-alive\",\n \"content-length\": \"5\",\n \"content-type\": \"text/html; charset=utf-8\",\n \"date\": \"Tue, 18 Mar 2025 20:55:58 GMT\",\n \"etag\": \"W/\\\"5-4JlqN8E9RMOwYHSTnUP6N1m9MsE\\\"\",\n \"keep-alive\": \"timeout=5\",\n \"x-powered-by\": \"Express\"\n },\n \"body\": \"first\"\n}" + implicit: true valid: true onResponseReceived: arguments: - statusCode: 444 - body: '4.45' + status: 200 + statusCode: 200 + body: first headers: - x-powered-by: Express - access-control-allow-origin: '*' access-control-allow-headers: 'Origin, X-Requested-With, Content-Type, Accept' - content-type: 'text/html; charset=utf-8' - content-length: '4' - etag: 'W/"4-HLLQjeteIwK7Xuhlj4t8Bu7p124"' - date: 'Sun, 08 Mar 2020 05:33:39 GMT' - connection: close - request: - uri: - protocol: 'http:' - slashes: true - auth: null - host: 'localhost:23080' - port: '23080' - hostname: localhost - hash: null - search: null - query: null - pathname: /number-payload - path: /number-payload - href: 'http://localhost:23080/number-payload' - method: post - headers: - Content-Length: 5 - tests: [] - valid: true - onMessageReceived: - arguments: - statusCode: 444 - body: '4.45' - headers: - x-powered-by: Express access-control-allow-origin: '*' - access-control-allow-headers: 'Origin, X-Requested-With, Content-Type, Accept' + connection: keep-alive + content-length: '5' content-type: 'text/html; charset=utf-8' - content-length: '4' - etag: 'W/"4-HLLQjeteIwK7Xuhlj4t8Bu7p124"' - date: 'Sun, 08 Mar 2020 05:33:39 GMT' - connection: close - request: - uri: - protocol: 'http:' - slashes: true - auth: null - host: 'localhost:23080' - port: '23080' - hostname: localhost - hash: null - search: null - query: null - pathname: /number-payload - path: /number-payload - href: 'http://localhost:23080/number-payload' - method: post - headers: - Content-Length: 5 - tests: [] - valid: true - type: http - publishTime: '2020-03-08T05:33:39.233Z' - iteration: 0 - totalIterations: 1 - hooks: - onInit: - valid: true - tests: [] - arguments: - elapsedTime: 0 - onFinish: - valid: true - tests: [] - arguments: - elapsedTime: 5 - time: - startTime: '2020-03-08T05:33:39.229Z' - endTime: '2020-03-08T05:33:39.234Z' - totalTime: 5 - timeout: 5000 - requisitions: [] - - - valid: true - name: examples/http-parallel.yml - id: 0233360793_ffaed7f194_617295 - level: 1 - subscriptions: [] - publishers: [] - iteration: 0 - totalIterations: 1 - hooks: - onInit: - valid: true - tests: [] - arguments: - elapsedTime: 0 - onFinish: - valid: true - tests: [] - arguments: - elapsedTime: 448 - time: - startTime: '2020-03-08T05:33:36.856Z' - endTime: '2020-03-08T05:33:37.304Z' - totalTime: 448 - timeout: 5000 - requisitions: - - - valid: true - name: 'Requisition #0' - id: 0233360793_f46fd5fb16_832313 - level: 2 - subscriptions: - - - id: 0233360793_e4de1090c9_181839 - name: 'Subscription #0' - type: http - hooks: - onInit: - valid: true - tests: [] - arguments: - elapsedTime: 0 - onFinish: - valid: true + date: 'Tue, 18 Mar 2025 20:55:58 GMT' + etag: 'W/"5-4JlqN8E9RMOwYHSTnUP6N1m9MsE"' + keep-alive: timeout=5 + x-powered-by: Express + elapsedTime: 209 tests: - + name: 'Assertion #0' valid: true - name: 'Message received' - description: "{\n \"headers\": {\n \"host\": \"localhost:23023\",\n \"content-length\": \"0\",\n \"connection\": \"close\"\n },\n \"params\": {},\n \"query\": {},\n \"url\": \"/first\",\n \"body\": \"\",\n \"elapsedTime\": 205\n}" - arguments: - executedHooks: - - onInit - - onMessageReceived - - onFinish - elapsedTime: 205 - onMessageReceived: + description: 'Expected ''statusCode'' to be equal to ''200''. Received ''200''' valid: true - tests: [] - arguments: - headers: - host: 'localhost:23023' - content-length: '0' - connection: close - params: {} - query: {} - url: /first - body: "" - elapsedTime: 414 - valid: true - subscriptionTime: '2020-03-08T05:33:36.951Z' - publishers: [] - iteration: 0 - totalIterations: 2 - hooks: - onInit: - valid: true - tests: [] - arguments: - elapsedTime: 0 - onFinish: - valid: true - tests: [] - arguments: - elapsedTime: 205 - time: - startTime: '2020-03-08T05:33:36.883Z' - endTime: '2020-03-08T05:33:37.088Z' - totalTime: 205 - timeout: 5000 - requisitions: [] - - - valid: true - name: 'Requisition #0' - id: 0233360793_f46fd5fb16_832313 - level: 2 - subscriptions: - - - id: 0233360793_e4de1090c9_181839 - name: 'Subscription #0' type: http - hooks: - onInit: - valid: true - tests: [] - arguments: - elapsedTime: 0 - onFinish: - valid: true - tests: - - - valid: true - name: 'Message received' - description: "{\n \"headers\": {\n \"host\": \"localhost:23023\",\n \"content-length\": \"0\",\n \"connection\": \"close\"\n },\n \"params\": {},\n \"query\": {},\n \"url\": \"/first\",\n \"body\": \"\",\n \"elapsedTime\": 208\n}" - arguments: - executedHooks: - - onInit - - onMessageReceived - - onFinish - elapsedTime: 209 - onMessageReceived: - valid: true - tests: [] - arguments: - headers: - host: 'localhost:23023' - content-length: '0' - connection: close - params: {} - query: {} - url: /first - body: "" - elapsedTime: 208 - valid: true - subscriptionTime: '2020-03-08T05:33:37.089Z' - publishers: [] - iteration: 1 + messageSentInstant: '2025-03-18T20:55:58.230Z' + iteration: 0 totalIterations: 2 hooks: onInit: @@ -2172,120 +1856,73 @@ requisitions: arguments: elapsedTime: 209 time: - startTime: '2020-03-08T05:33:37.089Z' - endTime: '2020-03-08T05:33:37.298Z' + startTime: '2025-03-18T20:55:58.021Z' + endTime: '2025-03-18T20:55:58.230Z' totalTime: 209 - timeout: 5000 - requisitions: [] + timeout: 10000 + tasks: [] - valid: true - name: 'Requisition #1' - id: 0233360793_4788339776_407161 + name: 'Task #2' + id: 1355570988_9e23299bad_75822 level: 2 - subscriptions: + sensors: [] + actuators: - - id: 0233360793_16f5b5fa56_314693 - name: 'Subscription #0' - type: http + id: 1355570988_ad37a06639_317985 + name: 'Actuator #0' + valid: true hooks: onInit: - valid: true - tests: [] arguments: elapsedTime: 0 - onFinish: + tests: [] valid: true + onFinish: + arguments: + executedHooks: + onInit: [] + onResponseReceived: + - status + - statusCode + - body + - headers + - elapsedTime + onFinish: + - executedHooks + - elapsedTime + elapsedTime: 202 tests: - + name: Published valid: true - name: 'Message received' - description: "{\n \"headers\": {\n \"host\": \"localhost:23023\",\n \"content-length\": \"0\",\n \"connection\": \"close\"\n },\n \"params\": {},\n \"query\": {},\n \"url\": \"/second\",\n \"body\": \"\",\n \"elapsedTime\": 207\n}" - arguments: - executedHooks: - - onInit - - onMessageReceived - - onFinish - elapsedTime: 208 - onMessageReceived: + description: "{\n \"statusCode\": 200,\n \"headers\": {\n \"access-control-allow-headers\": \"Origin, X-Requested-With, Content-Type, Accept\",\n \"access-control-allow-origin\": \"*\",\n \"connection\": \"keep-alive\",\n \"content-length\": \"5\",\n \"content-type\": \"text/html; charset=utf-8\",\n \"date\": \"Tue, 18 Mar 2025 20:55:58 GMT\",\n \"etag\": \"W/\\\"5-4JlqN8E9RMOwYHSTnUP6N1m9MsE\\\"\",\n \"keep-alive\": \"timeout=5\",\n \"x-powered-by\": \"Express\"\n },\n \"body\": \"first\"\n}" + implicit: true valid: true - tests: [] + onResponseReceived: arguments: + status: 200 + statusCode: 200 + body: first headers: - host: 'localhost:23023' - content-length: '0' - connection: close - params: {} - query: {} - url: /second - body: "" - elapsedTime: 419 - valid: true - subscriptionTime: '2020-03-08T05:33:36.951Z' - publishers: [] - iteration: 0 - totalIterations: 2 - hooks: - onInit: - valid: true - tests: [] - arguments: - elapsedTime: 0 - onFinish: - valid: true - tests: [] - arguments: - elapsedTime: 208 - time: - startTime: '2020-03-08T05:33:36.884Z' - endTime: '2020-03-08T05:33:37.092Z' - totalTime: 208 - timeout: 5000 - requisitions: [] - - - valid: true - name: 'Requisition #1' - id: 0233360793_4788339776_407161 - level: 2 - subscriptions: - - - id: 0233360793_16f5b5fa56_314693 - name: 'Subscription #0' - type: http - hooks: - onInit: - valid: true - tests: [] - arguments: - elapsedTime: 0 - onFinish: - valid: true + access-control-allow-headers: 'Origin, X-Requested-With, Content-Type, Accept' + access-control-allow-origin: '*' + connection: keep-alive + content-length: '5' + content-type: 'text/html; charset=utf-8' + date: 'Tue, 18 Mar 2025 20:55:58 GMT' + etag: 'W/"5-4JlqN8E9RMOwYHSTnUP6N1m9MsE"' + keep-alive: timeout=5 + x-powered-by: Express + elapsedTime: 202 tests: - + name: 'Assertion #0' valid: true - name: 'Message received' - description: "{\n \"headers\": {\n \"host\": \"localhost:23023\",\n \"content-length\": \"0\",\n \"connection\": \"close\"\n },\n \"params\": {},\n \"query\": {},\n \"url\": \"/second\",\n \"body\": \"\",\n \"elapsedTime\": 210\n}" - arguments: - executedHooks: - - onInit - - onMessageReceived - - onFinish - elapsedTime: 210 - onMessageReceived: + description: 'Expected ''statusCode'' to be equal to ''200''. Received ''200''' valid: true - tests: [] - arguments: - headers: - host: 'localhost:23023' - content-length: '0' - connection: close - params: {} - query: {} - url: /second - body: "" - elapsedTime: 210 - valid: true - subscriptionTime: '2020-03-08T05:33:37.093Z' - publishers: [] + type: http + messageSentInstant: '2025-03-18T20:55:58.433Z' iteration: 1 totalIterations: 2 hooks: @@ -2298,23 +1935,23 @@ requisitions: valid: true tests: [] arguments: - elapsedTime: 210 + elapsedTime: 202 time: - startTime: '2020-03-08T05:33:37.093Z' - endTime: '2020-03-08T05:33:37.303Z' - totalTime: 210 - timeout: 5000 - requisitions: [] + startTime: '2025-03-18T20:55:58.231Z' + endTime: '2025-03-18T20:55:58.433Z' + totalTime: 202 + timeout: 10000 + tasks: [] - valid: true - name: 'Requisition #2' - id: 0233360793_26a61bc62a_64352 + name: 'Task #3' + id: 1355570988_ab6beec7d0_125382 level: 2 - subscriptions: [] - publishers: + sensors: [] + actuators: - - id: 0233360793_fb73e2d9b8_394615 - name: 'Publisher #0' + id: 1355570988_09d1baed2f_574714 + name: 'Actuator #0' valid: true hooks: onInit: @@ -2325,87 +1962,48 @@ requisitions: onFinish: arguments: executedHooks: - - onInit - - onResponseReceived - - onMessageReceived - - onFinish - elapsedTime: 206 + onInit: [] + onResponseReceived: + - status + - statusCode + - body + - headers + - elapsedTime + onFinish: + - executedHooks + - elapsedTime + elapsedTime: 211 tests: - name: Published valid: true - description: "{\n \"statusCode\": 200,\n \"headers\": {\n \"x-powered-by\": \"Express\",\n \"access-control-allow-origin\": \"*\",\n \"access-control-allow-headers\": \"Origin, X-Requested-With, Content-Type, Accept\",\n \"content-type\": \"text/html; charset=utf-8\",\n \"content-length\": \"5\",\n \"etag\": \"W/\\\"5-4JlqN8E9RMOwYHSTnUP6N1m9MsE\\\"\",\n \"date\": \"Sun, 08 Mar 2020 05:33:37 GMT\",\n \"connection\": \"close\"\n },\n \"body\": \"first\"\n}" + description: "{\n \"statusCode\": 200,\n \"headers\": {\n \"access-control-allow-headers\": \"Origin, X-Requested-With, Content-Type, Accept\",\n \"access-control-allow-origin\": \"*\",\n \"connection\": \"keep-alive\",\n \"content-length\": \"6\",\n \"content-type\": \"text/html; charset=utf-8\",\n \"date\": \"Tue, 18 Mar 2025 20:55:58 GMT\",\n \"etag\": \"W/\\\"6-NS94KaI4SwAcwSsMJhPHVkVKH2o\\\"\",\n \"keep-alive\": \"timeout=5\",\n \"x-powered-by\": \"Express\"\n },\n \"body\": \"second\"\n}" + implicit: true valid: true onResponseReceived: arguments: + status: 200 statusCode: 200 - body: first + body: second headers: - x-powered-by: Express - access-control-allow-origin: '*' access-control-allow-headers: 'Origin, X-Requested-With, Content-Type, Accept' + access-control-allow-origin: '*' + connection: keep-alive + content-length: '6' content-type: 'text/html; charset=utf-8' - content-length: '5' - etag: 'W/"5-4JlqN8E9RMOwYHSTnUP6N1m9MsE"' - date: 'Sun, 08 Mar 2020 05:33:37 GMT' - connection: close - request: - uri: - protocol: 'http:' - slashes: true - auth: null - host: 'localhost:23023' - port: '23023' - hostname: localhost - hash: null - search: null - query: null - pathname: /first - path: /first - href: 'http://localhost:23023/first' - method: get - headers: - content-length: 0 + date: 'Tue, 18 Mar 2025 20:55:58 GMT' + etag: 'W/"6-NS94KaI4SwAcwSsMJhPHVkVKH2o"' + keep-alive: timeout=5 + x-powered-by: Express + elapsedTime: 211 tests: - name: 'Assertion #0' valid: true description: 'Expected ''statusCode'' to be equal to ''200''. Received ''200''' valid: true - onMessageReceived: - arguments: - statusCode: 200 - body: first - headers: - x-powered-by: Express - access-control-allow-origin: '*' - access-control-allow-headers: 'Origin, X-Requested-With, Content-Type, Accept' - content-type: 'text/html; charset=utf-8' - content-length: '5' - etag: 'W/"5-4JlqN8E9RMOwYHSTnUP6N1m9MsE"' - date: 'Sun, 08 Mar 2020 05:33:37 GMT' - connection: close - request: - uri: - protocol: 'http:' - slashes: true - auth: null - host: 'localhost:23023' - port: '23023' - hostname: localhost - hash: null - search: null - query: null - pathname: /first - path: /first - href: 'http://localhost:23023/first' - method: get - headers: - content-length: 0 - tests: [] - valid: true type: http - publishTime: '2020-03-08T05:33:37.090Z' + messageSentInstant: '2025-03-18T20:55:58.232Z' iteration: 0 totalIterations: 2 hooks: @@ -2418,23 +2016,23 @@ requisitions: valid: true tests: [] arguments: - elapsedTime: 206 + elapsedTime: 211 time: - startTime: '2020-03-08T05:33:36.884Z' - endTime: '2020-03-08T05:33:37.090Z' - totalTime: 206 - timeout: 5000 - requisitions: [] + startTime: '2025-03-18T20:55:58.021Z' + endTime: '2025-03-18T20:55:58.232Z' + totalTime: 211 + timeout: 10000 + tasks: [] - valid: true - name: 'Requisition #2' - id: 0233360793_26a61bc62a_64352 + name: 'Task #3' + id: 1355570988_ab6beec7d0_125382 level: 2 - subscriptions: [] - publishers: + sensors: [] + actuators: - - id: 0233360793_fb73e2d9b8_394615 - name: 'Publisher #0' + id: 1355570988_09d1baed2f_574714 + name: 'Actuator #0' valid: true hooks: onInit: @@ -2445,87 +2043,48 @@ requisitions: onFinish: arguments: executedHooks: - - onInit - - onResponseReceived - - onMessageReceived - - onFinish - elapsedTime: 209 + onInit: [] + onResponseReceived: + - status + - statusCode + - body + - headers + - elapsedTime + onFinish: + - executedHooks + - elapsedTime + elapsedTime: 202 tests: - name: Published valid: true - description: "{\n \"statusCode\": 200,\n \"headers\": {\n \"x-powered-by\": \"Express\",\n \"access-control-allow-origin\": \"*\",\n \"access-control-allow-headers\": \"Origin, X-Requested-With, Content-Type, Accept\",\n \"content-type\": \"text/html; charset=utf-8\",\n \"content-length\": \"5\",\n \"etag\": \"W/\\\"5-4JlqN8E9RMOwYHSTnUP6N1m9MsE\\\"\",\n \"date\": \"Sun, 08 Mar 2020 05:33:37 GMT\",\n \"connection\": \"close\"\n },\n \"body\": \"first\"\n}" + description: "{\n \"statusCode\": 200,\n \"headers\": {\n \"access-control-allow-headers\": \"Origin, X-Requested-With, Content-Type, Accept\",\n \"access-control-allow-origin\": \"*\",\n \"connection\": \"keep-alive\",\n \"content-length\": \"6\",\n \"content-type\": \"text/html; charset=utf-8\",\n \"date\": \"Tue, 18 Mar 2025 20:55:58 GMT\",\n \"etag\": \"W/\\\"6-NS94KaI4SwAcwSsMJhPHVkVKH2o\\\"\",\n \"keep-alive\": \"timeout=5\",\n \"x-powered-by\": \"Express\"\n },\n \"body\": \"second\"\n}" + implicit: true valid: true onResponseReceived: arguments: + status: 200 statusCode: 200 - body: first + body: second headers: - x-powered-by: Express - access-control-allow-origin: '*' access-control-allow-headers: 'Origin, X-Requested-With, Content-Type, Accept' + access-control-allow-origin: '*' + connection: keep-alive + content-length: '6' content-type: 'text/html; charset=utf-8' - content-length: '5' - etag: 'W/"5-4JlqN8E9RMOwYHSTnUP6N1m9MsE"' - date: 'Sun, 08 Mar 2020 05:33:37 GMT' - connection: close - request: - uri: - protocol: 'http:' - slashes: true - auth: null - host: 'localhost:23023' - port: '23023' - hostname: localhost - hash: null - search: null - query: null - pathname: /first - path: /first - href: 'http://localhost:23023/first' - method: get - headers: - content-length: 0 + date: 'Tue, 18 Mar 2025 20:55:58 GMT' + etag: 'W/"6-NS94KaI4SwAcwSsMJhPHVkVKH2o"' + keep-alive: timeout=5 + x-powered-by: Express + elapsedTime: 202 tests: - name: 'Assertion #0' valid: true description: 'Expected ''statusCode'' to be equal to ''200''. Received ''200''' valid: true - onMessageReceived: - arguments: - statusCode: 200 - body: first - headers: - x-powered-by: Express - access-control-allow-origin: '*' - access-control-allow-headers: 'Origin, X-Requested-With, Content-Type, Accept' - content-type: 'text/html; charset=utf-8' - content-length: '5' - etag: 'W/"5-4JlqN8E9RMOwYHSTnUP6N1m9MsE"' - date: 'Sun, 08 Mar 2020 05:33:37 GMT' - connection: close - request: - uri: - protocol: 'http:' - slashes: true - auth: null - host: 'localhost:23023' - port: '23023' - hostname: localhost - hash: null - search: null - query: null - pathname: /first - path: /first - href: 'http://localhost:23023/first' - method: get - headers: - content-length: 0 - tests: [] - valid: true type: http - publishTime: '2020-03-08T05:33:37.298Z' + messageSentInstant: '2025-03-18T20:55:58.434Z' iteration: 1 totalIterations: 2 hooks: @@ -2538,283 +2097,56 @@ requisitions: valid: true tests: [] arguments: - elapsedTime: 209 - time: - startTime: '2020-03-08T05:33:37.090Z' - endTime: '2020-03-08T05:33:37.299Z' - totalTime: 209 - timeout: 5000 - requisitions: [] - - - valid: true - name: 'Requisition #3' - id: 0233360793_6099fb1229_611959 - level: 2 - subscriptions: [] - publishers: - - - id: 0233360793_7d016346aa_595254 - name: 'Publisher #0' - valid: true - hooks: - onInit: - arguments: - elapsedTime: 0 - tests: [] - valid: true - onFinish: - arguments: - executedHooks: - - onInit - - onResponseReceived - - onMessageReceived - - onFinish - elapsedTime: 213 - tests: - - - name: Published - valid: true - description: "{\n \"statusCode\": 200,\n \"headers\": {\n \"x-powered-by\": \"Express\",\n \"access-control-allow-origin\": \"*\",\n \"access-control-allow-headers\": \"Origin, X-Requested-With, Content-Type, Accept\",\n \"content-type\": \"text/html; charset=utf-8\",\n \"content-length\": \"6\",\n \"etag\": \"W/\\\"6-NS94KaI4SwAcwSsMJhPHVkVKH2o\\\"\",\n \"date\": \"Sun, 08 Mar 2020 05:33:37 GMT\",\n \"connection\": \"close\"\n },\n \"body\": \"second\"\n}" - valid: true - onResponseReceived: - arguments: - statusCode: 200 - body: second - headers: - x-powered-by: Express - access-control-allow-origin: '*' - access-control-allow-headers: 'Origin, X-Requested-With, Content-Type, Accept' - content-type: 'text/html; charset=utf-8' - content-length: '6' - etag: 'W/"6-NS94KaI4SwAcwSsMJhPHVkVKH2o"' - date: 'Sun, 08 Mar 2020 05:33:37 GMT' - connection: close - request: - uri: - protocol: 'http:' - slashes: true - auth: null - host: 'localhost:23023' - port: '23023' - hostname: localhost - hash: null - search: null - query: null - pathname: /second - path: /second - href: 'http://localhost:23023/second' - method: get - headers: - content-length: 0 - tests: - - - name: 'Assertion #0' - valid: true - description: 'Expected ''statusCode'' to be equal to ''200''. Received ''200''' - valid: true - onMessageReceived: - arguments: - statusCode: 200 - body: second - headers: - x-powered-by: Express - access-control-allow-origin: '*' - access-control-allow-headers: 'Origin, X-Requested-With, Content-Type, Accept' - content-type: 'text/html; charset=utf-8' - content-length: '6' - etag: 'W/"6-NS94KaI4SwAcwSsMJhPHVkVKH2o"' - date: 'Sun, 08 Mar 2020 05:33:37 GMT' - connection: close - request: - uri: - protocol: 'http:' - slashes: true - auth: null - host: 'localhost:23023' - port: '23023' - hostname: localhost - hash: null - search: null - query: null - pathname: /second - path: /second - href: 'http://localhost:23023/second' - method: get - headers: - content-length: 0 - tests: [] - valid: true - type: http - publishTime: '2020-03-08T05:33:37.097Z' - iteration: 0 - totalIterations: 2 - hooks: - onInit: - valid: true - tests: [] - arguments: - elapsedTime: 0 - onFinish: - valid: true - tests: [] - arguments: - elapsedTime: 213 - time: - startTime: '2020-03-08T05:33:36.884Z' - endTime: '2020-03-08T05:33:37.097Z' - totalTime: 213 - timeout: 5000 - requisitions: [] - - - valid: true - name: 'Requisition #3' - id: 0233360793_6099fb1229_611959 - level: 2 - subscriptions: [] - publishers: - - - id: 0233360793_7d016346aa_595254 - name: 'Publisher #0' - valid: true - hooks: - onInit: - arguments: - elapsedTime: 0 - tests: [] - valid: true - onFinish: - arguments: - executedHooks: - - onInit - - onResponseReceived - - onMessageReceived - - onFinish - elapsedTime: 205 - tests: - - - name: Published - valid: true - description: "{\n \"statusCode\": 200,\n \"headers\": {\n \"x-powered-by\": \"Express\",\n \"access-control-allow-origin\": \"*\",\n \"access-control-allow-headers\": \"Origin, X-Requested-With, Content-Type, Accept\",\n \"content-type\": \"text/html; charset=utf-8\",\n \"content-length\": \"6\",\n \"etag\": \"W/\\\"6-NS94KaI4SwAcwSsMJhPHVkVKH2o\\\"\",\n \"date\": \"Sun, 08 Mar 2020 05:33:37 GMT\",\n \"connection\": \"close\"\n },\n \"body\": \"second\"\n}" - valid: true - onResponseReceived: - arguments: - statusCode: 200 - body: second - headers: - x-powered-by: Express - access-control-allow-origin: '*' - access-control-allow-headers: 'Origin, X-Requested-With, Content-Type, Accept' - content-type: 'text/html; charset=utf-8' - content-length: '6' - etag: 'W/"6-NS94KaI4SwAcwSsMJhPHVkVKH2o"' - date: 'Sun, 08 Mar 2020 05:33:37 GMT' - connection: close - request: - uri: - protocol: 'http:' - slashes: true - auth: null - host: 'localhost:23023' - port: '23023' - hostname: localhost - hash: null - search: null - query: null - pathname: /second - path: /second - href: 'http://localhost:23023/second' - method: get - headers: - content-length: 0 - tests: - - - name: 'Assertion #0' - valid: true - description: 'Expected ''statusCode'' to be equal to ''200''. Received ''200''' - valid: true - onMessageReceived: - arguments: - statusCode: 200 - body: second - headers: - x-powered-by: Express - access-control-allow-origin: '*' - access-control-allow-headers: 'Origin, X-Requested-With, Content-Type, Accept' - content-type: 'text/html; charset=utf-8' - content-length: '6' - etag: 'W/"6-NS94KaI4SwAcwSsMJhPHVkVKH2o"' - date: 'Sun, 08 Mar 2020 05:33:37 GMT' - connection: close - request: - uri: - protocol: 'http:' - slashes: true - auth: null - host: 'localhost:23023' - port: '23023' - hostname: localhost - hash: null - search: null - query: null - pathname: /second - path: /second - href: 'http://localhost:23023/second' - method: get - headers: - content-length: 0 - tests: [] - valid: true - type: http - publishTime: '2020-03-08T05:33:37.304Z' - iteration: 1 - totalIterations: 2 - hooks: - onInit: - valid: true - tests: [] - arguments: - elapsedTime: 0 - onFinish: - valid: true - tests: [] - arguments: - elapsedTime: 205 + elapsedTime: 202 time: - startTime: '2020-03-08T05:33:37.099Z' - endTime: '2020-03-08T05:33:37.304Z' - totalTime: 205 - timeout: 5000 - requisitions: [] + startTime: '2025-03-18T20:55:58.232Z' + endTime: '2025-03-18T20:55:58.434Z' + totalTime: 202 + timeout: 10000 + tasks: [] - valid: true name: examples/http-proxy.yml - id: 0233360793_aba5464e08_110976 + id: 1355570988_422eeb1e6f_508871 level: 1 - subscriptions: + sensors: - - id: 0233360793_be017cf7e2_373126 - name: 'proxy subscription' + id: 1355570988_ee4f61a440_15335 + name: 'proxy sensor' type: http-proxy hooks: onInit: valid: true tests: [] - arguments: - elapsedTime: 0 + arguments: {} onFinish: valid: true tests: - + implicit: true valid: true name: 'Message received' - description: 'Subscription has received its message' + description: 'Sensor has received its message' arguments: executedHooks: - - onInit - - onOriginalMessageReceived - - onMessageReceived - - onFinish - elapsedTime: 208 + onInit: [] + onOriginalMessageReceived: + - headers + - params + - query + - url + - body + - elapsedTime + onMessageReceived: + - status + - statusCode + - body + - headers + - elapsedTime + onFinish: + - executedHooks + - elapsedTime + elapsedTime: 193 onOriginalMessageReceived: valid: true tests: @@ -2824,16 +2156,22 @@ requisitions: description: 'Expected ''body'' to be equal to ''original''. Received ''original''' arguments: headers: - content-length: '8' host: 'localhost:23085' - connection: close + connection: keep-alive + content-type: text/plain;charset=UTF-8 + accept: '*/*' + accept-language: '*' + sec-fetch-mode: cors + user-agent: node + accept-encoding: 'gzip, deflate' + content-length: '8' params: - '0': enqueuer/123456 + id: '123456' query: query: proxied url: '/proxy/enqueuer/123456?query=proxied' body: original - elapsedTime: 161 + elapsedTime: 157 onMessageReceived: valid: true tests: @@ -2846,62 +2184,53 @@ requisitions: valid: true description: 'Expected ''statusCode'' to be equal to ''200''. Received ''200''' arguments: + status: 200 statusCode: 200 body: 'original -> proxy -> real' headers: - x-powered-by: Express - access-control-allow-origin: '*' access-control-allow-headers: 'Origin, X-Requested-With, Content-Type, Accept' - content-type: 'text/html; charset=utf-8' + access-control-allow-origin: '*' + connection: keep-alive content-length: '25' + content-type: 'text/html; charset=utf-8' + date: 'Tue, 18 Mar 2025 20:55:58 GMT' etag: 'W/"19-t4VpDbTMvQQ876rQU32330fLSVI"' - date: 'Sun, 08 Mar 2020 05:33:37 GMT' - connection: close - request: - uri: - protocol: 'http:' - slashes: true - auth: null - host: 'localhost:23086' - port: '23086' - hostname: localhost - hash: null - search: '?query=proxied' - query: query=proxied - pathname: /real/enqueuer/123456 - path: '/real/enqueuer/123456?query=proxied' - href: 'http://localhost:23086/real/enqueuer/123456?query=proxied' - method: POST - headers: - content-length: '8' - host: 'localhost:23085' - connection: close - Content-Length: 17 - valid: true - subscriptionTime: '2020-03-08T05:33:36.951Z' + keep-alive: timeout=5 + x-powered-by: Express + elapsedTime: 191 + valid: true + sensorTime: '2025-03-18T20:55:58.111Z' - - id: 0233360793_9421bcf470_5622 + id: 1355570988_f53ce68398_803950 name: real type: http hooks: onInit: valid: true tests: [] - arguments: - elapsedTime: 0 + arguments: {} onFinish: valid: true tests: - + implicit: true valid: true name: 'Message received' - description: "{\n \"headers\": {\n \"content-length\": \"17\",\n \"host\": \"localhost:23085\",\n \"connection\": \"close\"\n },\n \"params\": {\n \"id\": \"123456\"\n },\n \"query\": {\n \"query\": \"proxied\"\n },\n \"url\": \"/real/enqueuer/123456?query=proxied\",\n \"body\": \"original -> proxy\",\n \"elapsedTime\": 196\n}" + description: "{\n \"headers\": {\n \"host\": \"localhost:23086\",\n \"connection\": \"keep-alive\",\n \"content-type\": \"text/plain;charset=UTF-8\",\n \"accept\": \"*/*\",\n \"accept-language\": \"*\",\n \"sec-fetch-mode\": \"cors\",\n \"user-agent\": \"node\",\n \"accept-encoding\": \"gzip, deflate\",\n \"content-length\": \"17\"\n },\n \"params\": {\n \"id\": \"999\"\n },\n \"query\": {\n \"query\": \"proxied\"\n },\n \"url\": \"/real/enqueuer/999?query=proxied\",\n \"body\": \"original -> proxy\"\n}" arguments: executedHooks: - - onInit - - onMessageReceived - - onFinish - elapsedTime: 208 + onInit: [] + onMessageReceived: + - headers + - params + - query + - url + - body + - elapsedTime + onFinish: + - executedHooks + - elapsedTime + elapsedTime: 194 onMessageReceived: valid: true tests: @@ -2912,29 +2241,35 @@ requisitions: - name: 'Assertion #1' valid: true - description: 'Expected ''params.id'' to be equal to ''123456''. Received ''123456''' + description: 'Expected ''params.id'' to be equal to ''999''. Received ''999''' - name: 'Assertion #2' valid: true description: 'Expected ''query.query'' to be equal to ''proxied''. Received ''proxied''' arguments: headers: + host: 'localhost:23086' + connection: keep-alive + content-type: text/plain;charset=UTF-8 + accept: '*/*' + accept-language: '*' + sec-fetch-mode: cors + user-agent: node + accept-encoding: 'gzip, deflate' content-length: '17' - host: 'localhost:23085' - connection: close params: - id: '123456' + id: '999' query: query: proxied - url: '/real/enqueuer/123456?query=proxied' + url: '/real/enqueuer/999?query=proxied' body: 'original -> proxy' - elapsedTime: 196 + elapsedTime: 190 valid: true - subscriptionTime: '2020-03-08T05:33:36.951Z' - publishers: + sensorTime: '2025-03-18T20:55:58.111Z' + actuators: - - id: 0233360793_8e53ccc444_566876 - name: 'publisher proxy' + id: 1355570988_b3425b4e2f_670785 + name: 'actuator proxy' valid: true hooks: onInit: @@ -2945,91 +2280,44 @@ requisitions: onFinish: arguments: executedHooks: - - onInit - - onResponseReceived - - onMessageReceived - - onFinish - elapsedTime: 208 + onInit: [] + onResponseReceived: + - status + - statusCode + - body + - headers + - elapsedTime + onFinish: + - executedHooks + - elapsedTime + elapsedTime: 194 tests: - name: Published valid: true - description: "{\n \"statusCode\": 400,\n \"headers\": {\n \"x-powered-by\": \"Express\",\n \"access-control-allow-origin\": \"*\",\n \"access-control-allow-headers\": \"Origin, X-Requested-With, Content-Type, Accept\",\n \"content-type\": \"text/html; charset=utf-8\",\n \"content-length\": \"42\",\n \"etag\": \"W/\\\"19-t4VpDbTMvQQ876rQU32330fLSVI\\\"\",\n \"date\": \"Sun, 08 Mar 2020 05:33:37 GMT\",\n \"connection\": \"close\"\n },\n \"body\": \"original -> proxy -> real -> proxied again\"\n}" + description: "{\n \"statusCode\": 400,\n \"headers\": {\n \"access-control-allow-headers\": \"Origin, X-Requested-With, Content-Type, Accept\",\n \"access-control-allow-origin\": \"*\",\n \"connection\": \"keep-alive\",\n \"content-length\": \"44\",\n \"content-type\": \"text/html; charset=utf-8\",\n \"date\": \"Tue, 18 Mar 2025 20:55:58 GMT\",\n \"etag\": \"W/\\\"19-t4VpDbTMvQQ876rQU32330fLSVI\\\"\",\n \"keep-alive\": \"timeout=5\",\n \"x-powered-by\": \"Express\"\n },\n \"body\": \"original -> proxy -> real' -> proxied again'\"\n}" + implicit: true valid: true onResponseReceived: arguments: + status: 400 statusCode: 400 - body: 'original -> proxy -> real -> proxied again' + body: 'original -> proxy -> real'' -> proxied again''' headers: - x-powered-by: Express - access-control-allow-origin: '*' access-control-allow-headers: 'Origin, X-Requested-With, Content-Type, Accept' - content-type: 'text/html; charset=utf-8' - content-length: '42' - etag: 'W/"19-t4VpDbTMvQQ876rQU32330fLSVI"' - date: 'Sun, 08 Mar 2020 05:33:37 GMT' - connection: close - request: - uri: - protocol: 'http:' - slashes: true - auth: null - host: 'localhost:23085' - port: '23085' - hostname: localhost - hash: null - search: '?query=proxied' - query: query=proxied - pathname: /proxy/enqueuer/123456 - path: '/proxy/enqueuer/123456?query=proxied' - href: 'http://localhost:23085/proxy/enqueuer/123456?query=proxied' - method: patch - headers: - Content-Length: 8 - tests: [] - valid: true - onMessageReceived: - arguments: - statusCode: 400 - body: 'original -> proxy -> real -> proxied again' - headers: - x-powered-by: Express access-control-allow-origin: '*' - access-control-allow-headers: 'Origin, X-Requested-With, Content-Type, Accept' + connection: keep-alive + content-length: '44' content-type: 'text/html; charset=utf-8' - content-length: '42' + date: 'Tue, 18 Mar 2025 20:55:58 GMT' etag: 'W/"19-t4VpDbTMvQQ876rQU32330fLSVI"' - date: 'Sun, 08 Mar 2020 05:33:37 GMT' - connection: close - request: - uri: - protocol: 'http:' - slashes: true - auth: null - host: 'localhost:23085' - port: '23085' - hostname: localhost - hash: null - search: '?query=proxied' - query: query=proxied - pathname: /proxy/enqueuer/123456 - path: '/proxy/enqueuer/123456?query=proxied' - href: 'http://localhost:23085/proxy/enqueuer/123456?query=proxied' - method: patch - headers: - Content-Length: 8 - tests: - - - name: 'Assertion #0' - valid: true - description: 'Expected ''statusCode'' to be equal to ''400''. Received ''400''' - - - name: 'Assertion #1' - valid: true - description: 'Expected ''body'' to be equal to ''original -> proxy -> real -> proxied again''. Received ''original -> proxy -> real -> proxied again''' + keep-alive: timeout=5 + x-powered-by: Express + elapsedTime: 193 + tests: [] valid: true type: http - publishTime: '2020-03-08T05:33:37.064Z' + messageSentInstant: '2025-03-18T20:55:58.205Z' iteration: 0 totalIterations: 1 hooks: @@ -3042,160 +2330,500 @@ requisitions: valid: true tests: [] arguments: - elapsedTime: 208 + elapsedTime: 194 time: - startTime: '2020-03-08T05:33:36.857Z' - endTime: '2020-03-08T05:33:37.065Z' - totalTime: 208 + startTime: '2025-03-18T20:55:58.012Z' + endTime: '2025-03-18T20:55:58.206Z' + totalTime: 194 timeout: 5000 - requisitions: [] + tasks: [] - valid: true - name: examples/http.yml - id: 0233360793_07461360b5_845095 + name: examples/http-same-port.yml + id: 1355570988_7e4e495f2f_113776 level: 1 - subscriptions: - - - id: 0233360793_a0523c01e7_926595 - name: 'Subscription #0' - type: http - hooks: - onInit: - valid: true - tests: [] - arguments: - elapsedTime: 0 - onFinish: - valid: true - tests: - - - valid: true - name: 'Message received' - description: "{\n \"headers\": {\n \"content-length\": \"8\",\n \"host\": \"localhost:23075\",\n \"connection\": \"close\"\n },\n \"params\": {},\n \"query\": {},\n \"url\": \"/resource\",\n \"body\": \"enqueuer\",\n \"elapsedTime\": 162\n}" - arguments: - executedHooks: - - onInit - - onMessageReceived - - onFinish - elapsedTime: 178 - onMessageReceived: - valid: true - tests: - - - name: 'Assertion #0' - valid: true - description: 'Expecting ''enqueuer'' (body) to contain ''queue''' - arguments: - headers: - content-length: '8' - host: 'localhost:23075' - connection: close - params: {} - query: {} - url: /resource - body: enqueuer - elapsedTime: 162 + sensors: [] + actuators: [] + iteration: 0 + totalIterations: 1 + hooks: + onInit: + valid: true + tests: [] + arguments: + elapsedTime: 0 + onFinish: valid: true - subscriptionTime: '2020-03-08T05:33:36.951Z' - publishers: + tests: [] + arguments: + elapsedTime: 257 + time: + startTime: '2025-03-18T20:55:58.012Z' + endTime: '2025-03-18T20:55:58.269Z' + totalTime: 257 + timeout: 5000 + tasks: - - id: 0233360793_7b553c9aff_715465 - name: 'Publisher #0' valid: true - hooks: - onInit: - arguments: - elapsedTime: 0 - tests: [] - valid: true - onFinish: - arguments: - executedHooks: - - onInit - - onResponseReceived - - onMessageReceived - - onFinish - elapsedTime: 178 - tests: - - - name: Published + name: 'Task #0' + id: 1355570988_be96ed2f95_247344 + level: 2 + sensors: + - + id: 1355570988_8438322211_729194 + name: 'Sensor #0' + type: http + hooks: + onInit: valid: true - description: "{\n \"statusCode\": 444,\n \"headers\": {\n \"x-powered-by\": \"Express\",\n \"access-control-allow-origin\": \"*\",\n \"access-control-allow-headers\": \"Origin, X-Requested-With, Content-Type, Accept\",\n \"content-type\": \"text/html; charset=utf-8\",\n \"content-length\": \"4\",\n \"etag\": \"W/\\\"4-W/H9kn37hnlJai5s8Ay+UMHIcUU\\\"\",\n \"date\": \"Sun, 08 Mar 2020 05:33:36 GMT\",\n \"connection\": \"close\"\n },\n \"body\": \"blah\"\n}" - valid: true - onResponseReceived: - arguments: - statusCode: 444 - body: blah - headers: - x-powered-by: Express - access-control-allow-origin: '*' - access-control-allow-headers: 'Origin, X-Requested-With, Content-Type, Accept' - content-type: 'text/html; charset=utf-8' - content-length: '4' - etag: 'W/"4-W/H9kn37hnlJai5s8Ay+UMHIcUU"' - date: 'Sun, 08 Mar 2020 05:33:36 GMT' - connection: close - request: - uri: - protocol: 'http:' - slashes: true - auth: null - host: 'localhost:23075' - port: '23075' - hostname: localhost - hash: null - search: null - query: null - pathname: /resource - path: /resource - href: 'http://localhost:23075/resource' - method: post - headers: - Content-Length: 8 - tests: - - + tests: [] + arguments: {} + onFinish: + valid: true + tests: + - + implicit: true + valid: true + name: 'Message received' + description: "{\n \"headers\": {\n \"host\": \"localhost:23065\",\n \"connection\": \"keep-alive\",\n \"content-type\": \"application/json\",\n \"nqr\": \"actuator\",\n \"accept\": \"*/*\",\n \"accept-language\": \"*\",\n \"sec-fetch-mode\": \"cors\",\n \"user-agent\": \"node\",\n \"accept-encoding\": \"gzip, deflate\",\n \"content-length\": \"20\"\n },\n \"params\": {\n \"firstId\": \"idStuff\"\n },\n \"query\": {\n \"query\": \"2345\"\n },\n \"url\": \"/enqueuer/idStuff?query=2345\",\n \"body\": {\n \"enqueuer\": \"virgs\"\n }\n}" + arguments: + executedHooks: + onInit: [] + onMessageReceived: + - headers + - params + - query + - url + - body + - elapsedTime + onFinish: + - executedHooks + - elapsedTime + elapsedTime: 244 + onMessageReceived: + valid: true + tests: + - + name: Payload + valid: true + description: 'Expected ''JSON.parse(body).enqueuer'' to be equal to ''virgs''. Received ''virgs''' + - + name: Params + valid: true + description: 'Expected ''params.firstId'' to be equal to ''idStuff''. Received ''idStuff''' + - + name: Query + valid: true + description: 'Expected ''query.query'' to be equal to ''2345''. Received ''2345''' + - + name: Header + valid: true + description: 'Expected ''headers.nqr'' to be equal to ''actuator''. Received ''actuator''' + arguments: + headers: + host: 'localhost:23065' + connection: keep-alive + content-type: application/json + nqr: actuator + accept: '*/*' + accept-language: '*' + sec-fetch-mode: cors + user-agent: node + accept-encoding: 'gzip, deflate' + content-length: '20' + params: + firstId: idStuff + query: + query: '2345' + url: '/enqueuer/idStuff?query=2345' + body: '{"enqueuer":"virgs"}' + elapsedTime: 150 + valid: true + sensorTime: '2025-03-18T20:55:58.111Z' + - + id: 1355570988_bb1bad56ac_392821 + name: avoidable + type: http + hooks: + onInit: + valid: true + tests: [] + arguments: {} + onFinish: + valid: true + tests: + - + valid: true + implicit: true + name: 'Sensor avoided' + description: 'Avoidable sensor has not received any message' + arguments: + executedHooks: + onInit: [] + onFinish: + - executedHooks + - elapsedTime + elapsedTime: 244 + valid: true + sensorTime: '2025-03-18T20:55:58.111Z' + actuators: + - + id: 1355570988_ea8916d8d1_755330 + name: 'Actuator #0' + valid: true + hooks: + onInit: + arguments: + elapsedTime: 0 + tests: [] + valid: true + onFinish: + arguments: + executedHooks: + onInit: [] + onResponseReceived: + - status + - statusCode + - body + - headers + - elapsedTime + onFinish: + - executedHooks + - elapsedTime + elapsedTime: 244 + tests: + - + name: Published + valid: true + description: "{\n \"statusCode\": 321,\n \"headers\": {\n \"access-control-allow-headers\": \"Origin, X-Requested-With, Content-Type, Accept\",\n \"access-control-allow-origin\": \"*\",\n \"connection\": \"close\",\n \"content-length\": \"27\",\n \"content-type\": \"text/html; charset=utf-8\",\n \"date\": \"Tue, 18 Mar 2025 20:55:58 GMT\",\n \"etag\": \"W/\\\"1b-e5esTWfu+XftewZ5g2Tclr7ClTo\\\"\",\n \"nqr\": \"sensor\",\n \"x-powered-by\": \"Express\"\n },\n \"body\": \"dynamically changed payload\"\n}" + implicit: true + valid: true + onResponseReceived: + arguments: + status: 321 + statusCode: 321 + body: 'dynamically changed payload' + headers: + access-control-allow-headers: 'Origin, X-Requested-With, Content-Type, Accept' + access-control-allow-origin: '*' + connection: close + content-length: '27' + content-type: 'text/html; charset=utf-8' + date: 'Tue, 18 Mar 2025 20:55:58 GMT' + etag: 'W/"1b-e5esTWfu+XftewZ5g2Tclr7ClTo"' + nqr: sensor + x-powered-by: Express + elapsedTime: 166 + tests: + - + name: 'Status Code' + valid: true + description: 'Expected ''statusCode'' to be equal to ''321''. Received ''321''' + - + name: Body + valid: true + description: 'Expected ''body'' to be equal to ''dynamically changed payload''. Received ''dynamically changed payload''' + - + name: Header + valid: true + description: 'Expected ''headers.nqr'' to be equal to ''sensor''. Received ''sensor''' + valid: true + type: http + messageSentInstant: '2025-03-18T20:55:58.187Z' + iteration: 0 + totalIterations: 1 + hooks: + onInit: + valid: true + tests: [] + arguments: + elapsedTime: 0 + onFinish: + valid: true + tests: [] + arguments: + elapsedTime: 244 + time: + startTime: '2025-03-18T20:55:58.021Z' + endTime: '2025-03-18T20:55:58.265Z' + totalTime: 244 + timeout: 3000 + tasks: [] + - + valid: true + name: 'Task #1' + id: 1355570988_b7d51eb9d2_505796 + level: 2 + sensors: + - + id: 1355570988_56d279f381_963752 + name: fourth + type: http + hooks: + onInit: + valid: true + tests: [] + arguments: {} + onFinish: + valid: true + tests: + - + implicit: true + valid: true + name: 'Message received' + description: "{\n \"headers\": {\n \"host\": \"localhost:23065\",\n \"connection\": \"keep-alive\",\n \"content-type\": \"application/json\",\n \"accept\": \"*/*\",\n \"accept-language\": \"*\",\n \"sec-fetch-mode\": \"cors\",\n \"user-agent\": \"node\",\n \"accept-encoding\": \"gzip, deflate\",\n \"content-length\": \"19\"\n },\n \"params\": {\n \"secondId\": \"idStuff\"\n },\n \"query\": {\n \"query\": \"111\"\n },\n \"url\": \"/enqueuer/idStuff?query=111\",\n \"body\": {\n \"duplicated\": true\n }\n}" + arguments: + executedHooks: + onInit: [] + onMessageReceived: + - headers + - params + - query + - url + - body + - elapsedTime + onFinish: + - executedHooks + - elapsedTime + elapsedTime: 3 + onMessageReceived: + valid: true + tests: + - + name: Payload + valid: true + description: 'Expecting ''JSON.parse(body).duplicated'' to be true. Received: true' + arguments: + headers: + host: 'localhost:23065' + connection: keep-alive + content-type: application/json + accept: '*/*' + accept-language: '*' + sec-fetch-mode: cors + user-agent: node + accept-encoding: 'gzip, deflate' + content-length: '19' + params: + secondId: idStuff + query: + query: '111' + url: '/enqueuer/idStuff?query=111' + body: '{"duplicated":true}' + elapsedTime: 2 + valid: true + sensorTime: '2025-03-18T20:55:58.266Z' + actuators: + - + id: 1355570988_cdcc7b4c16_648002 + name: 'actuator description' + valid: true + hooks: + onInit: + arguments: + elapsedTime: 0 + tests: [] + valid: true + onFinish: + arguments: + executedHooks: + onInit: [] + onResponseReceived: + - status + - statusCode + - body + - headers + - elapsedTime + onFinish: + - executedHooks + - elapsedTime + elapsedTime: 3 + tests: + - + name: Published + valid: true + description: "{\n \"statusCode\": 321,\n \"headers\": {\n \"access-control-allow-headers\": \"Origin, X-Requested-With, Content-Type, Accept\",\n \"access-control-allow-origin\": \"*\",\n \"connection\": \"keep-alive\",\n \"content-length\": \"25\",\n \"content-type\": \"text/html; charset=utf-8\",\n \"date\": \"Tue, 18 Mar 2025 20:55:58 GMT\",\n \"etag\": \"W/\\\"19-yZRAgggcER0sMyRTVBBpErTPT/A\\\"\",\n \"keep-alive\": \"timeout=5\",\n \"x-powered-by\": \"Express\"\n },\n \"body\": \"duplicatedResponsePayload\"\n}" + implicit: true + valid: true + onResponseReceived: + arguments: + status: 321 + statusCode: 321 + body: duplicatedResponsePayload + headers: + access-control-allow-headers: 'Origin, X-Requested-With, Content-Type, Accept' + access-control-allow-origin: '*' + connection: keep-alive + content-length: '25' + content-type: 'text/html; charset=utf-8' + date: 'Tue, 18 Mar 2025 20:55:58 GMT' + etag: 'W/"19-yZRAgggcER0sMyRTVBBpErTPT/A"' + keep-alive: timeout=5 + x-powered-by: Express + elapsedTime: 3 + tests: + - + name: 'Status Code' + valid: true + description: 'Expected ''statusCode'' to be equal to ''321''. Received ''321''' + - + name: Body + valid: true + description: 'Expected ''body'' to be equal to ''duplicatedResponsePayload''. Received ''duplicatedResponsePayload''' + valid: true + type: http + messageSentInstant: '2025-03-18T20:55:58.269Z' + iteration: 0 + totalIterations: 1 + hooks: + onInit: + valid: true + tests: [] + arguments: + elapsedTime: 0 + onFinish: + valid: true + tests: [] + arguments: + elapsedTime: 4 + time: + startTime: '2025-03-18T20:55:58.265Z' + endTime: '2025-03-18T20:55:58.269Z' + totalTime: 4 + timeout: 35000 + tasks: [] + - + valid: true + name: examples/http.yml + id: 1355570988_19a154cd66_848319 + level: 1 + sensors: + - + id: 1355570988_a365346815_166289 + name: 'Sensor #0' + type: http + hooks: + onInit: + valid: true + tests: [] + arguments: {} + onFinish: + valid: true + tests: + - + implicit: true + valid: true + name: 'Message received' + description: "{\n \"headers\": {\n \"host\": \"localhost:23075\",\n \"connection\": \"keep-alive\",\n \"requestheaderkey\": \"requestHeaderValue\",\n \"content-type\": \"text/plain;charset=UTF-8\",\n \"accept\": \"*/*\",\n \"accept-language\": \"*\",\n \"sec-fetch-mode\": \"cors\",\n \"user-agent\": \"node\",\n \"accept-encoding\": \"gzip, deflate\",\n \"content-length\": \"8\"\n },\n \"params\": {},\n \"query\": {},\n \"url\": \"/resource\",\n \"body\": \"enqueuer\"\n}" + arguments: + executedHooks: + onInit: [] + onMessageReceived: + - headers + - params + - query + - url + - body + - elapsedTime + onFinish: + - executedHooks + - elapsedTime + elapsedTime: 171 + onMessageReceived: + valid: true + tests: + - name: 'Assertion #0' valid: true - description: 'Expected ''statusCode'' not to be greater than ''400''. Received ''444''' + description: 'Expecting ''enqueuer'' (body) to contain ''queue''' - name: 'Assertion #1' valid: true - description: 'Expected ''body'' to be equal to ''blah''. Received ''blah''' + description: 'Expected ''headers.requestheaderkey'' to be equal to ''requestHeaderValue''. Received ''requestHeaderValue''' + arguments: + headers: + host: 'localhost:23075' + connection: keep-alive + requestheaderkey: requestHeaderValue + content-type: text/plain;charset=UTF-8 + accept: '*/*' + accept-language: '*' + sec-fetch-mode: cors + user-agent: node + accept-encoding: 'gzip, deflate' + content-length: '8' + params: {} + query: {} + url: /resource + body: enqueuer + elapsedTime: 157 + valid: true + sensorTime: '2025-03-18T20:55:58.111Z' + actuators: + - + id: 1355570988_bbfbaaedee_990714 + name: 'Actuator #0' + valid: true + hooks: + onInit: + arguments: + elapsedTime: 0 + tests: [] valid: true - onMessageReceived: + onFinish: + arguments: + executedHooks: + onInit: [] + onResponseReceived: + - status + - statusCode + - body + - headers + - elapsedTime + onFinish: + - executedHooks + - elapsedTime + elapsedTime: 171 + tests: + - + name: Published + valid: true + description: "{\n \"statusCode\": 444,\n \"headers\": {\n \"access-control-allow-headers\": \"Origin, X-Requested-With, Content-Type, Accept\",\n \"access-control-allow-origin\": \"*\",\n \"connection\": \"keep-alive\",\n \"content-length\": \"4\",\n \"content-type\": \"text/html; charset=utf-8\",\n \"date\": \"Tue, 18 Mar 2025 20:55:58 GMT\",\n \"etag\": \"W/\\\"4-W/H9kn37hnlJai5s8Ay+UMHIcUU\\\"\",\n \"keep-alive\": \"timeout=5\",\n \"responseheaderkey\": \"responseHeaderValue\",\n \"x-powered-by\": \"Express\"\n },\n \"body\": \"blah\"\n}" + implicit: true + valid: true + onResponseReceived: arguments: + status: 444 statusCode: 444 body: blah headers: - x-powered-by: Express - access-control-allow-origin: '*' access-control-allow-headers: 'Origin, X-Requested-With, Content-Type, Accept' - content-type: 'text/html; charset=utf-8' + access-control-allow-origin: '*' + connection: keep-alive content-length: '4' + content-type: 'text/html; charset=utf-8' + date: 'Tue, 18 Mar 2025 20:55:58 GMT' etag: 'W/"4-W/H9kn37hnlJai5s8Ay+UMHIcUU"' - date: 'Sun, 08 Mar 2020 05:33:36 GMT' - connection: close - request: - uri: - protocol: 'http:' - slashes: true - auth: null - host: 'localhost:23075' - port: '23075' - hostname: localhost - hash: null - search: null - query: null - pathname: /resource - path: /resource - href: 'http://localhost:23075/resource' - method: post - headers: - Content-Length: 8 - tests: [] + keep-alive: timeout=5 + responseheaderkey: responseHeaderValue + x-powered-by: Express + elapsedTime: 171 + tests: + - + name: 'Assertion #0' + valid: true + description: 'Expected ''statusCode'' not to be greater than ''400''. Received ''444''' + - + name: 'Assertion #1' + valid: true + description: 'Expected ''body'' to be equal to ''blah''. Received ''blah''' + - + name: 'Assertion #2' + valid: true + description: 'Expected ''headers.responseheaderkey'' to be equal to ''responseHeaderValue''. Received ''responseHeaderValue''' valid: true type: http - publishTime: '2020-03-08T05:33:37.036Z' + messageSentInstant: '2025-03-18T20:55:58.184Z' iteration: 0 totalIterations: 1 hooks: @@ -3208,42 +2836,50 @@ requisitions: valid: true tests: [] arguments: - elapsedTime: 178 + elapsedTime: 171 time: - startTime: '2020-03-08T05:33:36.858Z' - endTime: '2020-03-08T05:33:37.037Z' - totalTime: 179 + startTime: '2025-03-18T20:55:58.013Z' + endTime: '2025-03-18T20:55:58.184Z' + totalTime: 171 timeout: 5000 - requisitions: [] + tasks: [] - valid: true name: examples/https.yml - id: 0233360793_d6075fad05_992906 + id: 1355570988_df10003961_318866 level: 1 - subscriptions: + sensors: - - id: 0233360793_81e3a69582_423410 - name: 'Subscription #0' + id: 1355570988_11c785964d_389003 + name: 'Sensor #0' type: https hooks: onInit: valid: true tests: [] - arguments: - elapsedTime: 0 + arguments: {} onFinish: valid: true tests: - + implicit: true valid: true name: 'Message received' - description: "{\n \"headers\": {\n \"content-type\": \"application/json\",\n \"content-length\": \"18\",\n \"host\": \"localhost:4430\",\n \"connection\": \"close\"\n },\n \"params\": {},\n \"query\": {},\n \"url\": \"/enqueuer\",\n \"body\": {\n \"https\": \"works!\"\n },\n \"elapsedTime\": 185\n}" + description: "{\n \"headers\": {\n \"host\": \"127.0.0.1:4430\",\n \"connection\": \"keep-alive\",\n \"content-type\": \"application/json\",\n \"accept\": \"*/*\",\n \"accept-language\": \"*\",\n \"sec-fetch-mode\": \"cors\",\n \"user-agent\": \"node\",\n \"accept-encoding\": \"br, gzip, deflate\",\n \"content-length\": \"18\"\n },\n \"params\": {},\n \"query\": {},\n \"url\": \"/enqueuer\",\n \"body\": {\n \"https\": \"works!\"\n }\n}" arguments: executedHooks: - - onInit - - onMessageReceived - - onFinish - elapsedTime: 189 + onInit: [] + onMessageReceived: + - headers + - params + - query + - url + - body + - elapsedTime + onFinish: + - executedHooks + - elapsedTime + elapsedTime: 180 onMessageReceived: valid: true tests: @@ -3253,21 +2889,26 @@ requisitions: description: 'Expected ''JSON.parse(body).https'' to be equal to ''works!''. Received ''works!''' arguments: headers: + host: '127.0.0.1:4430' + connection: keep-alive content-type: application/json + accept: '*/*' + accept-language: '*' + sec-fetch-mode: cors + user-agent: node + accept-encoding: 'br, gzip, deflate' content-length: '18' - host: 'localhost:4430' - connection: close params: {} query: {} url: /enqueuer body: '{"https":"works!"}' - elapsedTime: 185 + elapsedTime: 159 valid: true - subscriptionTime: '2020-03-08T05:33:36.951Z' - publishers: + sensorTime: '2025-03-18T20:55:58.111Z' + actuators: - - id: 0233360793_5cd9b40d14_659209 - name: 'Publisher #0' + id: 1355570988_54fca16b67_23795 + name: 'Actuator #0' valid: true hooks: onInit: @@ -3278,93 +2919,44 @@ requisitions: onFinish: arguments: executedHooks: - - onInit - - onResponseReceived - - onMessageReceived - - onFinish - elapsedTime: 189 + onInit: [] + onResponseReceived: + - status + - statusCode + - body + - headers + - elapsedTime + onFinish: + - executedHooks + - elapsedTime + elapsedTime: 180 tests: - name: Published valid: true - description: "{\n \"statusCode\": 200,\n \"headers\": {\n \"x-powered-by\": \"Express\",\n \"access-control-allow-origin\": \"*\",\n \"access-control-allow-headers\": \"Origin, X-Requested-With, Content-Type, Accept\",\n \"content-type\": \"text/html; charset=utf-8\",\n \"content-length\": \"5\",\n \"etag\": \"W/\\\"5-w0N9vHwSVdOiHURNhuvy6SNMIr0\\\"\",\n \"date\": \"Sun, 08 Mar 2020 05:33:37 GMT\",\n \"connection\": \"close\"\n },\n \"body\": \"https\"\n}" + description: "{\n \"statusCode\": 200,\n \"headers\": {\n \"access-control-allow-headers\": \"Origin, X-Requested-With, Content-Type, Accept\",\n \"access-control-allow-origin\": \"*\",\n \"connection\": \"keep-alive\",\n \"content-length\": \"5\",\n \"content-type\": \"text/html; charset=utf-8\",\n \"date\": \"Tue, 18 Mar 2025 20:55:58 GMT\",\n \"etag\": \"W/\\\"5-w0N9vHwSVdOiHURNhuvy6SNMIr0\\\"\",\n \"keep-alive\": \"timeout=5\",\n \"x-powered-by\": \"Express\"\n },\n \"body\": \"https\"\n}" + implicit: true valid: true onResponseReceived: arguments: + status: 200 statusCode: 200 body: https headers: - x-powered-by: Express - access-control-allow-origin: '*' access-control-allow-headers: 'Origin, X-Requested-With, Content-Type, Accept' - content-type: 'text/html; charset=utf-8' - content-length: '5' - etag: 'W/"5-w0N9vHwSVdOiHURNhuvy6SNMIr0"' - date: 'Sun, 08 Mar 2020 05:33:37 GMT' - connection: close - request: - uri: - protocol: 'https:' - slashes: true - auth: null - host: 'localhost:4430' - port: '4430' - hostname: localhost - hash: null - search: null - query: null - pathname: /enqueuer - path: /enqueuer - href: 'https://localhost:4430/enqueuer' - method: post - headers: - content-type: application/json - Content-Length: 18 - tests: [] - valid: true - onMessageReceived: - arguments: - statusCode: 200 - body: https - headers: - x-powered-by: Express access-control-allow-origin: '*' - access-control-allow-headers: 'Origin, X-Requested-With, Content-Type, Accept' - content-type: 'text/html; charset=utf-8' + connection: keep-alive content-length: '5' + content-type: 'text/html; charset=utf-8' + date: 'Tue, 18 Mar 2025 20:55:58 GMT' etag: 'W/"5-w0N9vHwSVdOiHURNhuvy6SNMIr0"' - date: 'Sun, 08 Mar 2020 05:33:37 GMT' - connection: close - request: - uri: - protocol: 'https:' - slashes: true - auth: null - host: 'localhost:4430' - port: '4430' - hostname: localhost - hash: null - search: null - query: null - pathname: /enqueuer - path: /enqueuer - href: 'https://localhost:4430/enqueuer' - method: post - headers: - content-type: application/json - Content-Length: 18 - tests: - - - name: 'Status Code' - valid: true - description: 'Expected ''statusCode'' to be equal to ''200''. Received ''200''' - - - name: Body - valid: true - description: 'Expected ''body'' to be equal to ''https''. Received ''https''' + keep-alive: timeout=5 + x-powered-by: Express + elapsedTime: 178 + tests: [] valid: true type: HTTPS - publishTime: '2020-03-08T05:33:37.047Z' + messageSentInstant: '2025-03-18T20:55:58.191Z' iteration: 0 totalIterations: 1 hooks: @@ -3377,20 +2969,20 @@ requisitions: valid: true tests: [] arguments: - elapsedTime: 189 + elapsedTime: 180 time: - startTime: '2020-03-08T05:33:36.859Z' - endTime: '2020-03-08T05:33:37.048Z' - totalTime: 189 - timeout: 3000 - requisitions: [] + startTime: '2025-03-18T20:55:58.013Z' + endTime: '2025-03-18T20:55:58.193Z' + totalTime: 180 + timeout: 5000 + tasks: [] - valid: true name: examples/ignore.yml - id: 0233360793_8ea767da7b_677632 + id: 1355570988_462ea965fc_88424 level: 1 - subscriptions: [] - publishers: [] + sensors: [] + actuators: [] iteration: 0 totalIterations: 1 hooks: @@ -3403,23 +2995,23 @@ requisitions: valid: true tests: [] arguments: - elapsedTime: 69 + elapsedTime: 87 time: - startTime: '2020-03-08T05:33:36.860Z' - endTime: '2020-03-08T05:33:36.929Z' - totalTime: 69 + startTime: '2025-03-18T20:55:58.014Z' + endTime: '2025-03-18T20:55:58.101Z' + totalTime: 87 timeout: 5000 - requisitions: + tasks: - valid: true - name: 'Requisition #0' - id: 0233360793_cbabde97ea_466080 + name: 'Task #0' + id: 1355570988_a55bebc0fd_853628 level: 2 - subscriptions: [] - publishers: + sensors: [] + actuators: - - id: 0233360794_c325787526_709001 - name: 'Publisher #0' + id: 1355570988_1f5a8da9cd_492843 + name: 'Actuator #0' ignored: true valid: true hooks: @@ -3442,22 +3034,22 @@ requisitions: valid: true tests: [] arguments: - elapsedTime: 32 + elapsedTime: 74 time: - startTime: '2020-03-08T05:33:36.885Z' - endTime: '2020-03-08T05:33:36.918Z' - totalTime: 33 + startTime: '2025-03-18T20:55:58.021Z' + endTime: '2025-03-18T20:55:58.095Z' + totalTime: 74 timeout: 5000 - requisitions: [] + tasks: [] - valid: true - name: 'Requisition #1' - id: 0233360794_24ec47c05e_220425 + name: 'Task #1' + id: 1355570988_4c097977ac_495643 level: 2 - subscriptions: + sensors: - - id: 0233360794_41bbcab029_693948 - name: 'Subscription #0' + id: 1355570988_ce955c26fb_854214 + name: 'Sensor #0' ignored: true type: file hooks: @@ -3468,7 +3060,7 @@ requisitions: valid: true tests: [] valid: true - publishers: [] + actuators: [] iteration: 0 totalIterations: 1 hooks: @@ -3481,21 +3073,21 @@ requisitions: valid: true tests: [] arguments: - elapsedTime: 2 + elapsedTime: 1 time: - startTime: '2020-03-08T05:33:36.923Z' - endTime: '2020-03-08T05:33:36.925Z' - totalTime: 2 + startTime: '2025-03-18T20:55:58.098Z' + endTime: '2025-03-18T20:55:58.099Z' + totalTime: 1 timeout: 5000 - requisitions: [] + tasks: [] - valid: true - name: 'Requisition #2' - id: 0233360794_73301c2bc1_824458 + name: 'Task #2' + id: 1355570988_f21399a597_212699 ignored: true level: 2 - subscriptions: [] - publishers: [] + sensors: [] + actuators: [] hooks: onInit: arguments: {} @@ -3506,17 +3098,17 @@ requisitions: valid: true tests: [] time: - startTime: '2020-03-08T05:33:36.927Z' - endTime: '2020-03-08T05:33:36.927Z' + startTime: '2025-03-18T20:55:58.100Z' + endTime: '2025-03-18T20:55:58.100Z' totalTime: 0 - requisitions: [] + tasks: [] - valid: true name: examples/import.yml - id: 0233360794_11d1ffbd36_804 + id: 1355570988_671d019c4a_586338 level: 1 - subscriptions: [] - publishers: [] + sensors: [] + actuators: [] iteration: 0 totalIterations: 1 hooks: @@ -3529,20 +3121,20 @@ requisitions: valid: true tests: [] arguments: - elapsedTime: 178 + elapsedTime: 183 time: - startTime: '2020-03-08T05:33:36.862Z' - endTime: '2020-03-08T05:33:37.040Z' - totalTime: 178 + startTime: '2025-03-18T20:55:58.014Z' + endTime: '2025-03-18T20:55:58.197Z' + totalTime: 183 timeout: 5000 - requisitions: + tasks: - valid: true - name: 'static importRequisition' - id: 0233360794_0c14282f98_513588 + name: 'static importTask' + id: 1355570988_7bed35a763_356445 level: 2 - subscriptions: [] - publishers: [] + sensors: [] + actuators: [] iteration: 0 totalIterations: 1 hooks: @@ -3557,22 +3149,22 @@ requisitions: - name: 'Assertion #0' valid: true - description: 'Expecting ''requisition.imported'' to be true. Received: true' + description: 'Expecting ''task.imported'' to be true. Received: true' arguments: - elapsedTime: 32 + elapsedTime: 72 time: - startTime: '2020-03-08T05:33:36.885Z' - endTime: '2020-03-08T05:33:36.917Z' - totalTime: 32 + startTime: '2025-03-18T20:55:58.022Z' + endTime: '2025-03-18T20:55:58.095Z' + totalTime: 73 timeout: 5000 - requisitions: [] + tasks: [] - valid: true - name: 'dynamic importRequisition' - id: 0233360794_6857a59a1d_946774 + name: 'dynamic importTask' + id: 1355570988_c497197b78_349904 level: 2 - subscriptions: [] - publishers: [] + sensors: [] + actuators: [] iteration: 0 totalIterations: 1 hooks: @@ -3587,75 +3179,89 @@ requisitions: - name: 'Assertion #0' valid: true - description: 'Expecting ''requisition.dynamicallyImported'' to be true. Received: true' + description: 'Expecting ''task.dynamicallyImported'' to be true. Received: true' - name: 'Assertion #1' valid: true - description: 'Expected ''requisition.priority'' to be equal to ''higher''. Received ''higher''' + description: 'Expected ''task.priority'' to be equal to ''higher''. Received ''higher''' arguments: - elapsedTime: 3 + elapsedTime: 2 time: - startTime: '2020-03-08T05:33:36.921Z' - endTime: '2020-03-08T05:33:36.924Z' - totalTime: 3 + startTime: '2025-03-18T20:55:58.097Z' + endTime: '2025-03-18T20:55:58.099Z' + totalTime: 2 timeout: 5000 - requisitions: [] + tasks: [] - valid: true - name: 'Requisition #2' - id: 0233360794_28f1b301b5_208127 + name: 'Task #2' + id: 1355570988_5f88266801_796789 level: 2 - subscriptions: + sensors: - - id: 0233360794_a7a23228b4_464151 - name: 'Subscription #0' + id: 1355570988_2fa2706f5b_562816 + name: 'Sensor #0' type: http hooks: onInit: valid: true tests: [] - arguments: - elapsedTime: 0 + arguments: {} onFinish: valid: true tests: - name: 'Assertion #0' valid: true - description: 'Expected ''subscription.type'' to be equal to ''http''. Received ''http''' + description: 'Expected ''sensor.type'' to be equal to ''http''. Received ''http''' - name: 'Assertion #1' valid: true - description: 'Expected ''subscription.endpoint'' to be equal to ''/subscription-reuse''. Received ''/subscription-reuse''' + description: 'Expected ''sensor.endpoint'' to be equal to ''/sensor-reuse''. Received ''/sensor-reuse''' - + implicit: true valid: true name: 'Message received' - description: "{\n \"headers\": {\n \"content-length\": \"5\",\n \"host\": \"localhost:23075\",\n \"connection\": \"close\"\n },\n \"params\": {},\n \"query\": {},\n \"url\": \"/subscription-reuse\",\n \"body\": \"virgs\",\n \"elapsedTime\": 98\n}" + description: "{\n \"headers\": {\n \"host\": \"localhost:23075\",\n \"connection\": \"keep-alive\",\n \"content-type\": \"text/plain;charset=UTF-8\",\n \"accept\": \"*/*\",\n \"accept-language\": \"*\",\n \"sec-fetch-mode\": \"cors\",\n \"user-agent\": \"node\",\n \"accept-encoding\": \"gzip, deflate\",\n \"content-length\": \"5\"\n },\n \"params\": {},\n \"query\": {},\n \"url\": \"/sensor-reuse\",\n \"body\": \"virgs\"\n}" arguments: executedHooks: - - onInit - - onMessageReceived - - onFinish - elapsedTime: 113 + onInit: [] + onMessageReceived: + - headers + - params + - query + - url + - body + - elapsedTime + onFinish: + - executedHooks + - elapsedTime + elapsedTime: 98 onMessageReceived: valid: true tests: [] arguments: headers: - content-length: '5' host: 'localhost:23075' - connection: close + connection: keep-alive + content-type: text/plain;charset=UTF-8 + accept: '*/*' + accept-language: '*' + sec-fetch-mode: cors + user-agent: node + accept-encoding: 'gzip, deflate' + content-length: '5' params: {} query: {} - url: /subscription-reuse + url: /sensor-reuse body: virgs - elapsedTime: 98 + elapsedTime: 90 valid: true - subscriptionTime: '2020-03-08T05:33:36.951Z' - publishers: + sensorTime: '2025-03-18T20:55:58.111Z' + actuators: - - id: 0233360794_5163661566_941755 - name: 'Publisher #0' + id: 1355570988_a0d38bedc4_300445 + name: 'Actuator #0' valid: true hooks: onInit: @@ -3666,95 +3272,52 @@ requisitions: onFinish: arguments: executedHooks: - - onInit - - onResponseReceived - - onMessageReceived - - onFinish - elapsedTime: 113 + onInit: [] + onResponseReceived: + - status + - statusCode + - body + - headers + - elapsedTime + onFinish: + - executedHooks + - elapsedTime + elapsedTime: 97 tests: - name: Published valid: true - description: "{\n \"statusCode\": 444,\n \"headers\": {\n \"x-powered-by\": \"Express\",\n \"access-control-allow-origin\": \"*\",\n \"access-control-allow-headers\": \"Origin, X-Requested-With, Content-Type, Accept\",\n \"content-type\": \"text/html; charset=utf-8\",\n \"content-length\": \"4\",\n \"etag\": \"W/\\\"4-W/H9kn37hnlJai5s8Ay+UMHIcUU\\\"\",\n \"date\": \"Sun, 08 Mar 2020 05:33:36 GMT\",\n \"connection\": \"close\"\n },\n \"body\": \"blah\"\n}" + description: "{\n \"statusCode\": 444,\n \"headers\": {\n \"access-control-allow-headers\": \"Origin, X-Requested-With, Content-Type, Accept\",\n \"access-control-allow-origin\": \"*\",\n \"connection\": \"keep-alive\",\n \"content-length\": \"4\",\n \"content-type\": \"text/html; charset=utf-8\",\n \"date\": \"Tue, 18 Mar 2025 20:55:58 GMT\",\n \"etag\": \"W/\\\"4-W/H9kn37hnlJai5s8Ay+UMHIcUU\\\"\",\n \"keep-alive\": \"timeout=5\",\n \"x-powered-by\": \"Express\"\n },\n \"body\": \"blah\"\n}" + implicit: true - name: 'Assertion #0' valid: true - description: 'Expected ''publisher.type'' to be equal to ''http''. Received ''http''' + description: 'Expected ''actuator.type'' to be equal to ''http''. Received ''http''' - name: 'Assertion #1' valid: true - description: 'Expected ''publisher.method'' to be equal to ''POST''. Received ''POST''' + description: 'Expected ''actuator.method'' to be equal to ''POST''. Received ''POST''' valid: true onResponseReceived: arguments: + status: 444 statusCode: 444 body: blah headers: - x-powered-by: Express - access-control-allow-origin: '*' access-control-allow-headers: 'Origin, X-Requested-With, Content-Type, Accept' - content-type: 'text/html; charset=utf-8' - content-length: '4' - etag: 'W/"4-W/H9kn37hnlJai5s8Ay+UMHIcUU"' - date: 'Sun, 08 Mar 2020 05:33:36 GMT' - connection: close - request: - uri: - protocol: 'http:' - slashes: true - auth: null - host: 'localhost:23075' - port: '23075' - hostname: localhost - hash: null - search: null - query: null - pathname: /subscription-reuse - path: /subscription-reuse - href: 'http://localhost:23075/subscription-reuse' - method: post - headers: - Content-Length: 5 - tests: [] - valid: true - onMessageReceived: - arguments: - statusCode: 444 - body: blah - headers: - x-powered-by: Express access-control-allow-origin: '*' - access-control-allow-headers: 'Origin, X-Requested-With, Content-Type, Accept' - content-type: 'text/html; charset=utf-8' + connection: keep-alive content-length: '4' + content-type: 'text/html; charset=utf-8' + date: 'Tue, 18 Mar 2025 20:55:58 GMT' etag: 'W/"4-W/H9kn37hnlJai5s8Ay+UMHIcUU"' - date: 'Sun, 08 Mar 2020 05:33:36 GMT' - connection: close - request: - uri: - protocol: 'http:' - slashes: true - auth: null - host: 'localhost:23075' - port: '23075' - hostname: localhost - hash: null - search: null - query: null - pathname: /subscription-reuse - path: /subscription-reuse - href: 'http://localhost:23075/subscription-reuse' - method: post - headers: - Content-Length: 5 - tests: - - - name: 'Assertion #0' - valid: true - description: 'Expected ''statusCode'' to be equal to ''444''. Received ''444''' + keep-alive: timeout=5 + x-powered-by: Express + elapsedTime: 97 + tests: [] valid: true type: http - publishTime: '2020-03-08T05:33:37.039Z' + messageSentInstant: '2025-03-18T20:55:58.197Z' iteration: 0 totalIterations: 1 hooks: @@ -3767,20 +3330,20 @@ requisitions: valid: true tests: [] arguments: - elapsedTime: 113 + elapsedTime: 98 time: - startTime: '2020-03-08T05:33:36.926Z' - endTime: '2020-03-08T05:33:37.040Z' - totalTime: 114 + startTime: '2025-03-18T20:55:58.099Z' + endTime: '2025-03-18T20:55:58.197Z' + totalTime: 98 timeout: 5000 - requisitions: [] + tasks: [] - valid: true name: examples/no-tests.yml - id: 0233360794_92334e1554_934193 + id: 1355570988_1fd3fcba18_616845 level: 1 - subscriptions: [] - publishers: [] + sensors: [] + actuators: [] iteration: 0 totalIterations: 1 hooks: @@ -3793,20 +3356,20 @@ requisitions: valid: true tests: [] arguments: - elapsedTime: 218 + elapsedTime: 210 time: - startTime: '2020-03-08T05:33:36.862Z' - endTime: '2020-03-08T05:33:37.081Z' - totalTime: 219 + startTime: '2025-03-18T20:55:58.014Z' + endTime: '2025-03-18T20:55:58.224Z' + totalTime: 210 timeout: 5000 - requisitions: + tasks: - valid: true - name: 'Requisition #0' - id: 0233360794_35cb1b4ae8_838119 + name: 'Task #0' + id: 1355570988_01ce9f0a63_6425 level: 2 - subscriptions: [] - publishers: [] + sensors: [] + actuators: [] iteration: 0 totalIterations: 1 hooks: @@ -3821,18 +3384,18 @@ requisitions: arguments: elapsedTime: 100 time: - startTime: '2020-03-08T05:33:36.980Z' - endTime: '2020-03-08T05:33:37.080Z' + startTime: '2025-03-18T20:55:58.124Z' + endTime: '2025-03-18T20:55:58.224Z' totalTime: 100 timeout: 5000 - requisitions: [] + tasks: [] - valid: true - name: examples/parallel-requisition.yml - id: 0233360794_e05b5acab1_820854 + name: examples/parallel-task.yml + id: 1355570988_1cb4ee5875_216905 level: 1 - subscriptions: [] - publishers: [] + sensors: [] + actuators: [] iteration: 0 totalIterations: 1 hooks: @@ -3845,20 +3408,20 @@ requisitions: valid: true tests: [] arguments: - elapsedTime: 858 + elapsedTime: 853 time: - startTime: '2020-03-08T05:33:36.864Z' - endTime: '2020-03-08T05:33:37.722Z' - totalTime: 858 + startTime: '2025-03-18T20:55:58.015Z' + endTime: '2025-03-18T20:55:58.868Z' + totalTime: 853 timeout: 5000 - requisitions: + tasks: - valid: true - name: 'Requisition #0' - id: 0233360794_e2f0694b46_166090 + name: 'Task #0' + id: 1355570988_7fe57f520c_649907 level: 2 - subscriptions: [] - publishers: [] + sensors: [] + actuators: [] iteration: 0 totalIterations: 1 hooks: @@ -3873,26 +3436,26 @@ requisitions: - name: 'started at the same time' valid: true - description: 'Expected ''Math.abs(requisition.requisitions[1].startTime.getTime() - requisition.requisitions[0].startTime.getTime())'' to be less than or equal to ''100''. Received ''1''' + description: 'Expected ''Math.abs(task.tasks[1].startTime.getTime() - task.tasks[0].startTime.getTime())'' to be less than or equal to ''100''. Received ''0''' - name: 'Assertion #1' valid: true - description: 'Expected ''elapsedTime'' to be less than or equal to ''400''. Received ''324''' + description: 'Expected ''elapsedTime'' to be less than or equal to ''400''. Received ''341''' arguments: - elapsedTime: 324 + elapsedTime: 341 time: - startTime: '2020-03-08T05:33:36.886Z' - endTime: '2020-03-08T05:33:37.211Z' - totalTime: 325 + startTime: '2025-03-18T20:55:58.022Z' + endTime: '2025-03-18T20:55:58.364Z' + totalTime: 342 timeout: 5000 - requisitions: + tasks: - valid: true - name: 'Requisition #0' - id: 0233360794_913ffe0794_861814 + name: 'Task #0' + id: 1355570988_5db8f2c023_14140 level: 3 - subscriptions: [] - publishers: [] + sensors: [] + actuators: [] iteration: 0 totalIterations: 1 hooks: @@ -3905,20 +3468,20 @@ requisitions: valid: true tests: [] arguments: - elapsedTime: 201 + elapsedTime: 202 time: - startTime: '2020-03-08T05:33:36.908Z' - endTime: '2020-03-08T05:33:37.110Z' + startTime: '2025-03-18T20:55:58.063Z' + endTime: '2025-03-18T20:55:58.265Z' totalTime: 202 timeout: 5000 - requisitions: [] + tasks: [] - valid: true - name: 'Requisition #1' - id: 0233360794_fdabeaad92_305365 + name: 'Task #1' + id: 1355570988_45d0cee5e7_398896 level: 3 - subscriptions: [] - publishers: [] + sensors: [] + actuators: [] iteration: 0 totalIterations: 1 hooks: @@ -3931,20 +3494,20 @@ requisitions: valid: true tests: [] arguments: - elapsedTime: 301 + elapsedTime: 300 time: - startTime: '2020-03-08T05:33:36.909Z' - endTime: '2020-03-08T05:33:37.210Z' - totalTime: 301 + startTime: '2025-03-18T20:55:58.063Z' + endTime: '2025-03-18T20:55:58.363Z' + totalTime: 300 timeout: 5000 - requisitions: [] + tasks: [] - valid: true - name: 'Requisition #1' - id: 0233360794_30bc9c1ac3_177900 + name: 'Task #1' + id: 1355570988_d3366a8bad_567254 level: 2 - subscriptions: [] - publishers: [] + sensors: [] + actuators: [] iteration: 0 totalIterations: 1 hooks: @@ -3959,26 +3522,26 @@ requisitions: - name: 'started after the other' valid: true - description: 'Expected ''requisition.requisitions[1].startTime.getTime() - requisition.requisitions[0].startTime.getTime()'' to be greater than or equal to ''200''. Received ''204''' + description: 'Expected ''task.tasks[1].startTime.getTime() - task.tasks[0].startTime.getTime()'' to be greater than or equal to ''200''. Received ''202''' - name: 'Assertion #1' valid: true - description: 'Expected ''elapsedTime'' to be less than or equal to ''600''. Received ''510''' + description: 'Expected ''elapsedTime'' to be less than or equal to ''600''. Received ''503''' arguments: - elapsedTime: 510 + elapsedTime: 503 time: - startTime: '2020-03-08T05:33:37.212Z' - endTime: '2020-03-08T05:33:37.722Z' - totalTime: 510 + startTime: '2025-03-18T20:55:58.364Z' + endTime: '2025-03-18T20:55:58.867Z' + totalTime: 503 timeout: 5000 - requisitions: + tasks: - valid: true - name: 'Requisition #0' - id: 0233360794_913ffe0794_479661 + name: 'Task #0' + id: 1355570988_5db8f2c023_860781 level: 3 - subscriptions: [] - publishers: [] + sensors: [] + actuators: [] iteration: 0 totalIterations: 1 hooks: @@ -3991,20 +3554,20 @@ requisitions: valid: true tests: [] arguments: - elapsedTime: 204 + elapsedTime: 202 time: - startTime: '2020-03-08T05:33:37.212Z' - endTime: '2020-03-08T05:33:37.416Z' - totalTime: 204 + startTime: '2025-03-18T20:55:58.364Z' + endTime: '2025-03-18T20:55:58.566Z' + totalTime: 202 timeout: 5000 - requisitions: [] + tasks: [] - valid: true - name: 'Requisition #1' - id: 0233360794_463f29d694_315020 + name: 'Task #1' + id: 1355570988_0c768854c4_835800 level: 3 - subscriptions: [] - publishers: [] + sensors: [] + actuators: [] iteration: 0 totalIterations: 1 hooks: @@ -4017,23 +3580,23 @@ requisitions: valid: true tests: [] arguments: - elapsedTime: 305 + elapsedTime: 301 time: - startTime: '2020-03-08T05:33:37.416Z' - endTime: '2020-03-08T05:33:37.721Z' - totalTime: 305 + startTime: '2025-03-18T20:55:58.566Z' + endTime: '2025-03-18T20:55:58.867Z' + totalTime: 301 timeout: 5000 - requisitions: [] + tasks: [] - valid: true - name: examples/parallel-test-publisher.yml - id: 0233360794_98a37b717a_67806 + name: examples/parallel-test-actuator.yml + id: 1355570988_6cafd01191_792511 level: 1 - subscriptions: [] - publishers: + sensors: [] + actuators: - - id: 0233360794_2955c0f42c_251125 - name: 'Publisher #0' + id: 1355570988_b0c204f8d7_305811 + name: 'Actuator #0' valid: true hooks: onInit: @@ -4044,15 +3607,21 @@ requisitions: onFinish: arguments: executedHooks: - - onInit - - onMessageReceived - - onFinish - elapsedTime: 508 + onInit: [] + onMessageReceived: + - payload + - stream + - elapsedTime + onFinish: + - executedHooks + - elapsedTime + elapsedTime: 1004 tests: - name: Published valid: true description: 'Published successfully' + implicit: true valid: true onMessageReceived: arguments: @@ -4060,8 +3629,8 @@ requisitions: stream: address: 127.0.0.1 family: IPv4 - port: 61981 - elapsedTime: 1509 + port: 57120 + elapsedTime: 2003 tests: - name: 'Back and forth' @@ -4073,7 +3642,7 @@ requisitions: description: 'Expected ''payload'' to be equal to ''anyValue''. Received ''anyValue''' valid: true type: tcp - publishTime: '2020-03-08T05:33:37.371Z' + messageSentInstant: '2025-03-18T20:55:59.019Z' iteration: 0 totalIterations: 1 hooks: @@ -4086,42 +3655,48 @@ requisitions: valid: true tests: [] arguments: - elapsedTime: 508 + elapsedTime: 1004 time: - startTime: '2020-03-08T05:33:36.864Z' - endTime: '2020-03-08T05:33:37.372Z' - totalTime: 508 - timeout: 3000 - requisitions: [] + startTime: '2025-03-18T20:55:58.015Z' + endTime: '2025-03-18T20:55:59.019Z' + totalTime: 1004 + timeout: 10000 + tasks: [] - valid: true - name: examples/parallel-test-subscription.yml - id: 0233360794_eb9c2c8618_255594 + name: examples/parallel-test-sensor.yml + id: 1355570988_fe639b1e21_752481 level: 1 - subscriptions: + sensors: - - id: 0233360795_b419ff22d8_113724 - name: 'Subscription #0' + id: 1355570988_ae5a5d0b7b_108571 + name: 'Sensor #0' type: tcp hooks: onInit: valid: true tests: [] - arguments: - elapsedTime: 0 + arguments: {} onFinish: valid: true tests: - + implicit: true valid: true name: 'Message received' - description: 'Subscription has received its message' + description: 'Sensor has received its message' arguments: executedHooks: - - onInit - - onMessageReceived - - onFinish - elapsedTime: 505 + onInit: [] + onMessageReceived: + - payload + - stream + - path + - elapsedTime + onFinish: + - executedHooks + - elapsedTime + elapsedTime: 1003 onMessageReceived: valid: true tests: @@ -4135,10 +3710,10 @@ requisitions: address: '::ffff:127.0.0.1' family: IPv6 port: 23081 - elapsedTime: 504 + elapsedTime: 1003 valid: true - subscriptionTime: '2020-03-08T05:33:36.951Z' - publishers: [] + sensorTime: '2025-03-18T20:55:58.111Z' + actuators: [] iteration: 0 totalIterations: 1 hooks: @@ -4151,20 +3726,20 @@ requisitions: valid: true tests: [] arguments: - elapsedTime: 505 + elapsedTime: 1003 time: - startTime: '2020-03-08T05:33:36.865Z' - endTime: '2020-03-08T05:33:37.370Z' - totalTime: 505 - timeout: 3000 - requisitions: [] + startTime: '2025-03-18T20:55:58.015Z' + endTime: '2025-03-18T20:55:59.018Z' + totalTime: 1003 + timeout: 10000 + tasks: [] - valid: true name: examples/parent.yml - id: 0233360795_b79240b3fa_96730 + id: 1355570989_da63bddcca_944032 level: 1 - subscriptions: [] - publishers: [] + sensors: [] + actuators: [] iteration: 0 totalIterations: 1 hooks: @@ -4177,20 +3752,20 @@ requisitions: valid: true tests: [] arguments: - elapsedTime: 164 + elapsedTime: 185 time: - startTime: '2020-03-08T05:33:36.865Z' - endTime: '2020-03-08T05:33:37.029Z' - totalTime: 164 + startTime: '2025-03-18T20:55:58.015Z' + endTime: '2025-03-18T20:55:58.200Z' + totalTime: 185 timeout: 5000 - requisitions: + tasks: - valid: true - name: 'Requisition #0' - id: 0233360795_c28c57bc75_142157 + name: 'Task #0' + id: 1355570989_3b15b37229_512254 level: 2 - subscriptions: [] - publishers: [] + sensors: [] + actuators: [] iteration: 0 totalIterations: 1 hooks: @@ -4203,22 +3778,22 @@ requisitions: valid: true tests: [] arguments: - elapsedTime: 31 + elapsedTime: 72 time: - startTime: '2020-03-08T05:33:36.886Z' - endTime: '2020-03-08T05:33:36.917Z' - totalTime: 31 + startTime: '2025-03-18T20:55:58.022Z' + endTime: '2025-03-18T20:55:58.095Z' + totalTime: 73 timeout: 5000 - requisitions: [] + tasks: [] - valid: true - name: 'Requisition #1' - id: 0233360795_52aeba2265_976420 + name: 'Task #1' + id: 1355570989_75be7f29de_679568 level: 2 - subscriptions: + sensors: - - id: 0233360795_f3eb57f16a_27206 - name: 'Subscription #0' + id: 1355570989_4219491ee0_369260 + name: 'Sensor #0' type: tcp hooks: onInit: @@ -4227,24 +3802,26 @@ requisitions: - name: 'Assertion #0' valid: true - description: 'Expected ''subscription.parent.parent.requisitions[0].file.length'' to be equal to ''10''. Received ''10''' - arguments: - elapsedTime: 0 + description: 'Expected ''sensor.parent.parent.tasks[0].file.length'' to be equal to ''10''. Received ''10''' + arguments: {} onFinish: valid: true tests: - valid: true - name: 'Subscription avoided' - description: 'Avoidable subscription has not received any message' + implicit: true + name: 'Sensor avoided' + description: 'Avoidable sensor has not received any message' arguments: executedHooks: - - onInit - - onFinish - elapsedTime: 107 - valid: true - subscriptionTime: '2020-03-08T05:33:36.951Z' - publishers: [] + onInit: [] + onFinish: + - executedHooks + - elapsedTime + elapsedTime: 102 + valid: true + sensorTime: '2025-03-18T20:55:58.111Z' + actuators: [] iteration: 0 totalIterations: 1 hooks: @@ -4257,23 +3834,23 @@ requisitions: valid: true tests: [] arguments: - elapsedTime: 107 + elapsedTime: 103 time: - startTime: '2020-03-08T05:33:36.921Z' - endTime: '2020-03-08T05:33:37.029Z' - totalTime: 108 + startTime: '2025-03-18T20:55:58.097Z' + endTime: '2025-03-18T20:55:58.200Z' + totalTime: 103 timeout: 5000 - requisitions: [] + tasks: [] - valid: true name: examples/readme-enqueuer-repo-hit.yml - id: 0233360795_5edd709fc3_130557 + id: 1355570989_80bd223bd7_760004 level: 1 - subscriptions: [] - publishers: + sensors: [] + actuators: - - id: 0233360795_cf5343e68d_486656 - name: 'Publisher #0' + id: 1355570989_4d2d1f7e97_684906 + name: 'Actuator #0' valid: true hooks: onInit: @@ -4284,115 +3861,56 @@ requisitions: onFinish: arguments: executedHooks: - - onInit - - onResponseReceived - - onMessageReceived - - onFinish - elapsedTime: 762 + onInit: [] + onResponseReceived: + - status + - statusCode + - body + - headers + - elapsedTime + onFinish: + - executedHooks + - elapsedTime + elapsedTime: 985 tests: - name: Published valid: true - description: "{\n \"statusCode\": 200,\n \"headers\": {\n \"date\": \"Sun, 08 Mar 2020 05:33:37 GMT\",\n \"content-type\": \"text/html; charset=utf-8\",\n \"server\": \"GitHub.com\",\n \"status\": \"200 OK\",\n \"vary\": \"X-PJAX, Accept-Encoding, Accept, X-Requested-With\",\n \"etag\": \"W/\\\"e488a776a390ca1e6112af885e19a43b\\\"\",\n \"cache-control\": \"max-age=0, private, must-revalidate\",\n \"strict-transport-security\": \"max-age=31536000; includeSubdomains; preload\",\n \"x-frame-options\": \"deny\",\n \"x-content-type-options\": \"nosniff\",\n \"x-xss-protection\": \"1; mode=block\",\n \"expect-ct\": \"max-age=2592000, report-uri=\\\"https://api.github.com/_private/browser/errors\\\"\",\n \"content-security-policy\": \"default-src 'none'; base-uri 'self'; block-all-mixed-content; connect-src 'self' uploads.github.com www.githubstatus.com collector.githubapp.com api.github.com www.google-analytics.com github-cloud.s3.amazonaws.com github-production-repository-file-5c1aeb.s3.amazonaws.com github-production-upload-manifest-file-7fdce7.s3.amazonaws.com github-production-user-asset-6210df.s3.amazonaws.com wss://live.github.com; font-src github.githubassets.com; form-action 'self' github.com gist.github.com; frame-ancestors 'none'; frame-src render.githubusercontent.com; img-src 'self' data: github.githubassets.com identicons.github.com collector.githubapp.com github-cloud.s3.amazonaws.com *.githubusercontent.com; manifest-src 'self'; media-src 'none'; script-src github.githubassets.com; style-src 'unsafe-inline' github.githubassets.com\",\n \"age\": \"0\",\n \"set-cookie\": [\n \"_gh_sess=NIjkGD7YYWQ115S4OHG6JQUkfOOgoFL39LjAItk%2BgABxY%2BDMA6dABKH6c%2B9c5eybzagK4jOboXCIoGV5%2BvY2sqWsSyM1a4L%2F3uk3okn2u%2FrYzgP2tHeaEcS89Qvu0v2UOmBp%2BDU0qQvjPTkNvjIfVtZnF9vUoPOWysJ4IUxXaqjkFFS4YWSWD7bqfpxCE6sXU74oFbMVpIQ6pd01yuO1W0ufWGTAYorrAiRqeOZ9%2Fgly%2BtJJTO7in8mfPyFhdnz930P0z8URtENxAMb9d6bBxQ%3D%3D--b4cNsBtPqvjmWGKH--dFzHLmLlq3thQfie%2FC2N4A%3D%3D; Path=/; HttpOnly; Secure\",\n \"_octo=GH1.1.471616304.1583645617; Path=/; Domain=github.com; Expires=Mon, 08 Mar 2021 05:33:37 GMT; Secure\",\n \"logged_in=no; Path=/; Domain=github.com; Expires=Mon, 08 Mar 2021 05:33:37 GMT; HttpOnly; Secure\"\n ],\n \"accept-ranges\": \"bytes\",\n \"transfer-encoding\": \"chunked\",\n \"connection\": \"close\",\n \"x-github-request-id\": \"F216:085B:70FC9:9E247:5E6483B1\"\n },\n \"body\": \"\\n\\n\\n\\n\\n\\n\\n\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n\\n\\n\\n \\n \\n \\n \\n \\n \\n \\n\\n\\n \\n \\n GitHub - enqueuer-land/enqueuer: Multi protocol microservice testing tool\\n \\n \\n \\n \\n\\n \\n \\n\\n \\n \\n \\n\\n \\n\\n\\n\\n \\n\\n \\n\\n \\n\\n \\n \\n \\n\\n \\n\\n\\n\\n\\n \\n\\n\\n\\n\\n\\n\\n \\n\\n \\n \\n\\n \\n\\n\\n \\n\\n \\n \\n\\n \\n\\n \\n\\n \\n\\n\\n \\n\\n\\n \\n\\n \\n\\n \\n \\n\\n\\n\\n\\n \\n\\n \\n\\n \\n \\n\\n
\\n Skip to content\\n \\n \\n \\n\\n \\n \\n\\n\\n\\n
\\n
\\n
\\n \\n \\n \\n
\\n\\n
\\n
\\n \\n
\\n\\n \\n\\n \\n
\\n
\\n
\\n\\n
\\n\\n
\\n\\n\\n
\\n\\n
\\n\\n\\n \\n\\n \\n\\n\\n\\n\\n
\\n
\\n
\\n \\n \\n\\n\\n\\n\\n \\n\\n\\n\\n\\n\\n\\n\\n\\n\\n
\\n\\n
\\n\\n
\\n

\\n \\n \\n enqueuer-land\\n \\n /\\n \\n enqueuer\\n \\n \\n

\\n\\n\\n
\\n\\n \\n\\n
\\n \\n\\n\\n\\n
\\n\\n\\n
\\n
\\n\\n \\n \\n \\n\\n\\n
\\n \\n Multi protocol microservice testing tool\\n \\n https://enqueuer.com\\n
\\n
\\n\\n \\n\\n\\n\\n \\n\\n
\\n \\n
\\n TypeScript\\n JavaScript\\n HTML\\n Shell\\n
\\n
\\n \\n
\\n\\n\\n\\n\\n\\n\\n
\\n \\n
\\n \\n Branch:\\n master\\n \\n \\n\\n \\n
\\n \\n \\n \\n
\\n
\\n
\\n\\n\\n \\n\\n
\\n \\n
\\n\\n
\\n\\n Find file\\n
\\n\\n \\n\\n\\n \\n\\n
\\n \\n Clone or download\\n \\n
\\n
\\n\\n
\\n
\\n\\n

\\n Clone with HTTPS\\n \\n \\n \\n

\\n

\\n Use Git or checkout with SVN using the web URL.\\n

\\n\\n
\\n \\n
\\n \\n
\\n
\\n\\n
\\n\\n \\n
\\n\\n \\n\\n
\\n

Launching GitHub Desktop

\\n

If nothing happens, download GitHub Desktop and try again.

\\n

\\n
\\n\\n
\\n

Launching GitHub Desktop

\\n

If nothing happens, download GitHub Desktop and try again.

\\n

\\n
\\n\\n
\\n

Launching Xcode

\\n

If nothing happens, download Xcode and try again.

\\n

\\n
\\n\\n
\\n

Launching Visual Studio

\\n

If nothing happens, download the GitHub extension for Visual Studio and try again.

\\n

\\n
\\n\\n
\\n
\\n
\\n\\n\\n
\\n\\n\\n\\n
\\n
\\n

Latest commit

\\n
\\n \\n
\\n \\\"\\\"\\n Fetching latest commitâ€Ļ\\n
\\n
\\n Cannot retrieve the latest commit at this time.\\n
\\n
\\n
\\n

Files

\\n \\n\\n\\n Permalink\\n\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n\\n\\n \\n \\n \\n \\n \\n\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n
TypeNameLatest commit messageCommit time
Failed to load latest commit information.
\\n \\n \\\"\\\"\\n \\n .github/ISSUE_TEMPLATE\\n \\n \\n \\n \\n \\n
\\n \\n \\\"\\\"\\n \\n conf\\n \\n \\n \\n \\n \\n
\\n \\n \\\"\\\"\\n \\n docs\\n \\n \\n \\n \\n \\n
\\n \\n \\\"\\\"\\n \\n examples\\n \\n \\n \\n \\n \\n
\\n \\n \\\"\\\"\\n \\n https-cert\\n \\n \\n \\n \\n \\n
\\n \\n \\\"\\\"\\n \\n misc\\n \\n \\n \\n \\n \\n
\\n \\n \\\"\\\"\\n \\n output\\n \\n \\n \\n \\n \\n
\\n \\n \\\"\\\"\\n \\n src\\n \\n \\n \\n \\n \\n
\\n \\n \\\"\\\"\\n \\n temp\\n \\n \\n \\n \\n \\n
\\n \\n \\\"\\\"\\n \\n .codeclimate.yml\\n \\n \\n \\n \\n \\n
\\n \\n \\\"\\\"\\n \\n .gitignore\\n \\n \\n \\n \\n \\n
\\n \\n \\\"\\\"\\n \\n .npmignore\\n \\n \\n \\n \\n \\n
\\n \\n \\\"\\\"\\n \\n .travis.yml\\n \\n \\n \\n \\n \\n
\\n \\n \\\"\\\"\\n \\n CNAME\\n \\n \\n \\n \\n \\n
\\n \\n \\\"\\\"\\n \\n CONTRIBUTING.md\\n \\n \\n \\n \\n \\n
\\n \\n \\\"\\\"\\n \\n License\\n \\n \\n \\n \\n \\n
\\n \\n \\\"\\\"\\n \\n README.md\\n \\n \\n \\n \\n \\n
\\n \\n \\\"\\\"\\n \\n package-lock.json\\n \\n \\n \\n \\n \\n
\\n \\n \\\"\\\"\\n \\n package.json\\n \\n \\n \\n \\n \\n
\\n \\n \\\"\\\"\\n \\n tsconfig.json\\n \\n \\n \\n \\n \\n
\\n \\n \\\"\\\"\\n \\n tslint.json\\n \\n \\n \\n \\n \\n
\\n\\n
\\n\\n\\n\\n
\\n\\n
\\n
\\n

\\n \\n README.md\\n

\\n
\\n
\\n
\\n
\\n
\\n
\\n
\\n\\n
\\n

\\\"npm\\\"\\n\\\"Build\\n\\\"Greenkeeper\\n\\\"Known\\n\\\"License:

\\n

\\\"enqueuerlogo\\\"

\\n

Why

\\n

Make sure your set of services is working properly by mocking its inputs and asserting against\\nits outputs

\\n

What

\\n

Enqueuer is a platform that provides the following capabilities:

\\n
    \\n
  • Support for multi protocols
  • \\n
  • Chainable message flows
  • \\n
  • Friendly for developers and non developers
  • \\n
  • Easily extensible through third party plugins, including yours
  • \\n
  • Automated end-to-end testing
  • \\n
  • Place tests front and center
  • \\n
\\n

Official page

\\n

https://enqueuer.com

\\n

Docs

\\n

Check out the full documentation

\\n
\\n
\\n
\\n\\n\\n\\n\\n
\\n
\\n\\n
\\n
\\n \\n\\n
\\n\\n \\n
\\n
\\n \\n\\n \\n \\n\\n \\n
\\n
\\n \\n
\\n
\\n\\n\\n\\n
\\n \\n \\n You can’t perform that action at this time.\\n
\\n\\n\\n \\n \\n \\n \\n \\n \\n \\n \\n
\\n \\n \\n \\n
\\n \\n\\n
\\n
\\n
\\n
\\n\\n
\\n\\n \\n\\n\\n\"\n}" + description: "{\n \"statusCode\": 200,\n \"headers\": {\n \"accept-ranges\": \"bytes\",\n \"cache-control\": \"max-age=0, private, must-revalidate\",\n \"content-encoding\": \"gzip\",\n \"content-security-policy\": \"default-src 'none'; base-uri 'self'; child-src github.githubassets.com github.com/assets-cdn/worker/ github.com/assets/ gist.github.com/assets-cdn/worker/; connect-src 'self' uploads.github.com www.githubstatus.com collector.github.com raw.githubusercontent.com api.github.com github-cloud.s3.amazonaws.com github-production-repository-file-5c1aeb.s3.amazonaws.com github-production-upload-manifest-file-7fdce7.s3.amazonaws.com github-production-user-asset-6210df.s3.amazonaws.com *.rel.tunnels.api.visualstudio.com wss://*.rel.tunnels.api.visualstudio.com objects-origin.githubusercontent.com copilot-proxy.githubusercontent.com proxy.individual.githubcopilot.com proxy.business.githubcopilot.com proxy.enterprise.githubcopilot.com *.actions.githubusercontent.com wss://*.actions.githubusercontent.com productionresultssa0.blob.core.windows.net/ productionresultssa1.blob.core.windows.net/ productionresultssa2.blob.core.windows.net/ productionresultssa3.blob.core.windows.net/ productionresultssa4.blob.core.windows.net/ productionresultssa5.blob.core.windows.net/ productionresultssa6.blob.core.windows.net/ productionresultssa7.blob.core.windows.net/ productionresultssa8.blob.core.windows.net/ productionresultssa9.blob.core.windows.net/ productionresultssa10.blob.core.windows.net/ productionresultssa11.blob.core.windows.net/ productionresultssa12.blob.core.windows.net/ productionresultssa13.blob.core.windows.net/ productionresultssa14.blob.core.windows.net/ productionresultssa15.blob.core.windows.net/ productionresultssa16.blob.core.windows.net/ productionresultssa17.blob.core.windows.net/ productionresultssa18.blob.core.windows.net/ productionresultssa19.blob.core.windows.net/ github-production-repository-image-32fea6.s3.amazonaws.com github-production-release-asset-2e65be.s3.amazonaws.com insights.github.com wss://alive.github.com api.githubcopilot.com api.individual.githubcopilot.com api.business.githubcopilot.com api.enterprise.githubcopilot.com; font-src github.githubassets.com; form-action 'self' github.com gist.github.com copilot-workspace.githubnext.com objects-origin.githubusercontent.com; frame-ancestors 'none'; frame-src viewscreen.githubusercontent.com notebooks.githubusercontent.com; img-src 'self' data: blob: github.githubassets.com media.githubusercontent.com camo.githubusercontent.com identicons.github.com avatars.githubusercontent.com private-avatars.githubusercontent.com github-cloud.s3.amazonaws.com objects.githubusercontent.com secured-user-images.githubusercontent.com/ user-images.githubusercontent.com/ private-user-images.githubusercontent.com opengraph.githubassets.com github-production-user-asset-6210df.s3.amazonaws.com customer-stories-feed.github.com spotlights-feed.github.com objects-origin.githubusercontent.com *.githubusercontent.com; manifest-src 'self'; media-src github.com user-images.githubusercontent.com/ secured-user-images.githubusercontent.com/ private-user-images.githubusercontent.com github-production-user-asset-6210df.s3.amazonaws.com gist.github.com; script-src github.githubassets.com; style-src 'unsafe-inline' github.githubassets.com; upgrade-insecure-requests; worker-src github.githubassets.com github.com/assets-cdn/worker/ github.com/assets/ gist.github.com/assets-cdn/worker/\",\n \"content-type\": \"text/html; charset=utf-8\",\n \"date\": \"Tue, 18 Mar 2025 20:55:58 GMT\",\n \"etag\": \"W/\\\"f9511f46ff50ad1d216094797be8b550\\\"\",\n \"referrer-policy\": \"no-referrer-when-downgrade\",\n \"server\": \"GitHub.com\",\n \"set-cookie\": \"logged_in=no; Path=/; Domain=github.com; Expires=Wed, 18 Mar 2026 20:55:58 GMT; HttpOnly; Secure; SameSite=Lax\",\n \"strict-transport-security\": \"max-age=31536000; includeSubdomains; preload\",\n \"transfer-encoding\": \"chunked\",\n \"vary\": \"X-PJAX, X-PJAX-Container, Turbo-Visit, Turbo-Frame, Accept-Encoding, Accept, X-Requested-With\",\n \"x-content-type-options\": \"nosniff\",\n \"x-frame-options\": \"deny\",\n \"x-github-request-id\": \"DF11:3659:2CD6D40:3F8465E:67D9DDDE\",\n \"x-xss-protection\": \"0\"\n },\n \"body\": \"\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n \\n \\n \\n \\n \\n \\n \\n \\n\\n \\n\\n \\n\\n \\n \\n \\n \\n \\n\\n\\n \\n\\n\\n \\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n \\n \\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n \\n\\n\\n\\n GitHub - enqueuer-land/enqueuer: Polyglot flow testing tool\\n\\n\\n\\n \\n \\n \\n\\n \\n \\n\\n\\n \\n\\n\\n \\n\\n\\n \\n \\n\\n \\n \\n\\n \\n\\n\\n\\n \\n\\n \\n\\n\\n\\n\\n \\n\\n \\n\\n \\n\\n \\n\\n \\n\\n \\n\\n \\n \\n \\n\\n \\n \\n \\n\\n\\n\\n\\n \\n\\n\\n\\n \\n\\n\\n \\n \\n \\n \\n\\n \\n\\n \\n \\n\\n \\n\\n\\n\\n \\n\\n\\n \\n\\n\\n \\n\\n \\n\\n \\n \\n \\n\\n\\n\\n\\n\\n \\n\\n \\n\\n \\n
\\n \\n\\n\\n
\\n Skip to content\\n\\n \\n \\n \\n \\n \\n\\n\\n\\n\\n \\n \\n
\\n\\n\\n\\n\\n\\n \\n\\n \\n\\n \\n\\n\\n
\\n

Navigation Menu

\\n\\n \\n\\n
\\n
\\n
\\n \\n
\\n\\n \\n \\n \\n\\n \\n\\n
\\n \\n Sign in\\n \\n
\\n
\\n\\n\\n
\\n
\\n \\n\\n
\\n \\n\\n\\n\\n \\n \\n
\\n \\n \\n\\n
\\n Search or jump to...\\n
\\n \\n\\n
\\n \\n\\n \\n\\n \\n
\\n \\n

Search code, repositories, users, issues, pull requests...

\\n
\\n \\n
\\n
\\n \\n
\\n \\n \\n \\n \\n \\n\\n \\n
\\n
\\n
\\n
\\n \\n
\\n
\\n Clear\\n \\n\\n
\\n \\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n
\\n \\n
\\n
\\n
\\n\\n \\n
\\n
\\n\\n
\\n
\\n
\\n \\n
\\n \\n\\n \\n
\\n
\\n
\\n

\\n Provide feedback\\n

\\n \\n
\\n
\\n \\n
\\n
\\n \\n
\\n \\n
\\n

We read every piece of feedback, and take your input very seriously.

\\n \\n \\n \\n
\\n
\\n \\n
\\n\\n \\n \\n\\n \\n
\\n
\\n
\\n

\\n Saved searches\\n

\\n

Use saved searches to filter your results more quickly

\\n
\\n
\\n \\n
\\n
\\n \\n
\\n \\n
\\n\\n \\n\\n
\\n
\\n
\\n\\n
\\n
\\n \\n
\\n
\\n
\\n\\n\\n\\n
\\n \\n Sign in\\n \\n
\\n\\n \\n Sign up\\n \\n \\n
\\n
\\n
\\n \\n\\n\\n \\n \\n\\n
\\n\\n\\n\\n\\n\\n\\n\\n\\n
\\n\\n\\n\\n\\n \\n
\\n\\n\\n \\n\\n\\n\\n\\n\\n\\n \\n
\\n
\\n \\n \\n\\n\\n\\n\\n\\n \\n \\n\\n \\n\\n\\n\\n\\n\\n\\n \\n
\\n\\n
\\n\\n
\\n \\n
\\n \\n \\n\\n \\n \\n \\n enqueuer-land\\n \\n /\\n \\n enqueuer\\n \\n\\n Public\\n
\\n\\n\\n
\\n\\n
\\n \\n\\n
\\n
\\n\\n
\\n
\\n

\\n Polyglot flow testing tool\\n

\\n
\\n \\n \\n\\n \\n enqueuer.com\\n \\n
\\n\\n \\n

License

\\n \\n\\n\\n \\n\\n
\\n \\n
\\n \\n \\nNotifications\\n You must be signed in to change notification settings\\n\\n
\\n \\n \\n\\n \\n
\\n
\\n\\n
\\n\\n\\n \\n\\n
\\n\\n \\n\\n\\n\\n\\n
\\n \\n\\n\\n\\n \\n \\n

enqueuer-land/enqueuer

\\n
\\n
\\n\\n
\\n
\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n \\n \\n

Repository files navigation

Enqueuer

\\n

\\\"npm\\\"\\n\\\"Build\\n\\\"Greenkeeper\\n\\\"Known\\n\\\"License:

\\n

\\\"enqueuerlogo\\\"

\\n

Why

\\n

Make sure your set of services is working properly by mocking its inputs and asserting against\\nits outputs

\\n

What

\\n

Enqueuer is a platform that provides the following capabilities:

\\n
    \\n
  • Support for multi protocols
  • \\n
  • Chainable message flows
  • \\n
  • Friendly for developers and non developers
  • \\n
  • Easily extensible through third party plugins, including yours
  • \\n
  • Automated end-to-end testing
  • \\n
  • Place tests front and center
  • \\n
\\n

Official page

\\n

https://enqueuer.com

\\n

Docs

\\n

Check out the full documentation

\\n
\\n\\n\\n \\n
\\n
\\n\\n
\\n
\\n
\\n
\\n

About

\\n\\n

\\n Polyglot flow testing tool\\n

\\n
\\n \\n \\n\\n \\n enqueuer.com\\n \\n
\\n\\n

Topics

\\n \\n\\n

Resources

\\n \\n\\n \\n

License

\\n \\n\\n\\n

Code of conduct

\\n \\n\\n

Security policy

\\n \\n\\n \\n \\n\\n \\n\\n \\n\\n

Stars

\\n \\n\\n

Watchers

\\n \\n\\n

Forks

\\n \\n\\n \\n
\\n\\n
\\n
\\n\\n \\n \\n\\n \\n \\n
\\n
\\n \\n

\\n Packages\\n

\\n\\n\\n
\\n No packages published
\\n
\\n\\n\\n\\n
\\n
\\n\\n \\n \\n\\n \\n
\\n
\\n

\\n Contributors\\n 5

\\n\\n\\n \\n
    \\n
  • \\n
    \\n
  • \\n
  • \\n
    \\n
  • \\n
  • \\n
    \\n
  • \\n
  • \\n
    \\n
  • \\n
  • \\n
    \\n
  • \\n
\\n
\\n\\n\\n
\\n
\\n\\n \\n \\n
\\n
\\n

Languages

\\n
\\n \\n \\n \\n \\n \\n
\\n\\n\\n
\\n
\\n\\n
\\n
\\n \\n
\\n\\n
\\n\\n\\n
\\n\\n
\\n\\n\\n
\\n
\\n\\n \\n\\n
\\n

Footer

\\n\\n \\n\\n\\n
\\n
\\n \\n \\n \\n\\n\\n \\n © 2025 GitHub, Inc.\\n \\n
\\n\\n \\n
\\n
\\n\\n\\n\\n \\n\\n\\n\\n \\n\\n \\n\\n
\\n
\\n
\\n
\\n\\n \\n\\n\\n\\n\\n\\n \\n\\n
\\n
\\n \\n\\n\\n\"\n}" + implicit: true valid: true onResponseReceived: arguments: + status: 200 statusCode: 200 - body: "\n\n\n\n\n\n\n\n \n \n \n \n \n \n \n \n \n\n\n\n \n \n \n \n \n \n \n\n\n \n \n GitHub - enqueuer-land/enqueuer: Multi protocol microservice testing tool\n \n \n \n \n\n \n \n\n \n \n \n\n \n\n\n\n \n\n \n\n \n\n \n \n \n\n \n\n\n\n\n \n\n\n\n\n\n\n \n\n \n \n\n \n\n\n \n\n \n \n\n \n\n \n\n \n\n\n \n\n\n \n\n \n\n \n \n\n\n\n\n \n\n \n\n \n \n\n
\n Skip to content\n \n \n \n\n \n \n\n\n\n
\n
\n
\n \n \n \n
\n\n
\n
\n \n
\n\n \n\n \n
\n
\n
\n\n
\n\n
\n\n\n
\n\n
\n\n\n \n\n \n\n\n\n\n
\n
\n
\n \n \n\n\n\n\n \n\n\n\n\n\n\n\n\n\n
\n\n
\n\n
\n

\n \n \n enqueuer-land\n \n /\n \n enqueuer\n \n \n

\n\n\n
\n\n \n\n
\n \n\n\n\n
\n\n\n
\n
\n\n \n \n \n\n\n
\n \n Multi protocol microservice testing tool\n \n https://enqueuer.com\n
\n
\n\n \n\n\n\n \n\n
\n \n
\n TypeScript\n JavaScript\n HTML\n Shell\n
\n
\n \n
\n\n\n\n\n\n\n
\n \n
\n \n Branch:\n master\n \n \n\n \n
\n \n \n \n
\n
\n
\n\n\n \n\n
\n \n
\n\n
\n\n Find file\n
\n\n \n\n\n \n\n
\n \n Clone or download\n \n
\n
\n\n
\n
\n\n

\n Clone with HTTPS\n \n \n \n

\n

\n Use Git or checkout with SVN using the web URL.\n

\n\n
\n \n
\n \n
\n
\n\n
\n\n \n
\n\n \n\n
\n

Launching GitHub Desktop

\n

If nothing happens, download GitHub Desktop and try again.

\n

\n
\n\n
\n

Launching GitHub Desktop

\n

If nothing happens, download GitHub Desktop and try again.

\n

\n
\n\n
\n

Launching Xcode

\n

If nothing happens, download Xcode and try again.

\n

\n
\n\n
\n

Launching Visual Studio

\n

If nothing happens, download the GitHub extension for Visual Studio and try again.

\n

\n
\n\n
\n
\n
\n\n\n
\n\n\n\n
\n
\n

Latest commit

\n
\n \n
\n \"\"\n Fetching latest commitâ€Ļ\n
\n
\n Cannot retrieve the latest commit at this time.\n
\n
\n
\n

Files

\n \n\n\n Permalink\n\n \n \n \n \n \n \n \n \n \n\n\n \n \n \n \n \n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
TypeNameLatest commit messageCommit time
Failed to load latest commit information.
\n \n \"\"\n \n .github/ISSUE_TEMPLATE\n \n \n \n \n \n
\n \n \"\"\n \n conf\n \n \n \n \n \n
\n \n \"\"\n \n docs\n \n \n \n \n \n
\n \n \"\"\n \n examples\n \n \n \n \n \n
\n \n \"\"\n \n https-cert\n \n \n \n \n \n
\n \n \"\"\n \n misc\n \n \n \n \n \n
\n \n \"\"\n \n output\n \n \n \n \n \n
\n \n \"\"\n \n src\n \n \n \n \n \n
\n \n \"\"\n \n temp\n \n \n \n \n \n
\n \n \"\"\n \n .codeclimate.yml\n \n \n \n \n \n
\n \n \"\"\n \n .gitignore\n \n \n \n \n \n
\n \n \"\"\n \n .npmignore\n \n \n \n \n \n
\n \n \"\"\n \n .travis.yml\n \n \n \n \n \n
\n \n \"\"\n \n CNAME\n \n \n \n \n \n
\n \n \"\"\n \n CONTRIBUTING.md\n \n \n \n \n \n
\n \n \"\"\n \n License\n \n \n \n \n \n
\n \n \"\"\n \n README.md\n \n \n \n \n \n
\n \n \"\"\n \n package-lock.json\n \n \n \n \n \n
\n \n \"\"\n \n package.json\n \n \n \n \n \n
\n \n \"\"\n \n tsconfig.json\n \n \n \n \n \n
\n \n \"\"\n \n tslint.json\n \n \n \n \n \n
\n\n
\n\n\n\n
\n\n
\n
\n

\n \n README.md\n

\n
\n
\n
\n
\n
\n
\n
\n\n
\n

\"npm\"\n\"Build\n\"Greenkeeper\n\"Known\n\"License:

\n

\"enqueuerlogo\"

\n

Why

\n

Make sure your set of services is working properly by mocking its inputs and asserting against\nits outputs

\n

What

\n

Enqueuer is a platform that provides the following capabilities:

\n
    \n
  • Support for multi protocols
  • \n
  • Chainable message flows
  • \n
  • Friendly for developers and non developers
  • \n
  • Easily extensible through third party plugins, including yours
  • \n
  • Automated end-to-end testing
  • \n
  • Place tests front and center
  • \n
\n

Official page

\n

https://enqueuer.com

\n

Docs

\n

Check out the full documentation

\n
\n
\n
\n\n\n\n\n
\n
\n\n
\n
\n \n\n
\n\n \n
\n
\n \n\n \n \n\n \n
\n
\n \n
\n
\n\n\n\n
\n \n \n You can’t perform that action at this time.\n
\n\n\n \n \n \n \n \n \n \n \n
\n \n \n \n
\n \n\n
\n
\n
\n
\n\n
\n\n \n\n\n" + body: "\n\n\n\n\n\n\n\n\n\n\n\n \n \n \n \n \n \n \n \n\n \n\n \n\n \n \n \n \n \n\n\n \n\n\n \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n \n \n\n\n\n\n\n\n\n\n\n\n \n\n\n\n GitHub - enqueuer-land/enqueuer: Polyglot flow testing tool\n\n\n\n \n \n \n\n \n \n\n\n \n\n\n \n\n\n \n \n\n \n \n\n \n\n\n\n \n\n \n\n\n\n\n \n\n \n\n \n\n \n\n \n\n \n\n \n \n \n\n \n \n \n\n\n\n\n \n\n\n\n \n\n\n \n \n \n \n\n \n\n \n \n\n \n\n\n\n \n\n\n \n\n\n \n\n \n\n \n \n \n\n\n\n\n\n \n\n \n\n \n
\n \n\n\n
\n Skip to content\n\n \n \n \n \n \n\n\n\n\n \n \n
\n\n\n\n\n\n \n\n \n\n \n\n\n
\n

Navigation Menu

\n\n \n\n
\n
\n
\n \n
\n\n \n \n \n\n \n\n
\n \n Sign in\n \n
\n
\n\n\n
\n
\n \n\n
\n \n\n\n\n \n \n
\n \n \n\n
\n Search or jump to...\n
\n \n\n
\n \n\n \n\n \n
\n \n

Search code, repositories, users, issues, pull requests...

\n
\n \n
\n
\n \n
\n \n \n \n \n \n\n \n
\n
\n
\n
\n \n
\n
\n Clear\n \n\n
\n \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
\n \n
\n
\n
\n\n \n
\n
\n\n
\n
\n
\n \n
\n \n\n \n
\n
\n
\n

\n Provide feedback\n

\n \n
\n
\n \n
\n
\n \n
\n \n
\n

We read every piece of feedback, and take your input very seriously.

\n \n \n \n
\n
\n \n
\n\n \n \n\n \n
\n
\n
\n

\n Saved searches\n

\n

Use saved searches to filter your results more quickly

\n
\n
\n \n
\n
\n \n
\n \n
\n\n \n\n
\n
\n
\n\n
\n
\n \n
\n
\n
\n\n\n\n
\n \n Sign in\n \n
\n\n \n Sign up\n \n \n
\n
\n
\n \n\n\n \n \n\n
\n\n\n\n\n\n\n\n\n
\n\n\n\n\n \n
\n\n\n \n\n\n\n\n\n\n \n
\n
\n \n \n\n\n\n\n\n \n \n\n \n\n\n\n\n\n\n \n
\n\n
\n\n
\n \n
\n \n \n\n \n \n \n enqueuer-land\n \n /\n \n enqueuer\n \n\n Public\n
\n\n\n
\n\n
\n \n\n
\n
\n\n
\n
\n

\n Polyglot flow testing tool\n

\n
\n \n \n\n \n enqueuer.com\n \n
\n\n \n

License

\n \n\n\n \n\n
\n \n
\n \n \nNotifications\n You must be signed in to change notification settings\n\n
\n \n \n\n \n
\n
\n\n
\n\n\n \n\n
\n\n \n\n\n\n\n
\n \n\n\n\n \n \n

enqueuer-land/enqueuer

\n
\n
\n\n
\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n \n \n

Repository files navigation

Enqueuer

\n

\"npm\"\n\"Build\n\"Greenkeeper\n\"Known\n\"License:

\n

\"enqueuerlogo\"

\n

Why

\n

Make sure your set of services is working properly by mocking its inputs and asserting against\nits outputs

\n

What

\n

Enqueuer is a platform that provides the following capabilities:

\n
    \n
  • Support for multi protocols
  • \n
  • Chainable message flows
  • \n
  • Friendly for developers and non developers
  • \n
  • Easily extensible through third party plugins, including yours
  • \n
  • Automated end-to-end testing
  • \n
  • Place tests front and center
  • \n
\n

Official page

\n

https://enqueuer.com

\n

Docs

\n

Check out the full documentation

\n
\n\n\n \n
\n
\n\n
\n
\n
\n
\n

About

\n\n

\n Polyglot flow testing tool\n

\n
\n \n \n\n \n enqueuer.com\n \n
\n\n

Topics

\n \n\n

Resources

\n \n\n \n

License

\n \n\n\n

Code of conduct

\n \n\n

Security policy

\n \n\n \n \n\n \n\n \n\n

Stars

\n \n\n

Watchers

\n \n\n

Forks

\n \n\n \n
\n\n
\n
\n\n \n \n\n \n \n
\n
\n \n

\n Packages\n

\n\n\n
\n No packages published
\n
\n\n\n\n
\n
\n\n \n \n\n \n
\n
\n

\n Contributors\n 5

\n\n\n \n
    \n
  • \n
    \n
  • \n
  • \n
    \n
  • \n
  • \n
    \n
  • \n
  • \n
    \n
  • \n
  • \n
    \n
  • \n
\n
\n\n\n
\n
\n\n \n \n
\n
\n

Languages

\n
\n \n \n \n \n \n
\n\n\n
\n
\n\n
\n
\n \n
\n\n
\n\n\n
\n\n
\n\n\n
\n
\n\n \n\n
\n

Footer

\n\n \n\n\n
\n
\n \n \n \n\n\n \n © 2025 GitHub, Inc.\n \n
\n\n \n
\n
\n\n\n\n \n\n\n\n \n\n \n\n
\n
\n
\n
\n\n \n\n\n\n\n\n \n\n
\n
\n \n\n\n" headers: - date: 'Sun, 08 Mar 2020 05:33:37 GMT' + accept-ranges: bytes + cache-control: 'max-age=0, private, must-revalidate' + content-encoding: gzip + content-security-policy: 'default-src ''none''; base-uri ''self''; child-src github.githubassets.com github.com/assets-cdn/worker/ github.com/assets/ gist.github.com/assets-cdn/worker/; connect-src ''self'' uploads.github.com www.githubstatus.com collector.github.com raw.githubusercontent.com api.github.com github-cloud.s3.amazonaws.com github-production-repository-file-5c1aeb.s3.amazonaws.com github-production-upload-manifest-file-7fdce7.s3.amazonaws.com github-production-user-asset-6210df.s3.amazonaws.com *.rel.tunnels.api.visualstudio.com wss://*.rel.tunnels.api.visualstudio.com objects-origin.githubusercontent.com copilot-proxy.githubusercontent.com proxy.individual.githubcopilot.com proxy.business.githubcopilot.com proxy.enterprise.githubcopilot.com *.actions.githubusercontent.com wss://*.actions.githubusercontent.com productionresultssa0.blob.core.windows.net/ productionresultssa1.blob.core.windows.net/ productionresultssa2.blob.core.windows.net/ productionresultssa3.blob.core.windows.net/ productionresultssa4.blob.core.windows.net/ productionresultssa5.blob.core.windows.net/ productionresultssa6.blob.core.windows.net/ productionresultssa7.blob.core.windows.net/ productionresultssa8.blob.core.windows.net/ productionresultssa9.blob.core.windows.net/ productionresultssa10.blob.core.windows.net/ productionresultssa11.blob.core.windows.net/ productionresultssa12.blob.core.windows.net/ productionresultssa13.blob.core.windows.net/ productionresultssa14.blob.core.windows.net/ productionresultssa15.blob.core.windows.net/ productionresultssa16.blob.core.windows.net/ productionresultssa17.blob.core.windows.net/ productionresultssa18.blob.core.windows.net/ productionresultssa19.blob.core.windows.net/ github-production-repository-image-32fea6.s3.amazonaws.com github-production-release-asset-2e65be.s3.amazonaws.com insights.github.com wss://alive.github.com api.githubcopilot.com api.individual.githubcopilot.com api.business.githubcopilot.com api.enterprise.githubcopilot.com; font-src github.githubassets.com; form-action ''self'' github.com gist.github.com copilot-workspace.githubnext.com objects-origin.githubusercontent.com; frame-ancestors ''none''; frame-src viewscreen.githubusercontent.com notebooks.githubusercontent.com; img-src ''self'' data: blob: github.githubassets.com media.githubusercontent.com camo.githubusercontent.com identicons.github.com avatars.githubusercontent.com private-avatars.githubusercontent.com github-cloud.s3.amazonaws.com objects.githubusercontent.com secured-user-images.githubusercontent.com/ user-images.githubusercontent.com/ private-user-images.githubusercontent.com opengraph.githubassets.com github-production-user-asset-6210df.s3.amazonaws.com customer-stories-feed.github.com spotlights-feed.github.com objects-origin.githubusercontent.com *.githubusercontent.com; manifest-src ''self''; media-src github.com user-images.githubusercontent.com/ secured-user-images.githubusercontent.com/ private-user-images.githubusercontent.com github-production-user-asset-6210df.s3.amazonaws.com gist.github.com; script-src github.githubassets.com; style-src ''unsafe-inline'' github.githubassets.com; upgrade-insecure-requests; worker-src github.githubassets.com github.com/assets-cdn/worker/ github.com/assets/ gist.github.com/assets-cdn/worker/' content-type: 'text/html; charset=utf-8' + date: 'Tue, 18 Mar 2025 20:55:58 GMT' + etag: 'W/"f9511f46ff50ad1d216094797be8b550"' + referrer-policy: no-referrer-when-downgrade server: GitHub.com - status: '200 OK' - vary: 'X-PJAX, Accept-Encoding, Accept, X-Requested-With' - etag: 'W/"e488a776a390ca1e6112af885e19a43b"' - cache-control: 'max-age=0, private, must-revalidate' + set-cookie: 'logged_in=no; Path=/; Domain=github.com; Expires=Wed, 18 Mar 2026 20:55:58 GMT; HttpOnly; Secure; SameSite=Lax' strict-transport-security: 'max-age=31536000; includeSubdomains; preload' - x-frame-options: deny - x-content-type-options: nosniff - x-xss-protection: '1; mode=block' - expect-ct: 'max-age=2592000, report-uri="https://api.github.com/_private/browser/errors"' - content-security-policy: 'default-src ''none''; base-uri ''self''; block-all-mixed-content; connect-src ''self'' uploads.github.com www.githubstatus.com collector.githubapp.com api.github.com www.google-analytics.com github-cloud.s3.amazonaws.com github-production-repository-file-5c1aeb.s3.amazonaws.com github-production-upload-manifest-file-7fdce7.s3.amazonaws.com github-production-user-asset-6210df.s3.amazonaws.com wss://live.github.com; font-src github.githubassets.com; form-action ''self'' github.com gist.github.com; frame-ancestors ''none''; frame-src render.githubusercontent.com; img-src ''self'' data: github.githubassets.com identicons.github.com collector.githubapp.com github-cloud.s3.amazonaws.com *.githubusercontent.com; manifest-src ''self''; media-src ''none''; script-src github.githubassets.com; style-src ''unsafe-inline'' github.githubassets.com' - age: '0' - set-cookie: - - '_gh_sess=NIjkGD7YYWQ115S4OHG6JQUkfOOgoFL39LjAItk%2BgABxY%2BDMA6dABKH6c%2B9c5eybzagK4jOboXCIoGV5%2BvY2sqWsSyM1a4L%2F3uk3okn2u%2FrYzgP2tHeaEcS89Qvu0v2UOmBp%2BDU0qQvjPTkNvjIfVtZnF9vUoPOWysJ4IUxXaqjkFFS4YWSWD7bqfpxCE6sXU74oFbMVpIQ6pd01yuO1W0ufWGTAYorrAiRqeOZ9%2Fgly%2BtJJTO7in8mfPyFhdnz930P0z8URtENxAMb9d6bBxQ%3D%3D--b4cNsBtPqvjmWGKH--dFzHLmLlq3thQfie%2FC2N4A%3D%3D; Path=/; HttpOnly; Secure' - - '_octo=GH1.1.471616304.1583645617; Path=/; Domain=github.com; Expires=Mon, 08 Mar 2021 05:33:37 GMT; Secure' - - 'logged_in=no; Path=/; Domain=github.com; Expires=Mon, 08 Mar 2021 05:33:37 GMT; HttpOnly; Secure' - accept-ranges: bytes transfer-encoding: chunked - connection: close - x-github-request-id: 'F216:085B:70FC9:9E247:5E6483B1' - request: - uri: - protocol: 'https:' - slashes: true - auth: null - host: github.com - port: 443 - hostname: github.com - hash: null - search: null - query: null - pathname: /enqueuer-land/enqueuer - path: /enqueuer-land/enqueuer - href: 'https://github.com/enqueuer-land/enqueuer' - method: get - headers: - content-length: 0 + vary: 'X-PJAX, X-PJAX-Container, Turbo-Visit, Turbo-Frame, Accept-Encoding, Accept, X-Requested-With' + x-content-type-options: nosniff + x-frame-options: deny + x-github-request-id: 'DF11:3659:2CD6D40:3F8465E:67D9DDDE' + x-xss-protection: '0' + elapsedTime: 977 tests: - name: 'Assertion #0' valid: true description: 'Expected ''statusCode'' to be equal to ''200''. Received ''200''' valid: true - onMessageReceived: - arguments: - statusCode: 200 - body: "\n\n\n\n\n\n\n\n \n \n \n \n \n \n \n \n \n\n\n\n \n \n \n \n \n \n \n\n\n \n \n GitHub - enqueuer-land/enqueuer: Multi protocol microservice testing tool\n \n \n \n \n\n \n \n\n \n \n \n\n \n\n\n\n \n\n \n\n \n\n \n \n \n\n \n\n\n\n\n \n\n\n\n\n\n\n \n\n \n \n\n \n\n\n \n\n \n \n\n \n\n \n\n \n\n\n \n\n\n \n\n \n\n \n \n\n\n\n\n \n\n \n\n \n \n\n
\n Skip to content\n \n \n \n\n \n \n\n\n\n
\n
\n
\n \n \n \n
\n\n
\n
\n \n
\n\n \n\n \n
\n
\n
\n\n
\n\n
\n\n\n
\n\n
\n\n\n \n\n \n\n\n\n\n
\n
\n
\n \n \n\n\n\n\n \n\n\n\n\n\n\n\n\n\n
\n\n
\n\n
\n

\n \n \n enqueuer-land\n \n /\n \n enqueuer\n \n \n

\n\n\n
\n\n \n\n
\n \n\n\n\n
\n\n\n
\n
\n\n \n \n \n\n\n
\n \n Multi protocol microservice testing tool\n \n https://enqueuer.com\n
\n
\n\n \n\n\n\n \n\n
\n \n
\n TypeScript\n JavaScript\n HTML\n Shell\n
\n
\n \n
\n\n\n\n\n\n\n
\n \n
\n \n Branch:\n master\n \n \n\n \n
\n \n \n \n
\n
\n
\n\n\n \n\n
\n \n
\n\n
\n\n Find file\n
\n\n \n\n\n \n\n
\n \n Clone or download\n \n
\n
\n\n
\n
\n\n

\n Clone with HTTPS\n \n \n \n

\n

\n Use Git or checkout with SVN using the web URL.\n

\n\n
\n \n
\n \n
\n
\n\n
\n\n \n
\n\n \n\n
\n

Launching GitHub Desktop

\n

If nothing happens, download GitHub Desktop and try again.

\n

\n
\n\n
\n

Launching GitHub Desktop

\n

If nothing happens, download GitHub Desktop and try again.

\n

\n
\n\n
\n

Launching Xcode

\n

If nothing happens, download Xcode and try again.

\n

\n
\n\n
\n

Launching Visual Studio

\n

If nothing happens, download the GitHub extension for Visual Studio and try again.

\n

\n
\n\n
\n
\n
\n\n\n
\n\n\n\n
\n
\n

Latest commit

\n
\n \n
\n \"\"\n Fetching latest commitâ€Ļ\n
\n
\n Cannot retrieve the latest commit at this time.\n
\n
\n
\n

Files

\n \n\n\n Permalink\n\n \n \n \n \n \n \n \n \n \n\n\n \n \n \n \n \n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
TypeNameLatest commit messageCommit time
Failed to load latest commit information.
\n \n \"\"\n \n .github/ISSUE_TEMPLATE\n \n \n \n \n \n
\n \n \"\"\n \n conf\n \n \n \n \n \n
\n \n \"\"\n \n docs\n \n \n \n \n \n
\n \n \"\"\n \n examples\n \n \n \n \n \n
\n \n \"\"\n \n https-cert\n \n \n \n \n \n
\n \n \"\"\n \n misc\n \n \n \n \n \n
\n \n \"\"\n \n output\n \n \n \n \n \n
\n \n \"\"\n \n src\n \n \n \n \n \n
\n \n \"\"\n \n temp\n \n \n \n \n \n
\n \n \"\"\n \n .codeclimate.yml\n \n \n \n \n \n
\n \n \"\"\n \n .gitignore\n \n \n \n \n \n
\n \n \"\"\n \n .npmignore\n \n \n \n \n \n
\n \n \"\"\n \n .travis.yml\n \n \n \n \n \n
\n \n \"\"\n \n CNAME\n \n \n \n \n \n
\n \n \"\"\n \n CONTRIBUTING.md\n \n \n \n \n \n
\n \n \"\"\n \n License\n \n \n \n \n \n
\n \n \"\"\n \n README.md\n \n \n \n \n \n
\n \n \"\"\n \n package-lock.json\n \n \n \n \n \n
\n \n \"\"\n \n package.json\n \n \n \n \n \n
\n \n \"\"\n \n tsconfig.json\n \n \n \n \n \n
\n \n \"\"\n \n tslint.json\n \n \n \n \n \n
\n\n
\n\n\n\n
\n\n
\n
\n

\n \n README.md\n

\n
\n
\n
\n
\n
\n
\n
\n\n
\n

\"npm\"\n\"Build\n\"Greenkeeper\n\"Known\n\"License:

\n

\"enqueuerlogo\"

\n

Why

\n

Make sure your set of services is working properly by mocking its inputs and asserting against\nits outputs

\n

What

\n

Enqueuer is a platform that provides the following capabilities:

\n
    \n
  • Support for multi protocols
  • \n
  • Chainable message flows
  • \n
  • Friendly for developers and non developers
  • \n
  • Easily extensible through third party plugins, including yours
  • \n
  • Automated end-to-end testing
  • \n
  • Place tests front and center
  • \n
\n

Official page

\n

https://enqueuer.com

\n

Docs

\n

Check out the full documentation

\n
\n
\n
\n\n\n\n\n
\n
\n\n
\n
\n \n\n
\n\n \n
\n
\n \n\n \n \n\n \n
\n
\n \n
\n
\n\n\n\n
\n \n \n You can’t perform that action at this time.\n
\n\n\n \n \n \n \n \n \n \n \n
\n \n \n \n
\n \n\n
\n
\n
\n
\n\n
\n\n \n\n\n" - headers: - date: 'Sun, 08 Mar 2020 05:33:37 GMT' - content-type: 'text/html; charset=utf-8' - server: GitHub.com - status: '200 OK' - vary: 'X-PJAX, Accept-Encoding, Accept, X-Requested-With' - etag: 'W/"e488a776a390ca1e6112af885e19a43b"' - cache-control: 'max-age=0, private, must-revalidate' - strict-transport-security: 'max-age=31536000; includeSubdomains; preload' - x-frame-options: deny - x-content-type-options: nosniff - x-xss-protection: '1; mode=block' - expect-ct: 'max-age=2592000, report-uri="https://api.github.com/_private/browser/errors"' - content-security-policy: 'default-src ''none''; base-uri ''self''; block-all-mixed-content; connect-src ''self'' uploads.github.com www.githubstatus.com collector.githubapp.com api.github.com www.google-analytics.com github-cloud.s3.amazonaws.com github-production-repository-file-5c1aeb.s3.amazonaws.com github-production-upload-manifest-file-7fdce7.s3.amazonaws.com github-production-user-asset-6210df.s3.amazonaws.com wss://live.github.com; font-src github.githubassets.com; form-action ''self'' github.com gist.github.com; frame-ancestors ''none''; frame-src render.githubusercontent.com; img-src ''self'' data: github.githubassets.com identicons.github.com collector.githubapp.com github-cloud.s3.amazonaws.com *.githubusercontent.com; manifest-src ''self''; media-src ''none''; script-src github.githubassets.com; style-src ''unsafe-inline'' github.githubassets.com' - age: '0' - set-cookie: - - '_gh_sess=NIjkGD7YYWQ115S4OHG6JQUkfOOgoFL39LjAItk%2BgABxY%2BDMA6dABKH6c%2B9c5eybzagK4jOboXCIoGV5%2BvY2sqWsSyM1a4L%2F3uk3okn2u%2FrYzgP2tHeaEcS89Qvu0v2UOmBp%2BDU0qQvjPTkNvjIfVtZnF9vUoPOWysJ4IUxXaqjkFFS4YWSWD7bqfpxCE6sXU74oFbMVpIQ6pd01yuO1W0ufWGTAYorrAiRqeOZ9%2Fgly%2BtJJTO7in8mfPyFhdnz930P0z8URtENxAMb9d6bBxQ%3D%3D--b4cNsBtPqvjmWGKH--dFzHLmLlq3thQfie%2FC2N4A%3D%3D; Path=/; HttpOnly; Secure' - - '_octo=GH1.1.471616304.1583645617; Path=/; Domain=github.com; Expires=Mon, 08 Mar 2021 05:33:37 GMT; Secure' - - 'logged_in=no; Path=/; Domain=github.com; Expires=Mon, 08 Mar 2021 05:33:37 GMT; HttpOnly; Secure' - accept-ranges: bytes - transfer-encoding: chunked - connection: close - x-github-request-id: 'F216:085B:70FC9:9E247:5E6483B1' - request: - uri: - protocol: 'https:' - slashes: true - auth: null - host: github.com - port: 443 - hostname: github.com - hash: null - search: null - query: null - pathname: /enqueuer-land/enqueuer - path: /enqueuer-land/enqueuer - href: 'https://github.com/enqueuer-land/enqueuer' - method: get - headers: - content-length: 0 - tests: [] - valid: true type: http - publishTime: '2020-03-08T05:33:37.626Z' + messageSentInstant: '2025-03-18T20:55:58.995Z' iteration: 0 totalIterations: 1 hooks: @@ -4405,20 +3923,20 @@ requisitions: valid: true tests: [] arguments: - elapsedTime: 762 + elapsedTime: 985 time: - startTime: '2020-03-08T05:33:36.866Z' - endTime: '2020-03-08T05:33:37.628Z' - totalTime: 762 + startTime: '2025-03-18T20:55:58.015Z' + endTime: '2025-03-18T20:55:59.000Z' + totalTime: 985 timeout: 5000 - requisitions: [] + tasks: [] - valid: true name: examples/recursion.yml - id: 0233360795_00da84a874_805085 + id: 1355570989_82eed4bb8e_179234 level: 1 - subscriptions: [] - publishers: [] + sensors: [] + actuators: [] iteration: 0 totalIterations: 1 hooks: @@ -4431,20 +3949,20 @@ requisitions: valid: true tests: [] arguments: - elapsedTime: 82 + elapsedTime: 93 time: - startTime: '2020-03-08T05:33:36.866Z' - endTime: '2020-03-08T05:33:36.948Z' - totalTime: 82 + startTime: '2025-03-18T20:55:58.016Z' + endTime: '2025-03-18T20:55:58.109Z' + totalTime: 93 timeout: 5000 - requisitions: + tasks: - valid: true - name: 'Requisition #0' - id: 0233360795_75977bc4c1_982184 + name: 'Task #0' + id: 1355570989_0c7f0fb7f7_171606 level: 2 - subscriptions: [] - publishers: [] + sensors: [] + actuators: [] iteration: 0 totalIterations: 1 hooks: @@ -4457,20 +3975,20 @@ requisitions: valid: true tests: [] arguments: - elapsedTime: 31 + elapsedTime: 72 time: - startTime: '2020-03-08T05:33:36.886Z' - endTime: '2020-03-08T05:33:36.917Z' - totalTime: 31 + startTime: '2025-03-18T20:55:58.022Z' + endTime: '2025-03-18T20:55:58.095Z' + totalTime: 73 timeout: 5000 - requisitions: [] + tasks: [] - valid: true - name: 'Requisition #1' - id: 0233360795_e87d17a22e_21789 + name: 'Task #1' + id: 1355570989_57cf642fae_901943 level: 2 - subscriptions: [] - publishers: [] + sensors: [] + actuators: [] iteration: 0 totalIterations: 3 hooks: @@ -4483,20 +4001,20 @@ requisitions: valid: true tests: [] arguments: - elapsedTime: 16 + elapsedTime: 9 time: - startTime: '2020-03-08T05:33:36.921Z' - endTime: '2020-03-08T05:33:36.937Z' - totalTime: 16 + startTime: '2025-03-18T20:55:58.097Z' + endTime: '2025-03-18T20:55:58.106Z' + totalTime: 9 timeout: 5000 - requisitions: + tasks: - valid: true - name: 'Requisition #0' - id: 0233360795_55233c1965_344410 + name: 'Task #0' + id: 1355570989_a83fe935c1_633469 level: 3 - subscriptions: [] - publishers: [] + sensors: [] + actuators: [] iteration: 0 totalIterations: 3 hooks: @@ -4509,20 +4027,20 @@ requisitions: valid: true tests: [] arguments: - elapsedTime: 7 + elapsedTime: 5 time: - startTime: '2020-03-08T05:33:36.923Z' - endTime: '2020-03-08T05:33:36.930Z' - totalTime: 7 + startTime: '2025-03-18T20:55:58.098Z' + endTime: '2025-03-18T20:55:58.103Z' + totalTime: 5 timeout: 5000 - requisitions: + tasks: - valid: true - name: 'Requisition #0' - id: 0233360795_5e02f9345e_207197 + name: 'Task #0' + id: 1355570989_7ffa627c18_29061 level: 4 - subscriptions: [] - publishers: [] + sensors: [] + actuators: [] iteration: 0 totalIterations: 3 hooks: @@ -4535,20 +4053,20 @@ requisitions: valid: true tests: [] arguments: - elapsedTime: 0 + elapsedTime: 1 time: - startTime: '2020-03-08T05:33:36.924Z' - endTime: '2020-03-08T05:33:36.925Z' + startTime: '2025-03-18T20:55:58.098Z' + endTime: '2025-03-18T20:55:58.099Z' totalTime: 1 timeout: 5000 - requisitions: [] + tasks: [] - valid: true - name: 'Requisition #0' - id: 0233360795_5e02f9345e_207197 + name: 'Task #0' + id: 1355570989_7ffa627c18_29061 level: 4 - subscriptions: [] - publishers: [] + sensors: [] + actuators: [] iteration: 1 totalIterations: 3 hooks: @@ -4563,18 +4081,18 @@ requisitions: arguments: elapsedTime: 1 time: - startTime: '2020-03-08T05:33:36.927Z' - endTime: '2020-03-08T05:33:36.928Z' + startTime: '2025-03-18T20:55:58.099Z' + endTime: '2025-03-18T20:55:58.100Z' totalTime: 1 timeout: 5000 - requisitions: [] + tasks: [] - valid: true - name: 'Requisition #0' - id: 0233360795_5e02f9345e_207197 + name: 'Task #0' + id: 1355570989_7ffa627c18_29061 level: 4 - subscriptions: [] - publishers: [] + sensors: [] + actuators: [] iteration: 2 totalIterations: 3 hooks: @@ -4587,20 +4105,20 @@ requisitions: valid: true tests: [] arguments: - elapsedTime: 0 + elapsedTime: 2 time: - startTime: '2020-03-08T05:33:36.929Z' - endTime: '2020-03-08T05:33:36.930Z' - totalTime: 1 + startTime: '2025-03-18T20:55:58.100Z' + endTime: '2025-03-18T20:55:58.102Z' + totalTime: 2 timeout: 5000 - requisitions: [] + tasks: [] - valid: true - name: 'Requisition #0' - id: 0233360795_55233c1965_344410 + name: 'Task #0' + id: 1355570989_a83fe935c1_633469 level: 3 - subscriptions: [] - publishers: [] + sensors: [] + actuators: [] iteration: 1 totalIterations: 3 hooks: @@ -4613,20 +4131,20 @@ requisitions: valid: true tests: [] arguments: - elapsedTime: 4 + elapsedTime: 2 time: - startTime: '2020-03-08T05:33:36.931Z' - endTime: '2020-03-08T05:33:36.935Z' - totalTime: 4 + startTime: '2025-03-18T20:55:58.103Z' + endTime: '2025-03-18T20:55:58.105Z' + totalTime: 2 timeout: 5000 - requisitions: + tasks: - valid: true - name: 'Requisition #0' - id: 0233360795_5e02f9345e_207197 + name: 'Task #0' + id: 1355570989_7ffa627c18_29061 level: 4 - subscriptions: [] - publishers: [] + sensors: [] + actuators: [] iteration: 0 totalIterations: 3 hooks: @@ -4639,20 +4157,20 @@ requisitions: valid: true tests: [] arguments: - elapsedTime: 1 + elapsedTime: 0 time: - startTime: '2020-03-08T05:33:36.931Z' - endTime: '2020-03-08T05:33:36.932Z' - totalTime: 1 + startTime: '2025-03-18T20:55:58.103Z' + endTime: '2025-03-18T20:55:58.103Z' + totalTime: 0 timeout: 5000 - requisitions: [] + tasks: [] - valid: true - name: 'Requisition #0' - id: 0233360795_5e02f9345e_207197 + name: 'Task #0' + id: 1355570989_7ffa627c18_29061 level: 4 - subscriptions: [] - publishers: [] + sensors: [] + actuators: [] iteration: 1 totalIterations: 3 hooks: @@ -4667,18 +4185,18 @@ requisitions: arguments: elapsedTime: 1 time: - startTime: '2020-03-08T05:33:36.932Z' - endTime: '2020-03-08T05:33:36.933Z' + startTime: '2025-03-18T20:55:58.103Z' + endTime: '2025-03-18T20:55:58.104Z' totalTime: 1 timeout: 5000 - requisitions: [] + tasks: [] - valid: true - name: 'Requisition #0' - id: 0233360795_5e02f9345e_207197 + name: 'Task #0' + id: 1355570989_7ffa627c18_29061 level: 4 - subscriptions: [] - publishers: [] + sensors: [] + actuators: [] iteration: 2 totalIterations: 3 hooks: @@ -4691,20 +4209,20 @@ requisitions: valid: true tests: [] arguments: - elapsedTime: 1 + elapsedTime: 0 time: - startTime: '2020-03-08T05:33:36.933Z' - endTime: '2020-03-08T05:33:36.934Z' - totalTime: 1 + startTime: '2025-03-18T20:55:58.104Z' + endTime: '2025-03-18T20:55:58.104Z' + totalTime: 0 timeout: 5000 - requisitions: [] + tasks: [] - valid: true - name: 'Requisition #0' - id: 0233360795_55233c1965_344410 + name: 'Task #0' + id: 1355570989_a83fe935c1_633469 level: 3 - subscriptions: [] - publishers: [] + sensors: [] + actuators: [] iteration: 2 totalIterations: 3 hooks: @@ -4717,20 +4235,20 @@ requisitions: valid: true tests: [] arguments: - elapsedTime: 2 + elapsedTime: 1 time: - startTime: '2020-03-08T05:33:36.935Z' - endTime: '2020-03-08T05:33:36.937Z' - totalTime: 2 + startTime: '2025-03-18T20:55:58.105Z' + endTime: '2025-03-18T20:55:58.106Z' + totalTime: 1 timeout: 5000 - requisitions: + tasks: - valid: true - name: 'Requisition #0' - id: 0233360795_5e02f9345e_207197 + name: 'Task #0' + id: 1355570989_7ffa627c18_29061 level: 4 - subscriptions: [] - publishers: [] + sensors: [] + actuators: [] iteration: 0 totalIterations: 3 hooks: @@ -4743,20 +4261,20 @@ requisitions: valid: true tests: [] arguments: - elapsedTime: 1 + elapsedTime: 0 time: - startTime: '2020-03-08T05:33:36.935Z' - endTime: '2020-03-08T05:33:36.936Z' - totalTime: 1 + startTime: '2025-03-18T20:55:58.105Z' + endTime: '2025-03-18T20:55:58.105Z' + totalTime: 0 timeout: 5000 - requisitions: [] + tasks: [] - valid: true - name: 'Requisition #0' - id: 0233360795_5e02f9345e_207197 + name: 'Task #0' + id: 1355570989_7ffa627c18_29061 level: 4 - subscriptions: [] - publishers: [] + sensors: [] + actuators: [] iteration: 1 totalIterations: 3 hooks: @@ -4769,20 +4287,20 @@ requisitions: valid: true tests: [] arguments: - elapsedTime: 1 + elapsedTime: 0 time: - startTime: '2020-03-08T05:33:36.936Z' - endTime: '2020-03-08T05:33:36.937Z' + startTime: '2025-03-18T20:55:58.105Z' + endTime: '2025-03-18T20:55:58.106Z' totalTime: 1 timeout: 5000 - requisitions: [] + tasks: [] - valid: true - name: 'Requisition #0' - id: 0233360795_5e02f9345e_207197 + name: 'Task #0' + id: 1355570989_7ffa627c18_29061 level: 4 - subscriptions: [] - publishers: [] + sensors: [] + actuators: [] iteration: 2 totalIterations: 3 hooks: @@ -4797,18 +4315,18 @@ requisitions: arguments: elapsedTime: 0 time: - startTime: '2020-03-08T05:33:36.937Z' - endTime: '2020-03-08T05:33:36.937Z' + startTime: '2025-03-18T20:55:58.106Z' + endTime: '2025-03-18T20:55:58.106Z' totalTime: 0 timeout: 5000 - requisitions: [] + tasks: [] - valid: true - name: 'Requisition #1' - id: 0233360795_e87d17a22e_21789 + name: 'Task #1' + id: 1355570989_57cf642fae_901943 level: 2 - subscriptions: [] - publishers: [] + sensors: [] + actuators: [] iteration: 1 totalIterations: 3 hooks: @@ -4821,20 +4339,20 @@ requisitions: valid: true tests: [] arguments: - elapsedTime: 4 + elapsedTime: 1 time: - startTime: '2020-03-08T05:33:36.938Z' - endTime: '2020-03-08T05:33:36.942Z' - totalTime: 4 + startTime: '2025-03-18T20:55:58.106Z' + endTime: '2025-03-18T20:55:58.107Z' + totalTime: 1 timeout: 5000 - requisitions: + tasks: - valid: true - name: 'Requisition #0' - id: 0233360795_55233c1965_344410 + name: 'Task #0' + id: 1355570989_a83fe935c1_633469 level: 3 - subscriptions: [] - publishers: [] + sensors: [] + actuators: [] iteration: 0 totalIterations: 3 hooks: @@ -4847,20 +4365,20 @@ requisitions: valid: true tests: [] arguments: - elapsedTime: 1 + elapsedTime: 0 time: - startTime: '2020-03-08T05:33:36.938Z' - endTime: '2020-03-08T05:33:36.939Z' - totalTime: 1 + startTime: '2025-03-18T20:55:58.106Z' + endTime: '2025-03-18T20:55:58.106Z' + totalTime: 0 timeout: 5000 - requisitions: + tasks: - valid: true - name: 'Requisition #0' - id: 0233360795_5e02f9345e_207197 + name: 'Task #0' + id: 1355570989_7ffa627c18_29061 level: 4 - subscriptions: [] - publishers: [] + sensors: [] + actuators: [] iteration: 0 totalIterations: 3 hooks: @@ -4875,18 +4393,18 @@ requisitions: arguments: elapsedTime: 0 time: - startTime: '2020-03-08T05:33:36.938Z' - endTime: '2020-03-08T05:33:36.938Z' + startTime: '2025-03-18T20:55:58.106Z' + endTime: '2025-03-18T20:55:58.106Z' totalTime: 0 timeout: 5000 - requisitions: [] + tasks: [] - valid: true - name: 'Requisition #0' - id: 0233360795_5e02f9345e_207197 + name: 'Task #0' + id: 1355570989_7ffa627c18_29061 level: 4 - subscriptions: [] - publishers: [] + sensors: [] + actuators: [] iteration: 1 totalIterations: 3 hooks: @@ -4901,18 +4419,18 @@ requisitions: arguments: elapsedTime: 0 time: - startTime: '2020-03-08T05:33:36.939Z' - endTime: '2020-03-08T05:33:36.939Z' + startTime: '2025-03-18T20:55:58.106Z' + endTime: '2025-03-18T20:55:58.106Z' totalTime: 0 timeout: 5000 - requisitions: [] + tasks: [] - valid: true - name: 'Requisition #0' - id: 0233360795_5e02f9345e_207197 + name: 'Task #0' + id: 1355570989_7ffa627c18_29061 level: 4 - subscriptions: [] - publishers: [] + sensors: [] + actuators: [] iteration: 2 totalIterations: 3 hooks: @@ -4927,18 +4445,18 @@ requisitions: arguments: elapsedTime: 0 time: - startTime: '2020-03-08T05:33:36.939Z' - endTime: '2020-03-08T05:33:36.939Z' + startTime: '2025-03-18T20:55:58.106Z' + endTime: '2025-03-18T20:55:58.106Z' totalTime: 0 timeout: 5000 - requisitions: [] + tasks: [] - valid: true - name: 'Requisition #0' - id: 0233360795_55233c1965_344410 + name: 'Task #0' + id: 1355570989_a83fe935c1_633469 level: 3 - subscriptions: [] - publishers: [] + sensors: [] + actuators: [] iteration: 1 totalIterations: 3 hooks: @@ -4953,18 +4471,18 @@ requisitions: arguments: elapsedTime: 1 time: - startTime: '2020-03-08T05:33:36.939Z' - endTime: '2020-03-08T05:33:36.940Z' + startTime: '2025-03-18T20:55:58.106Z' + endTime: '2025-03-18T20:55:58.107Z' totalTime: 1 timeout: 5000 - requisitions: + tasks: - valid: true - name: 'Requisition #0' - id: 0233360795_5e02f9345e_207197 + name: 'Task #0' + id: 1355570989_7ffa627c18_29061 level: 4 - subscriptions: [] - publishers: [] + sensors: [] + actuators: [] iteration: 0 totalIterations: 3 hooks: @@ -4979,18 +4497,18 @@ requisitions: arguments: elapsedTime: 0 time: - startTime: '2020-03-08T05:33:36.940Z' - endTime: '2020-03-08T05:33:36.940Z' + startTime: '2025-03-18T20:55:58.106Z' + endTime: '2025-03-18T20:55:58.106Z' totalTime: 0 timeout: 5000 - requisitions: [] + tasks: [] - valid: true - name: 'Requisition #0' - id: 0233360795_5e02f9345e_207197 + name: 'Task #0' + id: 1355570989_7ffa627c18_29061 level: 4 - subscriptions: [] - publishers: [] + sensors: [] + actuators: [] iteration: 1 totalIterations: 3 hooks: @@ -5005,18 +4523,18 @@ requisitions: arguments: elapsedTime: 0 time: - startTime: '2020-03-08T05:33:36.940Z' - endTime: '2020-03-08T05:33:36.940Z' - totalTime: 0 + startTime: '2025-03-18T20:55:58.106Z' + endTime: '2025-03-18T20:55:58.107Z' + totalTime: 1 timeout: 5000 - requisitions: [] + tasks: [] - valid: true - name: 'Requisition #0' - id: 0233360795_5e02f9345e_207197 + name: 'Task #0' + id: 1355570989_7ffa627c18_29061 level: 4 - subscriptions: [] - publishers: [] + sensors: [] + actuators: [] iteration: 2 totalIterations: 3 hooks: @@ -5031,18 +4549,18 @@ requisitions: arguments: elapsedTime: 0 time: - startTime: '2020-03-08T05:33:36.940Z' - endTime: '2020-03-08T05:33:36.940Z' + startTime: '2025-03-18T20:55:58.107Z' + endTime: '2025-03-18T20:55:58.107Z' totalTime: 0 timeout: 5000 - requisitions: [] + tasks: [] - valid: true - name: 'Requisition #0' - id: 0233360795_55233c1965_344410 + name: 'Task #0' + id: 1355570989_a83fe935c1_633469 level: 3 - subscriptions: [] - publishers: [] + sensors: [] + actuators: [] iteration: 2 totalIterations: 3 hooks: @@ -5055,20 +4573,20 @@ requisitions: valid: true tests: [] arguments: - elapsedTime: 1 + elapsedTime: 0 time: - startTime: '2020-03-08T05:33:36.941Z' - endTime: '2020-03-08T05:33:36.942Z' - totalTime: 1 + startTime: '2025-03-18T20:55:58.107Z' + endTime: '2025-03-18T20:55:58.107Z' + totalTime: 0 timeout: 5000 - requisitions: + tasks: - valid: true - name: 'Requisition #0' - id: 0233360795_5e02f9345e_207197 + name: 'Task #0' + id: 1355570989_7ffa627c18_29061 level: 4 - subscriptions: [] - publishers: [] + sensors: [] + actuators: [] iteration: 0 totalIterations: 3 hooks: @@ -5083,18 +4601,18 @@ requisitions: arguments: elapsedTime: 0 time: - startTime: '2020-03-08T05:33:36.941Z' - endTime: '2020-03-08T05:33:36.941Z' + startTime: '2025-03-18T20:55:58.107Z' + endTime: '2025-03-18T20:55:58.107Z' totalTime: 0 timeout: 5000 - requisitions: [] + tasks: [] - valid: true - name: 'Requisition #0' - id: 0233360795_5e02f9345e_207197 + name: 'Task #0' + id: 1355570989_7ffa627c18_29061 level: 4 - subscriptions: [] - publishers: [] + sensors: [] + actuators: [] iteration: 1 totalIterations: 3 hooks: @@ -5109,18 +4627,18 @@ requisitions: arguments: elapsedTime: 0 time: - startTime: '2020-03-08T05:33:36.941Z' - endTime: '2020-03-08T05:33:36.941Z' + startTime: '2025-03-18T20:55:58.107Z' + endTime: '2025-03-18T20:55:58.107Z' totalTime: 0 timeout: 5000 - requisitions: [] + tasks: [] - valid: true - name: 'Requisition #0' - id: 0233360795_5e02f9345e_207197 + name: 'Task #0' + id: 1355570989_7ffa627c18_29061 level: 4 - subscriptions: [] - publishers: [] + sensors: [] + actuators: [] iteration: 2 totalIterations: 3 hooks: @@ -5133,20 +4651,20 @@ requisitions: valid: true tests: [] arguments: - elapsedTime: 1 + elapsedTime: 0 time: - startTime: '2020-03-08T05:33:36.941Z' - endTime: '2020-03-08T05:33:36.942Z' - totalTime: 1 + startTime: '2025-03-18T20:55:58.107Z' + endTime: '2025-03-18T20:55:58.107Z' + totalTime: 0 timeout: 5000 - requisitions: [] + tasks: [] - valid: true - name: 'Requisition #1' - id: 0233360795_e87d17a22e_21789 + name: 'Task #1' + id: 1355570989_57cf642fae_901943 level: 2 - subscriptions: [] - publishers: [] + sensors: [] + actuators: [] iteration: 2 totalIterations: 3 hooks: @@ -5159,20 +4677,20 @@ requisitions: valid: true tests: [] arguments: - elapsedTime: 5 + elapsedTime: 2 time: - startTime: '2020-03-08T05:33:36.942Z' - endTime: '2020-03-08T05:33:36.947Z' - totalTime: 5 + startTime: '2025-03-18T20:55:58.107Z' + endTime: '2025-03-18T20:55:58.109Z' + totalTime: 2 timeout: 5000 - requisitions: + tasks: - valid: true - name: 'Requisition #0' - id: 0233360795_55233c1965_344410 + name: 'Task #0' + id: 1355570989_a83fe935c1_633469 level: 3 - subscriptions: [] - publishers: [] + sensors: [] + actuators: [] iteration: 0 totalIterations: 3 hooks: @@ -5185,20 +4703,20 @@ requisitions: valid: true tests: [] arguments: - elapsedTime: 1 + elapsedTime: 0 time: - startTime: '2020-03-08T05:33:36.943Z' - endTime: '2020-03-08T05:33:36.944Z' - totalTime: 1 + startTime: '2025-03-18T20:55:58.108Z' + endTime: '2025-03-18T20:55:58.108Z' + totalTime: 0 timeout: 5000 - requisitions: + tasks: - valid: true - name: 'Requisition #0' - id: 0233360795_5e02f9345e_207197 + name: 'Task #0' + id: 1355570989_7ffa627c18_29061 level: 4 - subscriptions: [] - publishers: [] + sensors: [] + actuators: [] iteration: 0 totalIterations: 3 hooks: @@ -5213,18 +4731,18 @@ requisitions: arguments: elapsedTime: 0 time: - startTime: '2020-03-08T05:33:36.943Z' - endTime: '2020-03-08T05:33:36.943Z' + startTime: '2025-03-18T20:55:58.108Z' + endTime: '2025-03-18T20:55:58.108Z' totalTime: 0 timeout: 5000 - requisitions: [] + tasks: [] - valid: true - name: 'Requisition #0' - id: 0233360795_5e02f9345e_207197 + name: 'Task #0' + id: 1355570989_7ffa627c18_29061 level: 4 - subscriptions: [] - publishers: [] + sensors: [] + actuators: [] iteration: 1 totalIterations: 3 hooks: @@ -5239,18 +4757,18 @@ requisitions: arguments: elapsedTime: 0 time: - startTime: '2020-03-08T05:33:36.943Z' - endTime: '2020-03-08T05:33:36.943Z' + startTime: '2025-03-18T20:55:58.108Z' + endTime: '2025-03-18T20:55:58.108Z' totalTime: 0 timeout: 5000 - requisitions: [] + tasks: [] - valid: true - name: 'Requisition #0' - id: 0233360795_5e02f9345e_207197 + name: 'Task #0' + id: 1355570989_7ffa627c18_29061 level: 4 - subscriptions: [] - publishers: [] + sensors: [] + actuators: [] iteration: 2 totalIterations: 3 hooks: @@ -5263,20 +4781,20 @@ requisitions: valid: true tests: [] arguments: - elapsedTime: 1 + elapsedTime: 0 time: - startTime: '2020-03-08T05:33:36.943Z' - endTime: '2020-03-08T05:33:36.944Z' - totalTime: 1 + startTime: '2025-03-18T20:55:58.108Z' + endTime: '2025-03-18T20:55:58.108Z' + totalTime: 0 timeout: 5000 - requisitions: [] + tasks: [] - valid: true - name: 'Requisition #0' - id: 0233360795_55233c1965_344410 + name: 'Task #0' + id: 1355570989_a83fe935c1_633469 level: 3 - subscriptions: [] - publishers: [] + sensors: [] + actuators: [] iteration: 1 totalIterations: 3 hooks: @@ -5289,20 +4807,20 @@ requisitions: valid: true tests: [] arguments: - elapsedTime: 1 + elapsedTime: 0 time: - startTime: '2020-03-08T05:33:36.945Z' - endTime: '2020-03-08T05:33:36.946Z' - totalTime: 1 + startTime: '2025-03-18T20:55:58.108Z' + endTime: '2025-03-18T20:55:58.108Z' + totalTime: 0 timeout: 5000 - requisitions: + tasks: - valid: true - name: 'Requisition #0' - id: 0233360795_5e02f9345e_207197 + name: 'Task #0' + id: 1355570989_7ffa627c18_29061 level: 4 - subscriptions: [] - publishers: [] + sensors: [] + actuators: [] iteration: 0 totalIterations: 3 hooks: @@ -5317,18 +4835,18 @@ requisitions: arguments: elapsedTime: 0 time: - startTime: '2020-03-08T05:33:36.945Z' - endTime: '2020-03-08T05:33:36.945Z' + startTime: '2025-03-18T20:55:58.108Z' + endTime: '2025-03-18T20:55:58.108Z' totalTime: 0 timeout: 5000 - requisitions: [] + tasks: [] - valid: true - name: 'Requisition #0' - id: 0233360795_5e02f9345e_207197 + name: 'Task #0' + id: 1355570989_7ffa627c18_29061 level: 4 - subscriptions: [] - publishers: [] + sensors: [] + actuators: [] iteration: 1 totalIterations: 3 hooks: @@ -5343,18 +4861,18 @@ requisitions: arguments: elapsedTime: 0 time: - startTime: '2020-03-08T05:33:36.945Z' - endTime: '2020-03-08T05:33:36.945Z' + startTime: '2025-03-18T20:55:58.108Z' + endTime: '2025-03-18T20:55:58.108Z' totalTime: 0 timeout: 5000 - requisitions: [] + tasks: [] - valid: true - name: 'Requisition #0' - id: 0233360795_5e02f9345e_207197 + name: 'Task #0' + id: 1355570989_7ffa627c18_29061 level: 4 - subscriptions: [] - publishers: [] + sensors: [] + actuators: [] iteration: 2 totalIterations: 3 hooks: @@ -5367,20 +4885,20 @@ requisitions: valid: true tests: [] arguments: - elapsedTime: 1 + elapsedTime: 0 time: - startTime: '2020-03-08T05:33:36.945Z' - endTime: '2020-03-08T05:33:36.946Z' - totalTime: 1 + startTime: '2025-03-18T20:55:58.108Z' + endTime: '2025-03-18T20:55:58.108Z' + totalTime: 0 timeout: 5000 - requisitions: [] + tasks: [] - valid: true - name: 'Requisition #0' - id: 0233360795_55233c1965_344410 + name: 'Task #0' + id: 1355570989_a83fe935c1_633469 level: 3 - subscriptions: [] - publishers: [] + sensors: [] + actuators: [] iteration: 2 totalIterations: 3 hooks: @@ -5395,18 +4913,18 @@ requisitions: arguments: elapsedTime: 1 time: - startTime: '2020-03-08T05:33:36.946Z' - endTime: '2020-03-08T05:33:36.947Z' + startTime: '2025-03-18T20:55:58.108Z' + endTime: '2025-03-18T20:55:58.109Z' totalTime: 1 timeout: 5000 - requisitions: + tasks: - valid: true - name: 'Requisition #0' - id: 0233360795_5e02f9345e_207197 + name: 'Task #0' + id: 1355570989_7ffa627c18_29061 level: 4 - subscriptions: [] - publishers: [] + sensors: [] + actuators: [] iteration: 0 totalIterations: 3 hooks: @@ -5421,18 +4939,18 @@ requisitions: arguments: elapsedTime: 0 time: - startTime: '2020-03-08T05:33:36.946Z' - endTime: '2020-03-08T05:33:36.946Z' + startTime: '2025-03-18T20:55:58.108Z' + endTime: '2025-03-18T20:55:58.108Z' totalTime: 0 timeout: 5000 - requisitions: [] + tasks: [] - valid: true - name: 'Requisition #0' - id: 0233360795_5e02f9345e_207197 + name: 'Task #0' + id: 1355570989_7ffa627c18_29061 level: 4 - subscriptions: [] - publishers: [] + sensors: [] + actuators: [] iteration: 1 totalIterations: 3 hooks: @@ -5445,20 +4963,20 @@ requisitions: valid: true tests: [] arguments: - elapsedTime: 1 + elapsedTime: 0 time: - startTime: '2020-03-08T05:33:36.946Z' - endTime: '2020-03-08T05:33:36.947Z' - totalTime: 1 + startTime: '2025-03-18T20:55:58.109Z' + endTime: '2025-03-18T20:55:58.109Z' + totalTime: 0 timeout: 5000 - requisitions: [] + tasks: [] - valid: true - name: 'Requisition #0' - id: 0233360795_5e02f9345e_207197 + name: 'Task #0' + id: 1355570989_7ffa627c18_29061 level: 4 - subscriptions: [] - publishers: [] + sensors: [] + actuators: [] iteration: 2 totalIterations: 3 hooks: @@ -5473,18 +4991,18 @@ requisitions: arguments: elapsedTime: 0 time: - startTime: '2020-03-08T05:33:36.947Z' - endTime: '2020-03-08T05:33:36.947Z' + startTime: '2025-03-18T20:55:58.109Z' + endTime: '2025-03-18T20:55:58.109Z' totalTime: 0 timeout: 5000 - requisitions: [] + tasks: [] - valid: true - name: 'Requisition #2' - id: 0233360795_aef615856f_583792 + name: 'Task #2' + id: 1355570989_ab1ea5077a_118527 level: 2 - subscriptions: [] - publishers: [] + sensors: [] + actuators: [] iteration: 0 totalIterations: 1 hooks: @@ -5501,20 +5019,20 @@ requisitions: valid: true tests: [] arguments: - elapsedTime: 1 + elapsedTime: 0 time: - startTime: '2020-03-08T05:33:36.947Z' - endTime: '2020-03-08T05:33:36.948Z' - totalTime: 1 + startTime: '2025-03-18T20:55:58.109Z' + endTime: '2025-03-18T20:55:58.109Z' + totalTime: 0 timeout: 5000 - requisitions: [] + tasks: [] - valid: true - name: examples/requisition-delay-iterations.yml - id: 0233360795_8e102bb4f5_510219 + name: examples/skipped.yml + id: 1355570989_f01dfa0923_153087 level: 1 - subscriptions: [] - publishers: [] + sensors: [] + actuators: [] iteration: 0 totalIterations: 1 hooks: @@ -5527,22 +5045,22 @@ requisitions: valid: true tests: [] arguments: - elapsedTime: 3071 + elapsedTime: 88 time: - startTime: '2020-03-08T05:33:36.867Z' - endTime: '2020-03-08T05:33:39.938Z' - totalTime: 3071 + startTime: '2025-03-18T20:55:58.017Z' + endTime: '2025-03-18T20:55:58.105Z' + totalTime: 88 timeout: 5000 - requisitions: + tasks: - valid: true - name: iterations - id: 0233360795_b4f515b70d_757234 + name: 'Task #0' + id: 1355570989_d0e9827c8e_627996 level: 2 - subscriptions: [] - publishers: [] + sensors: [] + actuators: [] iteration: 0 - totalIterations: 10 + totalIterations: 5 hooks: onInit: valid: true @@ -5553,22 +5071,22 @@ requisitions: valid: true tests: [] arguments: - elapsedTime: 30 + elapsedTime: 72 time: - startTime: '2020-03-08T05:33:36.887Z' - endTime: '2020-03-08T05:33:36.917Z' - totalTime: 30 + startTime: '2025-03-18T20:55:58.022Z' + endTime: '2025-03-18T20:55:58.095Z' + totalTime: 73 timeout: 5000 - requisitions: [] + tasks: [] - valid: true - name: iterations - id: 0233360795_b4f515b70d_757234 + name: 'Task #0' + id: 1355570989_d0e9827c8e_627996 level: 2 - subscriptions: [] - publishers: [] + sensors: [] + actuators: [] iteration: 1 - totalIterations: 10 + totalIterations: 5 hooks: onInit: valid: true @@ -5579,22 +5097,22 @@ requisitions: valid: true tests: [] arguments: - elapsedTime: 5 + elapsedTime: 3 time: - startTime: '2020-03-08T05:33:36.919Z' - endTime: '2020-03-08T05:33:36.924Z' - totalTime: 5 + startTime: '2025-03-18T20:55:58.096Z' + endTime: '2025-03-18T20:55:58.099Z' + totalTime: 3 timeout: 5000 - requisitions: [] + tasks: [] - valid: true - name: iterations - id: 0233360795_b4f515b70d_757234 + name: 'Task #0' + id: 1355570989_d0e9827c8e_627996 level: 2 - subscriptions: [] - publishers: [] + sensors: [] + actuators: [] iteration: 2 - totalIterations: 10 + totalIterations: 5 hooks: onInit: valid: true @@ -5605,22 +5123,22 @@ requisitions: valid: true tests: [] arguments: - elapsedTime: 2 + elapsedTime: 1 time: - startTime: '2020-03-08T05:33:36.925Z' - endTime: '2020-03-08T05:33:36.927Z' - totalTime: 2 + startTime: '2025-03-18T20:55:58.099Z' + endTime: '2025-03-18T20:55:58.100Z' + totalTime: 1 timeout: 5000 - requisitions: [] + tasks: [] - valid: true - name: iterations - id: 0233360795_b4f515b70d_757234 + name: 'Task #0' + id: 1355570989_d0e9827c8e_627996 level: 2 - subscriptions: [] - publishers: [] + sensors: [] + actuators: [] iteration: 3 - totalIterations: 10 + totalIterations: 5 hooks: onInit: valid: true @@ -5633,20 +5151,20 @@ requisitions: arguments: elapsedTime: 1 time: - startTime: '2020-03-08T05:33:36.928Z' - endTime: '2020-03-08T05:33:36.929Z' + startTime: '2025-03-18T20:55:58.100Z' + endTime: '2025-03-18T20:55:58.101Z' totalTime: 1 timeout: 5000 - requisitions: [] + tasks: [] - valid: true - name: iterations - id: 0233360795_b4f515b70d_757234 + name: 'Task #0' + id: 1355570989_d0e9827c8e_627996 level: 2 - subscriptions: [] - publishers: [] + sensors: [] + actuators: [] iteration: 4 - totalIterations: 10 + totalIterations: 5 hooks: onInit: valid: true @@ -5659,102 +5177,133 @@ requisitions: arguments: elapsedTime: 0 time: - startTime: '2020-03-08T05:33:36.930Z' - endTime: '2020-03-08T05:33:36.930Z' + startTime: '2025-03-18T20:55:58.102Z' + endTime: '2025-03-18T20:55:58.102Z' totalTime: 0 timeout: 5000 - requisitions: [] + tasks: [] - valid: true - name: iterations - id: 0233360795_b4f515b70d_757234 + name: 'Task #1' + id: 1355570989_0217f12f55_728788 level: 2 - subscriptions: [] - publishers: [] - iteration: 5 - totalIterations: 10 + sensors: [] + actuators: [] + iteration: 0 + totalIterations: 1 hooks: onInit: + valid: true + tests: + - + name: 'Assertion #0' + valid: true + description: 'Expected ''store.skippedExecutions'' to be equal to ''5''. Received ''5''' + arguments: + elapsedTime: 0 + onFinish: valid: true tests: [] arguments: elapsedTime: 0 + time: + startTime: '2025-03-18T20:55:58.103Z' + endTime: '2025-03-18T20:55:58.103Z' + totalTime: 0 + timeout: 5000 + tasks: [] + - + valid: true + name: 'Task #2' + id: 1355570989_44a821ab65_176262 + level: 2 + sensors: [] + actuators: [] + hooks: + onInit: + arguments: {} + valid: true + tests: [] onFinish: + arguments: {} valid: true - tests: [] - arguments: - elapsedTime: 1 + tests: + - + valid: true + name: 'Task skipped' + description: 'There is no iterations set to this task' time: - startTime: '2020-03-08T05:33:36.930Z' - endTime: '2020-03-08T05:33:36.931Z' - totalTime: 1 - timeout: 5000 - requisitions: [] + startTime: '2025-03-18T20:55:58.103Z' + endTime: '2025-03-18T20:55:58.103Z' + totalTime: 0 + tasks: [] - valid: true - name: iterations - id: 0233360795_b4f515b70d_757234 + name: 'Task #3' + id: 1355570989_e5e2d3b9a7_77304 level: 2 - subscriptions: [] - publishers: [] - iteration: 6 - totalIterations: 10 + sensors: [] + actuators: [] hooks: onInit: + arguments: {} valid: true tests: [] - arguments: - elapsedTime: 0 onFinish: + arguments: {} valid: true - tests: [] - arguments: - elapsedTime: 1 + tests: + - + valid: true + name: 'Task skipped' + description: 'There is no iterations set to this task' time: - startTime: '2020-03-08T05:33:36.931Z' - endTime: '2020-03-08T05:33:36.932Z' - totalTime: 1 - timeout: 5000 - requisitions: [] + startTime: '2025-03-18T20:55:58.103Z' + endTime: '2025-03-18T20:55:58.103Z' + totalTime: 0 + tasks: [] - valid: true - name: iterations - id: 0233360795_b4f515b70d_757234 + name: 'Task #4' + id: 1355570989_5c1186cc83_316216 level: 2 - subscriptions: [] - publishers: [] - iteration: 7 - totalIterations: 10 + sensors: [] + actuators: [] hooks: onInit: + arguments: {} valid: true tests: [] - arguments: - elapsedTime: 0 onFinish: + arguments: {} valid: true - tests: [] - arguments: - elapsedTime: 0 + tests: + - + valid: true + name: 'Task skipped' + description: 'There is no iterations set to this task' time: - startTime: '2020-03-08T05:33:36.933Z' - endTime: '2020-03-08T05:33:36.933Z' + startTime: '2025-03-18T20:55:58.103Z' + endTime: '2025-03-18T20:55:58.103Z' totalTime: 0 - timeout: 5000 - requisitions: [] + tasks: [] - valid: true - name: iterations - id: 0233360795_b4f515b70d_757234 + name: 'Task #5' + id: 1355570989_ae9d70b300_323170 level: 2 - subscriptions: [] - publishers: [] - iteration: 8 - totalIterations: 10 + sensors: [] + actuators: [] + iteration: 0 + totalIterations: '1' hooks: onInit: valid: true - tests: [] + tests: + - + name: 'Assertion #0' + valid: true + description: 'Expecting ''true'' to be true. Received: true' arguments: elapsedTime: 0 onFinish: @@ -5763,20 +5312,20 @@ requisitions: arguments: elapsedTime: 1 time: - startTime: '2020-03-08T05:33:36.933Z' - endTime: '2020-03-08T05:33:36.934Z' + startTime: '2025-03-18T20:55:58.103Z' + endTime: '2025-03-18T20:55:58.104Z' totalTime: 1 timeout: 5000 - requisitions: [] + tasks: [] - valid: true - name: iterations - id: 0233360795_b4f515b70d_757234 + name: 'Task #6' + id: 1355570989_7aca67c473_982740 level: 2 - subscriptions: [] - publishers: [] - iteration: 9 - totalIterations: 10 + sensors: [] + actuators: [] + iteration: 0 + totalIterations: 1 hooks: onInit: valid: true @@ -5787,58 +5336,50 @@ requisitions: valid: true tests: [] arguments: - elapsedTime: 1 + elapsedTime: 0 time: - startTime: '2020-03-08T05:33:36.934Z' - endTime: '2020-03-08T05:33:36.935Z' + startTime: '2025-03-18T20:55:58.104Z' + endTime: '2025-03-18T20:55:58.105Z' totalTime: 1 timeout: 5000 - requisitions: [] + tasks: [] - valid: true - name: delayed - id: 0233360795_ce1438bac6_499948 + name: 'Task #7' + id: 1355570989_f081cebcb2_508868 level: 2 - subscriptions: [] - publishers: [] + sensors: [] + actuators: [] iteration: 0 totalIterations: 1 hooks: onInit: valid: true - tests: [] + tests: + - + name: 'Assertion #0' + valid: true + description: 'Expecting ''store.executed'' to be defined' arguments: elapsedTime: 0 onFinish: valid: true - tests: - - - name: 'It was executed 10 times' - valid: true - description: 'Expected ''requisition.parent.requisitions[0].counter'' to be equal to ''10''. Received ''10''' - - - name: 'Elapsed time' - valid: true - description: 'Expected ''elapsedTime'' to be greater than or equal to ''2950''. Received ''3001''' - - - name: 'Assertion #2' - valid: true - description: 'Expected ''new Date().getTime() - requisition.startTime.getTime()'' to be greater than or equal to ''2950''. Received ''3002''' + tests: [] arguments: - elapsedTime: 3001 + elapsedTime: 0 time: - startTime: '2020-03-08T05:33:36.936Z' - endTime: '2020-03-08T05:33:39.938Z' - totalTime: 3002 + startTime: '2025-03-18T20:55:58.105Z' + endTime: '2025-03-18T20:55:58.105Z' + totalTime: 0 timeout: 5000 - requisitions: [] + tasks: [] - valid: true - name: examples/skipped.yml - id: 0233360795_bb333e1dde_738635 + name: examples/ssl.yml + id: 1355570989_b924ef6cfd_213651 level: 1 - subscriptions: [] - publishers: [] + sensors: [] + actuators: [] iteration: 0 totalIterations: 1 hooks: @@ -5851,74 +5392,116 @@ requisitions: valid: true tests: [] arguments: - elapsedTime: 68 + elapsedTime: 4179 time: - startTime: '2020-03-08T05:33:36.868Z' - endTime: '2020-03-08T05:33:36.936Z' - totalTime: 68 + startTime: '2025-03-18T20:55:58.017Z' + endTime: '2025-03-18T20:56:02.196Z' + totalTime: 4179 timeout: 5000 - requisitions: - - - valid: true - name: 'Requisition #0' - id: 0233360795_fc4b0751bf_854712 - level: 2 - subscriptions: [] - publishers: [] - iteration: 0 - totalIterations: 5 - hooks: - onInit: - valid: true - tests: [] - arguments: - elapsedTime: 0 - onFinish: - valid: true - tests: [] - arguments: - elapsedTime: 30 - time: - startTime: '2020-03-08T05:33:36.887Z' - endTime: '2020-03-08T05:33:36.917Z' - totalTime: 30 - timeout: 5000 - requisitions: [] + tasks: - valid: true - name: 'Requisition #0' - id: 0233360795_fc4b0751bf_854712 + name: 'Task #0' + id: 1355570989_bdc6ef7e9a_982863 level: 2 - subscriptions: [] - publishers: [] - iteration: 1 - totalIterations: 5 - hooks: - onInit: + sensors: + - + id: 1355570989_bcb968e58f_445880 + name: 'Sensor #0' + type: ssl + hooks: + onInit: + valid: true + tests: [] + arguments: {} + onFinish: + valid: true + tests: + - + implicit: true + valid: true + name: 'Message received' + description: 'Sensor has received its message' + arguments: + executedHooks: + onInit: [] + onMessageReceived: + - payload + - stream + - path + - elapsedTime + onFinish: + - executedHooks + - elapsedTime + elapsedTime: 2168 + onMessageReceived: + valid: true + tests: + - + name: 'Assertion #0' + valid: true + description: 'Expected ''payload'' to be equal to ''secureMessage''. Received ''secureMessage''' + arguments: + payload: secureMessage + stream: + address: '::1' + family: IPv6 + port: 23082 + elapsedTime: 171 valid: true - tests: [] - arguments: - elapsedTime: 0 - onFinish: + sensorTime: '2025-03-18T20:55:58.111Z' + actuators: + - + id: 1355570989_759a24aece_76421 + name: 'Actuator #0' valid: true - tests: [] - arguments: - elapsedTime: 4 - time: - startTime: '2020-03-08T05:33:36.920Z' - endTime: '2020-03-08T05:33:36.924Z' - totalTime: 4 - timeout: 5000 - requisitions: [] - - - valid: true - name: 'Requisition #0' - id: 0233360795_fc4b0751bf_854712 - level: 2 - subscriptions: [] - publishers: [] - iteration: 2 - totalIterations: 5 + hooks: + onInit: + arguments: + elapsedTime: 0 + tests: [] + valid: true + onFinish: + arguments: + executedHooks: + onInit: [] + onMessageReceived: + - payload + - stream + - elapsedTime + onFinish: + - executedHooks + - elapsedTime + elapsedTime: 2168 + tests: + - + name: Published + valid: true + description: 'Published successfully' + implicit: true + valid: true + onMessageReceived: + arguments: + payload: hisecureResponse + stream: + address: '::1' + family: IPv6 + port: 57108 + elapsedTime: 4174 + tests: + - + name: 'Assertion #0' + valid: true + description: 'Expected ''payload'' to be equal to ''hisecureResponse''. Received ''hisecureResponse''' + - + name: 'Assertion #0' + valid: true + description: 'Expected ''payload'' to be equal to ''hisecureResponse''. Received ''hisecureResponse''' + valid: true + type: ssl + messageSentInstant: '2025-03-18T20:56:00.191Z' + iteration: 0 + totalIterations: 1 hooks: onInit: valid: true @@ -5929,48 +5512,116 @@ requisitions: valid: true tests: [] arguments: - elapsedTime: 2 + elapsedTime: 2168 time: - startTime: '2020-03-08T05:33:36.925Z' - endTime: '2020-03-08T05:33:36.927Z' - totalTime: 2 + startTime: '2025-03-18T20:55:58.023Z' + endTime: '2025-03-18T20:56:00.191Z' + totalTime: 2168 timeout: 5000 - requisitions: [] + tasks: [] - valid: true - name: 'Requisition #0' - id: 0233360795_fc4b0751bf_854712 + name: 'Task #1' + id: 1355570989_6eba96f32f_961750 level: 2 - subscriptions: [] - publishers: [] - iteration: 3 - totalIterations: 5 - hooks: - onInit: + sensors: + - + id: 1355570989_7a4ecedb6b_51124 + name: 'Sensor #0' + type: ssl + hooks: + onInit: + valid: true + tests: [] + arguments: {} + onFinish: + valid: true + tests: + - + implicit: true + valid: true + name: 'Message received' + description: 'Sensor has received its message' + arguments: + executedHooks: + onInit: [] + onMessageReceived: + - payload + - stream + - path + - elapsedTime + onFinish: + - executedHooks + - elapsedTime + elapsedTime: 2004 + onMessageReceived: + valid: true + tests: + - + name: 'Assertion #0' + valid: true + description: 'Expected ''payload'' to be equal to ''reusingSecureMessage''. Received ''reusingSecureMessage''' + arguments: + payload: reusingSecureMessage + stream: + address: '::1' + family: IPv6 + port: 23082 + elapsedTime: 0 valid: true - tests: [] - arguments: - elapsedTime: 0 - onFinish: + sensorTime: '2025-03-18T20:56:00.192Z' + actuators: + - + id: 1355570989_450b411eba_257685 + name: 'Actuator #0' valid: true - tests: [] - arguments: - elapsedTime: 1 - time: - startTime: '2020-03-08T05:33:36.928Z' - endTime: '2020-03-08T05:33:36.929Z' - totalTime: 1 - timeout: 5000 - requisitions: [] - - - valid: true - name: 'Requisition #0' - id: 0233360795_fc4b0751bf_854712 - level: 2 - subscriptions: [] - publishers: [] - iteration: 4 - totalIterations: 5 + hooks: + onInit: + arguments: + elapsedTime: 0 + tests: [] + valid: true + onFinish: + arguments: + executedHooks: + onInit: [] + onMessageReceived: + - payload + - stream + - elapsedTime + onFinish: + - executedHooks + - elapsedTime + elapsedTime: 2004 + tests: + - + name: Published + valid: true + description: 'Published successfully' + implicit: true + valid: true + onMessageReceived: + arguments: + payload: reusingSecureResponse + stream: + address: '::1' + family: IPv6 + port: 57108 + elapsedTime: 2006 + tests: + - + name: 'Assertion #0' + valid: true + description: 'Expected ''payload'' to be equal to ''reusingSecureResponse''. Received ''reusingSecureResponse''' + - + name: 'Assertion #0' + valid: true + description: 'Expected ''payload'' to be equal to ''reusingSecureResponse''. Received ''reusingSecureResponse''' + valid: true + type: ssl + messageSentInstant: '2025-03-18T20:56:02.195Z' + iteration: 0 + totalIterations: 1 hooks: onInit: valid: true @@ -5981,157 +5632,255 @@ requisitions: valid: true tests: [] arguments: - elapsedTime: 0 + elapsedTime: 2004 time: - startTime: '2020-03-08T05:33:36.930Z' - endTime: '2020-03-08T05:33:36.930Z' - totalTime: 0 + startTime: '2025-03-18T20:56:00.192Z' + endTime: '2025-03-18T20:56:02.196Z' + totalTime: 2004 timeout: 5000 - requisitions: [] + tasks: [] + - + valid: true + name: examples/stdin.yml + id: 1355570989_357accc01a_960750 + level: 1 + sensors: - - valid: true - name: 'Requisition #1' - id: 0233360795_0813922659_753665 - level: 2 - subscriptions: [] - publishers: [] - iteration: 0 - totalIterations: 1 + id: anyIdTest + name: 'sensor description' + type: standard-input hooks: onInit: + valid: true + tests: [] + arguments: {} + onFinish: valid: true tests: - - name: 'Assertion #0' + implicit: true valid: true - description: 'Expected ''store.skippedExecutions'' to be equal to ''5''. Received ''5''' + name: 'Message received' + description: 'Sensor has received its message' arguments: - elapsedTime: 0 - onFinish: + executedHooks: + onInit: [] + onMessageReceived: + - message + - elapsedTime + onFinish: + - executedHooks + - elapsedTime + elapsedTime: 111 + onMessageReceived: valid: true - tests: [] + tests: + - + name: payload + valid: true + description: 'Expected ''message'' to be equal to ''enqueuer standard-input payload''. Received ''enqueuer standard-input payload''' arguments: - elapsedTime: 0 - time: - startTime: '2020-03-08T05:33:36.931Z' - endTime: '2020-03-08T05:33:36.931Z' - totalTime: 0 - timeout: 5000 - requisitions: [] + message: 'enqueuer standard-input payload' + elapsedTime: 111 + valid: true + sensorTime: '2025-03-18T20:55:58.066Z' + actuators: [] + iteration: 0 + totalIterations: 1 + hooks: + onInit: + valid: true + tests: [] + arguments: + elapsedTime: 0 + onFinish: + valid: true + tests: [] + arguments: + elapsedTime: 111 + time: + startTime: '2025-03-18T20:55:58.017Z' + endTime: '2025-03-18T20:55:58.128Z' + totalTime: 111 + timeout: 5000 + tasks: [] + - + valid: true + name: examples/store.yml + id: 1355570989_d97c8c8411_325820 + level: 1 + sensors: [] + actuators: [] + iteration: 0 + totalIterations: 1 + hooks: + onInit: + valid: true + tests: [] + arguments: + elapsedTime: 0 + onFinish: + valid: true + tests: [] + arguments: + elapsedTime: 84 + time: + startTime: '2025-03-18T20:55:58.018Z' + endTime: '2025-03-18T20:55:58.102Z' + totalTime: 84 + timeout: 5000 + tasks: - valid: true - name: 'Requisition #2' - id: 0233360795_38e558917b_848570 + name: 'Task #0' + id: 1355570989_ebc76a078d_270083 level: 2 - subscriptions: [] - publishers: [] + sensors: [] + actuators: [] + iteration: 0 + totalIterations: 1 hooks: onInit: - arguments: {} valid: true tests: [] + arguments: + elapsedTime: 0 onFinish: - arguments: {} valid: true - tests: - - - valid: true - name: 'Requisition skipped' - description: 'There is no iterations set to this requisition' + tests: [] + arguments: + elapsedTime: 71 time: - startTime: '2020-03-08T05:33:36.932Z' - endTime: '2020-03-08T05:33:36.932Z' - totalTime: 0 - requisitions: [] + startTime: '2025-03-18T20:55:58.023Z' + endTime: '2025-03-18T20:55:58.095Z' + totalTime: 72 + timeout: 5000 + tasks: [] - valid: true - name: 'Requisition #3' - id: 0233360795_4aa5c78448_51228 + name: 'Task #1' + id: 1355570989_c6f4e581d6_650969 level: 2 - subscriptions: [] - publishers: [] + sensors: [] + actuators: [] + iteration: 0 + totalIterations: 1 hooks: onInit: - arguments: {} valid: true tests: [] + arguments: + elapsedTime: 0 onFinish: - arguments: {} valid: true tests: - + name: Payload + valid: true + description: 'Expected ''123'' to be equal to ''123''. Received ''123''' + - + name: 'Environment Variables added' valid: true - name: 'Requisition skipped' - description: 'There is no iterations set to this requisition' + description: 'Expecting ''store.PATH'' to be defined' + arguments: + elapsedTime: 1 time: - startTime: '2020-03-08T05:33:36.932Z' - endTime: '2020-03-08T05:33:36.932Z' - totalTime: 0 - requisitions: [] + startTime: '2025-03-18T20:55:58.098Z' + endTime: '2025-03-18T20:55:58.099Z' + totalTime: 1 + timeout: 5000 + tasks: [] - valid: true - name: 'Requisition #4' - id: 0233360795_6ac450666c_171747 + name: 'Task #2' + id: 1355570989_681da84562_828504 level: 2 - subscriptions: [] - publishers: [] + sensors: [] + actuators: [] + iteration: 0 + totalIterations: 1 hooks: onInit: - arguments: {} valid: true tests: [] + arguments: + elapsedTime: 0 onFinish: - arguments: {} valid: true tests: - + name: Payload valid: true - name: 'Requisition skipped' - description: 'There is no iterations set to this requisition' + description: 'Expected ''124'' to be equal to ''124''. Received ''124''' + arguments: + elapsedTime: 0 time: - startTime: '2020-03-08T05:33:36.932Z' - endTime: '2020-03-08T05:33:36.932Z' + startTime: '2025-03-18T20:55:58.100Z' + endTime: '2025-03-18T20:55:58.100Z' totalTime: 0 - requisitions: [] + timeout: 5000 + tasks: [] + - + valid: true + name: examples/task-delay-iterations.yml + id: 1355570989_f867b9467c_128265 + level: 1 + sensors: [] + actuators: [] + iteration: 0 + totalIterations: 1 + hooks: + onInit: + valid: true + tests: [] + arguments: + elapsedTime: 0 + onFinish: + valid: true + tests: [] + arguments: + elapsedTime: 3088 + time: + startTime: '2025-03-18T20:55:58.018Z' + endTime: '2025-03-18T20:56:01.107Z' + totalTime: 3089 + timeout: 5000 + tasks: - valid: true - name: 'Requisition #5' - id: 0233360795_23f493719b_809056 + name: iterations + id: 1355570989_98e6c02ed8_808806 level: 2 - subscriptions: [] - publishers: [] + sensors: [] + actuators: [] iteration: 0 - totalIterations: '1' + totalIterations: 10 hooks: onInit: valid: true - tests: - - - name: 'Assertion #0' - valid: true - description: 'Expecting ''true'' to be true. Received: true' + tests: [] arguments: elapsedTime: 0 onFinish: valid: true tests: [] arguments: - elapsedTime: 0 + elapsedTime: 71 time: - startTime: '2020-03-08T05:33:36.933Z' - endTime: '2020-03-08T05:33:36.933Z' - totalTime: 0 + startTime: '2025-03-18T20:55:58.023Z' + endTime: '2025-03-18T20:55:58.095Z' + totalTime: 72 timeout: 5000 - requisitions: [] + tasks: [] - valid: true - name: 'Requisition #6' - id: 0233360795_f9b72d5147_592534 + name: iterations + id: 1355570989_98e6c02ed8_808806 level: 2 - subscriptions: [] - publishers: [] - iteration: 0 - totalIterations: 1 + sensors: [] + actuators: [] + iteration: 1 + totalIterations: 10 hooks: onInit: valid: true @@ -6142,160 +5891,74 @@ requisitions: valid: true tests: [] arguments: - elapsedTime: 0 + elapsedTime: 2 time: - startTime: '2020-03-08T05:33:36.934Z' - endTime: '2020-03-08T05:33:36.934Z' - totalTime: 0 + startTime: '2025-03-18T20:55:58.097Z' + endTime: '2025-03-18T20:55:58.099Z' + totalTime: 2 timeout: 5000 - requisitions: [] + tasks: [] - valid: true - name: 'Requisition #7' - id: 0233360795_b41f5d915d_934112 + name: iterations + id: 1355570989_98e6c02ed8_808806 level: 2 - subscriptions: [] - publishers: [] - iteration: 0 - totalIterations: 1 + sensors: [] + actuators: [] + iteration: 2 + totalIterations: 10 hooks: onInit: valid: true - tests: - - - name: 'Assertion #0' - valid: true - description: 'Expecting ''store.executed'' to be defined' + tests: [] arguments: elapsedTime: 0 onFinish: valid: true tests: [] arguments: - elapsedTime: 0 + elapsedTime: 1 time: - startTime: '2020-03-08T05:33:36.935Z' - endTime: '2020-03-08T05:33:36.935Z' - totalTime: 0 + startTime: '2025-03-18T20:55:58.099Z' + endTime: '2025-03-18T20:55:58.100Z' + totalTime: 1 timeout: 5000 - requisitions: [] - - - valid: true - name: examples/ssl.yml - id: 0233360795_fbc833de59_897737 - level: 1 - subscriptions: [] - publishers: [] - iteration: 0 - totalIterations: 1 - hooks: - onInit: - valid: true - tests: [] - arguments: - elapsedTime: 0 - onFinish: - valid: true - tests: [] - arguments: - elapsedTime: 4164 - time: - startTime: '2020-03-08T05:33:36.870Z' - endTime: '2020-03-08T05:33:41.034Z' - totalTime: 4164 - timeout: 5000 - requisitions: + tasks: [] - valid: true - name: 'Requisition #0' - id: 0233360795_ec6b6289cc_859238 + name: iterations + id: 1355570989_98e6c02ed8_808806 level: 2 - subscriptions: - - - id: 0233360795_d5884fd0ec_878362 - name: 'Subscription #0' - type: ssl - hooks: - onInit: - valid: true - tests: [] - arguments: - elapsedTime: 0 - onFinish: - valid: true - tests: - - - valid: true - name: 'Message received' - description: 'Subscription has received its message' - arguments: - executedHooks: - - onInit - - onMessageReceived - - onFinish - elapsedTime: 2142 - onMessageReceived: - valid: true - tests: - - - name: 'Assertion #0' - valid: true - description: 'Expected ''payload'' to be equal to ''secureMessage''. Received ''secureMessage''' - arguments: - payload: secureMessage - stream: - address: '::ffff:127.0.0.1' - family: IPv6 - port: 23082 - elapsedTime: 153 + sensors: [] + actuators: [] + iteration: 3 + totalIterations: 10 + hooks: + onInit: valid: true - subscriptionTime: '2020-03-08T05:33:36.951Z' - publishers: - - - id: 0233360795_e063362e26_185358 - name: 'Publisher #0' + tests: [] + arguments: + elapsedTime: 0 + onFinish: valid: true - hooks: - onInit: - arguments: - elapsedTime: 0 - tests: [] - valid: true - onFinish: - arguments: - executedHooks: - - onInit - - onMessageReceived - - onFinish - elapsedTime: 2142 - tests: - - - name: Published - valid: true - description: 'Published successfully' - valid: true - onMessageReceived: - arguments: - payload: hisecureResponse - stream: - address: 127.0.0.1 - family: IPv4 - port: 61964 - elapsedTime: 4147 - tests: - - - name: 'Assertion #0' - valid: true - description: 'Expected ''payload'' to be equal to ''hisecureResponse''. Received ''hisecureResponse''' - - - name: 'Assertion #0' - valid: true - description: 'Expected ''payload'' to be equal to ''hisecureResponse''. Received ''hisecureResponse''' - valid: true - type: ssl - publishTime: '2020-03-08T05:33:39.029Z' - iteration: 0 - totalIterations: 1 + tests: [] + arguments: + elapsedTime: 1 + time: + startTime: '2025-03-18T20:55:58.100Z' + endTime: '2025-03-18T20:55:58.101Z' + totalTime: 1 + timeout: 5000 + tasks: [] + - + valid: true + name: iterations + id: 1355570989_98e6c02ed8_808806 + level: 2 + sensors: [] + actuators: [] + iteration: 4 + totalIterations: 10 hooks: onInit: valid: true @@ -6306,104 +5969,22 @@ requisitions: valid: true tests: [] arguments: - elapsedTime: 2142 + elapsedTime: 0 time: - startTime: '2020-03-08T05:33:36.888Z' - endTime: '2020-03-08T05:33:39.030Z' - totalTime: 2142 + startTime: '2025-03-18T20:55:58.102Z' + endTime: '2025-03-18T20:55:58.102Z' + totalTime: 0 timeout: 5000 - requisitions: [] + tasks: [] - valid: true - name: 'Requisition #1' - id: 0233360796_c8e796a6f7_691554 + name: iterations + id: 1355570989_98e6c02ed8_808806 level: 2 - subscriptions: - - - id: 0233360796_464eaccac3_710694 - name: 'Subscription #0' - type: ssl - hooks: - onInit: - valid: true - tests: [] - arguments: - elapsedTime: 0 - onFinish: - valid: true - tests: - - - valid: true - name: 'Message received' - description: 'Subscription has received its message' - arguments: - executedHooks: - - onInit - - onMessageReceived - - onFinish - elapsedTime: 2002 - onMessageReceived: - valid: true - tests: - - - name: 'Assertion #0' - valid: true - description: 'Expected ''payload'' to be equal to ''reusingSecureMessage''. Received ''reusingSecureMessage''' - arguments: - payload: reusingSecureMessage - stream: - address: '::ffff:127.0.0.1' - family: IPv6 - port: 23082 - elapsedTime: 0 - valid: true - subscriptionTime: '2020-03-08T05:33:39.031Z' - publishers: - - - id: 0233360796_26f6887fe6_16302 - name: 'Publisher #0' - valid: true - hooks: - onInit: - arguments: - elapsedTime: 0 - tests: [] - valid: true - onFinish: - arguments: - executedHooks: - - onInit - - onMessageReceived - - onFinish - elapsedTime: 2002 - tests: - - - name: Published - valid: true - description: 'Published successfully' - valid: true - onMessageReceived: - arguments: - payload: reusingSecureResponse - stream: - address: 127.0.0.1 - family: IPv4 - port: 61964 - elapsedTime: 2004 - tests: - - - name: 'Assertion #0' - valid: true - description: 'Expected ''payload'' to be equal to ''reusingSecureResponse''. Received ''reusingSecureResponse''' - - - name: 'Assertion #0' - valid: true - description: 'Expected ''payload'' to be equal to ''reusingSecureResponse''. Received ''reusingSecureResponse''' - valid: true - type: ssl - publishTime: '2020-03-08T05:33:41.033Z' - iteration: 0 - totalIterations: 1 + sensors: [] + actuators: [] + iteration: 5 + totalIterations: 10 hooks: onInit: valid: true @@ -6414,23 +5995,22 @@ requisitions: valid: true tests: [] arguments: - elapsedTime: 2002 + elapsedTime: 1 time: - startTime: '2020-03-08T05:33:39.031Z' - endTime: '2020-03-08T05:33:41.033Z' - totalTime: 2002 + startTime: '2025-03-18T20:55:58.102Z' + endTime: '2025-03-18T20:55:58.103Z' + totalTime: 1 timeout: 5000 - requisitions: [] - - - valid: true - name: examples/stdin.yml - id: 0233360796_ae6c8a1714_155639 - level: 1 - subscriptions: + tasks: [] - - id: anyIdTest - name: 'subscription description' - type: standard-input + valid: true + name: iterations + id: 1355570989_98e6c02ed8_808806 + level: 2 + sensors: [] + actuators: [] + iteration: 6 + totalIterations: 10 hooks: onInit: valid: true @@ -6439,84 +6019,50 @@ requisitions: elapsedTime: 0 onFinish: valid: true - tests: - - - valid: true - name: 'Message received' - description: 'Subscription has received its message' + tests: [] arguments: - executedHooks: - - onInit - - onMessageReceived - - onFinish - elapsedTime: 115 - onMessageReceived: + elapsedTime: 0 + time: + startTime: '2025-03-18T20:55:58.103Z' + endTime: '2025-03-18T20:55:58.104Z' + totalTime: 1 + timeout: 5000 + tasks: [] + - + valid: true + name: iterations + id: 1355570989_98e6c02ed8_808806 + level: 2 + sensors: [] + actuators: [] + iteration: 7 + totalIterations: 10 + hooks: + onInit: valid: true - tests: - - - name: payload - valid: true - description: 'Expected ''message'' to be equal to ''enqueuer standard-input payload''. Received ''enqueuer standard-input payload''' + tests: [] arguments: - message: 'enqueuer standard-input payload' - elapsedTime: 115 - valid: true - subscriptionTime: '2020-03-08T05:33:36.911Z' - publishers: [] - iteration: 0 - totalIterations: 1 - hooks: - onInit: - valid: true - tests: [] - arguments: - elapsedTime: 0 - onFinish: - valid: true - tests: [] - arguments: - elapsedTime: 115 - time: - startTime: '2020-03-08T05:33:36.870Z' - endTime: '2020-03-08T05:33:36.985Z' - totalTime: 115 - timeout: 5000 - requisitions: [] - - - valid: true - name: examples/store.yml - id: 0233360796_7b9aa96592_15280 - level: 1 - subscriptions: [] - publishers: [] - iteration: 0 - totalIterations: 1 - hooks: - onInit: - valid: true - tests: [] - arguments: - elapsedTime: 0 - onFinish: - valid: true - tests: [] - arguments: - elapsedTime: 59 - time: - startTime: '2020-03-08T05:33:36.871Z' - endTime: '2020-03-08T05:33:36.930Z' - totalTime: 59 - timeout: 5000 - requisitions: + elapsedTime: 0 + onFinish: + valid: true + tests: [] + arguments: + elapsedTime: 0 + time: + startTime: '2025-03-18T20:55:58.104Z' + endTime: '2025-03-18T20:55:58.104Z' + totalTime: 0 + timeout: 5000 + tasks: [] - valid: true - name: 'Requisition #0' - id: 0233360796_478dfcaed5_446468 + name: iterations + id: 1355570989_98e6c02ed8_808806 level: 2 - subscriptions: [] - publishers: [] - iteration: 0 - totalIterations: 1 + sensors: [] + actuators: [] + iteration: 8 + totalIterations: 10 hooks: onInit: valid: true @@ -6527,22 +6073,22 @@ requisitions: valid: true tests: [] arguments: - elapsedTime: 29 + elapsedTime: 1 time: - startTime: '2020-03-08T05:33:36.888Z' - endTime: '2020-03-08T05:33:36.917Z' - totalTime: 29 + startTime: '2025-03-18T20:55:58.104Z' + endTime: '2025-03-18T20:55:58.105Z' + totalTime: 1 timeout: 5000 - requisitions: [] + tasks: [] - valid: true - name: 'Requisition #1' - id: 0233360796_c96621cca9_556304 + name: iterations + id: 1355570989_98e6c02ed8_808806 level: 2 - subscriptions: [] - publishers: [] - iteration: 0 - totalIterations: 1 + sensors: [] + actuators: [] + iteration: 9 + totalIterations: 10 hooks: onInit: valid: true @@ -6551,30 +6097,22 @@ requisitions: elapsedTime: 0 onFinish: valid: true - tests: - - - name: Payload - valid: true - description: 'Expected ''123'' to be equal to ''123''. Received ''123''' - - - name: 'Environment Variables added' - valid: true - description: 'Expecting ''store.PATH'' to be defined' + tests: [] arguments: - elapsedTime: 2 + elapsedTime: 0 time: - startTime: '2020-03-08T05:33:36.922Z' - endTime: '2020-03-08T05:33:36.924Z' - totalTime: 2 + startTime: '2025-03-18T20:55:58.105Z' + endTime: '2025-03-18T20:55:58.105Z' + totalTime: 0 timeout: 5000 - requisitions: [] + tasks: [] - valid: true - name: 'Requisition #2' - id: 0233360796_05a830b0a8_964737 + name: delayed + id: 1355570989_612a4aab97_201415 level: 2 - subscriptions: [] - publishers: [] + sensors: [] + actuators: [] iteration: 0 totalIterations: 1 hooks: @@ -6587,24 +6125,32 @@ requisitions: valid: true tests: - - name: Payload + name: 'It was executed 10 times' valid: true - description: 'Expected ''124'' to be equal to ''124''. Received ''124''' + description: 'Expected ''task.parent.tasks[0].counter'' to be equal to ''10''. Received ''10''' + - + name: 'Elapsed time' + valid: true + description: 'Expected ''elapsedTime'' to be greater than or equal to ''2950''. Received ''3001''' + - + name: 'Assertion #2' + valid: true + description: 'Expected ''new Date().getTime() - task.startTime.getTime()'' to be greater than or equal to ''2950''. Received ''3001''' arguments: - elapsedTime: 1 + elapsedTime: 3001 time: - startTime: '2020-03-08T05:33:36.927Z' - endTime: '2020-03-08T05:33:36.928Z' - totalTime: 1 + startTime: '2025-03-18T20:55:58.105Z' + endTime: '2025-03-18T20:56:01.106Z' + totalTime: 3001 timeout: 5000 - requisitions: [] + tasks: [] - valid: true name: examples/tcp.yml - id: 0233360796_bf35f3e127_429250 + id: 1355570989_e7b866b1d6_400567 level: 1 - subscriptions: [] - publishers: [] + sensors: [] + actuators: [] iteration: 0 totalIterations: 1 hooks: @@ -6617,42 +6163,48 @@ requisitions: valid: true tests: [] arguments: - elapsedTime: 2934 + elapsedTime: 2925 time: - startTime: '2020-03-08T05:33:36.873Z' - endTime: '2020-03-08T05:33:39.807Z' - totalTime: 2934 + startTime: '2025-03-18T20:55:58.018Z' + endTime: '2025-03-18T20:56:00.943Z' + totalTime: 2925 timeout: 5000 - requisitions: + tasks: - valid: true - name: 'Requisition #0' - id: 0233360796_595f6dfba1_826051 + name: 'Task #0' + id: 1355570989_aa0a05d694_902568 level: 2 - subscriptions: + sensors: - - id: 0233360796_4e5617f719_679309 - name: 'Subscription #0' + id: 1355570989_4e1b8c622b_900053 + name: 'Sensor #0' type: tcp hooks: onInit: valid: true tests: [] - arguments: - elapsedTime: 0 + arguments: {} onFinish: valid: true tests: - + implicit: true valid: true name: 'Message received' - description: 'Subscription has received its message' + description: 'Sensor has received its message' arguments: executedHooks: - - onInit - - onMessageReceived - - onFinish - elapsedTime: 1105 + onInit: [] + onMessageReceived: + - payload + - stream + - path + - elapsedTime + onFinish: + - executedHooks + - elapsedTime + elapsedTime: 1111 onMessageReceived: valid: true tests: @@ -6663,20 +6215,20 @@ requisitions: - name: 'Assertion #1' valid: true - description: 'Expecting ''::ffff:127.0.0.1'' (stream.address) to contain ''127.0.0.1''' + description: 'Expected ''stream.address'' to be any of ''[''127.0.0.1'', ::1]''. Received ''::1''' arguments: payload: 'Hey Jude' stream: - address: '::ffff:127.0.0.1' + address: '::1' family: IPv6 port: 23069 - elapsedTime: 106 + elapsedTime: 130 valid: true - subscriptionTime: '2020-03-08T05:33:36.951Z' - publishers: + sensorTime: '2025-03-18T20:55:58.111Z' + actuators: - - id: 0233360796_3b7a9e3645_11917 - name: 'Publisher #0' + id: 1355570989_a89f8d87b3_639140 + name: 'Actuator #0' valid: true hooks: onInit: @@ -6687,24 +6239,30 @@ requisitions: onFinish: arguments: executedHooks: - - onInit - - onMessageReceived - - onFinish - elapsedTime: 1105 + onInit: [] + onMessageReceived: + - payload + - stream + - elapsedTime + onFinish: + - executedHooks + - elapsedTime + elapsedTime: 1111 tests: - name: Published valid: true description: 'Published successfully' + implicit: true valid: true onMessageReceived: arguments: payload: 'Do not make it bad' stream: - address: 127.0.0.1 - family: IPv4 - port: 61963 - elapsedTime: 1108 + address: '::1' + family: IPv6 + port: 57107 + elapsedTime: 1114 tests: - name: 'Assertion #0' @@ -6716,7 +6274,7 @@ requisitions: description: 'Expected ''payload'' to be equal to ''Do not make it bad''. Received ''Do not make it bad''' valid: true type: tcp - publishTime: '2020-03-08T05:33:37.994Z' + messageSentInstant: '2025-03-18T20:55:59.133Z' iteration: 0 totalIterations: 1 hooks: @@ -6729,41 +6287,47 @@ requisitions: valid: true tests: [] arguments: - elapsedTime: 1105 + elapsedTime: 1111 time: - startTime: '2020-03-08T05:33:36.889Z' - endTime: '2020-03-08T05:33:37.994Z' - totalTime: 1105 + startTime: '2025-03-18T20:55:58.023Z' + endTime: '2025-03-18T20:55:59.134Z' + totalTime: 1111 timeout: 3000 - requisitions: [] + tasks: [] - valid: true - name: 'Requisition #1' - id: 0233360796_3f1b829a68_117288 + name: 'Task #1' + id: 1355570989_006b92378b_129582 level: 2 - subscriptions: + sensors: - - id: 0233360796_944c1488d1_831955 - name: 'Subscription #0' + id: 1355570989_b4295abd6f_184933 + name: 'Sensor #0' type: tcp hooks: onInit: valid: true tests: [] - arguments: - elapsedTime: 0 + arguments: {} onFinish: valid: true tests: - + implicit: true valid: true name: 'Message received' - description: 'Subscription has received its message' + description: 'Sensor has received its message' arguments: executedHooks: - - onInit - - onMessageReceived - - onFinish + onInit: [] + onMessageReceived: + - payload + - stream + - path + - elapsedTime + onFinish: + - executedHooks + - elapsedTime elapsedTime: 805 onMessageReceived: valid: true @@ -6775,16 +6339,16 @@ requisitions: arguments: payload: 'I do not care' stream: - address: '::ffff:127.0.0.1' + address: '::1' family: IPv6 port: 23070 - elapsedTime: 2 + elapsedTime: 4 valid: true - subscriptionTime: '2020-03-08T05:33:37.997Z' - publishers: + sensorTime: '2025-03-18T20:55:59.136Z' + actuators: - - id: 0233360796_369e07d49e_615425 - name: 'Publisher #0' + id: 1355570989_cb6fdf14a5_788185 + name: 'Actuator #0' valid: true hooks: onInit: @@ -6795,24 +6359,30 @@ requisitions: onFinish: arguments: executedHooks: - - onInit - - onMessageReceived - - onFinish + onInit: [] + onMessageReceived: + - payload + - stream + - elapsedTime + onFinish: + - executedHooks + - elapsedTime elapsedTime: 805 tests: - name: Published valid: true description: 'Published successfully' + implicit: true valid: true onMessageReceived: arguments: payload: EnqueuerRocks stream: - address: 127.0.0.1 - family: IPv4 - port: 61982 - elapsedTime: 1812 + address: '::1' + family: IPv6 + port: 57121 + elapsedTime: 1809 tests: - name: 'Assertion #0' @@ -6824,7 +6394,7 @@ requisitions: description: 'Expected ''payload'' to be equal to ''EnqueuerRocks''. Received ''EnqueuerRocks''' valid: true type: tcp - publishTime: '2020-03-08T05:33:38.801Z' + messageSentInstant: '2025-03-18T20:55:59.940Z' iteration: 0 totalIterations: 1 hooks: @@ -6839,40 +6409,46 @@ requisitions: arguments: elapsedTime: 805 time: - startTime: '2020-03-08T05:33:37.996Z' - endTime: '2020-03-08T05:33:38.801Z' + startTime: '2025-03-18T20:55:59.135Z' + endTime: '2025-03-18T20:55:59.940Z' totalTime: 805 timeout: 3000 - requisitions: [] + tasks: [] - valid: true - name: 'Requisition #2' - id: 0233360796_7b3d80f664_265369 + name: 'Task #2' + id: 1355570989_df4c8be8cb_933160 level: 2 - subscriptions: + sensors: - - id: 0233360796_a5c6b9dc6a_622561 - name: 'Subscription #0' + id: 1355570989_2c1d8ad513_787867 + name: 'Sensor #0' type: tcp hooks: onInit: valid: true tests: [] - arguments: - elapsedTime: 0 + arguments: {} onFinish: valid: true tests: - + implicit: true valid: true name: 'Message received' - description: 'Subscription has received its message' + description: 'Sensor has received its message' arguments: executedHooks: - - onInit - - onMessageReceived - - onFinish - elapsedTime: 1003 + onInit: [] + onMessageReceived: + - payload + - stream + - path + - elapsedTime + onFinish: + - executedHooks + - elapsedTime + elapsedTime: 1002 onMessageReceived: valid: true tests: @@ -6883,16 +6459,16 @@ requisitions: arguments: payload: 'The socket is still open' stream: - address: '::ffff:127.0.0.1' + address: '::1' family: IPv6 port: 23070 elapsedTime: 1 valid: true - subscriptionTime: '2020-03-08T05:33:38.804Z' - publishers: + sensorTime: '2025-03-18T20:55:59.940Z' + actuators: - - id: 0233360796_c04ec1e8aa_940022 - name: 'Publisher #0' + id: 1355570989_cce053e941_842824 + name: 'Actuator #0' valid: true hooks: onInit: @@ -6903,24 +6479,30 @@ requisitions: onFinish: arguments: executedHooks: - - onInit - - onMessageReceived - - onFinish - elapsedTime: 1004 + onInit: [] + onMessageReceived: + - payload + - stream + - elapsedTime + onFinish: + - executedHooks + - elapsedTime + elapsedTime: 1002 tests: - name: Published valid: true description: 'Published successfully' + implicit: true valid: true onMessageReceived: arguments: payload: 'enqueuer Rocks' stream: - address: 127.0.0.1 - family: IPv4 - port: 61982 - elapsedTime: 1005 + address: '::1' + family: IPv6 + port: 57121 + elapsedTime: 1004 tests: - name: 'Assertion #0' @@ -6932,7 +6514,7 @@ requisitions: description: 'Expected ''payload'' to be equal to ''enqueuer Rocks''. Received ''enqueuer Rocks''' valid: true type: tcp - publishTime: '2020-03-08T05:33:39.806Z' + messageSentInstant: '2025-03-18T20:56:00.942Z' iteration: 0 totalIterations: 1 hooks: @@ -6945,20 +6527,20 @@ requisitions: valid: true tests: [] arguments: - elapsedTime: 1003 + elapsedTime: 1002 time: - startTime: '2020-03-08T05:33:38.803Z' - endTime: '2020-03-08T05:33:39.807Z' - totalTime: 1004 + startTime: '2025-03-18T20:55:59.940Z' + endTime: '2025-03-18T20:56:00.942Z' + totalTime: 1002 timeout: 3000 - requisitions: [] + tasks: [] - valid: true name: examples/udp.yml - id: 0233360796_f69fa0cdb2_712296 + id: 1355570989_84d5864ece_310413 level: 1 - subscriptions: [] - publishers: [] + sensors: [] + actuators: [] iteration: 0 totalIterations: 1 hooks: @@ -6971,42 +6553,47 @@ requisitions: valid: true tests: [] arguments: - elapsedTime: 115 + elapsedTime: 111 time: - startTime: '2020-03-08T05:33:36.874Z' - endTime: '2020-03-08T05:33:36.989Z' - totalTime: 115 + startTime: '2025-03-18T20:55:58.019Z' + endTime: '2025-03-18T20:55:58.130Z' + totalTime: 111 timeout: 5000 - requisitions: + tasks: - valid: true - name: 'Requisition #0' - id: 0233360796_ac96cfadf9_902215 + name: 'Task #0' + id: 1355570989_801e85b90d_697242 level: 2 - subscriptions: + sensors: - - id: 0233360796_6e62597dc8_377983 - name: 'subscription description' + id: 1355570989_a8c5612df5_48918 + name: 'sensor description' type: udp hooks: onInit: valid: true tests: [] - arguments: - elapsedTime: 0 + arguments: {} onFinish: valid: true tests: - + implicit: true valid: true name: 'Message received' - description: 'Subscription has received its message' + description: 'Sensor has received its message' arguments: executedHooks: - - onInit - - onMessageReceived - - onFinish - elapsedTime: 99 + onInit: [] + onMessageReceived: + - payload + - remoteInfo + - elapsedTime + onFinish: + - executedHooks + - elapsedTime + elapsedTime: 107 onMessageReceived: valid: true tests: @@ -7027,15 +6614,15 @@ requisitions: remoteInfo: address: 127.0.0.1 family: IPv4 - port: 64082 + port: 57685 size: 2 - elapsedTime: 98 + elapsedTime: 106 valid: true - subscriptionTime: '2020-03-08T05:33:36.914Z' - publishers: + sensorTime: '2025-03-18T20:55:58.067Z' + actuators: - - id: 0233360796_f1c5db9ba9_424777 - name: 'publisher description' + id: 1355570989_2f217fdcff_677702 + name: 'actuator description' valid: true hooks: onInit: @@ -7046,17 +6633,20 @@ requisitions: onFinish: arguments: executedHooks: - - onInit - - onFinish - elapsedTime: 99 + onInit: [] + onFinish: + - executedHooks + - elapsedTime + elapsedTime: 107 tests: - name: Published valid: true description: 'Published successfully' + implicit: true valid: true type: udp - publishTime: '2020-03-08T05:33:36.960Z' + messageSentInstant: '2025-03-18T20:55:58.125Z' iteration: 0 totalIterations: 1 hooks: @@ -7069,20 +6659,20 @@ requisitions: valid: true tests: [] arguments: - elapsedTime: 99 + elapsedTime: 107 time: - startTime: '2020-03-08T05:33:36.890Z' - endTime: '2020-03-08T05:33:36.989Z' - totalTime: 99 + startTime: '2025-03-18T20:55:58.023Z' + endTime: '2025-03-18T20:55:58.130Z' + totalTime: 107 timeout: 5000 - requisitions: [] + tasks: [] - valid: true name: examples/uds.yml - id: 0233360796_c449648554_690772 + id: 1355570989_8525788e48_714984 level: 1 - subscriptions: [] - publishers: [] + sensors: [] + actuators: [] iteration: 0 totalIterations: 1 hooks: @@ -7095,42 +6685,48 @@ requisitions: valid: true tests: [] arguments: - elapsedTime: 5107 + elapsedTime: 7118 time: - startTime: '2020-03-08T05:33:36.877Z' - endTime: '2020-03-08T05:33:41.984Z' - totalTime: 5107 - timeout: 7000 - requisitions: + startTime: '2025-03-18T20:55:58.019Z' + endTime: '2025-03-18T20:56:05.137Z' + totalTime: 7118 + timeout: 10000 + tasks: - valid: true - name: 'Requisition #0' - id: 0233360796_7acc07c435_931555 + name: 'Task #0' + id: 1355570989_f6ae2177d8_761854 level: 2 - subscriptions: + sensors: - - id: 0233360797_5e03c0017a_298828 - name: 'Subscription #0' + id: 1355570989_fbae96be75_499663 + name: 'Sensor #0' type: uds hooks: onInit: valid: true tests: [] - arguments: - elapsedTime: 0 + arguments: {} onFinish: valid: true tests: - + implicit: true valid: true name: 'Message received' - description: 'Subscription has received its message' + description: 'Sensor has received its message' arguments: executedHooks: - - onInit - - onMessageReceived - - onFinish - elapsedTime: 2074 + onInit: [] + onMessageReceived: + - payload + - stream + - path + - elapsedTime + onFinish: + - executedHooks + - elapsedTime + elapsedTime: 3106 onMessageReceived: valid: true tests: @@ -7146,13 +6742,13 @@ requisitions: payload: enqueuer stream: {} path: /tmp/unix.sock - elapsedTime: 100 + elapsedTime: 124 valid: true - subscriptionTime: '2020-03-08T05:33:36.951Z' - publishers: + sensorTime: '2025-03-18T20:55:58.111Z' + actuators: - - id: 0233360796_b14ca3892b_776328 - name: 'Publisher #0' + id: 1355570989_b345b00bc1_962895 + name: 'Actuator #0' valid: true hooks: onInit: @@ -7163,21 +6759,27 @@ requisitions: onFinish: arguments: executedHooks: - - onInit - - onMessageReceived - - onFinish - elapsedTime: 2074 + onInit: [] + onMessageReceived: + - payload + - stream + - elapsedTime + onFinish: + - executedHooks + - elapsedTime + elapsedTime: 3106 tests: - name: Published valid: true description: 'Published successfully' + implicit: true valid: true onMessageReceived: arguments: payload: responsePayload stream: {} - elapsedTime: 2077 + elapsedTime: 3107 tests: - name: 'Assertion #0' @@ -7189,7 +6791,7 @@ requisitions: description: 'Expected ''payload'' to be equal to ''responsePayload''. Received ''responsePayload''' valid: true type: uds - publishTime: '2020-03-08T05:33:38.964Z' + messageSentInstant: '2025-03-18T20:56:01.129Z' iteration: 0 totalIterations: 1 hooks: @@ -7202,42 +6804,48 @@ requisitions: valid: true tests: [] arguments: - elapsedTime: 2074 + elapsedTime: 3106 time: - startTime: '2020-03-08T05:33:36.891Z' - endTime: '2020-03-08T05:33:38.965Z' - totalTime: 2074 + startTime: '2025-03-18T20:55:58.024Z' + endTime: '2025-03-18T20:56:01.130Z' + totalTime: 3106 timeout: 5000 - requisitions: [] + tasks: [] - valid: true - name: 'Requisition #1' - id: 0233360797_591e70c91e_492945 + name: 'Task #1' + id: 1355570989_d7d08996c9_271155 level: 2 - subscriptions: + sensors: - - id: 0233360797_81ccf3e7f8_794394 - name: 'Subscription #0' + id: 1355570989_70b5db124e_836654 + name: 'Sensor #0' type: uds hooks: onInit: valid: true tests: [] - arguments: - elapsedTime: 0 + arguments: {} onFinish: valid: true tests: - + implicit: true valid: true name: 'Message received' - description: 'Subscription has received its message' + description: 'Sensor has received its message' arguments: executedHooks: - - onInit - - onMessageReceived - - onFinish - elapsedTime: 1005 + onInit: [] + onMessageReceived: + - payload + - stream + - path + - elapsedTime + onFinish: + - executedHooks + - elapsedTime + elapsedTime: 1004 onMessageReceived: valid: true tests: @@ -7251,11 +6859,11 @@ requisitions: path: /tmp/unix2.sock elapsedTime: 1 valid: true - subscriptionTime: '2020-03-08T05:33:38.967Z' - publishers: + sensorTime: '2025-03-18T20:56:01.131Z' + actuators: - - id: 0233360797_297aae1eb3_710383 - name: 'Publisher #0' + id: 1355570989_bfdf7072a9_421839 + name: 'Actuator #0' valid: true hooks: onInit: @@ -7265,22 +6873,25 @@ requisitions: - name: 'Assertion #0' valid: true - description: 'Expected ''publisher.payload'' to be equal to ''enqueuer''. Received ''enqueuer''' + description: 'Expected ''actuator.payload'' to be equal to ''enqueuer''. Received ''enqueuer''' valid: true onFinish: arguments: executedHooks: - - onInit - - onFinish - elapsedTime: 1005 + onInit: [] + onFinish: + - executedHooks + - elapsedTime + elapsedTime: 1004 tests: - name: Published valid: true description: 'Published successfully' + implicit: true valid: true type: uds - publishTime: '2020-03-08T05:33:39.972Z' + messageSentInstant: '2025-03-18T20:56:02.133Z' iteration: 0 totalIterations: 1 hooks: @@ -7293,42 +6904,48 @@ requisitions: valid: true tests: [] arguments: - elapsedTime: 1005 + elapsedTime: 1004 time: - startTime: '2020-03-08T05:33:38.967Z' - endTime: '2020-03-08T05:33:39.972Z' - totalTime: 1005 + startTime: '2025-03-18T20:56:01.130Z' + endTime: '2025-03-18T20:56:02.134Z' + totalTime: 1004 timeout: 5000 - requisitions: [] + tasks: [] - valid: true - name: 'Requisition #2' - id: 0233360797_3321951a43_659411 + name: 'Task #2' + id: 1355570989_419ce8cd57_868213 level: 2 - subscriptions: + sensors: - - id: 0233360797_d308bbf817_967145 - name: 'Subscription #0' + id: 1355570989_a7b5fb04f1_894816 + name: 'Sensor #0' type: uds hooks: onInit: valid: true tests: [] - arguments: - elapsedTime: 0 + arguments: {} onFinish: valid: true tests: - + implicit: true valid: true name: 'Message received' - description: 'Subscription has received its message' + description: 'Sensor has received its message' arguments: executedHooks: - - onInit - - onMessageReceived - - onFinish - elapsedTime: 2010 + onInit: [] + onMessageReceived: + - payload + - stream + - path + - elapsedTime + onFinish: + - executedHooks + - elapsedTime + elapsedTime: 3002 onMessageReceived: valid: true tests: @@ -7339,13 +6956,13 @@ requisitions: arguments: payload: 'I am still opened' stream: {} - elapsedTime: 3 + elapsedTime: 1 valid: true - subscriptionTime: '2020-03-08T05:33:39.975Z' - publishers: + sensorTime: '2025-03-18T20:56:02.135Z' + actuators: - - id: 0233360797_b6920facd0_475742 - name: 'Publisher #0' + id: 1355570989_97331480ef_87367 + name: 'Actuator #0' valid: true hooks: onInit: @@ -7356,21 +6973,27 @@ requisitions: onFinish: arguments: executedHooks: - - onInit - - onMessageReceived - - onFinish - elapsedTime: 2010 + onInit: [] + onMessageReceived: + - payload + - stream + - elapsedTime + onFinish: + - executedHooks + - elapsedTime + elapsedTime: 3002 tests: - name: Published valid: true description: 'Published successfully' + implicit: true valid: true onMessageReceived: arguments: payload: 'I am still bidirectional' stream: {} - elapsedTime: 2008 + elapsedTime: 3001 tests: - name: 'Assertion #0' @@ -7378,7 +7001,7 @@ requisitions: description: 'Expected ''payload'' to be equal to ''I am still bidirectional''. Received ''I am still bidirectional''' valid: true type: uds - publishTime: '2020-03-08T05:33:41.982Z' + messageSentInstant: '2025-03-18T20:56:05.137Z' iteration: 0 totalIterations: 1 hooks: @@ -7391,20 +7014,20 @@ requisitions: valid: true tests: [] arguments: - elapsedTime: 2010 + elapsedTime: 3002 time: - startTime: '2020-03-08T05:33:39.974Z' - endTime: '2020-03-08T05:33:41.984Z' - totalTime: 2010 + startTime: '2025-03-18T20:56:02.135Z' + endTime: '2025-03-18T20:56:05.137Z' + totalTime: 3002 timeout: 5000 - requisitions: [] + tasks: [] - valid: true name: examples/variables.yml - id: 0233360797_3a6eb18d6d_491636 + id: 1355570989_a267a90add_578473 level: 1 - subscriptions: [] - publishers: [] + sensors: [] + actuators: [] iteration: 0 totalIterations: 1 hooks: @@ -7417,20 +7040,20 @@ requisitions: valid: true tests: [] arguments: - elapsedTime: 50 + elapsedTime: 81 time: - startTime: '2020-03-08T05:33:36.877Z' - endTime: '2020-03-08T05:33:36.928Z' - totalTime: 51 + startTime: '2025-03-18T20:55:58.019Z' + endTime: '2025-03-18T20:55:58.100Z' + totalTime: 81 timeout: 5000 - requisitions: + tasks: - valid: true - name: 'Requisition #0' - id: 0233360797_bdf0ac0681_548015 + name: 'Task #0' + id: 1355570989_1d45118746_81375 level: 2 - subscriptions: [] - publishers: [] + sensors: [] + actuators: [] iteration: 0 totalIterations: 1 hooks: @@ -7451,20 +7074,20 @@ requisitions: valid: true description: 'Expected ''store.otherKey / 2'' to be equal to ''1''. Received ''1''' arguments: - elapsedTime: 26 + elapsedTime: 70 time: - startTime: '2020-03-08T05:33:36.891Z' - endTime: '2020-03-08T05:33:36.917Z' - totalTime: 26 + startTime: '2025-03-18T20:55:58.024Z' + endTime: '2025-03-18T20:55:58.095Z' + totalTime: 71 timeout: 5000 - requisitions: [] + tasks: [] - valid: true - name: 'Requisition #1' - id: 0233360797_5153bc0c75_721312 + name: 'Task #1' + id: 1355570989_3baa6689ee_968319 level: 2 - subscriptions: [] - publishers: [] + sensors: [] + actuators: [] iteration: 0 totalIterations: 1 hooks: @@ -7474,7 +7097,7 @@ requisitions: - name: 'Assertion #0' valid: true - description: 'Expected ''requisition.anyName'' to be equal to ''10''. Received ''10''' + description: 'Expected ''task.anyName'' to be equal to ''10''. Received ''10''' - name: 'Assertion #1' valid: true @@ -7489,20 +7112,20 @@ requisitions: valid: true tests: [] arguments: - elapsedTime: 2 + elapsedTime: 1 time: - startTime: '2020-03-08T05:33:36.922Z' - endTime: '2020-03-08T05:33:36.924Z' - totalTime: 2 + startTime: '2025-03-18T20:55:58.098Z' + endTime: '2025-03-18T20:55:58.099Z' + totalTime: 1 timeout: 5000 - requisitions: [] + tasks: [] - valid: true name: examples/number.json - id: 0233360797_1925b227c5_645737 + id: 1355570989_fa8fbbf66f_919281 level: 1 - subscriptions: [] - publishers: [] + sensors: [] + actuators: [] iteration: 0 totalIterations: 1 hooks: @@ -7512,31 +7135,31 @@ requisitions: - name: 'Assertion #0' valid: true - description: 'Expected ''typeof requisition.number'' to be equal to ''number''. Received ''number''' + description: 'Expected ''typeof task.number'' to be equal to ''number''. Received ''number''' - name: 'Assertion #1' valid: true - description: 'Expected ''requisition.number'' to be equal to ''123''. Received ''123''' + description: 'Expected ''task.number'' to be equal to ''123''. Received ''123''' arguments: elapsedTime: 0 onFinish: valid: true tests: [] arguments: - elapsedTime: 39 + elapsedTime: 75 time: - startTime: '2020-03-08T05:33:36.878Z' - endTime: '2020-03-08T05:33:36.917Z' - totalTime: 39 + startTime: '2025-03-18T20:55:58.019Z' + endTime: '2025-03-18T20:55:58.095Z' + totalTime: 76 timeout: 5000 - requisitions: [] + tasks: [] - valid: true - name: examples/requisition-navigation.yaml - id: 0233360797_e848774ac1_305346 + name: examples/task-navigation.yaml + id: 1355570989_006e246507_557339 level: 1 - subscriptions: [] - publishers: [] + sensors: [] + actuators: [] iteration: 0 totalIterations: 1 hooks: @@ -7549,20 +7172,20 @@ requisitions: valid: true tests: [] arguments: - elapsedTime: 50 + elapsedTime: 80 time: - startTime: '2020-03-08T05:33:36.878Z' - endTime: '2020-03-08T05:33:36.928Z' - totalTime: 50 + startTime: '2025-03-18T20:55:58.020Z' + endTime: '2025-03-18T20:55:58.100Z' + totalTime: 80 timeout: 5000 - requisitions: + tasks: - valid: true name: first - id: 0233360797_542da8f206_750326 + id: 1355570990_dba1d7182d_939313 level: 2 - subscriptions: [] - publishers: [] + sensors: [] + actuators: [] iteration: 0 totalIterations: 1 hooks: @@ -7572,27 +7195,27 @@ requisitions: - name: 'Assertion #0' valid: true - description: 'Expected ''requisition.parent.requisitions[1].name'' to be equal to ''second''. Received ''second''' + description: 'Expected ''task.parent.tasks[1].name'' to be equal to ''second''. Received ''second''' arguments: elapsedTime: 0 onFinish: valid: true tests: [] arguments: - elapsedTime: 25 + elapsedTime: 71 time: - startTime: '2020-03-08T05:33:36.892Z' - endTime: '2020-03-08T05:33:36.917Z' - totalTime: 25 + startTime: '2025-03-18T20:55:58.024Z' + endTime: '2025-03-18T20:55:58.095Z' + totalTime: 71 timeout: 5000 - requisitions: [] + tasks: [] - valid: true name: second - id: 0233360797_d7c80e2b4e_252247 + id: 1355570990_8b8ad27229_816266 level: 2 - subscriptions: [] - publishers: [] + sensors: [] + actuators: [] iteration: 0 totalIterations: 1 hooks: @@ -7602,7 +7225,7 @@ requisitions: - name: 'Assertion #0' valid: true - description: 'Expected ''requisition.parent.requisitions[0].name'' to be equal to ''first''. Received ''first''' + description: 'Expected ''task.parent.tasks[0].name'' to be equal to ''first''. Received ''first''' arguments: elapsedTime: 0 onFinish: @@ -7611,8 +7234,8 @@ requisitions: arguments: elapsedTime: 1 time: - startTime: '2020-03-08T05:33:36.923Z' - endTime: '2020-03-08T05:33:36.924Z' + startTime: '2025-03-18T20:55:58.098Z' + endTime: '2025-03-18T20:55:58.099Z' totalTime: 1 timeout: 5000 - requisitions: [] + tasks: [] diff --git a/package-lock.json b/package-lock.json index 2b1b4620..987856f8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,11690 +1,12738 @@ { "name": "enqueuer", - "version": "5.0.4", - "lockfileVersion": 1, + "version": "6.0.3", + "lockfileVersion": 3, "requires": true, - "dependencies": { - "@babel/code-frame": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0.tgz", - "integrity": "sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA==", - "dev": true, - "requires": { - "@babel/highlight": "^7.0.0" + "packages": { + "": { + "name": "enqueuer", + "version": "6.0.3", + "license": "MIT", + "dependencies": { + "chalk": "^4.1.2", + "commander": "^12.1.0", + "express": "^4.19.2", + "glob": "^11.0.0", + "jest": "^29.7.0", + "json-placeholder-replacer": "^2.0.5", + "prettyjson": "^1.2.5", + "require-from-string": "^2.0.2", + "yamljs": "^0.3.0" + }, + "bin": { + "enqueuer": "dist/index.js", + "nqr": "dist/index.js" + }, + "devDependencies": { + "@types/commander": "^2.12.0", + "@types/express": "^4.17.21", + "@types/jest": "^29.5.12", + "@types/node": "^22.3.0", + "@types/object-hash": "^3.0.6", + "@types/prettyjson": "0.0.33", + "@types/require-from-string": "^1.2.3", + "@types/yamljs": "^0.2.34", + "commitizen": "^4.3.0", + "cz-conventional-changelog": "^3.3.0", + "pagedown": "^1.1.0", + "prettier": "^3.3.3", + "semantic-release": "^24.0.0", + "ts-jest": "^29.2.4", + "ts-node": "^10.9.2", + "typescript": "^5.5.4", + "yaml-lint": "^1.7.0" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "license": "Apache-2.0", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" } }, - "@babel/core": { - "version": "7.4.5", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.4.5.tgz", - "integrity": "sha512-OvjIh6aqXtlsA8ujtGKfC7LYWksYSX8yQcM8Ay3LuvVeQ63lcOKgoZWVqcpFwkd29aYU9rVx7jxhfhiEDV9MZA==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/generator": "^7.4.4", - "@babel/helpers": "^7.4.4", - "@babel/parser": "^7.4.5", - "@babel/template": "^7.4.4", - "@babel/traverse": "^7.4.5", - "@babel/types": "^7.4.4", - "convert-source-map": "^1.1.0", - "debug": "^4.1.0", - "json5": "^2.1.0", - "lodash": "^4.17.11", - "resolve": "^1.3.2", - "semver": "^5.4.1", - "source-map": "^0.5.0" - }, - "dependencies": { - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } + "node_modules/@babel/code-frame": { + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", + "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.25.9", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" } }, - "@babel/generator": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.4.4.tgz", - "integrity": "sha512-53UOLK6TVNqKxf7RUh8NE851EHRxOOeVXKbK2bivdb+iziMyk03Sr4eaE9OELCbyZAAafAKPDwF2TPUES5QbxQ==", - "dev": true, - "requires": { - "@babel/types": "^7.4.4", - "jsesc": "^2.5.1", - "lodash": "^4.17.11", - "source-map": "^0.5.0", - "trim-right": "^1.0.1" + "node_modules/@babel/compat-data": { + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.5.tgz", + "integrity": "sha512-XvcZi1KWf88RVbF9wn8MN6tYFloU5qX8KjuF3E1PVBmJ9eypXfs4GRiJwLuTZL0iSnJUKn1BFPa5BPZZJyFzPg==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" } }, - "@babel/helper-function-name": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.1.0.tgz", - "integrity": "sha512-A95XEoCpb3TO+KZzJ4S/5uW5fNe26DjBGqf1o9ucyLyCmi1dXq/B3c8iaWTfBk3VvetUxl16e8tIrd5teOCfGw==", - "dev": true, - "requires": { - "@babel/helper-get-function-arity": "^7.0.0", - "@babel/template": "^7.1.0", - "@babel/types": "^7.0.0" + "node_modules/@babel/core": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.0.tgz", + "integrity": "sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==", + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.26.0", + "@babel/generator": "^7.26.0", + "@babel/helper-compilation-targets": "^7.25.9", + "@babel/helper-module-transforms": "^7.26.0", + "@babel/helpers": "^7.26.0", + "@babel/parser": "^7.26.0", + "@babel/template": "^7.25.9", + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.26.0", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" } }, - "@babel/helper-get-function-arity": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0.tgz", - "integrity": "sha512-r2DbJeg4svYvt3HOS74U4eWKsUAMRH01Z1ds1zx8KNTPtpTL5JAsdFv8BNyOpVqdFhHkkRDIg5B4AsxmkjAlmQ==", - "dev": true, - "requires": { - "@babel/types": "^7.0.0" + "node_modules/@babel/core/node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "@babel/helper-plugin-utils": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.0.0.tgz", - "integrity": "sha512-CYAOUCARwExnEixLdB6sDm2dIJ/YgEAKDM1MOeMeZu9Ld/bDgVo8aiWrXwcY7OBh+1Ea2uUcVRcxKk0GJvW7QA==", - "dev": true + "node_modules/@babel/core/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==", + "license": "MIT" }, - "@babel/helper-split-export-declaration": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.4.4.tgz", - "integrity": "sha512-Ro/XkzLf3JFITkW6b+hNxzZ1n5OQ80NvIUdmHspih1XAhtN3vPTuUFT4eQnela+2MaZ5ulH+iyP513KJrxbN7Q==", - "dev": true, - "requires": { - "@babel/types": "^7.4.4" + "node_modules/@babel/generator": { + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.5.tgz", + "integrity": "sha512-2caSP6fN9I7HOe6nqhtft7V4g7/V/gfDsC3Ag4W7kEzzvRGKqiv0pu0HogPiZ3KaVSoNDhUws6IJjDjpfmYIXw==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.26.5", + "@babel/types": "^7.26.5", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" } }, - "@babel/helpers": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.4.4.tgz", - "integrity": "sha512-igczbR/0SeuPR8RFfC7tGrbdTbFL3QTvH6D+Z6zNxnTe//GyqmtHmDkzrqDmyZ3eSwPqB/LhyKoU5DXsp+Vp2A==", - "dev": true, - "requires": { - "@babel/template": "^7.4.4", - "@babel/traverse": "^7.4.4", - "@babel/types": "^7.4.4" + "node_modules/@babel/helper-compilation-targets": { + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.26.5.tgz", + "integrity": "sha512-IXuyn5EkouFJscIDuFF5EsiSolseme1s0CZB+QxVugqJLYmKdxI1VfIBOst0SUu4rnk2Z7kqTwmoO1lp3HIfnA==", + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.26.5", + "@babel/helper-validator-option": "^7.25.9", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" } }, - "@babel/highlight": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0.tgz", - "integrity": "sha512-UFMC4ZeFC48Tpvj7C8UgLvtkaUuovQX+5xNWrsIoMG8o2z+XFKjKaN9iVmS84dPwVN00W4wPmqvYoZF3EGAsfw==", - "dev": true, - "requires": { - "chalk": "^2.0.0", - "esutils": "^2.0.2", - "js-tokens": "^4.0.0" + "node_modules/@babel/helper-module-imports": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", + "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==", + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" } }, - "@babel/parser": { - "version": "7.4.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.4.5.tgz", - "integrity": "sha512-9mUqkL1FF5T7f0WDFfAoDdiMVPWsdD1gZYzSnaXsxUCUqzuch/8of9G3VUSNiZmMBoRxT3neyVsqeiL/ZPcjew==", - "dev": true - }, - "@babel/plugin-syntax-object-rest-spread": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.2.0.tgz", - "integrity": "sha512-t0JKGgqk2We+9may3t0xDdmneaXmyxq0xieYcKHxIsrJO64n1OiMWNUtc5gQK1PA0NpdCRrtZp4z+IUaKugrSA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "node_modules/@babel/helper-module-transforms": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz", + "integrity": "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9", + "@babel/traverse": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.26.5.tgz", + "integrity": "sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", + "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", + "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz", + "integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.26.10", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.10.tgz", + "integrity": "sha512-UPYc3SauzZ3JGgj87GgZ89JVdC5dj0AoetR5Bw6wj4niittNyFh6+eOGonYvJ1ao6B8lEa3Q3klS7ADZ53bc5g==", + "license": "MIT", + "dependencies": { + "@babel/template": "^7.26.9", + "@babel/types": "^7.26.10" + }, + "engines": { + "node": ">=6.9.0" } }, - "@babel/template": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.4.4.tgz", - "integrity": "sha512-CiGzLN9KgAvgZsnivND7rkA+AeJ9JB0ciPOD4U59GKbQP2iQl+olF1l76kJOupqidozfZ32ghwBEJDhnk9MEcw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.4.4", - "@babel/types": "^7.4.4" + "node_modules/@babel/parser": { + "version": "7.26.10", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.10.tgz", + "integrity": "sha512-6aQR2zGE/QFi8JpDLjUZEPYOs7+mhKXm86VaKFiLP35JQwQb6bwUE+XbvkH0EptsYhbNBSUGaUBLKqxH1xSgsA==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.26.10" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" } }, - "@babel/traverse": { - "version": "7.4.5", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.4.5.tgz", - "integrity": "sha512-Vc+qjynwkjRmIFGxy0KYoPj4FdVDxLej89kMHFsWScq999uX+pwcX4v9mWRjW0KcAYTPAuVQl2LKP1wEVLsp+A==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/generator": "^7.4.4", - "@babel/helper-function-name": "^7.1.0", - "@babel/helper-split-export-declaration": "^7.4.4", - "@babel/parser": "^7.4.5", - "@babel/types": "^7.4.4", - "debug": "^4.1.0", - "globals": "^11.1.0", - "lodash": "^4.17.11" - }, - "dependencies": { - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/types": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.4.4.tgz", - "integrity": "sha512-dOllgYdnEFOebhkKCjzSVFqw/PmmB8pH6RGOWkY4GsboQNd47b1fBThBSwlHAq9alF9vc1M3+6oqR47R50L0tQ==", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.11", - "to-fast-properties": "^2.0.0" + "node_modules/@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@cnakazawa/watch": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@cnakazawa/watch/-/watch-1.0.3.tgz", - "integrity": "sha512-r5160ogAvGyHsal38Kux7YYtodEKOj89RGb28ht1jh3SJb08VwRwAKKJL0bGb04Zd/3r9FL3BFIc3bBidYffCA==", - "dev": true, - "requires": { - "exec-sh": "^0.3.2", - "minimist": "^1.2.0" + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@jest/console": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-24.7.1.tgz", - "integrity": "sha512-iNhtIy2M8bXlAOULWVTUxmnelTLFneTNEkHCgPmgd+zNwy9zVddJ6oS5rZ9iwoscNdT5mMwUd0C51v/fSlzItg==", - "dev": true, - "requires": { - "@jest/source-map": "^24.3.0", - "chalk": "^2.0.1", - "slash": "^2.0.0" + "node_modules/@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@jest/core": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-24.8.0.tgz", - "integrity": "sha512-R9rhAJwCBQzaRnrRgAdVfnglUuATXdwTRsYqs6NMdVcAl5euG8LtWDe+fVkN27YfKVBW61IojVsXKaOmSnqd/A==", - "dev": true, - "requires": { - "@jest/console": "^24.7.1", - "@jest/reporters": "^24.8.0", - "@jest/test-result": "^24.8.0", - "@jest/transform": "^24.8.0", - "@jest/types": "^24.8.0", - "ansi-escapes": "^3.0.0", - "chalk": "^2.0.1", - "exit": "^0.1.2", - "graceful-fs": "^4.1.15", - "jest-changed-files": "^24.8.0", - "jest-config": "^24.8.0", - "jest-haste-map": "^24.8.0", - "jest-message-util": "^24.8.0", - "jest-regex-util": "^24.3.0", - "jest-resolve-dependencies": "^24.8.0", - "jest-runner": "^24.8.0", - "jest-runtime": "^24.8.0", - "jest-snapshot": "^24.8.0", - "jest-util": "^24.8.0", - "jest-validate": "^24.8.0", - "jest-watcher": "^24.8.0", - "micromatch": "^3.1.10", - "p-each-series": "^1.0.0", - "pirates": "^4.0.1", - "realpath-native": "^1.1.0", - "rimraf": "^2.5.4", - "strip-ansi": "^5.0.0" - } - }, - "@jest/environment": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-24.8.0.tgz", - "integrity": "sha512-vlGt2HLg7qM+vtBrSkjDxk9K0YtRBi7HfRFaDxoRtyi+DyVChzhF20duvpdAnKVBV6W5tym8jm0U9EfXbDk1tw==", - "dev": true, - "requires": { - "@jest/fake-timers": "^24.8.0", - "@jest/transform": "^24.8.0", - "@jest/types": "^24.8.0", - "jest-mock": "^24.8.0" - } - }, - "@jest/fake-timers": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-24.8.0.tgz", - "integrity": "sha512-2M4d5MufVXwi6VzZhJ9f5S/wU4ud2ck0kxPof1Iz3zWx6Y+V2eJrES9jEktB6O3o/oEyk+il/uNu9PvASjWXQw==", - "dev": true, - "requires": { - "@jest/types": "^24.8.0", - "jest-message-util": "^24.8.0", - "jest-mock": "^24.8.0" - } - }, - "@jest/reporters": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-24.8.0.tgz", - "integrity": "sha512-eZ9TyUYpyIIXfYCrw0UHUWUvE35vx5I92HGMgS93Pv7du+GHIzl+/vh8Qj9MCWFK/4TqyttVBPakWMOfZRIfxw==", - "dev": true, - "requires": { - "@jest/environment": "^24.8.0", - "@jest/test-result": "^24.8.0", - "@jest/transform": "^24.8.0", - "@jest/types": "^24.8.0", - "chalk": "^2.0.1", - "exit": "^0.1.2", - "glob": "^7.1.2", - "istanbul-lib-coverage": "^2.0.2", - "istanbul-lib-instrument": "^3.0.1", - "istanbul-lib-report": "^2.0.4", - "istanbul-lib-source-maps": "^3.0.1", - "istanbul-reports": "^2.1.1", - "jest-haste-map": "^24.8.0", - "jest-resolve": "^24.8.0", - "jest-runtime": "^24.8.0", - "jest-util": "^24.8.0", - "jest-worker": "^24.6.0", - "node-notifier": "^5.2.1", - "slash": "^2.0.0", - "source-map": "^0.6.0", - "string-length": "^2.0.0" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.26.0.tgz", + "integrity": "sha512-e2dttdsJ1ZTpi3B9UYGLw41hifAubg19AtCu/2I/F1QNVclOBr1dYpTdmdyZ84Xiz43BS/tCUkMAZNLv12Pi+A==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@jest/source-map": { - "version": "24.3.0", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-24.3.0.tgz", - "integrity": "sha512-zALZt1t2ou8le/crCeeiRYzvdnTzaIlpOWaet45lNSqNJUnXbppUUFR4ZUAlzgDmKee4Q5P/tKXypI1RiHwgag==", - "dev": true, - "requires": { - "callsites": "^3.0.0", - "graceful-fs": "^4.1.15", - "source-map": "^0.6.0" - }, + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "license": "MIT", "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@jest/test-result": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-24.8.0.tgz", - "integrity": "sha512-+YdLlxwizlfqkFDh7Mc7ONPQAhA4YylU1s529vVM1rsf67vGZH/2GGm5uO8QzPeVyaVMobCQ7FTxl38QrKRlng==", - "dev": true, - "requires": { - "@jest/console": "^24.7.1", - "@jest/types": "^24.8.0", - "@types/istanbul-lib-coverage": "^2.0.0" - } - }, - "@jest/test-sequencer": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-24.8.0.tgz", - "integrity": "sha512-OzL/2yHyPdCHXEzhoBuq37CE99nkme15eHkAzXRVqthreWZamEMA0WoetwstsQBCXABhczpK03JNbc4L01vvLg==", - "dev": true, - "requires": { - "@jest/test-result": "^24.8.0", - "jest-haste-map": "^24.8.0", - "jest-runner": "^24.8.0", - "jest-runtime": "^24.8.0" - } - }, - "@jest/transform": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-24.8.0.tgz", - "integrity": "sha512-xBMfFUP7TortCs0O+Xtez2W7Zu1PLH9bvJgtraN1CDST6LBM/eTOZ9SfwS/lvV8yOfcDpFmwf9bq5cYbXvqsvA==", - "dev": true, - "requires": { - "@babel/core": "^7.1.0", - "@jest/types": "^24.8.0", - "babel-plugin-istanbul": "^5.1.0", - "chalk": "^2.0.1", - "convert-source-map": "^1.4.0", - "fast-json-stable-stringify": "^2.0.0", - "graceful-fs": "^4.1.15", - "jest-haste-map": "^24.8.0", - "jest-regex-util": "^24.3.0", - "jest-util": "^24.8.0", - "micromatch": "^3.1.10", - "realpath-native": "^1.1.0", - "slash": "^2.0.0", - "source-map": "^0.6.1", - "write-file-atomic": "2.4.1" - }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "license": "MIT", "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@jest/types": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.8.0.tgz", - "integrity": "sha512-g17UxVr2YfBtaMUxn9u/4+siG1ptg9IGYAYwvpwn61nBg779RXnjE/m7CxYcIzEt0AbHZZAHSEZNhkE2WxURVg==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", - "@types/yargs": "^12.0.9" + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.25.9.tgz", + "integrity": "sha512-ld6oezHQMZsZfp6pWtbjaNDF2tiiCYYDqQszHt5VV437lewP9aSi2Of99CK0D0XB21k7FLgnLcmQKyKzynfeAA==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@mrmlnc/readdir-enhanced": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz", - "integrity": "sha512-bPHp6Ji8b41szTOcaP63VlnbbO5Ny6dwAATtY6JTjh5N2OLrb5Qk/Th5cRkRQhkWCt+EJsYrNB0MiL+Gpn6e3g==", - "dev": true, - "requires": { - "call-me-maybe": "^1.0.1", - "glob-to-regexp": "^0.3.0" + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@nodelib/fs.stat": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz", - "integrity": "sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw==", - "dev": true - }, - "@octokit/endpoint": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-5.2.0.tgz", - "integrity": "sha512-g4r1MKr8GJ8qubJQp3HP3JrxDY+ZeVqjYBTgtu1lPEDLhfQDY6rOhyZOoHKOw+gaIF6aAcmuvPPNZUro2OwmOg==", - "dev": true, - "requires": { - "deepmerge": "3.3.0", - "is-plain-object": "^3.0.0", - "universal-user-agent": "^2.1.0", - "url-template": "^2.0.8" - }, - "dependencies": { - "is-plain-object": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-3.0.0.tgz", - "integrity": "sha512-tZIpofR+P05k8Aocp7UI/2UTa9lTJSebCXpFFoR9aibpokDj/uXBsJ8luUu0tTVYKkMU6URDUuOfJZ7koewXvg==", - "dev": true, - "requires": { - "isobject": "^4.0.0" - } - }, - "isobject": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-4.0.0.tgz", - "integrity": "sha512-S/2fF5wH8SJA/kmwr6HYhK/RI/OkhD84k8ntalo0iJjZikgq1XFvR5M8NPT1x5F7fBwCG3qHfnzeP/Vh/ZxCUA==", - "dev": true - } + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@octokit/request": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-4.1.1.tgz", - "integrity": "sha512-LOyL0i3oxRo418EXRSJNk/3Q4I0/NKawTn6H/CQp+wnrG1UFLGu080gSsgnWobhPo5BpUNgSQ5BRk5FOOJhD1Q==", - "dev": true, - "requires": { - "@octokit/endpoint": "^5.1.0", - "@octokit/request-error": "^1.0.1", - "deprecation": "^2.0.0", - "is-plain-object": "^3.0.0", - "node-fetch": "^2.3.0", - "once": "^1.4.0", - "universal-user-agent": "^2.1.0" - }, - "dependencies": { - "is-plain-object": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-3.0.0.tgz", - "integrity": "sha512-tZIpofR+P05k8Aocp7UI/2UTa9lTJSebCXpFFoR9aibpokDj/uXBsJ8luUu0tTVYKkMU6URDUuOfJZ7koewXvg==", - "dev": true, - "requires": { - "isobject": "^4.0.0" - } - }, - "isobject": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-4.0.0.tgz", - "integrity": "sha512-S/2fF5wH8SJA/kmwr6HYhK/RI/OkhD84k8ntalo0iJjZikgq1XFvR5M8NPT1x5F7fBwCG3qHfnzeP/Vh/ZxCUA==", - "dev": true - } + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@octokit/request-error": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-1.0.4.tgz", - "integrity": "sha512-L4JaJDXn8SGT+5G0uX79rZLv0MNJmfGa4vb4vy1NnpjSnWDLJRy6m90udGwvMmavwsStgbv2QNkPzzTCMmL+ig==", - "dev": true, - "requires": { - "deprecation": "^2.0.0", - "once": "^1.4.0" - } - }, - "@octokit/rest": { - "version": "16.28.2", - "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-16.28.2.tgz", - "integrity": "sha512-csuYiHvJ1P/GFDadVn0QhwO83R1+YREjcwCY7ZIezB6aJTRIEidJZj+R7gAkUhT687cqYb4cXTZsDVu9F+Fmug==", - "dev": true, - "requires": { - "@octokit/request": "^4.0.1", - "@octokit/request-error": "^1.0.2", - "atob-lite": "^2.0.0", - "before-after-hook": "^1.4.0", - "btoa-lite": "^1.0.0", - "deprecation": "^2.0.0", - "lodash.get": "^4.4.2", - "lodash.set": "^4.3.2", - "lodash.uniq": "^4.5.0", - "octokit-pagination-methods": "^1.1.0", - "once": "^1.4.0", - "universal-user-agent": "^2.0.0", - "url-template": "^2.0.8" - } - }, - "@semantic-release/commit-analyzer": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/@semantic-release/commit-analyzer/-/commit-analyzer-6.2.0.tgz", - "integrity": "sha512-oUtPydYcbtJsEY6WCPi4wynTgRecK5zCkKaGmHi+9Xl7d6jGf7LomnJCg++6dNF1tyavrbGMSdXTCPH6Dx9LbA==", - "dev": true, - "requires": { - "conventional-changelog-angular": "^5.0.0", - "conventional-commits-filter": "^2.0.0", - "conventional-commits-parser": "^3.0.0", - "debug": "^4.0.0", - "import-from": "^3.0.0", - "lodash": "^4.17.4" - }, - "dependencies": { - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@semantic-release/error": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@semantic-release/error/-/error-2.2.0.tgz", - "integrity": "sha512-9Tj/qn+y2j+sjCI3Jd+qseGtHjOAeg7dU2/lVcqIQ9TV3QDaDXDYXcoOHU+7o2Hwh8L8ymL4gfuO7KxDs3q2zg==", - "dev": true - }, - "@semantic-release/github": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@semantic-release/github/-/github-5.4.0.tgz", - "integrity": "sha512-qtaV/8xIu2a8jt6lACFps0gVDfJby1h7Ra5On3VF60PkrKft87ru9sywAl9gtTfSfOyUlFaNCNlf3mvDaekpzQ==", - "dev": true, - "requires": { - "@octokit/rest": "^16.27.0", - "@semantic-release/error": "^2.2.0", - "aggregate-error": "^3.0.0", - "bottleneck": "^2.18.1", - "debug": "^4.0.0", - "dir-glob": "^2.0.0", - "fs-extra": "^8.0.0", - "globby": "^9.0.0", - "http-proxy-agent": "^2.1.0", - "https-proxy-agent": "^2.2.1", - "issue-parser": "^4.0.0", - "lodash": "^4.17.4", - "mime": "^2.4.3", - "p-filter": "^2.0.0", - "p-retry": "^4.0.0", - "parse-github-url": "^1.0.1", - "url-join": "^4.0.0" - }, - "dependencies": { - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "fs-extra": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", - "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", - "dev": true, - "requires": { - "graceful-fs": "^4.2.0", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - } - }, - "graceful-fs": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.0.tgz", - "integrity": "sha512-jpSvDPV4Cq/bgtpndIWbI5hmYxhQGHPC4d4cqBPb4DLniCfhJokdXhwhaDuLBGLQdvvRum/UiX6ECVIPvDXqdg==", - "dev": true - }, - "mime": { - "version": "2.4.4", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz", - "integrity": "sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA==", - "dev": true - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@semantic-release/npm": { - "version": "5.3.5", - "resolved": "https://registry.npmjs.org/@semantic-release/npm/-/npm-5.3.5.tgz", - "integrity": "sha512-AOREQ6rUT8OAiqXvWCp0kMNjcdnLLq1JdP0voZL4l5zf6Tgs/65YA7ctP+9shthW01Ps85Nu0pILW3p9GkaYuw==", - "dev": true, - "requires": { - "@semantic-release/error": "^2.2.0", - "aggregate-error": "^3.0.0", - "execa": "^3.2.0", - "fs-extra": "^8.0.0", - "lodash": "^4.17.15", - "nerf-dart": "^1.0.0", - "normalize-url": "^4.0.0", - "npm": "^6.10.3", - "rc": "^1.2.8", - "read-pkg": "^5.0.0", - "registry-auth-token": "^4.0.0", - "tempy": "^0.3.0" - }, - "dependencies": { - "cross-spawn": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.1.tgz", - "integrity": "sha512-u7v4o84SwFpD32Z8IIcPZ6z1/ie24O6RU3RbtL5Y316l3KuHVPx9ItBgWQ6VlfAFnRnTtMUrsQ9MUUTuEZjogg==", - "dev": true, - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - }, - "execa": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-3.4.0.tgz", - "integrity": "sha512-r9vdGQk4bmCuK1yKQu1KTwcT2zwfWdbdaXfCtAh+5nU/4fSX+JAb7vZGvI5naJrQlvONrEB20jeruESI69530g==", - "dev": true, - "requires": { - "cross-spawn": "^7.0.0", - "get-stream": "^5.0.0", - "human-signals": "^1.1.1", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.0", - "onetime": "^5.1.0", - "p-finally": "^2.0.0", - "signal-exit": "^3.0.2", - "strip-final-newline": "^2.0.0" - } - }, - "fs-extra": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", - "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", - "dev": true, - "requires": { - "graceful-fs": "^4.2.0", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - } - }, - "get-stream": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz", - "integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==", - "dev": true, - "requires": { - "pump": "^3.0.0" - } - }, - "graceful-fs": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", - "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", - "dev": true - }, - "is-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", - "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", - "dev": true - }, - "lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", - "dev": true - }, - "merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true - }, - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true - }, - "npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, - "requires": { - "path-key": "^3.0.0" - } - }, - "onetime": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.0.tgz", - "integrity": "sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==", - "dev": true, - "requires": { - "mimic-fn": "^2.1.0" - } - }, - "p-finally": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-2.0.1.tgz", - "integrity": "sha512-vpm09aKwq6H9phqRQzecoDpD8TmVyGw70qmWlyq5onxY7tqyTTFVvxMykxQSQKILBSFlbXpypIw2T1Ml7+DDtw==", - "dev": true - }, - "parse-json": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.0.0.tgz", - "integrity": "sha512-OOY5b7PAEFV0E2Fir1KOkxchnZNCdowAJgQ5NuxjpBKTRP3pQhwkrkxqQjeoKJ+fO7bCpmIZaogI4eZGDMEGOw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1", - "lines-and-columns": "^1.1.6" - } - }, - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true - }, - "read-pkg": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", - "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", - "dev": true, - "requires": { - "@types/normalize-package-data": "^2.4.0", - "normalize-package-data": "^2.5.0", - "parse-json": "^5.0.0", - "type-fest": "^0.6.0" - } - }, - "shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - } + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@semantic-release/release-notes-generator": { - "version": "7.2.1", - "resolved": "https://registry.npmjs.org/@semantic-release/release-notes-generator/-/release-notes-generator-7.2.1.tgz", - "integrity": "sha512-TdlYgYH6amhE80i9L9HPcTwYzk4Rma7qM1g7XJEEfip7dNXWgmrBeibN4DJmTg/qrUFDd4GD86lFDcYXNZDNow==", - "dev": true, - "requires": { - "conventional-changelog-angular": "^5.0.0", - "conventional-changelog-writer": "^4.0.0", - "conventional-commits-filter": "^2.0.0", - "conventional-commits-parser": "^3.0.0", - "debug": "^4.0.0", - "get-stream": "^5.0.0", - "import-from": "^3.0.0", - "into-stream": "^5.0.0", - "lodash": "^4.17.4", - "read-pkg-up": "^6.0.0" - }, - "dependencies": { - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "get-stream": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz", - "integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==", - "dev": true, - "requires": { - "pump": "^3.0.0" - } - }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } - }, - "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 - }, - "read-pkg": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.1.1.tgz", - "integrity": "sha512-dFcTLQi6BZ+aFUaICg7er+/usEoqFdQxiEBsEMNGoipenihtxxtdrQuBXvyANCEI8VuUIVYFgeHGx9sLLvim4w==", - "dev": true, - "requires": { - "@types/normalize-package-data": "^2.4.0", - "normalize-package-data": "^2.5.0", - "parse-json": "^4.0.0", - "type-fest": "^0.4.1" - }, - "dependencies": { - "type-fest": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.4.1.tgz", - "integrity": "sha512-IwzA/LSfD2vC1/YDYMv/zHP4rDF1usCwllsDpbolT3D4fUepIO7f9K70jjmUewU/LmGUKJcwcVtDCpnKk4BPMw==", - "dev": true - } - } - }, - "read-pkg-up": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-6.0.0.tgz", - "integrity": "sha512-odtTvLl+EXo1eTsMnoUHRmg/XmXdTkwXVxy4VFE9Kp6cCq7b3l7QMdBndND3eAFzrbSAXC/WCUOQQ9rLjifKZw==", - "dev": true, - "requires": { - "find-up": "^4.0.0", - "read-pkg": "^5.1.1", - "type-fest": "^0.5.0" - } - }, - "type-fest": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.5.2.tgz", - "integrity": "sha512-DWkS49EQKVX//Tbupb9TFa19c7+MK1XmzkrZUR8TAktmE/DizXoaoJV6TZ/tSIPXipqNiRI6CyAe7x69Jb6RSw==", - "dev": true - } + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@types/babel__core": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.2.tgz", - "integrity": "sha512-cfCCrFmiGY/yq0NuKNxIQvZFy9kY/1immpSpTngOnyIbD4+eJOG5mxphhHDv3CHL9GltO4GcKr54kGBg3RNdbg==", - "dev": true, - "requires": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@types/babel__generator": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.0.2.tgz", - "integrity": "sha512-NHcOfab3Zw4q5sEE2COkpfXjoE7o+PmqD9DQW4koUT3roNxwziUdXGnRndMat/LJNUtePwn1TlP4do3uoe3KZQ==", - "dev": true, - "requires": { - "@babel/types": "^7.0.0" + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.25.9.tgz", + "integrity": "sha512-hjMgRy5hb8uJJjUcdWunWVcoi9bGpJp8p5Ol1229PoN6aytsLwNMgmdftO23wnCLMfVmTwZDWMPNq/D1SY60JQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@types/babel__template": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.0.2.tgz", - "integrity": "sha512-/K6zCpeW7Imzgab2bLkLEbz0+1JlFSrUMdw7KoIIu+IUdu51GWaBZpd3y1VXGVXzynvGa4DaIaxNZHiON3GXUg==", - "dev": true, - "requires": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" + "node_modules/@babel/template": { + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.26.9.tgz", + "integrity": "sha512-qyRplbeIpNZhmzOysF/wFMuP9sctmh2cFzRAZOn1YapxBsE1i9bJIY586R/WBLfLcmcBlM8ROBiQURnnNy+zfA==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.26.2", + "@babel/parser": "^7.26.9", + "@babel/types": "^7.26.9" + }, + "engines": { + "node": ">=6.9.0" } }, - "@types/babel__traverse": { - "version": "7.0.7", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.0.7.tgz", - "integrity": "sha512-CeBpmX1J8kWLcDEnI3Cl2Eo6RfbGvzUctA+CjZUhOKDFbLfcr7fc4usEqLNWetrlJd7RhAkyYe2czXop4fICpw==", - "dev": true, - "requires": { - "@babel/types": "^7.3.0" + "node_modules/@babel/traverse": { + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.5.tgz", + "integrity": "sha512-rkOSPOw+AXbgtwUga3U4u8RpoK9FEFWBNAlTpcnkLFjL5CT+oyHNuUUC/xx6XefEJ16r38r8Bc/lfp6rYuHeJQ==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.26.2", + "@babel/generator": "^7.26.5", + "@babel/parser": "^7.26.5", + "@babel/template": "^7.25.9", + "@babel/types": "^7.26.5", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" } }, - "@types/body-parser": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.17.0.tgz", - "integrity": "sha512-a2+YeUjPkztKJu5aIF2yArYFQQp8d51wZ7DavSHjFuY1mqVgidGyzEQ41JIVNy82fXj8yPgy2vJmfIywgESW6w==", - "dev": true, - "requires": { - "@types/connect": "*", - "@types/node": "*" + "node_modules/@babel/traverse/node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "@types/caseless": { - "version": "0.12.1", - "resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.1.tgz", - "integrity": "sha512-FhlMa34NHp9K5MY1Uz8yb+ZvuX0pnvn3jScRSNAb75KHGB8d3rEU6hqMs3Z2vjuytcMfRg6c5CHMc3wtYyD2/A==", - "dev": true + "node_modules/@babel/traverse/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==", + "license": "MIT" }, - "@types/connect": { - "version": "3.4.32", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.32.tgz", - "integrity": "sha512-4r8qa0quOvh7lGD0pre62CAb1oni1OO6ecJLGCezTmhQ8Fz50Arx9RUszryR8KlgK6avuSXvviL6yWyViQABOg==", - "dev": true, - "requires": { - "@types/node": "*" + "node_modules/@babel/types": { + "version": "7.26.10", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.10.tgz", + "integrity": "sha512-emqcG3vHrpxUKTrxcblR36dcrcoRDvKmnL/dCL6ZsHaShW80qxCAcNhzQZrpeM765VzEos+xOi4s+r4IXzTwdQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" } }, - "@types/events": { - "version": "1.2.0", - "resolved": "http://registry.npmjs.org/@types/events/-/events-1.2.0.tgz", - "integrity": "sha512-KEIlhXnIutzKwRbQkGWb/I4HFqBuUykAdHgDED6xqwXJfONCjF5VoE0cXEiurh3XauygxzeDzgtXUqvLkxFzzA==", - "dev": true + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "license": "MIT" }, - "@types/express": { - "version": "4.17.0", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.0.tgz", - "integrity": "sha512-CjaMu57cjgjuZbh9DpkloeGxV45CnMGlVd+XpG7Gm9QgVrd7KFq+X4HY0vM+2v0bczS48Wg7bvnMY5TN+Xmcfw==", + "node_modules/@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", "dev": true, - "requires": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "*", - "@types/serve-static": "*" + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.1.90" } }, - "@types/express-serve-static-core": { - "version": "4.16.7", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.16.7.tgz", - "integrity": "sha512-847KvL8Q1y3TtFLRTXcVakErLJQgdpFSaq+k043xefz9raEf0C7HalpSY7OW5PyjCnY8P7bPW5t/Co9qqp+USg==", + "node_modules/@commitlint/config-validator": { + "version": "19.5.0", + "resolved": "https://registry.npmjs.org/@commitlint/config-validator/-/config-validator-19.5.0.tgz", + "integrity": "sha512-CHtj92H5rdhKt17RmgALhfQt95VayrUo2tSqY9g2w+laAXyk7K/Ef6uPm9tn5qSIwSmrLjKaXK9eiNuxmQrDBw==", "dev": true, - "requires": { - "@types/node": "*", - "@types/range-parser": "*" + "license": "MIT", + "optional": true, + "dependencies": { + "@commitlint/types": "^19.5.0", + "ajv": "^8.11.0" + }, + "engines": { + "node": ">=v18" } }, - "@types/form-data": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@types/form-data/-/form-data-2.2.1.tgz", - "integrity": "sha512-JAMFhOaHIciYVh8fb5/83nmuO/AHwmto+Hq7a9y8FzLDcC1KCU344XDOMEmahnrTFlHjgh4L0WJFczNIX2GxnQ==", + "node_modules/@commitlint/execute-rule": { + "version": "19.5.0", + "resolved": "https://registry.npmjs.org/@commitlint/execute-rule/-/execute-rule-19.5.0.tgz", + "integrity": "sha512-aqyGgytXhl2ejlk+/rfgtwpPexYyri4t8/n4ku6rRJoRhGZpLFMqrZ+YaubeGysCP6oz4mMA34YSTaSOKEeNrg==", "dev": true, - "requires": { - "@types/node": "*" + "license": "MIT", + "optional": true, + "engines": { + "node": ">=v18" } }, - "@types/glob": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.1.tgz", - "integrity": "sha512-1Bh06cbWJUHMC97acuD6UMG29nMt0Aqz1vF3guLfG+kHHJhy3AyohZFFxYk2f7Q1SQIrNwvncxAE0N/9s70F2w==", + "node_modules/@commitlint/load": { + "version": "19.6.1", + "resolved": "https://registry.npmjs.org/@commitlint/load/-/load-19.6.1.tgz", + "integrity": "sha512-kE4mRKWWNju2QpsCWt428XBvUH55OET2N4QKQ0bF85qS/XbsRGG1MiTByDNlEVpEPceMkDr46LNH95DtRwcsfA==", "dev": true, - "requires": { - "@types/events": "*", - "@types/minimatch": "*", - "@types/node": "*" + "license": "MIT", + "optional": true, + "dependencies": { + "@commitlint/config-validator": "^19.5.0", + "@commitlint/execute-rule": "^19.5.0", + "@commitlint/resolve-extends": "^19.5.0", + "@commitlint/types": "^19.5.0", + "chalk": "^5.3.0", + "cosmiconfig": "^9.0.0", + "cosmiconfig-typescript-loader": "^6.1.0", + "lodash.isplainobject": "^4.0.6", + "lodash.merge": "^4.6.2", + "lodash.uniq": "^4.5.0" + }, + "engines": { + "node": ">=v18" } }, - "@types/istanbul-lib-coverage": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.1.tgz", - "integrity": "sha512-hRJD2ahnnpLgsj6KWMYSrmXkM3rm2Dl1qkx6IOFD5FnuNPXJIG5L0dhgKXCYTRMGzU4n0wImQ/xfmRc4POUFlg==", - "dev": true + "node_modules/@commitlint/load/node_modules/chalk": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", + "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } }, - "@types/istanbul-lib-report": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-1.1.1.tgz", - "integrity": "sha512-3BUTyMzbZa2DtDI2BkERNC6jJw2Mr2Y0oGI7mRxYNBPxppbtEK1F66u3bKwU2g+wxwWI7PAoRpJnOY1grJqzHg==", + "node_modules/@commitlint/resolve-extends": { + "version": "19.5.0", + "resolved": "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-19.5.0.tgz", + "integrity": "sha512-CU/GscZhCUsJwcKTJS9Ndh3AKGZTNFIOoQB2n8CmFnizE0VnEuJoum+COW+C1lNABEeqk6ssfc1Kkalm4bDklA==", "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "*" + "license": "MIT", + "optional": true, + "dependencies": { + "@commitlint/config-validator": "^19.5.0", + "@commitlint/types": "^19.5.0", + "global-directory": "^4.0.1", + "import-meta-resolve": "^4.0.0", + "lodash.mergewith": "^4.6.2", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=v18" } }, - "@types/istanbul-reports": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-1.1.1.tgz", - "integrity": "sha512-UpYjBi8xefVChsCoBpKShdxTllC9pwISirfoZsUa2AAdQg/Jd2KQGtSbw+ya7GPo7x/wAPlH6JBhKhAsXUEZNA==", + "node_modules/@commitlint/types": { + "version": "19.5.0", + "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-19.5.0.tgz", + "integrity": "sha512-DSHae2obMSMkAtTBSOulg5X7/z+rGLxcXQIkg3OmWvY6wifojge5uVMydfhUvs7yQj+V7jNmRZ2Xzl8GJyqRgg==", "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "*", - "@types/istanbul-lib-report": "*" + "license": "MIT", + "optional": true, + "dependencies": { + "@types/conventional-commits-parser": "^5.0.0", + "chalk": "^5.3.0" + }, + "engines": { + "node": ">=v18" } }, - "@types/jest": { - "version": "24.0.15", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-24.0.15.tgz", - "integrity": "sha512-MU1HIvWUme74stAoc3mgAi+aMlgKOudgEvQDIm1v4RkrDudBh1T+NFp5sftpBAdXdx1J0PbdpJ+M2EsSOi1djA==", + "node_modules/@commitlint/types/node_modules/chalk": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", + "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==", "dev": true, - "requires": { - "@types/jest-diff": "*" + "license": "MIT", + "optional": true, + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "@types/jest-diff": { - "version": "20.0.1", - "resolved": "https://registry.npmjs.org/@types/jest-diff/-/jest-diff-20.0.1.tgz", - "integrity": "sha512-yALhelO3i0hqZwhjtcr6dYyaLoCHbAMshwtj6cGxTvHZAKXHsYGdff6E8EPw3xLKY0ELUTQ69Q1rQiJENnccMA==", - "dev": true + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } }, - "@types/mime": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-2.0.1.tgz", - "integrity": "sha512-FwI9gX75FgVBJ7ywgnq/P7tw+/o1GUbtP0KzbtusLigAOgIgNISRK0ZPl4qertvXSIE8YbsVJueQ90cDt9YYyw==", - "dev": true + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } }, - "@types/minimatch": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", - "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==", - "dev": true - }, - "@types/node": { - "version": "11.13.17", - "resolved": "https://registry.npmjs.org/@types/node/-/node-11.13.17.tgz", - "integrity": "sha512-7W3kSMa8diVH6s24a8Qrmvwu+vG3ahOC/flMHFdWSdnPYoQI0yPO84h5zOWYXAha2Npn3Pw3SSuQSwBUfaniyQ==", - "dev": true - }, - "@types/normalize-package-data": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz", - "integrity": "sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==", - "dev": true - }, - "@types/object-hash": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@types/object-hash/-/object-hash-1.3.0.tgz", - "integrity": "sha512-il4NIe4jTx4lfhkKaksmmGHw5EsVkO8sHWkpJHM9m59r1dtsVadLSrJqdE8zU74NENDAsR3oLIOlooRAXlPLNA==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/prettyjson": { - "version": "0.0.28", - "resolved": "http://registry.npmjs.org/@types/prettyjson/-/prettyjson-0.0.28.tgz", - "integrity": "sha1-ExqJDe1kIbG1RfRRCkrqvG1GUnU=", - "dev": true - }, - "@types/range-parser": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.3.tgz", - "integrity": "sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA==", - "dev": true - }, - "@types/request": { - "version": "2.48.1", - "resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.1.tgz", - "integrity": "sha512-ZgEZ1TiD+KGA9LiAAPPJL68Id2UWfeSO62ijSXZjFJArVV+2pKcsVHmrcu+1oiE3q6eDGiFiSolRc4JHoerBBg==", - "dev": true, - "requires": { - "@types/caseless": "*", - "@types/form-data": "*", - "@types/node": "*", - "@types/tough-cookie": "*" - } - }, - "@types/require-from-string": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@types/require-from-string/-/require-from-string-1.2.0.tgz", - "integrity": "sha512-5vE9WoOOC9/DoD3Zj53UISpM+5tSvh8k0mL4fe2zFI6vO715/W4IQ3EdVUrWVMrFi1/NZhyMvm2iKsDFkEGddQ==", - "dev": true - }, - "@types/retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", - "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==", - "dev": true - }, - "@types/serve-static": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.2.tgz", - "integrity": "sha512-/BZ4QRLpH/bNYgZgwhKEh+5AsboDBcUdlBYgzoLX0fpj3Y2gp6EApyOlM3bK53wQS/OE1SrdSYBAbux2D1528Q==", - "dev": true, - "requires": { - "@types/express-serve-static-core": "*", - "@types/mime": "*" - } - }, - "@types/stack-utils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-1.0.1.tgz", - "integrity": "sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw==", - "dev": true - }, - "@types/tough-cookie": { - "version": "2.3.4", - "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-2.3.4.tgz", - "integrity": "sha512-Set5ZdrAaKI/qHdFlVMgm/GsAv/wkXhSTuZFkJ+JI7HK+wIkIlOaUXSXieIvJ0+OvGIqtREFoE+NHJtEq0gtEw==", - "dev": true - }, - "@types/yamljs": { - "version": "0.2.30", - "resolved": "https://registry.npmjs.org/@types/yamljs/-/yamljs-0.2.30.tgz", - "integrity": "sha1-0DTh0ynkbo0Pc3yajbl/aPgbU4I=", - "dev": true - }, - "@types/yargs": { - "version": "12.0.12", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-12.0.12.tgz", - "integrity": "sha512-SOhuU4wNBxhhTHxYaiG5NY4HBhDIDnJF60GU+2LqHAdKKer86//e4yg69aENCtQ04n0ovz+tq2YPME5t5yp4pw==", - "dev": true - }, - "JSONStream": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", - "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", - "dev": true, - "requires": { - "jsonparse": "^1.2.0", - "through": ">=2.2.7 <3" - } - }, - "abab": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.0.tgz", - "integrity": "sha512-sY5AXXVZv4Y1VACTtR11UJCPHHudgY5i26Qj5TypE6DKlIApbwb5uqhXcJ5UUGbvZNRh7EeIoW+LrJumBsKp7w==", - "dev": true - }, - "accepts": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", - "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", - "requires": { - "mime-types": "~2.1.24", - "negotiator": "0.6.2" - }, - "dependencies": { - "mime-db": { - "version": "1.40.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", - "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==" - }, - "mime-types": { - "version": "2.1.24", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", - "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", - "requires": { - "mime-db": "1.40.0" - } - } + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" } }, - "acorn": { - "version": "5.7.3", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz", - "integrity": "sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==", - "dev": true - }, - "acorn-globals": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-4.3.2.tgz", - "integrity": "sha512-BbzvZhVtZP+Bs1J1HcwrQe8ycfO0wStkSGxuul3He3GkHOIZ6eTqOkPuw9IP1X3+IkOo4wiJmwkobzXYz4wewQ==", - "dev": true, - "requires": { - "acorn": "^6.0.1", - "acorn-walk": "^6.0.1" + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "license": "MIT", + "engines": { + "node": ">=12" }, - "dependencies": { - "acorn": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.1.1.tgz", - "integrity": "sha512-jPTiwtOxaHNaAPg/dmrJ/beuzLRnXtB0kQPQ8JpotKJgTB6rX6c8mlf315941pyjBSaPg8NHXS9fhP4u17DpGA==", - "dev": true - } + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "acorn-walk": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-6.1.1.tgz", - "integrity": "sha512-OtUw6JUTgxA2QoqqmrmQ7F2NYqiBPi/L2jqHyFtllhOUvXYQXf0Z1CYUinIfyT4bTCGmrA7gX9FvHA81uzCoVw==", - "dev": true - }, - "agent-base": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", - "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", - "dev": true, - "requires": { - "es6-promisify": "^5.0.0" - } + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "license": "MIT" }, - "aggregate-error": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.0.0.tgz", - "integrity": "sha512-yKD9kEoJIR+2IFqhMwayIBgheLYbB3PS2OBhWae1L/ODTd/JF/30cW0bc9TqzRL3k4U41Dieu3BF4I29p8xesA==", - "dev": true, - "requires": { - "clean-stack": "^2.0.0", - "indent-string": "^3.2.0" + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "ajv": { - "version": "6.9.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.9.2.tgz", - "integrity": "sha512-4UFy0/LgDo7Oa/+wOAlj44tp9K78u38E5/359eSrqEp1Z5PdVfimCcs7SluXMP755RUQu6d2b4AvF0R1C9RZjg==", - "requires": { - "fast-deep-equal": "^2.0.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "ansi-escapes": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", - "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", - "dev": true - }, - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "requires": { - "color-convert": "^1.9.0" + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "ansicolors": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/ansicolors/-/ansicolors-0.3.2.tgz", - "integrity": "sha1-ZlWX3oap/+Oqm/vmyuXG6kJrSXk=", - "dev": true - }, - "anymatch": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", - "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", - "dev": true, - "requires": { - "micromatch": "^3.1.4", - "normalize-path": "^2.1.1" + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "license": "ISC", + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" } }, - "argparse": { + "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "requires": { + "license": "MIT", + "dependencies": { "sprintf-js": "~1.0.2" } }, - "argv-formatter": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/argv-formatter/-/argv-formatter-1.0.0.tgz", - "integrity": "sha1-oMoMvCmltz6Dbuvhy/bF4OTrgvk=", - "dev": true - }, - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true - }, - "arr-flatten": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", - "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", - "dev": true - }, - "arr-union": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", - "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", - "dev": true - }, - "array-equal": { - "version": "1.0.0", - "resolved": "http://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz", - "integrity": "sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=", - "dev": true - }, - "array-find-index": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", - "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=", - "dev": true - }, - "array-flatten": { - "version": "1.1.1", - "resolved": "http://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" - }, - "array-ify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-ify/-/array-ify-1.0.0.tgz", - "integrity": "sha1-nlKHYrSpBmrRY6aWKjZEGOlibs4=", - "dev": true - }, - "array-union": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", - "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", - "dev": true, - "requires": { - "array-uniq": "^1.0.1" + "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" } }, - "array-uniq": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", - "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", - "dev": true - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true - }, - "arrify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", - "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", - "dev": true - }, - "asn1": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", - "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", - "requires": { - "safer-buffer": "~2.1.0" + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/console": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", + "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" - }, - "assign-symbols": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", - "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", - "dev": true - }, - "astral-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", - "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", - "dev": true - }, - "async-limiter": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz", - "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==", - "dev": true - }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" - }, - "atob": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", - "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", - "dev": true - }, - "atob-lite": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/atob-lite/-/atob-lite-2.0.0.tgz", - "integrity": "sha1-D+9a1G8b16hQLGVyfwNn1e5D1pY=", - "dev": true + "node_modules/@jest/core": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", + "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", + "license": "MIT", + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/reporters": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-changed-files": "^29.7.0", + "jest-config": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-resolve-dependencies": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "jest-watcher": "^29.7.0", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } }, - "aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" - }, - "aws4": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", - "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==" - }, - "babel-jest": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-24.8.0.tgz", - "integrity": "sha512-+5/kaZt4I9efoXzPlZASyK/lN9qdRKmmUav9smVc0ruPQD7IsfucQ87gpOE8mn2jbDuS6M/YOW6n3v9ZoIfgnw==", - "dev": true, - "requires": { - "@jest/transform": "^24.8.0", - "@jest/types": "^24.8.0", - "@types/babel__core": "^7.1.0", - "babel-plugin-istanbul": "^5.1.0", - "babel-preset-jest": "^24.6.0", - "chalk": "^2.4.2", - "slash": "^2.0.0" - } - }, - "babel-plugin-istanbul": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-5.1.4.tgz", - "integrity": "sha512-dySz4VJMH+dpndj0wjJ8JPs/7i1TdSPb1nRrn56/92pKOF9VKC1FMFJmMXjzlGGusnCAqujP6PBCiKq0sVA+YQ==", - "dev": true, - "requires": { - "find-up": "^3.0.0", - "istanbul-lib-instrument": "^3.3.0", - "test-exclude": "^5.2.3" - } - }, - "babel-plugin-jest-hoist": { - "version": "24.6.0", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-24.6.0.tgz", - "integrity": "sha512-3pKNH6hMt9SbOv0F3WVmy5CWQ4uogS3k0GY5XLyQHJ9EGpAT9XWkFd2ZiXXtkwFHdAHa5j7w7kfxSP5lAIwu7w==", - "dev": true, - "requires": { - "@types/babel__traverse": "^7.0.6" + "node_modules/@jest/environment": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", + "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", + "license": "MIT", + "dependencies": { + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "babel-preset-jest": { - "version": "24.6.0", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-24.6.0.tgz", - "integrity": "sha512-pdZqLEdmy1ZK5kyRUfvBb2IfTPb2BUvIJczlPspS8fWmBQslNNDBqVfh7BW5leOVJMDZKzjD8XEyABTk6gQ5yw==", - "dev": true, - "requires": { - "@babel/plugin-syntax-object-rest-spread": "^7.0.0", - "babel-plugin-jest-hoist": "^24.6.0" + "node_modules/@jest/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", + "license": "MIT", + "dependencies": { + "expect": "^29.7.0", + "jest-snapshot": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" - }, - "base": { - "version": "0.11.2", - "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", - "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", - "dev": true, - "requires": { - "cache-base": "^1.0.1", - "class-utils": "^0.3.5", - "component-emitter": "^1.2.1", - "define-property": "^1.0.0", - "isobject": "^3.0.1", - "mixin-deep": "^1.2.0", - "pascalcase": "^0.1.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } + "node_modules/@jest/expect-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", + "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", + "license": "MIT", + "dependencies": { + "jest-get-type": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", - "requires": { - "tweetnacl": "^0.14.3" + "node_modules/@jest/fake-timers": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", + "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@sinonjs/fake-timers": "^10.0.2", + "@types/node": "*", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "before-after-hook": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-1.4.0.tgz", - "integrity": "sha512-l5r9ir56nda3qu14nAXIlyq1MmUSs0meCIaFAh8HwkFwP1F8eToOuS3ah2VAHHcY04jaYD7FpJC5JTXHYRbkzg==", - "dev": true - }, - "body-parser": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", - "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", - "requires": { - "bytes": "3.1.0", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "~1.1.2", - "http-errors": "1.7.2", - "iconv-lite": "0.4.24", - "on-finished": "~2.3.0", - "qs": "6.7.0", - "raw-body": "2.4.0", - "type-is": "~1.6.17" + "node_modules/@jest/globals": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", + "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/types": "^29.6.3", + "jest-mock": "^29.7.0" }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/reporters": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", + "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", + "license": "MIT", "dependencies": { - "qs": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", - "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^6.0.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "slash": "^3.0.0", + "string-length": "^4.0.1", + "strip-ansi": "^6.0.0", + "v8-to-istanbul": "^9.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true } } }, - "bottleneck": { - "version": "2.19.1", - "resolved": "https://registry.npmjs.org/bottleneck/-/bottleneck-2.19.1.tgz", - "integrity": "sha512-TrdmthuOG5SpYfFQTprSQOtqQUTreAgPkCFfow/aVOnryRRpIwiwfiQmmJUiiHUKgPV7LBNlfGecJ5hCVs30gw==", - "dev": true - }, - "brace-expansion": { + "node_modules/@jest/reporters/node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "requires": { + "license": "MIT", + "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } + "node_modules/@jest/reporters/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "browser-process-hrtime": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-0.1.3.tgz", - "integrity": "sha512-bRFnI4NnjO6cnyLmOV/7PVoDEMJChlcfN0z4s1YMBY989/SvlfMI1lgCnkFUs53e9gQF+w7qu7XdllSTiSl8Aw==", - "dev": true + "node_modules/@jest/reporters/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } }, - "browser-resolve": { - "version": "1.11.3", - "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-1.11.3.tgz", - "integrity": "sha512-exDi1BYWB/6raKHmDTCicQfTkqwN5fioMFV4j8BsfMU4R2DK/QfZfK7kOVkmWCNANf0snkBzqGqAJBao9gZMdQ==", - "dev": true, - "requires": { - "resolve": "1.1.7" + "node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "license": "MIT", + "dependencies": { + "@sinclair/typebox": "^0.27.8" }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/source-map": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", + "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", + "license": "MIT", "dependencies": { - "resolve": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", - "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", - "dev": true - } + "@jridgewell/trace-mapping": "^0.3.18", + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "bs-logger": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", - "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", - "dev": true, - "requires": { - "fast-json-stable-stringify": "2.x" + "node_modules/@jest/test-result": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", + "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", + "license": "MIT", + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "bser": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.0.tgz", - "integrity": "sha512-8zsjWrQkkBoLK6uxASk1nJ2SKv97ltiGDo6A3wA0/yRPz+CwmEyDo0hUrhIuukG2JHpAl3bvFIixw2/3Hi0DOg==", - "dev": true, - "requires": { - "node-int64": "^0.4.0" + "node_modules/@jest/test-sequencer": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", + "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", + "license": "MIT", + "dependencies": { + "@jest/test-result": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "btoa-lite": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/btoa-lite/-/btoa-lite-1.0.0.tgz", - "integrity": "sha1-M3dm2hWAEhD92VbCLpxokaudAzc=", - "dev": true + "node_modules/@jest/transform": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", + "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", + "license": "MIT", + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.2" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } }, - "buffer-from": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", - "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", - "dev": true + "node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } }, - "builtin-modules": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", - "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", - "dev": true + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", + "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", + "license": "MIT", + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } }, - "bytes": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", - "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } }, - "cache-base": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", - "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", - "dev": true, - "requires": { - "collection-visit": "^1.0.0", - "component-emitter": "^1.2.1", - "get-value": "^2.0.6", - "has-value": "^1.0.0", - "isobject": "^3.0.1", - "set-value": "^2.0.0", - "to-object-path": "^0.3.0", - "union-value": "^1.0.0", - "unset-value": "^1.0.0" - } - }, - "cachedir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cachedir/-/cachedir-2.1.0.tgz", - "integrity": "sha512-xGBpPqoBvn3unBW7oxgb8aJn42K0m9m1/wyjmazah10Fq7bROGG3kRAE6OIyr3U3PIJUqGuebhCEdMk9OKJG0A==", - "dev": true + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } }, - "call-me-maybe": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.1.tgz", - "integrity": "sha1-JtII6onje1y95gJQoV8DHBak1ms=", - "dev": true + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } }, - "caller-callsite": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz", - "integrity": "sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ=", + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", "dev": true, - "requires": { - "callsites": "^2.0.0" - }, + "license": "MIT", "dependencies": { - "callsites": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", - "integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=", - "dev": true - } + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" } }, - "caller-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz", - "integrity": "sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ=", + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", "dev": true, - "requires": { - "caller-callsite": "^2.0.0" + "license": "MIT", + "engines": { + "node": ">= 8" } }, - "callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } }, - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true + "node_modules/@octokit/auth-token": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-5.1.1.tgz", + "integrity": "sha512-rh3G3wDO8J9wSjfI436JUKzHIxq8NaiL0tVeB2aXmG6p/9859aUOAjA9pmSPNGGZxfwmaJ9ozOJImuNVJdpvbA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 18" + } }, - "camelcase-keys": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-4.2.0.tgz", - "integrity": "sha1-oqpfsa9oh1glnDLBQUJteJI7m3c=", + "node_modules/@octokit/core": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-6.1.3.tgz", + "integrity": "sha512-z+j7DixNnfpdToYsOutStDgeRzJSMnbj8T1C/oQjB6Aa+kRfNjs/Fn7W6c8bmlt6mfy3FkgeKBRnDjxQow5dow==", "dev": true, - "requires": { - "camelcase": "^4.1.0", - "map-obj": "^2.0.0", - "quick-lru": "^1.0.0" - }, + "license": "MIT", "dependencies": { - "camelcase": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", - "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", - "dev": true - } + "@octokit/auth-token": "^5.0.0", + "@octokit/graphql": "^8.1.2", + "@octokit/request": "^9.1.4", + "@octokit/request-error": "^6.1.6", + "@octokit/types": "^13.6.2", + "before-after-hook": "^3.0.2", + "universal-user-agent": "^7.0.0" + }, + "engines": { + "node": ">= 18" } }, - "capture-exit": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/capture-exit/-/capture-exit-2.0.0.tgz", - "integrity": "sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g==", + "node_modules/@octokit/endpoint": { + "version": "10.1.2", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-10.1.2.tgz", + "integrity": "sha512-XybpFv9Ms4hX5OCHMZqyODYqGTZ3H6K6Vva+M9LR7ib/xr1y1ZnlChYv9H680y77Vd/i/k+thXApeRASBQkzhA==", "dev": true, - "requires": { - "rsvp": "^4.8.4" + "license": "MIT", + "dependencies": { + "@octokit/types": "^13.6.2", + "universal-user-agent": "^7.0.2" + }, + "engines": { + "node": ">= 18" } }, - "cardinal": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/cardinal/-/cardinal-2.1.1.tgz", - "integrity": "sha1-fMEFXYItISlU0HsIXeolHMe8VQU=", + "node_modules/@octokit/graphql": { + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-8.1.2.tgz", + "integrity": "sha512-bdlj/CJVjpaz06NBpfHhp4kGJaRZfz7AzC+6EwUImRtrwIw8dIgJ63Xg0OzV9pRn3rIzrt5c2sa++BL0JJ8GLw==", "dev": true, - "requires": { - "ansicolors": "~0.3.2", - "redeyed": "~2.1.0" + "license": "MIT", + "dependencies": { + "@octokit/request": "^9.1.4", + "@octokit/types": "^13.6.2", + "universal-user-agent": "^7.0.0" + }, + "engines": { + "node": ">= 18" } }, - "caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" + "node_modules/@octokit/openapi-types": { + "version": "23.0.1", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-23.0.1.tgz", + "integrity": "sha512-izFjMJ1sir0jn0ldEKhZ7xegCTj/ObmEDlEfpFrx4k/JyZSMRHbO3/rBwgE7f3m2DHt+RrNGIVw4wSmwnm3t/g==", + "dev": true, + "license": "MIT" }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "node_modules/@octokit/plugin-paginate-rest": { + "version": "11.4.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-11.4.0.tgz", + "integrity": "sha512-ttpGck5AYWkwMkMazNCZMqxKqIq1fJBNxBfsFwwfyYKTf914jKkLF0POMS3YkPBwp5g1c2Y4L79gDz01GhSr1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/types": "^13.7.0" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@octokit/core": ">=6" } }, - "chardet": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", - "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", - "dev": true - }, - "ci-info": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", - "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", - "dev": true - }, - "class-utils": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", - "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", - "dev": true, - "requires": { - "arr-union": "^3.1.0", - "define-property": "^0.2.5", - "isobject": "^3.0.0", - "static-extend": "^0.1.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - } + "node_modules/@octokit/plugin-retry": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/@octokit/plugin-retry/-/plugin-retry-7.1.3.tgz", + "integrity": "sha512-8nKOXvYWnzv89gSyIvgFHmCBAxfQAOPRlkacUHL9r5oWtp5Whxl8Skb2n3ACZd+X6cYijD6uvmrQuPH/UCL5zQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/request-error": "^6.1.6", + "@octokit/types": "^13.6.2", + "bottleneck": "^2.15.3" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@octokit/core": ">=6" } }, - "clean-stack": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.1.0.tgz", - "integrity": "sha512-uQWrpRm+iZZUCAp7ZZJQbd4Za9I3AjR/3YTjmcnAtkauaIm/T5CT6U8zVI6e60T6OANqBFAzuR9/HB3NzuZCRA==", - "dev": true - }, - "cli-cursor": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", - "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", + "node_modules/@octokit/plugin-throttling": { + "version": "9.4.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-throttling/-/plugin-throttling-9.4.0.tgz", + "integrity": "sha512-IOlXxXhZA4Z3m0EEYtrrACkuHiArHLZ3CvqWwOez/pURNqRuwfoFlTPbN5Muf28pzFuztxPyiUiNwz8KctdZaQ==", "dev": true, - "requires": { - "restore-cursor": "^2.0.0" + "license": "MIT", + "dependencies": { + "@octokit/types": "^13.7.0", + "bottleneck": "^2.15.3" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@octokit/core": "^6.1.3" } }, - "cli-table": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/cli-table/-/cli-table-0.3.1.tgz", - "integrity": "sha1-9TsFJmqLGguTSz0IIebi3FkUriM=", + "node_modules/@octokit/request": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-9.2.0.tgz", + "integrity": "sha512-kXLfcxhC4ozCnAXy2ff+cSxpcF0A1UqxjvYMqNuPIeOAzJbVWQ+dy5G2fTylofB/gTbObT8O6JORab+5XtA1Kw==", "dev": true, - "requires": { - "colors": "1.0.3" - }, + "license": "MIT", "dependencies": { - "colors": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", - "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=", - "dev": true - } + "@octokit/endpoint": "^10.0.0", + "@octokit/request-error": "^6.0.1", + "@octokit/types": "^13.6.2", + "fast-content-type-parse": "^2.0.0", + "universal-user-agent": "^7.0.2" + }, + "engines": { + "node": ">= 18" } }, - "cli-width": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", - "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=", - "dev": true - }, - "cliui": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", - "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", + "node_modules/@octokit/request-error": { + "version": "6.1.6", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-6.1.6.tgz", + "integrity": "sha512-pqnVKYo/at0NuOjinrgcQYpEbv4snvP3bKMRqHaD9kIsk9u1LCpb2smHZi8/qJfgeNqLo5hNW4Z7FezNdEo0xg==", "dev": true, - "requires": { - "string-width": "^2.1.1", - "strip-ansi": "^4.0.0", - "wrap-ansi": "^2.0.0" - }, + "license": "MIT", "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - } + "@octokit/types": "^13.6.2" + }, + "engines": { + "node": ">= 18" } }, - "co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", - "dev": true + "node_modules/@octokit/types": { + "version": "13.7.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.7.0.tgz", + "integrity": "sha512-BXfRP+3P3IN6fd4uF3SniaHKOO4UXWBfkdR3vA8mIvaoO/wLjGN5qivUtW0QRitBHHMcfC41SLhNVYIZZE+wkA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^23.0.1" + } }, - "code-point-at": { + "node_modules/@pnpm/config.env-replace": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "dev": true - }, - "collection-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", - "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "resolved": "https://registry.npmjs.org/@pnpm/config.env-replace/-/config.env-replace-1.1.0.tgz", + "integrity": "sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w==", "dev": true, - "requires": { - "map-visit": "^1.0.0", - "object-visit": "^1.0.0" + "license": "MIT", + "engines": { + "node": ">=12.22.0" } }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "requires": { - "color-name": "1.1.3" + "node_modules/@pnpm/network.ca-file": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@pnpm/network.ca-file/-/network.ca-file-1.0.2.tgz", + "integrity": "sha512-YcPQ8a0jwYU9bTdJDpXjMi7Brhkr1mXsXrUJvjqM2mQDgkRiz8jFaQGOdaLxgjtUfQgZhKy/O3cG/YwmgKaxLA==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "4.2.10" + }, + "engines": { + "node": ">=12.22.0" } }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" - }, - "colors": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.3.3.tgz", - "integrity": "sha512-mmGt/1pZqYRjMxB1axhTo16/snVZ5krrKkcmMeVKxzECMMXoCgnvTPp10QgHfcbQZw8Dq2jMNG6je4JlWU0gWg==", - "optional": true + "node_modules/@pnpm/network.ca-file/node_modules/graceful-fs": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "dev": true, + "license": "ISC" }, - "combined-stream": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.7.tgz", - "integrity": "sha512-brWl9y6vOB1xYPZcpZde3N9zDByXTosAeMDo4p1wzo6UMOX4vumB+TP1RZ76sfE6Md68Q0NJSrE/gbezd4Ul+w==", - "requires": { - "delayed-stream": "~1.0.0" + "node_modules/@pnpm/npm-conf": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@pnpm/npm-conf/-/npm-conf-2.3.1.tgz", + "integrity": "sha512-c83qWb22rNRuB0UaVCI0uRPNRr8Z0FWnEIvT47jiHAmOIUHbBOg5XvV7pM5x+rKn9HRpjxquDbXYSXr3fAKFcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@pnpm/config.env-replace": "^1.1.0", + "@pnpm/network.ca-file": "^1.0.1", + "config-chain": "^1.1.11" + }, + "engines": { + "node": ">=12" } }, - "commander": { - "version": "2.20.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz", - "integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==" + "node_modules/@sec-ant/readable-stream": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@sec-ant/readable-stream/-/readable-stream-0.4.1.tgz", + "integrity": "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==", + "dev": true, + "license": "MIT" }, - "commitizen": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/commitizen/-/commitizen-3.1.1.tgz", - "integrity": "sha512-n5pnG8sNM5a3dS3Kkh3rYr+hFdPWZlqV6pfz6KGLmWV/gsIiTqAwhTgFKkcF/paKUpfIMp0x4YZlD0xLBNTW9g==", + "node_modules/@semantic-release/commit-analyzer": { + "version": "13.0.1", + "resolved": "https://registry.npmjs.org/@semantic-release/commit-analyzer/-/commit-analyzer-13.0.1.tgz", + "integrity": "sha512-wdnBPHKkr9HhNhXOhZD5a2LNl91+hs8CC2vsAVYxtZH3y0dV3wKn+uZSN61rdJQZ8EGxzWB3inWocBHV9+u/CQ==", "dev": true, - "requires": { - "cachedir": "2.1.0", - "cz-conventional-changelog": "2.1.0", - "dedent": "0.7.0", - "detect-indent": "^5.0.0", - "find-node-modules": "2.0.0", - "find-root": "1.1.0", - "fs-extra": "^7.0.0", - "glob": "7.1.3", - "inquirer": "6.2.0", - "is-utf8": "^0.2.1", - "lodash": "4.17.11", - "minimist": "1.2.0", - "shelljs": "0.7.6", - "strip-bom": "3.0.0", - "strip-json-comments": "2.0.1" - }, - "dependencies": { - "glob": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", - "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", - "dev": true, - "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" - } - }, - "lodash": { - "version": "4.17.11", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", - "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==", - "dev": true - }, - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true - }, - "shelljs": { - "version": "0.7.6", - "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.7.6.tgz", - "integrity": "sha1-N5zM+1a5HIYB5HkzVutTgpJN6a0=", - "dev": true, - "requires": { - "glob": "^7.0.0", - "interpret": "^1.0.0", - "rechoir": "^0.6.2" - } - } + "license": "MIT", + "dependencies": { + "conventional-changelog-angular": "^8.0.0", + "conventional-changelog-writer": "^8.0.0", + "conventional-commits-filter": "^5.0.0", + "conventional-commits-parser": "^6.0.0", + "debug": "^4.0.0", + "import-from-esm": "^2.0.0", + "lodash-es": "^4.17.21", + "micromatch": "^4.0.2" + }, + "engines": { + "node": ">=20.8.1" + }, + "peerDependencies": { + "semantic-release": ">=20.1.0" } }, - "compare-func": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/compare-func/-/compare-func-1.3.2.tgz", - "integrity": "sha1-md0LpFfh+bxyKxLAjsM+6rMfpkg=", + "node_modules/@semantic-release/commit-analyzer/node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", "dev": true, - "requires": { - "array-ify": "^1.0.0", - "dot-prop": "^3.0.0" + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "component-emitter": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", - "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", - "dev": true - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + "node_modules/@semantic-release/commit-analyzer/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, + "license": "MIT" }, - "content-disposition": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", - "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", - "requires": { - "safe-buffer": "5.1.2" + "node_modules/@semantic-release/error": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@semantic-release/error/-/error-4.0.0.tgz", + "integrity": "sha512-mgdxrHTLOjOddRVYIYDo0fR3/v61GNN1YGkfbrjuIKg/uMgCd+Qzo3UAXJ+woLQQpos4pl5Esuw5A7AoNlzjUQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" } }, - "content-type": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" + "node_modules/@semantic-release/github": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/@semantic-release/github/-/github-11.0.1.tgz", + "integrity": "sha512-Z9cr0LgU/zgucbT9cksH0/pX9zmVda9hkDPcgIE0uvjMQ8w/mElDivGjx1w1pEQ+MuQJ5CBq3VCF16S6G4VH3A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/core": "^6.0.0", + "@octokit/plugin-paginate-rest": "^11.0.0", + "@octokit/plugin-retry": "^7.0.0", + "@octokit/plugin-throttling": "^9.0.0", + "@semantic-release/error": "^4.0.0", + "aggregate-error": "^5.0.0", + "debug": "^4.3.4", + "dir-glob": "^3.0.1", + "globby": "^14.0.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.0", + "issue-parser": "^7.0.0", + "lodash-es": "^4.17.21", + "mime": "^4.0.0", + "p-filter": "^4.0.0", + "url-join": "^5.0.0" + }, + "engines": { + "node": ">=20.8.1" + }, + "peerDependencies": { + "semantic-release": ">=24.1.0" + } }, - "conventional-changelog-angular": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-5.0.3.tgz", - "integrity": "sha512-YD1xzH7r9yXQte/HF9JBuEDfvjxxwDGGwZU1+ndanbY0oFgA+Po1T9JDSpPLdP0pZT6MhCAsdvFKC4TJ4MTJTA==", + "node_modules/@semantic-release/github/node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", "dev": true, - "requires": { - "compare-func": "^1.3.1", - "q": "^1.5.1" + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "conventional-changelog-writer": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-4.0.3.tgz", - "integrity": "sha512-bIlpSiQtQZ1+nDVHEEh798Erj2jhN/wEjyw9sfxY9es6h7pREE5BNJjfv0hXGH/FTrAsEpHUq4xzK99eePpwuA==", - "dev": true, - "requires": { - "compare-func": "^1.3.1", - "conventional-commits-filter": "^2.0.1", - "dateformat": "^3.0.0", - "handlebars": "^4.1.0", - "json-stringify-safe": "^5.0.1", - "lodash": "^4.2.1", - "meow": "^4.0.0", - "semver": "^5.5.0", - "split": "^1.0.0", - "through2": "^2.0.0" - } - }, - "conventional-commit-types": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/conventional-commit-types/-/conventional-commit-types-2.2.0.tgz", - "integrity": "sha1-XblXOdbCEqy+e29lahG5QLqmiUY=", - "dev": true + "node_modules/@semantic-release/github/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, + "license": "MIT" }, - "conventional-commits-filter": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/conventional-commits-filter/-/conventional-commits-filter-2.0.1.tgz", - "integrity": "sha512-92OU8pz/977udhBjgPEbg3sbYzIxMDFTlQT97w7KdhR9igNqdJvy8smmedAAgn4tPiqseFloKkrVfbXCVd+E7A==", + "node_modules/@semantic-release/npm": { + "version": "12.0.1", + "resolved": "https://registry.npmjs.org/@semantic-release/npm/-/npm-12.0.1.tgz", + "integrity": "sha512-/6nntGSUGK2aTOI0rHPwY3ZjgY9FkXmEHbW9Kr+62NVOsyqpKKeP0lrCH+tphv+EsNdJNmqqwijTEnVWUMQ2Nw==", "dev": true, - "requires": { - "is-subset": "^0.1.1", - "modify-values": "^1.0.0" + "license": "MIT", + "dependencies": { + "@semantic-release/error": "^4.0.0", + "aggregate-error": "^5.0.0", + "execa": "^9.0.0", + "fs-extra": "^11.0.0", + "lodash-es": "^4.17.21", + "nerf-dart": "^1.0.0", + "normalize-url": "^8.0.0", + "npm": "^10.5.0", + "rc": "^1.2.8", + "read-pkg": "^9.0.0", + "registry-auth-token": "^5.0.0", + "semver": "^7.1.2", + "tempy": "^3.0.0" + }, + "engines": { + "node": ">=20.8.1" + }, + "peerDependencies": { + "semantic-release": ">=20.1.0" } }, - "conventional-commits-parser": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-3.0.1.tgz", - "integrity": "sha512-P6U5UOvDeidUJ8ebHVDIoXzI7gMlQ1OF/id6oUvp8cnZvOXMt1n8nYl74Ey9YMn0uVQtxmCtjPQawpsssBWtGg==", + "node_modules/@semantic-release/npm/node_modules/@sindresorhus/merge-streams": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-4.0.0.tgz", + "integrity": "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==", "dev": true, - "requires": { - "JSONStream": "^1.0.4", - "is-text-path": "^1.0.0", - "lodash": "^4.2.1", - "meow": "^4.0.0", - "split2": "^2.0.0", - "through2": "^2.0.0", - "trim-off-newlines": "^1.0.0" + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "convert-source-map": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.6.0.tgz", - "integrity": "sha512-eFu7XigvxdZ1ETfbgPBohgyQ/Z++C0eEhTor0qRwBw9unw+L0/6V8wkSuGgzdThkiS5lSpdptOQPD8Ak40a+7A==", + "node_modules/@semantic-release/npm/node_modules/execa": { + "version": "9.5.2", + "resolved": "https://registry.npmjs.org/execa/-/execa-9.5.2.tgz", + "integrity": "sha512-EHlpxMCpHWSAh1dgS6bVeoLAXGnJNdR93aabr4QCGbzOM73o5XmRfM/e5FUqsw3aagP8S8XEWUWFAxnRBnAF0Q==", "dev": true, - "requires": { - "safe-buffer": "~5.1.1" + "license": "MIT", + "dependencies": { + "@sindresorhus/merge-streams": "^4.0.0", + "cross-spawn": "^7.0.3", + "figures": "^6.1.0", + "get-stream": "^9.0.0", + "human-signals": "^8.0.0", + "is-plain-obj": "^4.1.0", + "is-stream": "^4.0.1", + "npm-run-path": "^6.0.0", + "pretty-ms": "^9.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^4.0.0", + "yoctocolors": "^2.0.0" + }, + "engines": { + "node": "^18.19.0 || >=20.5.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, - "cookie": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", - "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" - }, - "cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" - }, - "copy-descriptor": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", - "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", - "dev": true - }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" - }, - "cosmiconfig": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz", - "integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==", + "node_modules/@semantic-release/npm/node_modules/figures": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-6.1.0.tgz", + "integrity": "sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==", "dev": true, - "requires": { - "import-fresh": "^2.0.0", - "is-directory": "^0.3.1", - "js-yaml": "^3.13.1", - "parse-json": "^4.0.0" + "license": "MIT", + "dependencies": { + "is-unicode-supported": "^2.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "node_modules/@semantic-release/npm/node_modules/fs-extra": { + "version": "11.3.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.0.tgz", + "integrity": "sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==", "dev": true, - "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" } }, - "crypto-random-string": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-1.0.0.tgz", - "integrity": "sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4=", - "dev": true - }, - "cssom": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.6.tgz", - "integrity": "sha512-DtUeseGk9/GBW0hl0vVPpU22iHL6YB5BUX7ml1hB+GMpo0NX5G4voX3kdWiMSEguFtcW3Vh3djqNF4aIe6ne0A==", - "dev": true + "node_modules/@semantic-release/npm/node_modules/get-stream": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-9.0.1.tgz", + "integrity": "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sec-ant/readable-stream": "^0.4.1", + "is-stream": "^4.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "cssstyle": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-1.2.2.tgz", - "integrity": "sha512-43wY3kl1CVQSvL7wUY1qXkxVGkStjpkDmVjiIKX8R97uhajy8Bybay78uOtqvh7Q5GK75dNPfW0geWjE6qQQow==", + "node_modules/@semantic-release/npm/node_modules/human-signals": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-8.0.0.tgz", + "integrity": "sha512-/1/GPCpDUCCYwlERiYjxoczfP0zfvZMU/OWgQPMya9AbAE24vseigFdhAMObpc8Q4lc/kjutPfUddDYyAmejnA==", "dev": true, - "requires": { - "cssom": "0.3.x" + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" } }, - "currently-unhandled": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", - "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", + "node_modules/@semantic-release/npm/node_modules/is-stream": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-4.0.1.tgz", + "integrity": "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==", "dev": true, - "requires": { - "array-find-index": "^1.0.1" + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "cz-conventional-changelog": { + "node_modules/@semantic-release/npm/node_modules/is-unicode-supported": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cz-conventional-changelog/-/cz-conventional-changelog-2.1.0.tgz", - "integrity": "sha1-L0vHOQ4yROTfKT5ro1Hkx0Cnx2Q=", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", + "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", "dev": true, - "requires": { - "conventional-commit-types": "^2.0.0", - "lodash.map": "^4.5.1", - "longest": "^1.0.1", - "right-pad": "^1.0.1", - "word-wrap": "^1.0.3" + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", - "requires": { - "assert-plus": "^1.0.0" + "node_modules/@semantic-release/npm/node_modules/npm-run-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-6.0.0.tgz", + "integrity": "sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^4.0.0", + "unicorn-magic": "^0.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "data-urls": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-1.1.0.tgz", - "integrity": "sha512-YTWYI9se1P55u58gL5GkQHW4P6VJBJ5iBT+B5a7i2Tjadhv52paJG0qHX4A0OR6/t52odI64KP2YvFpkDOi3eQ==", - "dev": true, - "requires": { - "abab": "^2.0.0", - "whatwg-mimetype": "^2.2.0", - "whatwg-url": "^7.0.0" - }, - "dependencies": { - "whatwg-url": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.0.0.tgz", - "integrity": "sha512-37GeVSIJ3kn1JgKyjiYNmSLP1yzbpb29jdmwBSgkD9h40/hyrR/OifpVUndji3tmwGgD8qpw7iQu3RSbCrBpsQ==", - "dev": true, - "requires": { - "lodash.sortby": "^4.7.0", - "tr46": "^1.0.1", - "webidl-conversions": "^4.0.2" - } - } + "node_modules/@semantic-release/npm/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "dateformat": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz", - "integrity": "sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==", - "dev": true + "node_modules/@semantic-release/npm/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" + "node_modules/@semantic-release/npm/node_modules/strip-final-newline": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-4.0.0.tgz", + "integrity": "sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", - "dev": true + "node_modules/@semantic-release/npm/node_modules/unicorn-magic": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz", + "integrity": "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "decamelize-keys": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.0.tgz", - "integrity": "sha1-0XGoeTMlKAfrPLYdwcFEXQeN8tk=", + "node_modules/@semantic-release/release-notes-generator": { + "version": "14.0.3", + "resolved": "https://registry.npmjs.org/@semantic-release/release-notes-generator/-/release-notes-generator-14.0.3.tgz", + "integrity": "sha512-XxAZRPWGwO5JwJtS83bRdoIhCiYIx8Vhr+u231pQAsdFIAbm19rSVJLdnBN+Avvk7CKvNQE/nJ4y7uqKH6WTiw==", "dev": true, - "requires": { - "decamelize": "^1.1.0", - "map-obj": "^1.0.0" + "license": "MIT", + "dependencies": { + "conventional-changelog-angular": "^8.0.0", + "conventional-changelog-writer": "^8.0.0", + "conventional-commits-filter": "^5.0.0", + "conventional-commits-parser": "^6.0.0", + "debug": "^4.0.0", + "get-stream": "^7.0.0", + "import-from-esm": "^2.0.0", + "into-stream": "^7.0.0", + "lodash-es": "^4.17.21", + "read-package-up": "^11.0.0" + }, + "engines": { + "node": ">=20.8.1" }, + "peerDependencies": { + "semantic-release": ">=20.1.0" + } + }, + "node_modules/@semantic-release/release-notes-generator/node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dev": true, + "license": "MIT", "dependencies": { - "map-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", - "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", - "dev": true + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true } } }, - "decode-uri-component": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", - "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", - "dev": true - }, - "dedent": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", - "integrity": "sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=", - "dev": true - }, - "deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "dev": true + "node_modules/@semantic-release/release-notes-generator/node_modules/get-stream": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-7.0.1.tgz", + "integrity": "sha512-3M8C1EOFN6r8AMUhwUAACIoXZJEOufDU5+0gFFN5uNs6XYOralD2Pqkl7m046va6x77FwposWXbAhPPIOus7mQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "deep-is": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", - "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", - "dev": true + "node_modules/@semantic-release/release-notes-generator/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, + "license": "MIT" }, - "deepmerge": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-3.3.0.tgz", - "integrity": "sha512-GRQOafGHwMHpjPx9iCvTgpu9NojZ49q794EEL94JVEw6VaeA8XTUyBKvAkOOjBX9oJNiV6G3P+T+tihFjo2TqA==", - "dev": true + "node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "license": "MIT" }, - "define-properties": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", - "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "node_modules/@sindresorhus/is": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", + "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", "dev": true, - "requires": { - "object-keys": "^1.0.12" + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" } }, - "define-property": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", - "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", - "dev": true, - "requires": { - "is-descriptor": "^1.0.2", - "isobject": "^3.0.1" - }, - "dependencies": { - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } + "node_modules/@sindresorhus/merge-streams": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz", + "integrity": "sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" - }, - "depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + "node_modules/@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "license": "BSD-3-Clause", + "dependencies": { + "type-detect": "4.0.8" + } }, - "deprecation": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", - "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==", - "dev": true + "node_modules/@sinonjs/fake-timers": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", + "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.0" + } }, - "destroy": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", - "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + "node_modules/@tsconfig/node10": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", + "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", + "devOptional": true, + "license": "MIT" }, - "detect-file": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", - "integrity": "sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=", - "dev": true + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "devOptional": true, + "license": "MIT" }, - "detect-indent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-5.0.0.tgz", - "integrity": "sha1-OHHMCmoALow+Wzz38zYmRnXwa50=", - "dev": true + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "devOptional": true, + "license": "MIT" }, - "detect-newline": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-2.1.0.tgz", - "integrity": "sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I=", - "dev": true + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } }, - "diff": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", - "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", - "dev": true + "node_modules/@types/babel__generator": { + "version": "7.6.8", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", + "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } }, - "diff-sequences": { - "version": "24.3.0", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-24.3.0.tgz", - "integrity": "sha512-xLqpez+Zj9GKSnPWS0WZw1igGocZ+uua8+y+5dDNTT934N3QuY1sp2LkHzwiaYQGz60hMq0pjAshdeXm5VUOEw==", - "dev": true + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } }, - "dir-glob": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-2.2.2.tgz", - "integrity": "sha512-f9LBi5QWzIW3I6e//uxZoLBlUt9kcp66qo0sSCxL6YZKc75R1c4MFCoe/LaZiBGmgujvQdxc5Bn3QhfyvK5Hsw==", - "dev": true, - "requires": { - "path-type": "^3.0.0" + "node_modules/@types/babel__traverse": { + "version": "7.20.6", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.6.tgz", + "integrity": "sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.20.7" } }, - "domexception": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/domexception/-/domexception-1.0.1.tgz", - "integrity": "sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==", + "node_modules/@types/body-parser": { + "version": "1.19.5", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", + "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", "dev": true, - "requires": { - "webidl-conversions": "^4.0.2" + "license": "MIT", + "dependencies": { + "@types/connect": "*", + "@types/node": "*" } }, - "dot-prop": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-3.0.0.tgz", - "integrity": "sha1-G3CK8JSknJoOfbyteQq6U52sEXc=", + "node_modules/@types/commander": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/@types/commander/-/commander-2.12.0.tgz", + "integrity": "sha512-DDmRkovH7jPjnx7HcbSnqKg2JeNANyxNZeUvB0iE+qKBLN+vzN5iSIwt+J2PFSmBuYEut4mgQvI/fTX9YQH/vw==", "dev": true, - "requires": { - "is-obj": "^1.0.0" + "license": "MIT", + "dependencies": { + "commander": "*" } }, - "duplexer2": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", - "integrity": "sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=", + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", "dev": true, - "requires": { - "readable-stream": "^2.0.2" + "license": "MIT", + "dependencies": { + "@types/node": "*" } }, - "ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", - "requires": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" - } - }, - "ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" - }, - "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true - }, - "encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" - }, - "end-of-stream": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", - "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", + "node_modules/@types/conventional-commits-parser": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@types/conventional-commits-parser/-/conventional-commits-parser-5.0.1.tgz", + "integrity": "sha512-7uz5EHdzz2TqoMfV7ee61Egf5y6NkcO4FB/1iCCQnbeiI1F3xzv3vK5dBCXUCLQgGYS+mUeigK1iKQzvED+QnQ==", "dev": true, - "requires": { - "once": "^1.4.0" + "license": "MIT", + "optional": true, + "dependencies": { + "@types/node": "*" } }, - "env-ci": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/env-ci/-/env-ci-4.1.1.tgz", - "integrity": "sha512-eTgpkALDeYRGNhYM2fO9LKsWDifoUgKL7hxpPZqFMP2IU7f+r89DtKqCmk3yQB/jxS8CmZTfKnWO5TiIDFs9Hw==", + "node_modules/@types/express": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", + "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", "dev": true, - "requires": { - "execa": "^1.0.0", - "java-properties": "^1.0.0" + "license": "MIT", + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.33", + "@types/qs": "*", + "@types/serve-static": "*" } }, - "error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "node_modules/@types/express-serve-static-core": { + "version": "4.19.6", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.6.tgz", + "integrity": "sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A==", "dev": true, - "requires": { - "is-arrayish": "^0.2.1" + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" } }, - "es-abstract": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.13.0.tgz", - "integrity": "sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg==", - "dev": true, - "requires": { - "es-to-primitive": "^1.2.0", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "is-callable": "^1.1.4", - "is-regex": "^1.0.4", - "object-keys": "^1.0.12" + "node_modules/@types/graceful-fs": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", + "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", + "license": "MIT", + "dependencies": { + "@types/node": "*" } }, - "es-to-primitive": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz", - "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==", + "node_modules/@types/http-errors": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", + "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==", "dev": true, - "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" + "license": "MIT" + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "license": "MIT" + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-coverage": "*" } }, - "es6-promise": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", - "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==", - "dev": true + "node_modules/@types/istanbul-reports": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-report": "*" + } }, - "es6-promisify": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", - "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", + "node_modules/@types/jest": { + "version": "29.5.14", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.14.tgz", + "integrity": "sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==", "dev": true, - "requires": { - "es6-promise": "^4.0.3" + "license": "MIT", + "dependencies": { + "expect": "^29.0.0", + "pretty-format": "^29.0.0" } }, - "escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + "node_modules/@types/mime": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", + "dev": true, + "license": "MIT" }, - "escodegen": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.11.1.tgz", - "integrity": "sha512-JwiqFD9KdGVVpeuRa68yU3zZnBEOcPs0nKW7wZzXky8Z7tffdYUHbe11bPCV5jYlK6DVdKLWLm0f5I/QlL0Kmw==", - "dev": true, - "requires": { - "esprima": "^3.1.3", - "estraverse": "^4.2.0", - "esutils": "^2.0.2", - "optionator": "^0.8.1", - "source-map": "~0.6.1" - }, - "dependencies": { - "esprima": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz", - "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "optional": true - } + "node_modules/@types/node": { + "version": "22.10.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.10.tgz", + "integrity": "sha512-X47y/mPNzxviAGY5TcYPtYL8JsY3kAq2n8fMmKoRCxq/c4v4pyGNCzM2R6+M5/umG4ZfHuT+sgqDYqWc9rJ6ww==", + "license": "MIT", + "dependencies": { + "undici-types": "~6.20.0" } }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true + "node_modules/@types/normalize-package-data": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", + "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==", + "dev": true, + "license": "MIT" }, - "estraverse": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", - "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", - "dev": true + "node_modules/@types/object-hash": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/object-hash/-/object-hash-3.0.6.tgz", + "integrity": "sha512-fOBV8C1FIu2ELinoILQ+ApxcUKz4ngq+IWUYrxSGjXzzjUALijilampwkMgEtJ+h2njAW3pi853QpzNVCHB73w==", + "dev": true, + "license": "MIT" }, - "esutils": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", - "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", - "dev": true + "node_modules/@types/prettyjson": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/@types/prettyjson/-/prettyjson-0.0.33.tgz", + "integrity": "sha512-hHZMkavT9OXFq8p6pTCiaREtPxMRfy9NMp+Qa4PWH0RINQjyh0crOhoqUFA/cvIZncpjBpdvxkoe7nmVbyBJXw==", + "dev": true, + "license": "MIT" }, - "etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" + "node_modules/@types/qs": { + "version": "6.9.18", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.18.tgz", + "integrity": "sha512-kK7dgTYDyGqS+e2Q4aK9X3D7q234CIZ1Bv0q/7Z5IwRDoADNU81xXJK/YVyLbLTZCoIwUoDoffFeF+p/eIklAA==", + "dev": true, + "license": "MIT" }, - "exec-sh": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.3.2.tgz", - "integrity": "sha512-9sLAvzhI5nc8TpuQUh4ahMdCrWT00wPWz7j47/emR5+2qEfoZP5zzUXvx+vdx+H6ohhnsYC31iX04QLYJK8zTg==", - "dev": true + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "dev": true, + "license": "MIT" }, - "execa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "node_modules/@types/require-from-string": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@types/require-from-string/-/require-from-string-1.2.3.tgz", + "integrity": "sha512-kxLU5xvefySGpp1Z7VCt4m5AhQJUZ8HjW8ADdeS7GieqFPHLAde007fd9bxeXEsFXyaA0LeWIoQXyXP17mGpIg==", "dev": true, - "requires": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - } + "license": "MIT" }, - "exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", - "dev": true - }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "dev": true, - "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } + "node_modules/@types/semver": { + "version": "7.5.8", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", + "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", + "dev": true, + "license": "MIT" }, - "expand-tilde": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", - "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=", + "node_modules/@types/send": { + "version": "0.17.4", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", + "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", "dev": true, - "requires": { - "homedir-polyfill": "^1.0.1" + "license": "MIT", + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" } }, - "expect": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/expect/-/expect-24.8.0.tgz", - "integrity": "sha512-/zYvP8iMDrzaaxHVa724eJBCKqSHmO0FA7EDkBiRHxg6OipmMn1fN+C8T9L9K8yr7UONkOifu6+LLH+z76CnaA==", + "node_modules/@types/serve-static": { + "version": "1.15.7", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.7.tgz", + "integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==", "dev": true, - "requires": { - "@jest/types": "^24.8.0", - "ansi-styles": "^3.2.0", - "jest-get-type": "^24.8.0", - "jest-matcher-utils": "^24.8.0", - "jest-message-util": "^24.8.0", - "jest-regex-util": "^24.3.0" + "license": "MIT", + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*", + "@types/send": "*" } }, - "express": { - "version": "4.17.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", - "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", - "requires": { - "accepts": "~1.3.7", - "array-flatten": "1.1.1", - "body-parser": "1.19.0", - "content-disposition": "0.5.3", - "content-type": "~1.0.4", - "cookie": "0.4.0", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "~1.1.2", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "~1.1.2", - "fresh": "0.5.2", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.5", - "qs": "6.7.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.1.2", - "send": "0.17.1", - "serve-static": "1.14.1", - "setprototypeof": "1.1.1", - "statuses": "~1.5.0", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" + "node_modules/@types/stack-utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", + "license": "MIT" + }, + "node_modules/@types/yamljs": { + "version": "0.2.34", + "resolved": "https://registry.npmjs.org/@types/yamljs/-/yamljs-0.2.34.tgz", + "integrity": "sha512-gJvfRlv9ErxdOv7ux7UsJVePtX54NAvQyd8ncoiFqK8G5aeHIfQfGH2fbruvjAQ9657HwAaO54waS+Dsk2QTUQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/yargs": { + "version": "17.0.33", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", + "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "license": "MIT", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", + "license": "MIT" + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", + "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", + "devOptional": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", + "devOptional": true, + "license": "MIT", "dependencies": { - "qs": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", - "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" - } + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" } }, - "extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + "node_modules/agent-base": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", + "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } }, - "extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", - "dev": true, - "requires": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" - }, - "dependencies": { - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dev": true, - "requires": { - "is-plain-object": "^2.0.4" - } - } + "node_modules/aggregate-error": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-5.0.0.tgz", + "integrity": "sha512-gOsf2YwSlleG6IjRYG2A7k0HmBMEo6qVNk9Bp/EaLgAJT5ngH6PXbqa4ItvnEwCm/velL5jAnQgsHsWnjhGmvw==", + "dev": true, + "license": "MIT", + "dependencies": { + "clean-stack": "^5.2.0", + "indent-string": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "external-editor": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.0.3.tgz", - "integrity": "sha512-bn71H9+qWoOQKyZDo25mOMVpSmXROAsTJVVVYzrrtol3d4y+AsKjf4Iwl2Q+IuT0kFSQ1qo166UuIwqYq7mGnA==", + "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==", "dev": true, - "requires": { - "chardet": "^0.7.0", - "iconv-lite": "^0.4.24", - "tmp": "^0.0.33" + "license": "MIT", + "optional": true, + "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/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "license": "MIT", "dependencies": { - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dev": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - } + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "dev": true, - "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } + "node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "extsprintf": { + "node_modules/any-promise": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true, + "license": "MIT" }, - "fast-deep-equal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", - "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=" - }, - "fast-glob": { - "version": "2.2.7", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-2.2.7.tgz", - "integrity": "sha512-g1KuQwHOZAmOZMuBtHdxDtju+T2RT8jgCC9aANsbpdiDDTSnjgfuVsIBNKbUeJI3oKMRExcfNDtJl4OhbffMsw==", - "dev": true, - "requires": { - "@mrmlnc/readdir-enhanced": "^2.2.1", - "@nodelib/fs.stat": "^1.1.2", - "glob-parent": "^3.1.0", - "is-glob": "^4.0.0", - "merge2": "^1.2.3", - "micromatch": "^3.1.10" + "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==", + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" } }, - "fast-json-stable-stringify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", - "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" - }, - "fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", - "dev": true + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "devOptional": true, + "license": "MIT" }, - "fb-watchman": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.0.tgz", - "integrity": "sha1-VOmr99+i8mzZsWNsWIwa/AXeXVg=", + "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, - "requires": { - "bser": "^2.0.0" - } + "license": "Python-2.0" }, - "figures": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", - "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", + "node_modules/argv-formatter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/argv-formatter/-/argv-formatter-1.0.0.tgz", + "integrity": "sha512-F2+Hkm9xFaRg+GkaNnbwXNDV5O6pnCFEmqyhvfC/Ic5LbgOWjJh3L+mN/s91rxVL3znE7DYVpW0GJFT+4YBgWw==", "dev": true, - "requires": { - "escape-string-regexp": "^1.0.5" - } + "license": "MIT" }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "license": "MIT" }, - "finalhandler": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", - "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", - "requires": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "statuses": "~1.5.0", - "unpipe": "~1.0.0" - } + "node_modules/array-ify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-ify/-/array-ify-1.0.0.tgz", + "integrity": "sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==", + "dev": true, + "license": "MIT" }, - "find-node-modules": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/find-node-modules/-/find-node-modules-2.0.0.tgz", - "integrity": "sha512-8MWIBRgJi/WpjjfVXumjPKCtmQ10B+fjx6zmSA+770GMJirLhWIzg8l763rhjl9xaeaHbnxPNRQKq2mgMhr+aw==", + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", "dev": true, - "requires": { - "findup-sync": "^3.0.0", - "merge": "^1.2.1" + "license": "MIT", + "engines": { + "node": ">=8" } }, - "find-root": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", - "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", - "dev": true + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "dev": true, + "license": "MIT" }, - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "node_modules/at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", "dev": true, - "requires": { - "locate-path": "^3.0.0" + "license": "ISC", + "engines": { + "node": ">= 4.0.0" } }, - "find-versions": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/find-versions/-/find-versions-3.1.0.tgz", - "integrity": "sha512-NCTfNiVzeE/xL+roNDffGuRbrWI6atI18lTJ22vKp7rs2OhYzMK3W1dIdO2TUndH/QMcacM4d1uWwgcZcHK69Q==", - "dev": true, - "requires": { - "array-uniq": "^2.1.0", - "semver-regex": "^2.0.0" + "node_modules/babel-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", + "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", + "license": "MIT", + "dependencies": { + "@jest/transform": "^29.7.0", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^29.6.3", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" }, + "peerDependencies": { + "@babel/core": "^7.8.0" + } + }, + "node_modules/babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "license": "BSD-3-Clause", "dependencies": { - "array-uniq": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-2.1.0.tgz", - "integrity": "sha512-bdHxtev7FN6+MXI1YFW0Q8mQ8dTJc2S8AMfju+ZR77pbg2yAdVyDlwkaUI7Har0LyOMRFPHrJ9lYdyjZZswdlQ==", - "dev": true - } + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" } }, - "findup-sync": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-3.0.0.tgz", - "integrity": "sha512-YbffarhcicEhOrm4CtrwdKBdCuz576RLdhJDsIfvNtxUuhdRet1qZcsMjqbePtAseKdAnDyM/IyXbu7PRPRLYg==", - "dev": true, - "requires": { - "detect-file": "^1.0.0", - "is-glob": "^4.0.0", - "micromatch": "^3.0.4", - "resolve-dir": "^1.0.1" + "node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "license": "BSD-3-Clause", + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" } }, - "for-in": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", - "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", - "dev": true + "node_modules/babel-plugin-jest-hoist": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", + "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", + "license": "MIT", + "dependencies": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.1.14", + "@types/babel__traverse": "^7.0.6" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } }, - "forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" + "node_modules/babel-preset-current-node-syntax": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.1.0.tgz", + "integrity": "sha512-ldYss8SbBlWva1bs28q78Ju5Zq1F+8BrqBZZ0VFhLBvhh6lCpC2o3gDJi/5DRLs9FgYZCnmPYIVFU4lRXCkyUw==", + "license": "MIT", + "dependencies": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-import-attributes": "^7.24.7", + "@babel/plugin-syntax-import-meta": "^7.10.4", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } }, - "form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" + "node_modules/babel-preset-jest": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", + "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", + "license": "MIT", + "dependencies": { + "babel-plugin-jest-hoist": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, - "forwarded": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", - "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" + "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==", + "license": "MIT" }, - "fragment-cache": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", - "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", "dev": true, - "requires": { - "map-cache": "^0.2.2" - } - }, - "fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" + "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" }, - "from2": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", - "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=", + "node_modules/before-after-hook": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-3.0.2.tgz", + "integrity": "sha512-Nik3Sc0ncrMK4UUdXQmAnRtzmNQTAAXmXIopizwZ1W1t8QmfJj+zL4OA2I7XPTPW5z5TDqv4hRo/JzouDJnX3A==", "dev": true, - "requires": { - "inherits": "^2.0.1", - "readable-stream": "^2.0.0" - } + "license": "Apache-2.0" }, - "fs-extra": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", - "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" + "license": "MIT", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" } }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + "node_modules/body-parser": { + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", + "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.13.0", + "raw-body": "2.5.2", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } }, - "fsevents": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.9.tgz", - "integrity": "sha512-oeyj2H3EjjonWcFjD5NvZNE9Rqe4UW+nQBU2HNeKw0koVLEFIhtyETyAakeAM3de7Z/SW5kcA+fZUait9EApnw==", + "node_modules/bottleneck": { + "version": "2.19.5", + "resolved": "https://registry.npmjs.org/bottleneck/-/bottleneck-2.19.5.tgz", + "integrity": "sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw==", "dev": true, - "optional": true, - "requires": { - "nan": "^2.12.1", - "node-pre-gyp": "^0.12.0" + "license": "MIT" + }, + "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==", + "license": "MIT", + "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==", + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.24.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", + "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", "dependencies": { - "abbrev": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "ansi-regex": { - "version": "2.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "aproba": { - "version": "1.2.0", - "bundled": true, - "dev": true, - "optional": true - }, - "are-we-there-yet": { - "version": "1.1.5", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" - } - }, - "balanced-match": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "brace-expansion": { - "version": "1.1.11", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "chownr": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "code-point-at": { - "version": "1.1.0", - "bundled": true, - "dev": true, - "optional": true - }, - "concat-map": { - "version": "0.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "console-control-strings": { - "version": "1.1.0", - "bundled": true, - "dev": true, - "optional": true - }, - "core-util-is": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "debug": { - "version": "4.1.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "ms": "^2.1.1" - } - }, - "deep-extend": { - "version": "0.6.0", - "bundled": true, - "dev": true, - "optional": true - }, - "delegates": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "detect-libc": { - "version": "1.0.3", - "bundled": true, - "dev": true, - "optional": true - }, - "fs-minipass": { - "version": "1.2.5", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "minipass": "^2.2.1" - } - }, - "fs.realpath": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "gauge": { - "version": "2.7.4", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "aproba": "^1.0.3", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.0", - "object-assign": "^4.1.0", - "signal-exit": "^3.0.0", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wide-align": "^1.1.0" - } - }, - "glob": { - "version": "7.1.3", - "bundled": true, - "dev": true, - "optional": true, - "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" - } - }, - "has-unicode": { - "version": "2.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "iconv-lite": { - "version": "0.4.24", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "ignore-walk": { - "version": "3.0.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "minimatch": "^3.0.4" - } - }, - "inflight": { - "version": "1.0.6", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.3", - "bundled": true, - "dev": true, - "optional": true - }, - "ini": { - "version": "1.3.5", - "bundled": true, - "dev": true, - "optional": true - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "isarray": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "minimatch": { - "version": "3.0.4", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "0.0.8", - "bundled": true, - "dev": true, - "optional": true - }, - "minipass": { - "version": "2.3.5", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "safe-buffer": "^5.1.2", - "yallist": "^3.0.0" - } - }, - "minizlib": { - "version": "1.2.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "minipass": "^2.2.1" - } - }, - "mkdirp": { - "version": "0.5.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "minimist": "0.0.8" - } - }, - "ms": { - "version": "2.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "needle": { - "version": "2.3.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "debug": "^4.1.0", - "iconv-lite": "^0.4.4", - "sax": "^1.2.4" - } - }, - "node-pre-gyp": { - "version": "0.12.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "detect-libc": "^1.0.2", - "mkdirp": "^0.5.1", - "needle": "^2.2.1", - "nopt": "^4.0.1", - "npm-packlist": "^1.1.6", - "npmlog": "^4.0.2", - "rc": "^1.2.7", - "rimraf": "^2.6.1", - "semver": "^5.3.0", - "tar": "^4" - } - }, - "nopt": { - "version": "4.0.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "abbrev": "1", - "osenv": "^0.1.4" - } - }, - "npm-bundled": { - "version": "1.0.6", - "bundled": true, - "dev": true, - "optional": true - }, - "npm-packlist": { - "version": "1.4.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "ignore-walk": "^3.0.1", - "npm-bundled": "^1.0.1" - } - }, - "npmlog": { - "version": "4.1.2", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.3", - "set-blocking": "~2.0.0" - } - }, - "number-is-nan": { - "version": "1.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "object-assign": { - "version": "4.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "once": { - "version": "1.4.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "wrappy": "1" - } - }, - "os-homedir": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "os-tmpdir": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "osenv": { - "version": "0.1.5", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" - } - }, - "path-is-absolute": { - "version": "1.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "process-nextick-args": { - "version": "2.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "rc": { - "version": "1.2.8", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "bundled": true, - "dev": true, - "optional": true - } - } - }, - "readable-stream": { - "version": "2.3.6", - "bundled": true, - "dev": true, - "optional": true, - "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" - } - }, - "rimraf": { - "version": "2.6.3", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "glob": "^7.1.3" - } - }, - "safe-buffer": { - "version": "5.1.2", - "bundled": true, - "dev": true, - "optional": true - }, - "safer-buffer": { - "version": "2.1.2", - "bundled": true, - "dev": true, - "optional": true - }, - "sax": { - "version": "1.2.4", - "bundled": true, - "dev": true, - "optional": true - }, - "semver": { - "version": "5.7.0", - "bundled": true, - "dev": true, - "optional": true - }, - "set-blocking": { - "version": "2.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "signal-exit": { - "version": "3.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "string-width": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - }, - "string_decoder": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "safe-buffer": "~5.1.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "strip-json-comments": { - "version": "2.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "tar": { - "version": "4.4.8", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "chownr": "^1.1.1", - "fs-minipass": "^1.2.5", - "minipass": "^2.3.4", - "minizlib": "^1.1.1", - "mkdirp": "^0.5.0", - "safe-buffer": "^5.1.2", - "yallist": "^3.0.2" - } - }, - "util-deprecate": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "wide-align": { - "version": "1.1.3", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "string-width": "^1.0.2 || 2" - } - }, - "wrappy": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "yallist": { - "version": "3.0.3", - "bundled": true, - "dev": true, - "optional": true - } + "caniuse-lite": "^1.0.30001688", + "electron-to-chromium": "^1.5.73", + "node-releases": "^2.0.19", + "update-browserslist-db": "^1.1.1" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true + "node_modules/bs-logger": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", + "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-json-stable-stringify": "2.x" + }, + "engines": { + "node": ">= 6" + } }, - "get-caller-file": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", - "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", - "dev": true + "node_modules/bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "license": "Apache-2.0", + "dependencies": { + "node-int64": "^0.4.0" + } }, - "get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", "dev": true, - "requires": { - "pump": "^3.0.0" + "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", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" } }, - "get-value": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", - "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", - "dev": true + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "license": "MIT" }, - "getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", - "requires": { - "assert-plus": "^1.0.0" + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" } }, - "git-log-parser": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/git-log-parser/-/git-log-parser-1.2.0.tgz", - "integrity": "sha1-LmpMGxP8AAKCB7p5WnrDFme5/Uo=", + "node_modules/cachedir": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/cachedir/-/cachedir-2.3.0.tgz", + "integrity": "sha512-A+Fezp4zxnit6FanDmv9EqXNAi3vt9DWp51/71UEhXukb7QUuvtv9344h91dyAxuTLoSYJFU299qzR3tzwPAhw==", "dev": true, - "requires": { - "argv-formatter": "~1.0.0", - "spawn-error-forwarder": "~1.0.0", - "split2": "~1.0.0", - "stream-combiner2": "~1.1.1", - "through2": "~2.0.0", - "traverse": "~0.6.6" - }, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.1.tgz", + "integrity": "sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==", + "license": "MIT", "dependencies": { - "split2": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/split2/-/split2-1.0.0.tgz", - "integrity": "sha1-UuLiIdiMdfmnP5BVbiY/+WdysxQ=", - "dev": true, - "requires": { - "through2": "~2.0.0" - } - } + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" } }, - "glob": { - "version": "7.1.4", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", - "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", - "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" + "node_modules/call-bound": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.3.tgz", + "integrity": "sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "glob-parent": { + "node_modules/callsites": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", - "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", - "dev": true, - "requires": { - "is-glob": "^3.1.0", - "path-dirname": "^1.0.0" - }, - "dependencies": { - "is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", - "dev": true, - "requires": { - "is-extglob": "^2.1.0" - } - } + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "license": "MIT", + "engines": { + "node": ">=6" } }, - "glob-to-regexp": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz", - "integrity": "sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs=", - "dev": true + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001695", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001695.tgz", + "integrity": "sha512-vHyLade6wTgI2u1ec3WQBxv+2BrTERV28UXQu9LO6lZ9pYeMk34vjXFLOxo1A4UBA8XTL4njRQZdno/yYaSmWw==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" }, - "global-modules": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", - "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", - "dev": true, - "requires": { - "global-prefix": "^1.0.1", - "is-windows": "^1.0.1", - "resolve-dir": "^1.0.0" + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "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" } }, - "global-prefix": { + "node_modules/char-regex": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", - "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=", - "dev": true, - "requires": { - "expand-tilde": "^2.0.2", - "homedir-polyfill": "^1.0.1", - "ini": "^1.3.4", - "is-windows": "^1.0.1", - "which": "^1.2.14" + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "license": "MIT", + "engines": { + "node": ">=10" } }, - "globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true + "node_modules/chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "dev": true, + "license": "MIT" }, - "globby": { - "version": "9.2.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-9.2.0.tgz", - "integrity": "sha512-ollPHROa5mcxDEkwg6bPt3QbEf4pDQSNtd6JPL1YvOvAo/7/0VAm9TccUeoTmarjPw4pfUthSCqcyfNB1I3ZSg==", - "dev": true, - "requires": { - "@types/glob": "^7.1.1", - "array-union": "^1.0.2", - "dir-glob": "^2.2.2", - "fast-glob": "^2.2.6", - "glob": "^7.1.3", - "ignore": "^4.0.3", - "pify": "^4.0.1", - "slash": "^2.0.0" - }, - "dependencies": { - "pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "dev": true + "node_modules/ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" } + ], + "license": "MIT", + "engines": { + "node": ">=8" } }, - "graceful-fs": { - "version": "4.1.15", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", - "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==", - "dev": true + "node_modules/cjs-module-lexer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.1.tgz", + "integrity": "sha512-cuSVIHi9/9E/+821Qjdvngor+xpnlwnuwIyZOaLmHBVdXL+gP+I6QQB9VkO7RI77YIcTV+S1W9AreJ5eN63JBA==", + "license": "MIT" }, - "growly": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", - "integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=", - "dev": true - }, - "handlebars": { - "version": "4.5.3", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.5.3.tgz", - "integrity": "sha512-3yPecJoJHK/4c6aZhSvxOyG4vJKDshV36VHp0iVCDVh7o9w2vwi3NSnL2MMPj3YdduqaBcu7cGbggJQM0br9xA==", - "dev": true, - "requires": { - "neo-async": "^2.6.0", - "optimist": "^0.6.1", - "source-map": "^0.6.1", - "uglify-js": "^3.1.4" - }, + "node_modules/clean-stack": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-5.2.0.tgz", + "integrity": "sha512-TyUIUJgdFnCISzG5zu3291TAsE77ddchd0bepon1VVQrKLGKFED4iXFEDQ24mIPdPBbyE16PK3F8MYE1CmcBEQ==", + "dev": true, + "license": "MIT", "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } + "escape-string-regexp": "5.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" - }, - "har-validator": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", - "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", - "requires": { - "ajv": "^6.5.5", - "har-schema": "^2.0.0" + "node_modules/clean-stack/node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", "dev": true, - "requires": { - "function-bind": "^1.1.1" + "license": "MIT", + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" } }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" - }, - "has-symbols": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", - "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=", - "dev": true + "node_modules/cli-highlight": { + "version": "2.1.11", + "resolved": "https://registry.npmjs.org/cli-highlight/-/cli-highlight-2.1.11.tgz", + "integrity": "sha512-9KDcoEVwyUXrjcJNvHD0NFc/hiwe/WPVYIleQh2O1N2Zro5gWJZ/K+3DGn8w8P/F6FxOgzyC5bxDyHIgCSPhGg==", + "dev": true, + "license": "ISC", + "dependencies": { + "chalk": "^4.0.0", + "highlight.js": "^10.7.1", + "mz": "^2.4.0", + "parse5": "^5.1.1", + "parse5-htmlparser2-tree-adapter": "^6.0.0", + "yargs": "^16.0.0" + }, + "bin": { + "highlight": "bin/highlight" + }, + "engines": { + "node": ">=8.0.0", + "npm": ">=5.0.0" + } }, - "has-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", - "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "node_modules/cli-highlight/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, - "requires": { - "get-value": "^2.0.6", - "has-values": "^1.0.0", - "isobject": "^3.0.0" + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" } }, - "has-values": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", - "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", - "dev": true, - "requires": { - "is-number": "^3.0.0", - "kind-of": "^4.0.0" - }, - "dependencies": { - "kind-of": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", - "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } + "node_modules/cli-highlight/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, + "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" } }, - "homedir-polyfill": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", - "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", + "node_modules/cli-highlight/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, - "requires": { - "parse-passwd": "^1.0.0" + "license": "ISC", + "engines": { + "node": ">=10" } }, - "hook-std": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/hook-std/-/hook-std-2.0.0.tgz", - "integrity": "sha512-zZ6T5WcuBMIUVh49iPQS9t977t7C0l7OtHrpeMb5uk48JdflRX0NSFvCekfYNmGQETnLq9W/isMyHl69kxGi8g==", - "dev": true + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "hosted-git-info": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz", - "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==", - "dev": true + "node_modules/cli-table3": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.5.tgz", + "integrity": "sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "string-width": "^4.2.0" + }, + "engines": { + "node": "10.* || >= 12.*" + }, + "optionalDependencies": { + "@colors/colors": "1.5.0" + } }, - "html-encoding-sniffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz", - "integrity": "sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw==", + "node_modules/cli-width": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", + "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", "dev": true, - "requires": { - "whatwg-encoding": "^1.0.1" + "license": "ISC", + "engines": { + "node": ">= 10" } }, - "http-errors": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", - "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", - "requires": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.1", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.0" + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" } }, - "http-proxy-agent": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz", - "integrity": "sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg==", - "dev": true, - "requires": { - "agent-base": "4", - "debug": "3.1.0" - }, - "dependencies": { - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - } + "node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8" } }, - "http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", - "requires": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - } - }, - "https-proxy-agent": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz", - "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==", - "dev": true, - "requires": { - "agent-base": "^4.3.0", - "debug": "^3.1.0" - }, - "dependencies": { - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "license": "MIT", + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" } }, - "human-signals": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", - "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", - "dev": true + "node_modules/collect-v8-coverage": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", + "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", + "license": "MIT" }, - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "requires": { - "safer-buffer": ">= 2.1.2 < 3" + "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==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" } }, - "ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", - "dev": true + "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==", + "license": "MIT" }, - "import-fresh": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz", - "integrity": "sha1-2BNVwVYS04bGH53dOSLUMEgipUY=", - "dev": true, - "requires": { - "caller-path": "^2.0.0", - "resolve-from": "^3.0.0" + "node_modules/colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", + "license": "MIT", + "engines": { + "node": ">=0.1.90" } }, - "import-from": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/import-from/-/import-from-3.0.0.tgz", - "integrity": "sha512-CiuXOFFSzkU5x/CR0+z7T91Iht4CXgfCxVOFRhh2Zyhg5wOpWvvDLQUsWl+gcN+QscYBjez8hDCt85O7RLDttQ==", - "dev": true, - "requires": { - "resolve-from": "^5.0.0" - }, - "dependencies": { - "resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true - } + "node_modules/commander": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "license": "MIT", + "engines": { + "node": ">=18" } }, - "import-local": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz", - "integrity": "sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==", + "node_modules/commitizen": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/commitizen/-/commitizen-4.3.1.tgz", + "integrity": "sha512-gwAPAVTy/j5YcOOebcCRIijn+mSjWJC+IYKivTu6aG8Ei/scoXgfsMRnuAk6b0GRste2J4NGxVdMN3ZpfNaVaw==", "dev": true, - "requires": { - "pkg-dir": "^3.0.0", - "resolve-cwd": "^2.0.0" - } - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true - }, - "indent-string": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz", - "integrity": "sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok=", - "dev": true - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "requires": { - "once": "^1.3.0", - "wrappy": "1" + "license": "MIT", + "dependencies": { + "cachedir": "2.3.0", + "cz-conventional-changelog": "3.3.0", + "dedent": "0.7.0", + "detect-indent": "6.1.0", + "find-node-modules": "^2.1.2", + "find-root": "1.1.0", + "fs-extra": "9.1.0", + "glob": "7.2.3", + "inquirer": "8.2.5", + "is-utf8": "^0.2.1", + "lodash": "4.17.21", + "minimist": "1.2.7", + "strip-bom": "4.0.0", + "strip-json-comments": "3.1.1" + }, + "bin": { + "commitizen": "bin/commitizen", + "cz": "bin/git-cz", + "git-cz": "bin/git-cz" + }, + "engines": { + "node": ">= 12" } }, - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" - }, - "ini": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", - "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", - "dev": true - }, - "inquirer": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.2.0.tgz", - "integrity": "sha512-QIEQG4YyQ2UYZGDC4srMZ7BjHOmNk1lR2JQj5UknBapklm6WHA+VVH7N+sUdX3A7NeCfGF8o4X1S3Ao7nAcIeg==", - "dev": true, - "requires": { - "ansi-escapes": "^3.0.0", - "chalk": "^2.0.0", - "cli-cursor": "^2.1.0", - "cli-width": "^2.0.0", - "external-editor": "^3.0.0", - "figures": "^2.0.0", - "lodash": "^4.17.10", - "mute-stream": "0.0.7", - "run-async": "^2.2.0", - "rxjs": "^6.1.0", - "string-width": "^2.1.0", - "strip-ansi": "^4.0.0", - "through": "^2.3.6" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - } + "node_modules/commitizen/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "interpret": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.2.0.tgz", - "integrity": "sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw==", - "dev": true - }, - "into-stream": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/into-stream/-/into-stream-5.1.0.tgz", - "integrity": "sha512-cbDhb8qlxKMxPBk/QxTtYg1DQ4CwXmadu7quG3B7nrJsgSncEreF2kwWKZFdnjc/lSNNIkFPsjI7SM0Cx/QXPw==", + "node_modules/commitizen/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, - "requires": { - "from2": "^2.3.0", - "p-is-promise": "^2.0.0" + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "invariant": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", - "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "node_modules/commitizen/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, - "requires": { - "loose-envify": "^1.0.0" + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" } }, - "invert-kv": { + "node_modules/compare-func": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", - "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==", - "dev": true - }, - "ipaddr.js": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.0.tgz", - "integrity": "sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA==" - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "http://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } + "resolved": "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz", + "integrity": "sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-ify": "^1.0.0", + "dot-prop": "^5.1.0" } }, - "is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", - "dev": true - }, - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", - "dev": true - }, - "is-callable": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", - "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==", - "dev": true + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "license": "MIT" }, - "is-ci": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", - "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", + "node_modules/config-chain": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", + "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==", "dev": true, - "requires": { - "ci-info": "^2.0.0" + "license": "MIT", + "dependencies": { + "ini": "^1.3.4", + "proto-list": "~1.2.1" } }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "http://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "node_modules/config-chain/node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, + "license": "ISC" + }, + "node_modules/consola": { + "version": "2.15.3", + "resolved": "https://registry.npmjs.org/consola/-/consola-2.15.3.tgz", + "integrity": "sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "license": "MIT", "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" } }, - "is-date-object": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", - "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=", - "dev": true - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - }, - "dependencies": { - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" } }, - "is-directory": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz", - "integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=", - "dev": true - }, - "is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", - "dev": true - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "is-generator-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", - "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", - "dev": true - }, - "is-glob": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.0.tgz", - "integrity": "sha1-lSHHaEXMJhCoUgPd8ICpWML/q8A=", + "node_modules/conventional-changelog-angular": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-8.0.0.tgz", + "integrity": "sha512-CLf+zr6St0wIxos4bmaKHRXWAcsCXrJU6F4VdNDrGRK3B8LDLKoX3zuMV5GhtbGkVR/LohZ6MT6im43vZLSjmA==", "dev": true, - "requires": { - "is-extglob": "^2.1.1" + "license": "ISC", + "dependencies": { + "compare-func": "^2.0.0" + }, + "engines": { + "node": ">=18" } }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "node_modules/conventional-changelog-writer": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-8.0.0.tgz", + "integrity": "sha512-TQcoYGRatlAnT2qEWDON/XSfnVG38JzA7E0wcGScu7RElQBkg9WWgZd1peCWFcWDh1xfb2CfsrcvOn1bbSzztA==", "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, + "license": "MIT", "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } + "@types/semver": "^7.5.5", + "conventional-commits-filter": "^5.0.0", + "handlebars": "^4.7.7", + "meow": "^13.0.0", + "semver": "^7.5.2" + }, + "bin": { + "conventional-changelog-writer": "dist/cli/index.js" + }, + "engines": { + "node": ">=18" } }, - "is-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", - "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=", - "dev": true - }, - "is-plain-obj": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", - "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", - "dev": true - }, - "is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "node_modules/conventional-changelog-writer/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", "dev": true, - "requires": { - "isobject": "^3.0.1" + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" } }, - "is-promise": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", - "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", - "dev": true + "node_modules/conventional-commit-types": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/conventional-commit-types/-/conventional-commit-types-3.0.0.tgz", + "integrity": "sha512-SmmCYnOniSsAa9GqWOeLqc179lfr5TRu5b4QFDkbsrJ5TZjPJx85wtOr3zn+1dbeNiXDKGPbZ72IKbPhLXh/Lg==", + "dev": true, + "license": "ISC" }, - "is-regex": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", - "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", + "node_modules/conventional-commits-filter": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/conventional-commits-filter/-/conventional-commits-filter-5.0.0.tgz", + "integrity": "sha512-tQMagCOC59EVgNZcC5zl7XqO30Wki9i9J3acbUvkaosCT6JX3EeFwJD7Qqp4MCikRnzS18WXV3BLIQ66ytu6+Q==", "dev": true, - "requires": { - "has": "^1.0.1" + "license": "MIT", + "engines": { + "node": ">=18" } }, - "is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", - "dev": true - }, - "is-subset": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-subset/-/is-subset-0.1.1.tgz", - "integrity": "sha1-ilkRfZMt4d4A8kX83TnOQ/HpOaY=", - "dev": true - }, - "is-symbol": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", - "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==", + "node_modules/conventional-commits-parser": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-6.0.0.tgz", + "integrity": "sha512-TbsINLp48XeMXR8EvGjTnKGsZqBemisPoyWESlpRyR8lif0lcwzqz+NMtYSj1ooF/WYjSuu7wX0CtdeeMEQAmA==", "dev": true, - "requires": { - "has-symbols": "^1.0.0" + "license": "MIT", + "dependencies": { + "meow": "^13.0.0" + }, + "bin": { + "conventional-commits-parser": "dist/cli/index.js" + }, + "engines": { + "node": ">=18" } }, - "is-text-path": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-text-path/-/is-text-path-1.0.1.tgz", - "integrity": "sha1-Thqg+1G/vLPpJogAE5cgLBd1tm4=", + "node_modules/convert-hrtime": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/convert-hrtime/-/convert-hrtime-5.0.0.tgz", + "integrity": "sha512-lOETlkIeYSJWcbbcvjRKGxVMXJR+8+OQb/mTPbA4ObPMytYIsUbuOE0Jzy60hjARYszq1id0j8KgVhC+WGZVTg==", "dev": true, - "requires": { - "text-extensions": "^1.0.0" + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" - }, - "is-utf8": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", - "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", - "dev": true - }, - "is-windows": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", - "dev": true - }, - "is-wsl": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", - "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=", - "dev": true - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, - "isexe": { + "node_modules/convert-source-map": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "license": "MIT" }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true + "node_modules/cookie": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } }, - "isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", + "license": "MIT" }, - "issue-parser": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/issue-parser/-/issue-parser-4.0.0.tgz", - "integrity": "sha512-1RmmAXHl5+cqTZ9dRr861xWy0Gkc9TWTEklgjKv+nhlB1dY1NmGBV8b20jTWRL5cPGpOIXkz84kEcDBM8Nc0cw==", + "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==", "dev": true, - "requires": { - "lodash.capitalize": "^4.2.1", - "lodash.escaperegexp": "^4.1.2", - "lodash.isplainobject": "^4.0.6", - "lodash.isstring": "^4.0.1", - "lodash.uniqby": "^4.7.0" - } - }, - "istanbul-lib-coverage": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", - "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==", - "dev": true + "license": "MIT" }, - "istanbul-lib-instrument": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-3.3.0.tgz", - "integrity": "sha512-5nnIN4vo5xQZHdXno/YDXJ0G+I3dAm4XgzfSVTPLQpj/zAV2dV6Juy0yaf10/zrJOJeHoN3fraFe+XRq2bFVZA==", - "dev": true, - "requires": { - "@babel/generator": "^7.4.0", - "@babel/parser": "^7.4.3", - "@babel/template": "^7.4.0", - "@babel/traverse": "^7.4.3", - "@babel/types": "^7.4.0", - "istanbul-lib-coverage": "^2.0.5", - "semver": "^6.0.0" - }, - "dependencies": { - "semver": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.1.1.tgz", - "integrity": "sha512-rWYq2e5iYW+fFe/oPPtYJxYgjBm8sC4rmoGdUOgBB7VnwKt6HrL793l2voH1UlsyYZpJ4g0wfjnTEO1s1NP2eQ==", - "dev": true + "node_modules/cosmiconfig": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz", + "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==", + "dev": true, + "license": "MIT", + "dependencies": { + "env-paths": "^2.2.1", + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true } } }, - "istanbul-lib-report": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-2.0.8.tgz", - "integrity": "sha512-fHBeG573EIihhAblwgxrSenp0Dby6tJMFR/HvlerBsrCTD5bkUuoNtn3gVh29ZCS824cGGBPn7Sg7cNk+2xUsQ==", + "node_modules/cosmiconfig-typescript-loader": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-6.1.0.tgz", + "integrity": "sha512-tJ1w35ZRUiM5FeTzT7DtYWAFFv37ZLqSRkGi2oeCK1gPhvaWjkAtfXvLmvE1pRfxxp9aQo6ba/Pvg1dKj05D4g==", "dev": true, - "requires": { - "istanbul-lib-coverage": "^2.0.5", - "make-dir": "^2.1.0", - "supports-color": "^6.1.0" - }, + "license": "MIT", + "optional": true, "dependencies": { - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } + "jiti": "^2.4.1" + }, + "engines": { + "node": ">=v18" + }, + "peerDependencies": { + "@types/node": "*", + "cosmiconfig": ">=9", + "typescript": ">=5" } }, - "istanbul-lib-source-maps": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.6.tgz", - "integrity": "sha512-R47KzMtDJH6X4/YW9XTx+jrLnZnscW4VpNN+1PViSYTejLVPWv7oov+Duf8YQSPyVRUvueQqz1TcsC6mooZTXw==", - "dev": true, - "requires": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^2.0.5", - "make-dir": "^2.1.0", - "rimraf": "^2.6.3", - "source-map": "^0.6.1" + "node_modules/create-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", + "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "prompts": "^2.0.1" }, + "bin": { + "create-jest": "bin/create-jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", "dependencies": { - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" } }, - "istanbul-reports": { - "version": "2.2.6", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-2.2.6.tgz", - "integrity": "sha512-SKi4rnMyLBKe0Jy2uUdx28h8oG7ph2PPuQPvIAh31d+Ci+lSiEu4C+h3oBPuJ9+mPKhOyW0M8gY4U5NM1WLeXA==", + "node_modules/crypto-random-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-4.0.0.tgz", + "integrity": "sha512-x8dy3RnvYdlUcPOjkEHqozhiwzKNSq7GcPuXFbnyMOCHxX8V3OgIg/pYuabl2sbUPfIJaeAQB7PMOK8DFIdoRA==", "dev": true, - "requires": { - "handlebars": "^4.1.2" + "license": "MIT", + "dependencies": { + "type-fest": "^1.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "java-properties": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/java-properties/-/java-properties-1.0.1.tgz", - "integrity": "sha512-HbTaaXlIHoDVNXjmp4flOBWOfYBkrVN8dD1tp4m+95M/ADSDW/BxWbiwyVIhw/2+5d0cof4PHZCbE7+S1ukTQw==", - "dev": true - }, - "jest": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/jest/-/jest-24.8.0.tgz", - "integrity": "sha512-o0HM90RKFRNWmAWvlyV8i5jGZ97pFwkeVoGvPW1EtLTgJc2+jcuqcbbqcSZLE/3f2S5pt0y2ZBETuhpWNl1Reg==", - "dev": true, - "requires": { - "import-local": "^2.0.0", - "jest-cli": "^24.8.0" - }, - "dependencies": { - "jest-cli": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-24.8.0.tgz", - "integrity": "sha512-+p6J00jSMPQ116ZLlHJJvdf8wbjNbZdeSX9ptfHX06/MSNaXmKihQzx5vQcw0q2G6JsdVkUIdWbOWtSnaYs3yA==", - "dev": true, - "requires": { - "@jest/core": "^24.8.0", - "@jest/test-result": "^24.8.0", - "@jest/types": "^24.8.0", - "chalk": "^2.0.1", - "exit": "^0.1.2", - "import-local": "^2.0.0", - "is-ci": "^2.0.0", - "jest-config": "^24.8.0", - "jest-util": "^24.8.0", - "jest-validate": "^24.8.0", - "prompts": "^2.0.1", - "realpath-native": "^1.1.0", - "yargs": "^12.0.2" - } - } + "node_modules/crypto-random-string/node_modules/type-fest": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", + "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "jest-changed-files": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-24.8.0.tgz", - "integrity": "sha512-qgANC1Yrivsq+UrLXsvJefBKVoCsKB0Hv+mBb6NMjjZ90wwxCDmU3hsCXBya30cH+LnPYjwgcU65i6yJ5Nfuug==", - "dev": true, - "requires": { - "@jest/types": "^24.8.0", - "execa": "^1.0.0", - "throat": "^4.0.0" - } - }, - "jest-config": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-24.8.0.tgz", - "integrity": "sha512-Czl3Nn2uEzVGsOeaewGWoDPD8GStxCpAe0zOYs2x2l0fZAgPbCr3uwUkgNKV3LwE13VXythM946cd5rdGkkBZw==", - "dev": true, - "requires": { - "@babel/core": "^7.1.0", - "@jest/test-sequencer": "^24.8.0", - "@jest/types": "^24.8.0", - "babel-jest": "^24.8.0", - "chalk": "^2.0.1", - "glob": "^7.1.1", - "jest-environment-jsdom": "^24.8.0", - "jest-environment-node": "^24.8.0", - "jest-get-type": "^24.8.0", - "jest-jasmine2": "^24.8.0", - "jest-regex-util": "^24.3.0", - "jest-resolve": "^24.8.0", - "jest-util": "^24.8.0", - "jest-validate": "^24.8.0", - "micromatch": "^3.1.10", - "pretty-format": "^24.8.0", - "realpath-native": "^1.1.0" - } - }, - "jest-diff": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-24.8.0.tgz", - "integrity": "sha512-wxetCEl49zUpJ/bvUmIFjd/o52J+yWcoc5ZyPq4/W1LUKGEhRYDIbP1KcF6t+PvqNrGAFk4/JhtxDq/Nnzs66g==", - "dev": true, - "requires": { - "chalk": "^2.0.1", - "diff-sequences": "^24.3.0", - "jest-get-type": "^24.8.0", - "pretty-format": "^24.8.0" - } - }, - "jest-docblock": { - "version": "24.3.0", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-24.3.0.tgz", - "integrity": "sha512-nlANmF9Yq1dufhFlKG9rasfQlrY7wINJbo3q01tu56Jv5eBU5jirylhF2O5ZBnLxzOVBGRDz/9NAwNyBtG4Nyg==", - "dev": true, - "requires": { - "detect-newline": "^2.1.0" - } - }, - "jest-each": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-24.8.0.tgz", - "integrity": "sha512-NrwK9gaL5+XgrgoCsd9svsoWdVkK4gnvyhcpzd6m487tXHqIdYeykgq3MKI1u4I+5Zf0tofr70at9dWJDeb+BA==", - "dev": true, - "requires": { - "@jest/types": "^24.8.0", - "chalk": "^2.0.1", - "jest-get-type": "^24.8.0", - "jest-util": "^24.8.0", - "pretty-format": "^24.8.0" - } - }, - "jest-environment-jsdom": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-24.8.0.tgz", - "integrity": "sha512-qbvgLmR7PpwjoFjM/sbuqHJt/NCkviuq9vus9NBn/76hhSidO+Z6Bn9tU8friecegbJL8gzZQEMZBQlFWDCwAQ==", - "dev": true, - "requires": { - "@jest/environment": "^24.8.0", - "@jest/fake-timers": "^24.8.0", - "@jest/types": "^24.8.0", - "jest-mock": "^24.8.0", - "jest-util": "^24.8.0", - "jsdom": "^11.5.1" - } - }, - "jest-environment-node": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-24.8.0.tgz", - "integrity": "sha512-vIGUEScd1cdDgR6sqn2M08sJTRLQp6Dk/eIkCeO4PFHxZMOgy+uYLPMC4ix3PEfM5Au/x3uQ/5Tl0DpXXZsJ/Q==", - "dev": true, - "requires": { - "@jest/environment": "^24.8.0", - "@jest/fake-timers": "^24.8.0", - "@jest/types": "^24.8.0", - "jest-mock": "^24.8.0", - "jest-util": "^24.8.0" - } - }, - "jest-get-type": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-24.8.0.tgz", - "integrity": "sha512-RR4fo8jEmMD9zSz2nLbs2j0zvPpk/KCEz3a62jJWbd2ayNo0cb+KFRxPHVhE4ZmgGJEQp0fosmNz84IfqM8cMQ==", - "dev": true - }, - "jest-haste-map": { - "version": "24.8.1", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-24.8.1.tgz", - "integrity": "sha512-SwaxMGVdAZk3ernAx2Uv2sorA7jm3Kx+lR0grp6rMmnY06Kn/urtKx1LPN2mGTea4fCT38impYT28FfcLUhX0g==", - "dev": true, - "requires": { - "@jest/types": "^24.8.0", - "anymatch": "^2.0.0", - "fb-watchman": "^2.0.0", - "fsevents": "^1.2.7", - "graceful-fs": "^4.1.15", - "invariant": "^2.2.4", - "jest-serializer": "^24.4.0", - "jest-util": "^24.8.0", - "jest-worker": "^24.6.0", - "micromatch": "^3.1.10", - "sane": "^4.0.3", - "walker": "^1.0.7" - } - }, - "jest-jasmine2": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-24.8.0.tgz", - "integrity": "sha512-cEky88npEE5LKd5jPpTdDCLvKkdyklnaRycBXL6GNmpxe41F0WN44+i7lpQKa/hcbXaQ+rc9RMaM4dsebrYong==", - "dev": true, - "requires": { - "@babel/traverse": "^7.1.0", - "@jest/environment": "^24.8.0", - "@jest/test-result": "^24.8.0", - "@jest/types": "^24.8.0", - "chalk": "^2.0.1", - "co": "^4.6.0", - "expect": "^24.8.0", - "is-generator-fn": "^2.0.0", - "jest-each": "^24.8.0", - "jest-matcher-utils": "^24.8.0", - "jest-message-util": "^24.8.0", - "jest-runtime": "^24.8.0", - "jest-snapshot": "^24.8.0", - "jest-util": "^24.8.0", - "pretty-format": "^24.8.0", - "throat": "^4.0.0" + "node_modules/cz-conventional-changelog": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/cz-conventional-changelog/-/cz-conventional-changelog-3.3.0.tgz", + "integrity": "sha512-U466fIzU5U22eES5lTNiNbZ+d8dfcHcssH4o7QsdWaCcRs/feIPCxKYSWkYBNs5mny7MvEfwpTLWjvbm94hecw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^2.4.1", + "commitizen": "^4.0.3", + "conventional-commit-types": "^3.0.0", + "lodash.map": "^4.5.1", + "longest": "^2.0.1", + "word-wrap": "^1.0.3" + }, + "engines": { + "node": ">= 10" + }, + "optionalDependencies": { + "@commitlint/load": ">6.1.1" } }, - "jest-leak-detector": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-24.8.0.tgz", - "integrity": "sha512-cG0yRSK8A831LN8lIHxI3AblB40uhv0z+SsQdW3GoMMVcK+sJwrIIyax5tu3eHHNJ8Fu6IMDpnLda2jhn2pD/g==", + "node_modules/cz-conventional-changelog/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, - "requires": { - "pretty-format": "^24.8.0" + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" } }, - "jest-matcher-utils": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-24.8.0.tgz", - "integrity": "sha512-lex1yASY51FvUuHgm0GOVj7DCYEouWSlIYmCW7APSqB9v8mXmKSn5+sWVF0MhuASG0bnYY106/49JU1FZNl5hw==", + "node_modules/cz-conventional-changelog/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dev": true, - "requires": { - "chalk": "^2.0.1", - "jest-diff": "^24.8.0", - "jest-get-type": "^24.8.0", - "pretty-format": "^24.8.0" + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" } }, - "jest-message-util": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-24.8.0.tgz", - "integrity": "sha512-p2k71rf/b6ns8btdB0uVdljWo9h0ovpnEe05ZKWceQGfXYr4KkzgKo3PBi8wdnd9OtNh46VpNIJynUn/3MKm1g==", + "node_modules/cz-conventional-changelog/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@jest/test-result": "^24.8.0", - "@jest/types": "^24.8.0", - "@types/stack-utils": "^1.0.1", - "chalk": "^2.0.1", - "micromatch": "^3.1.10", - "slash": "^2.0.0", - "stack-utils": "^1.0.1" + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" } }, - "jest-mock": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-24.8.0.tgz", - "integrity": "sha512-6kWugwjGjJw+ZkK4mDa0Df3sDlUTsV47MSrT0nGQ0RBWJbpODDQ8MHDVtGtUYBne3IwZUhtB7elxHspU79WH3A==", + "node_modules/cz-conventional-changelog/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", "dev": true, - "requires": { - "@jest/types": "^24.8.0" - } + "license": "MIT" }, - "jest-pnp-resolver": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.1.tgz", - "integrity": "sha512-pgFw2tm54fzgYvc/OHrnysABEObZCUNFnhjoRjaVOCN8NYc032/gVjPaHD4Aq6ApkSieWtfKAFQtmDKAmhupnQ==", - "dev": true - }, - "jest-regex-util": { - "version": "24.3.0", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-24.3.0.tgz", - "integrity": "sha512-tXQR1NEOyGlfylyEjg1ImtScwMq8Oh3iJbGTjN7p0J23EuVX1MA8rwU69K4sLbCmwzgCUbVkm0FkSF9TdzOhtg==", - "dev": true - }, - "jest-resolve": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-24.8.0.tgz", - "integrity": "sha512-+hjSzi1PoRvnuOICoYd5V/KpIQmkAsfjFO71458hQ2Whi/yf1GDeBOFj8Gxw4LrApHsVJvn5fmjcPdmoUHaVKw==", - "dev": true, - "requires": { - "@jest/types": "^24.8.0", - "browser-resolve": "^1.11.3", - "chalk": "^2.0.1", - "jest-pnp-resolver": "^1.2.1", - "realpath-native": "^1.1.0" - } - }, - "jest-resolve-dependencies": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-24.8.0.tgz", - "integrity": "sha512-hyK1qfIf/krV+fSNyhyJeq3elVMhK9Eijlwy+j5jqmZ9QsxwKBiP6qukQxaHtK8k6zql/KYWwCTQ+fDGTIJauw==", - "dev": true, - "requires": { - "@jest/types": "^24.8.0", - "jest-regex-util": "^24.3.0", - "jest-snapshot": "^24.8.0" - } - }, - "jest-runner": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-24.8.0.tgz", - "integrity": "sha512-utFqC5BaA3JmznbissSs95X1ZF+d+4WuOWwpM9+Ak356YtMhHE/GXUondZdcyAAOTBEsRGAgH/0TwLzfI9h7ow==", - "dev": true, - "requires": { - "@jest/console": "^24.7.1", - "@jest/environment": "^24.8.0", - "@jest/test-result": "^24.8.0", - "@jest/types": "^24.8.0", - "chalk": "^2.4.2", - "exit": "^0.1.2", - "graceful-fs": "^4.1.15", - "jest-config": "^24.8.0", - "jest-docblock": "^24.3.0", - "jest-haste-map": "^24.8.0", - "jest-jasmine2": "^24.8.0", - "jest-leak-detector": "^24.8.0", - "jest-message-util": "^24.8.0", - "jest-resolve": "^24.8.0", - "jest-runtime": "^24.8.0", - "jest-util": "^24.8.0", - "jest-worker": "^24.6.0", - "source-map-support": "^0.5.6", - "throat": "^4.0.0" - } - }, - "jest-runtime": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-24.8.0.tgz", - "integrity": "sha512-Mq0aIXhvO/3bX44ccT+czU1/57IgOMyy80oM0XR/nyD5zgBcesF84BPabZi39pJVA6UXw+fY2Q1N+4BiVUBWOA==", - "dev": true, - "requires": { - "@jest/console": "^24.7.1", - "@jest/environment": "^24.8.0", - "@jest/source-map": "^24.3.0", - "@jest/transform": "^24.8.0", - "@jest/types": "^24.8.0", - "@types/yargs": "^12.0.2", - "chalk": "^2.0.1", - "exit": "^0.1.2", - "glob": "^7.1.3", - "graceful-fs": "^4.1.15", - "jest-config": "^24.8.0", - "jest-haste-map": "^24.8.0", - "jest-message-util": "^24.8.0", - "jest-mock": "^24.8.0", - "jest-regex-util": "^24.3.0", - "jest-resolve": "^24.8.0", - "jest-snapshot": "^24.8.0", - "jest-util": "^24.8.0", - "jest-validate": "^24.8.0", - "realpath-native": "^1.1.0", - "slash": "^2.0.0", - "strip-bom": "^3.0.0", - "yargs": "^12.0.2" - } - }, - "jest-serializer": { - "version": "24.4.0", - "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-24.4.0.tgz", - "integrity": "sha512-k//0DtglVstc1fv+GY/VHDIjrtNjdYvYjMlbLUed4kxrE92sIUewOi5Hj3vrpB8CXfkJntRPDRjCrCvUhBdL8Q==", - "dev": true - }, - "jest-snapshot": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-24.8.0.tgz", - "integrity": "sha512-5ehtWoc8oU9/cAPe6fez6QofVJLBKyqkY2+TlKTOf0VllBB/mqUNdARdcjlZrs9F1Cv+/HKoCS/BknT0+tmfPg==", - "dev": true, - "requires": { - "@babel/types": "^7.0.0", - "@jest/types": "^24.8.0", - "chalk": "^2.0.1", - "expect": "^24.8.0", - "jest-diff": "^24.8.0", - "jest-matcher-utils": "^24.8.0", - "jest-message-util": "^24.8.0", - "jest-resolve": "^24.8.0", - "mkdirp": "^0.5.1", - "natural-compare": "^1.4.0", - "pretty-format": "^24.8.0", - "semver": "^5.5.0" + "node_modules/cz-conventional-changelog/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" } }, - "jest-util": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-24.8.0.tgz", - "integrity": "sha512-DYZeE+XyAnbNt0BG1OQqKy/4GVLPtzwGx5tsnDrFcax36rVE3lTA5fbvgmbVPUZf9w77AJ8otqR4VBbfFJkUZA==", + "node_modules/cz-conventional-changelog/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, - "requires": { - "@jest/console": "^24.7.1", - "@jest/fake-timers": "^24.8.0", - "@jest/source-map": "^24.3.0", - "@jest/test-result": "^24.8.0", - "@jest/types": "^24.8.0", - "callsites": "^3.0.0", - "chalk": "^2.0.1", - "graceful-fs": "^4.1.15", - "is-ci": "^2.0.0", - "mkdirp": "^0.5.1", - "slash": "^2.0.0", - "source-map": "^0.6.0" + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" }, + "engines": { + "node": ">=4" + } + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } + "ms": "2.0.0" } }, - "jest-validate": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-24.8.0.tgz", - "integrity": "sha512-+/N7VOEMW1Vzsrk3UWBDYTExTPwf68tavEPKDnJzrC6UlHtUDU/fuEdXqFoHzv9XnQ+zW6X3qMZhJ3YexfeLDA==", + "node_modules/dedent": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", + "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==", "dev": true, - "requires": { - "@jest/types": "^24.8.0", - "camelcase": "^5.0.0", - "chalk": "^2.0.1", - "jest-get-type": "^24.8.0", - "leven": "^2.1.0", - "pretty-format": "^24.8.0" - } + "license": "MIT" }, - "jest-watcher": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-24.8.0.tgz", - "integrity": "sha512-SBjwHt5NedQoVu54M5GEx7cl7IGEFFznvd/HNT8ier7cCAx/Qgu9ZMlaTQkvK22G1YOpcWBLQPFSImmxdn3DAw==", + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", "dev": true, - "requires": { - "@jest/test-result": "^24.8.0", - "@jest/types": "^24.8.0", - "@types/yargs": "^12.0.9", - "ansi-escapes": "^3.0.0", - "chalk": "^2.0.1", - "jest-util": "^24.8.0", - "string-length": "^2.0.0" + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" } }, - "jest-worker": { - "version": "24.6.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-24.6.0.tgz", - "integrity": "sha512-jDwgW5W9qGNvpI1tNnvajh0a5IE/PuGLFmHk6aR/BZFz8tSgGw17GsDPXAJ6p91IvYDjOw8GpFbvvZGAK+DPQQ==", + "node_modules/defaults": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", "dev": true, - "requires": { - "merge-stream": "^1.0.1", - "supports-color": "^6.1.0" - }, + "license": "MIT", "dependencies": { - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } + "clone": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "js-yaml": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", - "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" } }, - "jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" - }, - "jsdom": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-11.12.0.tgz", - "integrity": "sha512-y8Px43oyiBM13Zc1z780FrfNLJCXTL40EWlty/LXUtcjykRBNgLlCjWXpfSPBl2iv+N7koQN+dvqszHZgT/Fjw==", - "dev": true, - "requires": { - "abab": "^2.0.0", - "acorn": "^5.5.3", - "acorn-globals": "^4.1.0", - "array-equal": "^1.0.0", - "cssom": ">= 0.3.2 < 0.4.0", - "cssstyle": "^1.0.0", - "data-urls": "^1.0.0", - "domexception": "^1.0.1", - "escodegen": "^1.9.1", - "html-encoding-sniffer": "^1.0.2", - "left-pad": "^1.3.0", - "nwsapi": "^2.0.7", - "parse5": "4.0.0", - "pn": "^1.1.0", - "request": "^2.87.0", - "request-promise-native": "^1.0.5", - "sax": "^1.2.4", - "symbol-tree": "^3.2.2", - "tough-cookie": "^2.3.4", - "w3c-hr-time": "^1.0.1", - "webidl-conversions": "^4.0.2", - "whatwg-encoding": "^1.0.3", - "whatwg-mimetype": "^2.1.0", - "whatwg-url": "^6.4.1", - "ws": "^5.2.0", - "xml-name-validator": "^3.0.0" - } - }, - "jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true - }, - "json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", - "dev": true - }, - "json-placeholder-replacer": { - "version": "1.0.34", - "resolved": "https://registry.npmjs.org/json-placeholder-replacer/-/json-placeholder-replacer-1.0.34.tgz", - "integrity": "sha512-QNwmBDRGG6KYoaffysyP5LLSdDioyEbL1bo2eNcR/REtBJRw53PaPPkoemNCnI2ozqKZVnVVlkDabA5lKJrnkQ==" - }, - "json-schema": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" - }, - "json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "license": "MIT", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } }, - "json5": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.0.tgz", - "integrity": "sha512-8Mh9h6xViijj36g7Dxi+Y4S6hNGV96vcJZr/SrlHh1LR/pEn/8j/+qIBbs44YKl69Lrfctp4QD+AdWLTMqEZAQ==", + "node_modules/detect-file": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", + "integrity": "sha512-DtCOLG98P007x7wiiOmfI0fi3eIKyWiLTGJ2MDnVi/E04lWGbf+JzrRHMm0rgIIZJGtHpKpbVgLWHrv8xXpc3Q==", "dev": true, - "requires": { - "minimist": "^1.2.0" + "license": "MIT", + "engines": { + "node": ">=0.10.0" } }, - "jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "node_modules/detect-indent": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz", + "integrity": "sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==", "dev": true, - "requires": { - "graceful-fs": "^4.1.6" + "license": "MIT", + "engines": { + "node": ">=8" } }, - "jsonparse": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", - "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=", - "dev": true - }, - "jsprim": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", - "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", - "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.2.3", - "verror": "1.10.0" + "node_modules/detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "license": "MIT", + "engines": { + "node": ">=8" } }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", - "dev": true + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "devOptional": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } }, - "kleur": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", - "dev": true + "node_modules/diff-sequences": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } }, - "lcid": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", - "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", "dev": true, - "requires": { - "invert-kv": "^2.0.0" + "license": "MIT", + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, - "left-pad": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/left-pad/-/left-pad-1.3.0.tgz", - "integrity": "sha512-XI5MPzVNApjAyhQzphX8BkmKsKUxD4LdyK24iZeQGinBN9yTQT3bFlCBy/aVx2HrNcqQGsdot8ghrjyrvMCoEA==", - "dev": true - }, - "leven": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-2.1.0.tgz", - "integrity": "sha1-wuep93IJTe6dNCAq6KzORoeHVYA=", - "dev": true - }, - "levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "node_modules/dot-prop": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", + "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", "dev": true, - "requires": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" + "license": "MIT", + "dependencies": { + "is-obj": "^2.0.0" + }, + "engines": { + "node": ">=8" } }, - "lines-and-columns": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", - "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=", - "dev": true + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } }, - "load-json-file": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", - "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", + "node_modules/duplexer2": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", + "integrity": "sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==", "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^4.0.0", - "pify": "^3.0.0", - "strip-bom": "^3.0.0" + "license": "BSD-3-Clause", + "dependencies": { + "readable-stream": "^2.0.2" } }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "node_modules/duplexer2/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==", "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" + "license": "MIT", + "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" } }, - "lodash": { - "version": "4.17.14", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.14.tgz", - "integrity": "sha512-mmKYbW3GLuJeX+iGP+Y7Gp1AiGHGbXHCOh/jZmrawMmsE7MS4znI3RL2FsjbqOyMayHInjOeykW7PEajUk1/xw==", - "dev": true - }, - "lodash.capitalize": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/lodash.capitalize/-/lodash.capitalize-4.2.1.tgz", - "integrity": "sha1-+CbJtOKoUR2E46yinbBeGk87cqk=", - "dev": true - }, - "lodash.escaperegexp": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz", - "integrity": "sha1-ZHYsSGGAglGKw99Mz11YhtriA0c=", - "dev": true - }, - "lodash.get": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", - "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=", - "dev": true - }, - "lodash.isplainobject": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=", - "dev": true - }, - "lodash.isstring": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", - "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=", - "dev": true - }, - "lodash.map": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.map/-/lodash.map-4.6.0.tgz", - "integrity": "sha1-dx7Hg540c9nEzeKLGTlMNWL09tM=", - "dev": true - }, - "lodash.set": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/lodash.set/-/lodash.set-4.3.2.tgz", - "integrity": "sha1-2HV7HagH3eJIFrDWqEvqGnYjCyM=", - "dev": true + "node_modules/duplexer2/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==", + "dev": true, + "license": "MIT" }, - "lodash.sortby": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", - "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=", - "dev": true + "node_modules/duplexer2/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==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } }, - "lodash.toarray": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.toarray/-/lodash.toarray-4.4.0.tgz", - "integrity": "sha1-JMS/zWsvuji/0FlNsRedjptlZWE=", - "dev": true + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "license": "MIT" }, - "lodash.uniq": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", - "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=", - "dev": true + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" }, - "lodash.uniqby": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/lodash.uniqby/-/lodash.uniqby-4.7.0.tgz", - "integrity": "sha1-2ZwHpmnp5tJOE2Lf4mbGdhavEwI=", - "dev": true + "node_modules/ejs": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", + "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "jake": "^10.8.5" + }, + "bin": { + "ejs": "bin/cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.87", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.87.tgz", + "integrity": "sha512-mPFwmEWmRivw2F8x3w3l2m6htAUN97Gy0kwpO++2m9iT1Gt8RCFVUfv9U/sIbHJ6rY4P6/ooqFL/eL7ock+pPg==", + "license": "ISC" + }, + "node_modules/emittery": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", + "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } }, - "longest": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", - "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=", - "dev": true + "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==", + "license": "MIT" }, - "loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "node_modules/emojilib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/emojilib/-/emojilib-2.4.0.tgz", + "integrity": "sha512-5U0rVMU5Y2n2+ykNLQqMoqklN9ICBT/KsvC1Gz6vqHbz2AXXGkG+Pm5rMWk/8Vjrr/mY9985Hi8DYzn1F09Nyw==", "dev": true, - "requires": { - "js-tokens": "^3.0.0 || ^4.0.0" + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" } }, - "loud-rejection": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", - "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", + "node_modules/env-ci": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/env-ci/-/env-ci-11.1.0.tgz", + "integrity": "sha512-Z8dnwSDbV1XYM9SBF2J0GcNVvmfmfh3a49qddGIROhBoVro6MZVTji15z/sJbQ2ko2ei8n988EU1wzoLU/tF+g==", "dev": true, - "requires": { - "currently-unhandled": "^0.4.1", - "signal-exit": "^3.0.0" + "license": "MIT", + "dependencies": { + "execa": "^8.0.0", + "java-properties": "^1.0.2" + }, + "engines": { + "node": "^18.17 || >=20.6.1" } }, - "macos-release": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/macos-release/-/macos-release-2.3.0.tgz", - "integrity": "sha512-OHhSbtcviqMPt7yfw5ef5aghS2jzFVKEFyCJndQt2YpSQ9qRVSEv2axSJI1paVThEu+FFGs584h/1YhxjVqajA==", - "dev": true - }, - "make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "node_modules/env-ci/node_modules/execa": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", + "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", "dev": true, - "requires": { - "pify": "^4.0.1", - "semver": "^5.6.0" - }, + "license": "MIT", "dependencies": { - "pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "dev": true - } + "cross-spawn": "^7.0.3", + "get-stream": "^8.0.1", + "human-signals": "^5.0.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^3.0.0" + }, + "engines": { + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, - "make-error": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.5.tgz", - "integrity": "sha512-c3sIjNUow0+8swNwVpqoH4YCShKNFkMaw6oH1mNS2haDZQqkeZFlHS3dhoeEbKKmJB4vXpJucU6oH75aDYeE9g==", - "dev": true - }, - "makeerror": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.11.tgz", - "integrity": "sha1-4BpckQnyr3lmDk6LlYd5AYT1qWw=", + "node_modules/env-ci/node_modules/get-stream": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", + "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", "dev": true, - "requires": { - "tmpl": "1.0.x" + "license": "MIT", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "map-age-cleaner": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", - "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", + "node_modules/env-ci/node_modules/human-signals": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", + "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", "dev": true, - "requires": { - "p-defer": "^1.0.0" + "license": "Apache-2.0", + "engines": { + "node": ">=16.17.0" } }, - "map-cache": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", - "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", - "dev": true - }, - "map-obj": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-2.0.0.tgz", - "integrity": "sha1-plzSkIepJZi4eRJXpSPgISIqwfk=", - "dev": true - }, - "map-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", - "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", + "node_modules/env-ci/node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", "dev": true, - "requires": { - "object-visit": "^1.0.0" + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "marked": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/marked/-/marked-0.6.2.tgz", - "integrity": "sha512-LqxwVH3P/rqKX4EKGz7+c2G9r98WeM/SW34ybhgNGhUQNKtf1GmmSkJ6cDGJ/t6tiyae49qRkpyTw2B9HOrgUA==", - "dev": true - }, - "marked-terminal": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/marked-terminal/-/marked-terminal-3.2.0.tgz", - "integrity": "sha512-Yr1yVS0BbDG55vx7be1D0mdv+jGs9AW563o/Tt/7FTsId2J0yqhrTeXAqq/Q0DyyXltIn6CSxzesQuFqXgafjQ==", + "node_modules/env-ci/node_modules/mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", "dev": true, - "requires": { - "ansi-escapes": "^3.1.0", - "cardinal": "^2.1.1", - "chalk": "^2.4.1", - "cli-table": "^0.3.1", - "node-emoji": "^1.4.1", - "supports-hyperlinks": "^1.0.1" + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "media-typer": { - "version": "0.3.0", - "resolved": "http://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" - }, - "mem": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz", - "integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==", + "node_modules/env-ci/node_modules/npm-run-path": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", + "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", "dev": true, - "requires": { - "map-age-cleaner": "^0.1.1", - "mimic-fn": "^2.0.0", - "p-is-promise": "^2.0.0" - }, + "license": "MIT", "dependencies": { - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true - } + "path-key": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "meow": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/meow/-/meow-4.0.1.tgz", - "integrity": "sha512-xcSBHD5Z86zaOc+781KrupuHAzeGXSLtiAOmBsiLDiPSaYSB6hdew2ng9EBAnZ62jagG9MHAOdxpDi/lWBFJ/A==", - "dev": true, - "requires": { - "camelcase-keys": "^4.0.0", - "decamelize-keys": "^1.0.0", - "loud-rejection": "^1.0.0", - "minimist": "^1.1.3", - "minimist-options": "^3.0.1", - "normalize-package-data": "^2.3.4", - "read-pkg-up": "^3.0.0", - "redent": "^2.0.0", - "trim-newlines": "^2.0.0" - }, - "dependencies": { - "find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", - "dev": true, - "requires": { - "locate-path": "^2.0.0" - } - }, - "locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", - "dev": true, - "requires": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - } - }, - "p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", - "dev": true, - "requires": { - "p-try": "^1.0.0" - } - }, - "p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", - "dev": true, - "requires": { - "p-limit": "^1.1.0" - } - }, - "p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", - "dev": true - }, - "read-pkg-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz", - "integrity": "sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc=", - "dev": true, - "requires": { - "find-up": "^2.0.0", - "read-pkg": "^3.0.0" - } - } + "node_modules/env-ci/node_modules/onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "merge": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/merge/-/merge-1.2.1.tgz", - "integrity": "sha512-VjFo4P5Whtj4vsLzsYBu5ayHhoHJ0UqNm7ibvShmbmoz7tGi0vXaoJbGdB+GmDMLUdg8DpQXEIeVDAe8MaABvQ==", - "dev": true - }, - "merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" - }, - "merge-stream": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-1.0.1.tgz", - "integrity": "sha1-QEEgLVCKNCugAXQAjfDCUbjBNeE=", + "node_modules/env-ci/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", "dev": true, - "requires": { - "readable-stream": "^2.0.1" + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "merge2": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.2.3.tgz", - "integrity": "sha512-gdUU1Fwj5ep4kplwcmftruWofEFt6lfpkkr3h860CXbAB9c3hGb55EOL2ali0Td5oebvW0E1+3Sr+Ur7XfKpRA==", - "dev": true - }, - "methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - } - }, - "mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" - }, - "mime-db": { - "version": "1.38.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.38.0.tgz", - "integrity": "sha512-bqVioMFFzc2awcdJZIzR3HjZFX20QhilVS7hytkKrv7xFAn8bM1gzc/FOX2awLISvWe0PV8ptFKcon+wZ5qYkg==" - }, - "mime-types": { - "version": "2.1.22", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.22.tgz", - "integrity": "sha512-aGl6TZGnhm/li6F7yx82bJiBZwgiEa4Hf6CNr8YO+r5UHr53tSTYZb102zyU50DOWWKeOv0uQLRL0/9EiKWCog==", - "requires": { - "mime-db": "~1.38.0" + "node_modules/env-ci/node_modules/strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "mimic-fn": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", - "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", - "dev": true - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "requires": { - "brace-expansion": "^1.1.7" + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" } }, - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true - }, - "minimist-options": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-3.0.2.tgz", - "integrity": "sha512-FyBrT/d0d4+uiZRbqznPXqw3IpZZG3gl3wKWiX784FycUKVwBt0uLBFkQrtE4tZOrgo78nZp2jnKz3L65T5LdQ==", + "node_modules/environment": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz", + "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==", "dev": true, - "requires": { - "arrify": "^1.0.1", - "is-plain-obj": "^1.1.0" + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "mixin-deep": { + "node_modules/error-ex": { "version": "1.3.2", - "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", - "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", - "dev": true, - "requires": { - "for-in": "^1.0.2", - "is-extendable": "^1.0.1" - }, - "dependencies": { - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dev": true, - "requires": { - "is-plain-object": "^2.0.4" - } - } - } - }, - "mkdirp": { - "version": "0.5.1", - "resolved": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "dev": true, - "requires": { - "minimist": "0.0.8" - }, + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "license": "MIT", "dependencies": { - "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "dev": true - } + "is-arrayish": "^0.2.1" } }, - "modify-values": { + "node_modules/es-define-property": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/modify-values/-/modify-values-1.0.1.tgz", - "integrity": "sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw==", - "dev": true - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "mute-stream": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", - "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", - "dev": true - }, - "nan": { - "version": "2.14.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", - "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==", - "dev": true, - "optional": true + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } }, - "nanomatch": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", - "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", - "dev": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "fragment-cache": "^0.2.1", - "is-windows": "^1.0.2", - "kind-of": "^6.0.2", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - } - }, - "natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", - "dev": true + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } }, - "negotiator": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", - "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } }, - "neo-async": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.1.tgz", - "integrity": "sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw==", - "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==", + "license": "MIT", + "engines": { + "node": ">=6" + } }, - "nerf-dart": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/nerf-dart/-/nerf-dart-1.0.0.tgz", - "integrity": "sha1-5tq3/r9a2Bbqgc9cYpxaDr3nLBo=", - "dev": true + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" }, - "nice-try": { + "node_modules/escape-string-regexp": { "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", - "dev": true - }, - "node-emoji": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.10.0.tgz", - "integrity": "sha512-Yt3384If5H6BYGVHiHwTL+99OzJKHhgp82S8/dktEK73T26BazdgZ4JZh92xSVtGNJvz9UbXdNAc5hcrXV42vw==", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "dev": true, - "requires": { - "lodash.toarray": "^4.4.0" + "license": "MIT", + "engines": { + "node": ">=0.8.0" } }, - "node-fetch": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz", - "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==", - "dev": true - }, - "node-int64": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs=", - "dev": true - }, - "node-modules-regexp": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz", - "integrity": "sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA=", - "dev": true - }, - "node-notifier": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-5.4.0.tgz", - "integrity": "sha512-SUDEb+o71XR5lXSTyivXd9J7fCloE3SyP4lSgt3lU2oSANiox+SxlNRGPjDKrwU1YN3ix2KN/VGGCg0t01rttQ==", - "dev": true, - "requires": { - "growly": "^1.3.0", - "is-wsl": "^1.1.0", - "semver": "^5.5.0", - "shellwords": "^0.1.1", - "which": "^1.3.0" + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" } }, - "normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dev": true, - "requires": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" } }, - "normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", - "dev": true, - "requires": { - "remove-trailing-separator": "^1.0.1" + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, - "normalize-url": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.0.tgz", - "integrity": "sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==", - "dev": true - }, - "npm": { - "version": "6.13.7", - "resolved": "https://registry.npmjs.org/npm/-/npm-6.13.7.tgz", - "integrity": "sha512-X967EKTT407CvgrWFjXusnPh0VLERcmR9hZFSVgkEquOomZkvpwLJ5zrQ3qrG9SpPLKJE4bPLUu76exKQ4a3Cg==", - "dev": true, - "requires": { - "JSONStream": "^1.3.5", - "abbrev": "~1.1.1", - "ansicolors": "~0.3.2", - "ansistyles": "~0.1.3", - "aproba": "^2.0.0", - "archy": "~1.0.0", - "bin-links": "^1.1.7", - "bluebird": "^3.5.5", - "byte-size": "^5.0.1", - "cacache": "^12.0.3", - "call-limit": "^1.1.1", - "chownr": "^1.1.3", - "ci-info": "^2.0.0", - "cli-columns": "^3.1.2", - "cli-table3": "^0.5.1", - "cmd-shim": "^3.0.3", - "columnify": "~1.5.4", - "config-chain": "^1.1.12", - "debuglog": "*", - "detect-indent": "~5.0.0", - "detect-newline": "^2.1.0", - "dezalgo": "~1.0.3", - "editor": "~1.0.0", - "figgy-pudding": "^3.5.1", - "find-npm-prefix": "^1.0.2", - "fs-vacuum": "~1.2.10", - "fs-write-stream-atomic": "~1.0.10", - "gentle-fs": "^2.3.0", - "glob": "^7.1.4", - "graceful-fs": "^4.2.3", - "has-unicode": "~2.0.1", - "hosted-git-info": "^2.8.5", - "iferr": "^1.0.2", - "imurmurhash": "*", - "infer-owner": "^1.0.4", - "inflight": "~1.0.6", - "inherits": "^2.0.4", - "ini": "^1.3.5", - "init-package-json": "^1.10.3", - "is-cidr": "^3.0.0", - "json-parse-better-errors": "^1.0.2", - "lazy-property": "~1.0.0", - "libcipm": "^4.0.7", - "libnpm": "^3.0.1", - "libnpmaccess": "^3.0.2", - "libnpmhook": "^5.0.3", - "libnpmorg": "^1.0.1", - "libnpmsearch": "^2.0.2", - "libnpmteam": "^1.0.2", - "libnpx": "^10.2.2", - "lock-verify": "^2.1.0", - "lockfile": "^1.0.4", - "lodash._baseindexof": "*", - "lodash._baseuniq": "~4.6.0", - "lodash._bindcallback": "*", - "lodash._cacheindexof": "*", - "lodash._createcache": "*", - "lodash._getnative": "*", - "lodash.clonedeep": "~4.5.0", - "lodash.restparam": "*", - "lodash.union": "~4.6.0", - "lodash.uniq": "~4.5.0", - "lodash.without": "~4.4.0", - "lru-cache": "^5.1.1", - "meant": "~1.0.1", - "mississippi": "^3.0.0", - "mkdirp": "~0.5.1", - "move-concurrently": "^1.0.1", - "node-gyp": "^5.0.7", - "nopt": "~4.0.1", - "normalize-package-data": "^2.5.0", - "npm-audit-report": "^1.3.2", - "npm-cache-filename": "~1.0.2", - "npm-install-checks": "^3.0.2", - "npm-lifecycle": "^3.1.4", - "npm-package-arg": "^6.1.1", - "npm-packlist": "^1.4.7", - "npm-pick-manifest": "^3.0.2", - "npm-profile": "^4.0.2", - "npm-registry-fetch": "^4.0.2", - "npm-user-validate": "~1.0.0", - "npmlog": "~4.1.2", - "once": "~1.4.0", - "opener": "^1.5.1", - "osenv": "^0.1.5", - "pacote": "^9.5.12", - "path-is-inside": "~1.0.2", - "promise-inflight": "~1.0.1", - "qrcode-terminal": "^0.12.0", - "query-string": "^6.8.2", - "qw": "~1.0.1", - "read": "~1.0.7", - "read-cmd-shim": "^1.0.5", - "read-installed": "~4.0.3", - "read-package-json": "^2.1.1", - "read-package-tree": "^5.3.1", - "readable-stream": "^3.4.0", - "readdir-scoped-modules": "^1.1.0", - "request": "^2.88.0", - "retry": "^0.12.0", - "rimraf": "^2.6.3", - "safe-buffer": "^5.1.2", - "semver": "^5.7.1", - "sha": "^3.0.0", - "slide": "~1.1.6", - "sorted-object": "~2.0.1", - "sorted-union-stream": "~2.1.3", - "ssri": "^6.0.1", - "stringify-package": "^1.0.1", - "tar": "^4.4.13", - "text-table": "~0.2.0", - "tiny-relative-date": "^1.3.0", - "uid-number": "0.0.6", - "umask": "~1.1.0", - "unique-filename": "^1.1.1", - "unpipe": "~1.0.0", - "update-notifier": "^2.5.0", - "uuid": "^3.3.3", - "validate-npm-package-license": "^3.0.4", - "validate-npm-package-name": "~3.0.0", - "which": "^1.3.1", - "worker-farm": "^1.7.0", - "write-file-atomic": "^2.4.3" - }, - "dependencies": { - "JSONStream": { - "version": "1.3.5", - "bundled": true, - "dev": true, - "requires": { - "jsonparse": "^1.2.0", - "through": ">=2.2.7 <3" - } - }, - "abbrev": { - "version": "1.1.1", - "bundled": true, - "dev": true - }, - "agent-base": { - "version": "4.3.0", - "bundled": true, - "dev": true, - "requires": { - "es6-promisify": "^5.0.0" - } - }, - "agentkeepalive": { - "version": "3.5.2", - "bundled": true, - "dev": true, - "requires": { - "humanize-ms": "^1.2.1" - } - }, - "ajv": { - "version": "5.5.2", - "bundled": true, - "dev": true, - "requires": { - "co": "^4.6.0", - "fast-deep-equal": "^1.0.0", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.3.0" - } - }, - "ansi-align": { - "version": "2.0.0", - "bundled": true, - "dev": true, - "requires": { - "string-width": "^2.0.0" - } - }, - "ansi-regex": { - "version": "2.1.1", - "bundled": true, - "dev": true - }, - "ansi-styles": { - "version": "3.2.1", - "bundled": true, - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "ansicolors": { - "version": "0.3.2", - "bundled": true, - "dev": true - }, - "ansistyles": { - "version": "0.1.3", - "bundled": true, - "dev": true - }, - "aproba": { - "version": "2.0.0", - "bundled": true, - "dev": true - }, - "archy": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "are-we-there-yet": { - "version": "1.1.4", - "bundled": true, - "dev": true, - "requires": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.6", - "bundled": true, - "dev": true, - "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" - } - }, - "string_decoder": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, - "asap": { - "version": "2.0.6", - "bundled": true, - "dev": true - }, - "asn1": { - "version": "0.2.4", - "bundled": true, - "dev": true, - "requires": { - "safer-buffer": "~2.1.0" - } - }, - "assert-plus": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "asynckit": { - "version": "0.4.0", - "bundled": true, - "dev": true - }, - "aws-sign2": { - "version": "0.7.0", - "bundled": true, - "dev": true - }, - "aws4": { - "version": "1.8.0", - "bundled": true, - "dev": true - }, - "balanced-match": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "bcrypt-pbkdf": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "tweetnacl": "^0.14.3" - } - }, - "bin-links": { - "version": "1.1.7", - "bundled": true, - "dev": true, - "requires": { - "bluebird": "^3.5.3", - "cmd-shim": "^3.0.0", - "gentle-fs": "^2.3.0", - "graceful-fs": "^4.1.15", - "npm-normalize-package-bin": "^1.0.0", - "write-file-atomic": "^2.3.0" - } - }, - "bluebird": { - "version": "3.5.5", - "bundled": true, - "dev": true - }, - "boxen": { - "version": "1.3.0", - "bundled": true, - "dev": true, - "requires": { - "ansi-align": "^2.0.0", - "camelcase": "^4.0.0", - "chalk": "^2.0.1", - "cli-boxes": "^1.0.0", - "string-width": "^2.0.0", - "term-size": "^1.2.0", - "widest-line": "^2.0.0" - } - }, - "brace-expansion": { - "version": "1.1.11", - "bundled": true, - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "buffer-from": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "builtins": { - "version": "1.0.3", - "bundled": true, - "dev": true - }, - "byline": { - "version": "5.0.0", - "bundled": true, - "dev": true - }, - "byte-size": { - "version": "5.0.1", - "bundled": true, - "dev": true - }, - "cacache": { - "version": "12.0.3", - "bundled": true, - "dev": true, - "requires": { - "bluebird": "^3.5.5", - "chownr": "^1.1.1", - "figgy-pudding": "^3.5.1", - "glob": "^7.1.4", - "graceful-fs": "^4.1.15", - "infer-owner": "^1.0.3", - "lru-cache": "^5.1.1", - "mississippi": "^3.0.0", - "mkdirp": "^0.5.1", - "move-concurrently": "^1.0.1", - "promise-inflight": "^1.0.1", - "rimraf": "^2.6.3", - "ssri": "^6.0.1", - "unique-filename": "^1.1.1", - "y18n": "^4.0.0" - } - }, - "call-limit": { - "version": "1.1.1", - "bundled": true, - "dev": true - }, - "camelcase": { - "version": "4.1.0", - "bundled": true, - "dev": true - }, - "capture-stack-trace": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "caseless": { - "version": "0.12.0", - "bundled": true, - "dev": true - }, - "chalk": { - "version": "2.4.1", - "bundled": true, - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "chownr": { - "version": "1.1.3", - "bundled": true, - "dev": true - }, - "ci-info": { - "version": "2.0.0", - "bundled": true, - "dev": true - }, - "cidr-regex": { - "version": "2.0.10", - "bundled": true, - "dev": true, - "requires": { - "ip-regex": "^2.1.0" - } - }, - "cli-boxes": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "cli-columns": { - "version": "3.1.2", - "bundled": true, - "dev": true, - "requires": { - "string-width": "^2.0.0", - "strip-ansi": "^3.0.1" - } - }, - "cli-table3": { - "version": "0.5.1", - "bundled": true, - "dev": true, - "requires": { - "colors": "^1.1.2", - "object-assign": "^4.1.0", - "string-width": "^2.1.1" - } - }, - "cliui": { - "version": "4.1.0", - "bundled": true, - "dev": true, - "requires": { - "string-width": "^2.1.1", - "strip-ansi": "^4.0.0", - "wrap-ansi": "^2.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "bundled": true, - "dev": true - }, - "strip-ansi": { - "version": "4.0.0", - "bundled": true, - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - } - } - }, - "clone": { - "version": "1.0.4", - "bundled": true, - "dev": true - }, - "cmd-shim": { - "version": "3.0.3", - "bundled": true, - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "mkdirp": "~0.5.0" - } - }, - "co": { - "version": "4.6.0", - "bundled": true, - "dev": true - }, - "code-point-at": { - "version": "1.1.0", - "bundled": true, - "dev": true - }, - "color-convert": { - "version": "1.9.1", - "bundled": true, - "dev": true, - "requires": { - "color-name": "^1.1.1" - } - }, - "color-name": { - "version": "1.1.3", - "bundled": true, - "dev": true - }, - "colors": { - "version": "1.3.3", - "bundled": true, - "dev": true, - "optional": true - }, - "columnify": { - "version": "1.5.4", - "bundled": true, - "dev": true, - "requires": { - "strip-ansi": "^3.0.0", - "wcwidth": "^1.0.0" - } - }, - "combined-stream": { - "version": "1.0.6", - "bundled": true, - "dev": true, - "requires": { - "delayed-stream": "~1.0.0" - } - }, - "concat-map": { - "version": "0.0.1", - "bundled": true, - "dev": true - }, - "concat-stream": { - "version": "1.6.2", - "bundled": true, - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^2.2.2", - "typedarray": "^0.0.6" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.6", - "bundled": true, - "dev": true, - "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" - } - }, - "string_decoder": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, - "config-chain": { - "version": "1.1.12", - "bundled": true, - "dev": true, - "requires": { - "ini": "^1.3.4", - "proto-list": "~1.2.1" - } - }, - "configstore": { - "version": "3.1.2", - "bundled": true, - "dev": true, - "requires": { - "dot-prop": "^4.1.0", - "graceful-fs": "^4.1.2", - "make-dir": "^1.0.0", - "unique-string": "^1.0.0", - "write-file-atomic": "^2.0.0", - "xdg-basedir": "^3.0.0" - } - }, - "console-control-strings": { - "version": "1.1.0", - "bundled": true, - "dev": true - }, - "copy-concurrently": { - "version": "1.0.5", - "bundled": true, - "dev": true, - "requires": { - "aproba": "^1.1.1", - "fs-write-stream-atomic": "^1.0.8", - "iferr": "^0.1.5", - "mkdirp": "^0.5.1", - "rimraf": "^2.5.4", - "run-queue": "^1.0.0" - }, - "dependencies": { - "aproba": { - "version": "1.2.0", - "bundled": true, - "dev": true - }, - "iferr": { - "version": "0.1.5", - "bundled": true, - "dev": true - } - } - }, - "core-util-is": { - "version": "1.0.2", - "bundled": true, - "dev": true - }, - "create-error-class": { - "version": "3.0.2", - "bundled": true, - "dev": true, - "requires": { - "capture-stack-trace": "^1.0.0" - } - }, - "cross-spawn": { - "version": "5.1.0", - "bundled": true, - "dev": true, - "requires": { - "lru-cache": "^4.0.1", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - }, - "dependencies": { - "lru-cache": { - "version": "4.1.5", - "bundled": true, - "dev": true, - "requires": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" - } - }, - "yallist": { - "version": "2.1.2", - "bundled": true, - "dev": true - } - } - }, - "crypto-random-string": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "cyclist": { - "version": "0.2.2", - "bundled": true, - "dev": true - }, - "dashdash": { - "version": "1.14.1", - "bundled": true, - "dev": true, - "requires": { - "assert-plus": "^1.0.0" - } - }, - "debug": { - "version": "3.1.0", - "bundled": true, - "dev": true, - "requires": { - "ms": "2.0.0" - }, - "dependencies": { - "ms": { - "version": "2.0.0", - "bundled": true, - "dev": true - } - } - }, - "debuglog": { - "version": "1.0.1", - "bundled": true, - "dev": true - }, - "decamelize": { - "version": "1.2.0", - "bundled": true, - "dev": true - }, - "decode-uri-component": { - "version": "0.2.0", - "bundled": true, - "dev": true - }, - "deep-extend": { - "version": "0.5.1", - "bundled": true, - "dev": true - }, - "defaults": { - "version": "1.0.3", - "bundled": true, - "dev": true, - "requires": { - "clone": "^1.0.2" - } - }, - "define-properties": { - "version": "1.1.3", - "bundled": true, - "dev": true, - "requires": { - "object-keys": "^1.0.12" - } - }, - "delayed-stream": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "delegates": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "detect-indent": { - "version": "5.0.0", - "bundled": true, - "dev": true - }, - "detect-newline": { - "version": "2.1.0", - "bundled": true, - "dev": true - }, - "dezalgo": { - "version": "1.0.3", - "bundled": true, - "dev": true, - "requires": { - "asap": "^2.0.0", - "wrappy": "1" - } - }, - "dot-prop": { - "version": "4.2.0", - "bundled": true, - "dev": true, - "requires": { - "is-obj": "^1.0.0" - } - }, - "dotenv": { - "version": "5.0.1", - "bundled": true, - "dev": true - }, - "duplexer3": { - "version": "0.1.4", - "bundled": true, - "dev": true - }, - "duplexify": { - "version": "3.6.0", - "bundled": true, - "dev": true, - "requires": { - "end-of-stream": "^1.0.0", - "inherits": "^2.0.1", - "readable-stream": "^2.0.0", - "stream-shift": "^1.0.0" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.6", - "bundled": true, - "dev": true, - "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" - } - }, - "string_decoder": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, - "ecc-jsbn": { - "version": "0.1.2", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" - } - }, - "editor": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "encoding": { - "version": "0.1.12", - "bundled": true, - "dev": true, - "requires": { - "iconv-lite": "~0.4.13" - } - }, - "end-of-stream": { - "version": "1.4.1", - "bundled": true, - "dev": true, - "requires": { - "once": "^1.4.0" - } - }, - "env-paths": { - "version": "2.2.0", - "bundled": true, - "dev": true - }, - "err-code": { - "version": "1.1.2", - "bundled": true, - "dev": true - }, - "errno": { - "version": "0.1.7", - "bundled": true, - "dev": true, - "requires": { - "prr": "~1.0.1" - } - }, - "es-abstract": { - "version": "1.12.0", - "bundled": true, - "dev": true, - "requires": { - "es-to-primitive": "^1.1.1", - "function-bind": "^1.1.1", - "has": "^1.0.1", - "is-callable": "^1.1.3", - "is-regex": "^1.0.4" - } - }, - "es-to-primitive": { - "version": "1.2.0", - "bundled": true, - "dev": true, - "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - } - }, - "es6-promise": { - "version": "4.2.8", - "bundled": true, - "dev": true - }, - "es6-promisify": { - "version": "5.0.0", - "bundled": true, - "dev": true, - "requires": { - "es6-promise": "^4.0.3" - } - }, - "escape-string-regexp": { - "version": "1.0.5", - "bundled": true, - "dev": true - }, - "execa": { - "version": "0.7.0", - "bundled": true, - "dev": true, - "requires": { - "cross-spawn": "^5.0.1", - "get-stream": "^3.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - }, - "dependencies": { - "get-stream": { - "version": "3.0.0", - "bundled": true, - "dev": true - } - } - }, - "extend": { - "version": "3.0.2", - "bundled": true, - "dev": true - }, - "extsprintf": { - "version": "1.3.0", - "bundled": true, - "dev": true - }, - "fast-deep-equal": { - "version": "1.1.0", - "bundled": true, - "dev": true - }, - "fast-json-stable-stringify": { - "version": "2.0.0", - "bundled": true, - "dev": true - }, - "figgy-pudding": { - "version": "3.5.1", - "bundled": true, - "dev": true - }, - "find-npm-prefix": { - "version": "1.0.2", - "bundled": true, - "dev": true - }, - "find-up": { - "version": "2.1.0", - "bundled": true, - "dev": true, - "requires": { - "locate-path": "^2.0.0" - } - }, - "flush-write-stream": { - "version": "1.0.3", - "bundled": true, - "dev": true, - "requires": { - "inherits": "^2.0.1", - "readable-stream": "^2.0.4" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.6", - "bundled": true, - "dev": true, - "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" - } - }, - "string_decoder": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, - "forever-agent": { - "version": "0.6.1", - "bundled": true, - "dev": true - }, - "form-data": { - "version": "2.3.2", - "bundled": true, - "dev": true, - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "1.0.6", - "mime-types": "^2.1.12" - } - }, - "from2": { - "version": "2.3.0", - "bundled": true, - "dev": true, - "requires": { - "inherits": "^2.0.1", - "readable-stream": "^2.0.0" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.6", - "bundled": true, - "dev": true, - "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" - } - }, - "string_decoder": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, - "fs-minipass": { - "version": "1.2.7", - "bundled": true, - "dev": true, - "requires": { - "minipass": "^2.6.0" - }, - "dependencies": { - "minipass": { - "version": "2.9.0", - "bundled": true, - "dev": true, - "requires": { - "safe-buffer": "^5.1.2", - "yallist": "^3.0.0" - } - } - } - }, - "fs-vacuum": { - "version": "1.2.10", - "bundled": true, - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "path-is-inside": "^1.0.1", - "rimraf": "^2.5.2" - } - }, - "fs-write-stream-atomic": { - "version": "1.0.10", - "bundled": true, - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "iferr": "^0.1.5", - "imurmurhash": "^0.1.4", - "readable-stream": "1 || 2" - }, - "dependencies": { - "iferr": { - "version": "0.1.5", - "bundled": true, - "dev": true - }, - "readable-stream": { - "version": "2.3.6", - "bundled": true, - "dev": true, - "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" - } - }, - "string_decoder": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, - "fs.realpath": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "function-bind": { - "version": "1.1.1", - "bundled": true, - "dev": true - }, - "gauge": { - "version": "2.7.4", - "bundled": true, - "dev": true, - "requires": { - "aproba": "^1.0.3", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.0", - "object-assign": "^4.1.0", - "signal-exit": "^3.0.0", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wide-align": "^1.1.0" - }, - "dependencies": { - "aproba": { - "version": "1.2.0", - "bundled": true, - "dev": true - }, - "string-width": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - } - } - }, - "genfun": { - "version": "5.0.0", - "bundled": true, - "dev": true - }, - "gentle-fs": { - "version": "2.3.0", - "bundled": true, - "dev": true, - "requires": { - "aproba": "^1.1.2", - "chownr": "^1.1.2", - "cmd-shim": "^3.0.3", - "fs-vacuum": "^1.2.10", - "graceful-fs": "^4.1.11", - "iferr": "^0.1.5", - "infer-owner": "^1.0.4", - "mkdirp": "^0.5.1", - "path-is-inside": "^1.0.2", - "read-cmd-shim": "^1.0.1", - "slide": "^1.1.6" - }, - "dependencies": { - "aproba": { - "version": "1.2.0", - "bundled": true, - "dev": true - }, - "iferr": { - "version": "0.1.5", - "bundled": true, - "dev": true - } - } - }, - "get-caller-file": { - "version": "1.0.3", - "bundled": true, - "dev": true - }, - "get-stream": { - "version": "4.1.0", - "bundled": true, - "dev": true, - "requires": { - "pump": "^3.0.0" - } - }, - "getpass": { - "version": "0.1.7", - "bundled": true, - "dev": true, - "requires": { - "assert-plus": "^1.0.0" - } - }, - "glob": { - "version": "7.1.4", - "bundled": true, - "dev": true, - "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" - } - }, - "global-dirs": { - "version": "0.1.1", - "bundled": true, - "dev": true, - "requires": { - "ini": "^1.3.4" - } - }, - "got": { - "version": "6.7.1", - "bundled": true, - "dev": true, - "requires": { - "create-error-class": "^3.0.0", - "duplexer3": "^0.1.4", - "get-stream": "^3.0.0", - "is-redirect": "^1.0.0", - "is-retry-allowed": "^1.0.0", - "is-stream": "^1.0.0", - "lowercase-keys": "^1.0.0", - "safe-buffer": "^5.0.1", - "timed-out": "^4.0.0", - "unzip-response": "^2.0.1", - "url-parse-lax": "^1.0.0" - }, - "dependencies": { - "get-stream": { - "version": "3.0.0", - "bundled": true, - "dev": true - } - } - }, - "graceful-fs": { - "version": "4.2.3", - "bundled": true, - "dev": true - }, - "har-schema": { - "version": "2.0.0", - "bundled": true, - "dev": true - }, - "har-validator": { - "version": "5.1.0", - "bundled": true, - "dev": true, - "requires": { - "ajv": "^5.3.0", - "har-schema": "^2.0.0" - } - }, - "has": { - "version": "1.0.3", - "bundled": true, - "dev": true, - "requires": { - "function-bind": "^1.1.1" - } - }, - "has-flag": { - "version": "3.0.0", - "bundled": true, - "dev": true - }, - "has-symbols": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "has-unicode": { - "version": "2.0.1", - "bundled": true, - "dev": true - }, - "hosted-git-info": { - "version": "2.8.5", - "bundled": true, - "dev": true - }, - "http-cache-semantics": { - "version": "3.8.1", - "bundled": true, - "dev": true - }, - "http-proxy-agent": { - "version": "2.1.0", - "bundled": true, - "dev": true, - "requires": { - "agent-base": "4", - "debug": "3.1.0" - } - }, - "http-signature": { - "version": "1.2.0", - "bundled": true, - "dev": true, - "requires": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - } - }, - "https-proxy-agent": { - "version": "2.2.4", - "bundled": true, - "dev": true, - "requires": { - "agent-base": "^4.3.0", - "debug": "^3.1.0" - } - }, - "humanize-ms": { - "version": "1.2.1", - "bundled": true, - "dev": true, - "requires": { - "ms": "^2.0.0" - } - }, - "iconv-lite": { - "version": "0.4.23", - "bundled": true, - "dev": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "iferr": { - "version": "1.0.2", - "bundled": true, - "dev": true - }, - "ignore-walk": { - "version": "3.0.3", - "bundled": true, - "dev": true, - "requires": { - "minimatch": "^3.0.4" - } - }, - "import-lazy": { - "version": "2.1.0", - "bundled": true, - "dev": true - }, - "imurmurhash": { - "version": "0.1.4", - "bundled": true, - "dev": true - }, - "infer-owner": { - "version": "1.0.4", - "bundled": true, - "dev": true - }, - "inflight": { - "version": "1.0.6", - "bundled": true, - "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "bundled": true, - "dev": true - }, - "ini": { - "version": "1.3.5", - "bundled": true, - "dev": true - }, - "init-package-json": { - "version": "1.10.3", - "bundled": true, - "dev": true, - "requires": { - "glob": "^7.1.1", - "npm-package-arg": "^4.0.0 || ^5.0.0 || ^6.0.0", - "promzard": "^0.3.0", - "read": "~1.0.1", - "read-package-json": "1 || 2", - "semver": "2.x || 3.x || 4 || 5", - "validate-npm-package-license": "^3.0.1", - "validate-npm-package-name": "^3.0.0" - } - }, - "invert-kv": { - "version": "2.0.0", - "bundled": true, - "dev": true - }, - "ip": { - "version": "1.1.5", - "bundled": true, - "dev": true - }, - "ip-regex": { - "version": "2.1.0", - "bundled": true, - "dev": true - }, - "is-callable": { - "version": "1.1.4", - "bundled": true, - "dev": true - }, - "is-ci": { - "version": "1.1.0", - "bundled": true, - "dev": true, - "requires": { - "ci-info": "^1.0.0" - }, - "dependencies": { - "ci-info": { - "version": "1.6.0", - "bundled": true, - "dev": true - } - } - }, - "is-cidr": { - "version": "3.0.0", - "bundled": true, - "dev": true, - "requires": { - "cidr-regex": "^2.0.10" - } - }, - "is-date-object": { - "version": "1.0.1", - "bundled": true, - "dev": true - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "is-installed-globally": { - "version": "0.1.0", - "bundled": true, - "dev": true, - "requires": { - "global-dirs": "^0.1.0", - "is-path-inside": "^1.0.0" - } - }, - "is-npm": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "is-obj": { - "version": "1.0.1", - "bundled": true, - "dev": true - }, - "is-path-inside": { - "version": "1.0.1", - "bundled": true, - "dev": true, - "requires": { - "path-is-inside": "^1.0.1" - } - }, - "is-redirect": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "is-regex": { - "version": "1.0.4", - "bundled": true, - "dev": true, - "requires": { - "has": "^1.0.1" - } - }, - "is-retry-allowed": { - "version": "1.1.0", - "bundled": true, - "dev": true - }, - "is-stream": { - "version": "1.1.0", - "bundled": true, - "dev": true - }, - "is-symbol": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "requires": { - "has-symbols": "^1.0.0" - } - }, - "is-typedarray": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "isarray": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "isexe": { - "version": "2.0.0", - "bundled": true, - "dev": true - }, - "isstream": { - "version": "0.1.2", - "bundled": true, - "dev": true - }, - "jsbn": { - "version": "0.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "json-parse-better-errors": { - "version": "1.0.2", - "bundled": true, - "dev": true - }, - "json-schema": { - "version": "0.2.3", - "bundled": true, - "dev": true - }, - "json-schema-traverse": { - "version": "0.3.1", - "bundled": true, - "dev": true - }, - "json-stringify-safe": { - "version": "5.0.1", - "bundled": true, - "dev": true - }, - "jsonparse": { - "version": "1.3.1", - "bundled": true, - "dev": true - }, - "jsprim": { - "version": "1.4.1", - "bundled": true, - "dev": true, - "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.2.3", - "verror": "1.10.0" - } - }, - "latest-version": { - "version": "3.1.0", - "bundled": true, - "dev": true, - "requires": { - "package-json": "^4.0.0" - } - }, - "lazy-property": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "lcid": { - "version": "2.0.0", - "bundled": true, - "dev": true, - "requires": { - "invert-kv": "^2.0.0" - } - }, - "libcipm": { - "version": "4.0.7", - "bundled": true, - "dev": true, - "requires": { - "bin-links": "^1.1.2", - "bluebird": "^3.5.1", - "figgy-pudding": "^3.5.1", - "find-npm-prefix": "^1.0.2", - "graceful-fs": "^4.1.11", - "ini": "^1.3.5", - "lock-verify": "^2.0.2", - "mkdirp": "^0.5.1", - "npm-lifecycle": "^3.0.0", - "npm-logical-tree": "^1.2.1", - "npm-package-arg": "^6.1.0", - "pacote": "^9.1.0", - "read-package-json": "^2.0.13", - "rimraf": "^2.6.2", - "worker-farm": "^1.6.0" - } - }, - "libnpm": { - "version": "3.0.1", - "bundled": true, - "dev": true, - "requires": { - "bin-links": "^1.1.2", - "bluebird": "^3.5.3", - "find-npm-prefix": "^1.0.2", - "libnpmaccess": "^3.0.2", - "libnpmconfig": "^1.2.1", - "libnpmhook": "^5.0.3", - "libnpmorg": "^1.0.1", - "libnpmpublish": "^1.1.2", - "libnpmsearch": "^2.0.2", - "libnpmteam": "^1.0.2", - "lock-verify": "^2.0.2", - "npm-lifecycle": "^3.0.0", - "npm-logical-tree": "^1.2.1", - "npm-package-arg": "^6.1.0", - "npm-profile": "^4.0.2", - "npm-registry-fetch": "^4.0.0", - "npmlog": "^4.1.2", - "pacote": "^9.5.3", - "read-package-json": "^2.0.13", - "stringify-package": "^1.0.0" - } - }, - "libnpmaccess": { - "version": "3.0.2", - "bundled": true, - "dev": true, - "requires": { - "aproba": "^2.0.0", - "get-stream": "^4.0.0", - "npm-package-arg": "^6.1.0", - "npm-registry-fetch": "^4.0.0" - } - }, - "libnpmconfig": { - "version": "1.2.1", - "bundled": true, - "dev": true, - "requires": { - "figgy-pudding": "^3.5.1", - "find-up": "^3.0.0", - "ini": "^1.3.5" - }, - "dependencies": { - "find-up": { - "version": "3.0.0", - "bundled": true, - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "locate-path": { - "version": "3.0.0", - "bundled": true, - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "p-limit": { - "version": "2.2.0", - "bundled": true, - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "bundled": true, - "dev": true, - "requires": { - "p-limit": "^2.0.0" - } - }, - "p-try": { - "version": "2.2.0", - "bundled": true, - "dev": true - } - } - }, - "libnpmhook": { - "version": "5.0.3", - "bundled": true, - "dev": true, - "requires": { - "aproba": "^2.0.0", - "figgy-pudding": "^3.4.1", - "get-stream": "^4.0.0", - "npm-registry-fetch": "^4.0.0" - } - }, - "libnpmorg": { - "version": "1.0.1", - "bundled": true, - "dev": true, - "requires": { - "aproba": "^2.0.0", - "figgy-pudding": "^3.4.1", - "get-stream": "^4.0.0", - "npm-registry-fetch": "^4.0.0" - } - }, - "libnpmpublish": { - "version": "1.1.2", - "bundled": true, - "dev": true, - "requires": { - "aproba": "^2.0.0", - "figgy-pudding": "^3.5.1", - "get-stream": "^4.0.0", - "lodash.clonedeep": "^4.5.0", - "normalize-package-data": "^2.4.0", - "npm-package-arg": "^6.1.0", - "npm-registry-fetch": "^4.0.0", - "semver": "^5.5.1", - "ssri": "^6.0.1" - } - }, - "libnpmsearch": { - "version": "2.0.2", - "bundled": true, - "dev": true, - "requires": { - "figgy-pudding": "^3.5.1", - "get-stream": "^4.0.0", - "npm-registry-fetch": "^4.0.0" - } - }, - "libnpmteam": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "requires": { - "aproba": "^2.0.0", - "figgy-pudding": "^3.4.1", - "get-stream": "^4.0.0", - "npm-registry-fetch": "^4.0.0" - } - }, - "libnpx": { - "version": "10.2.2", - "bundled": true, - "dev": true, - "requires": { - "dotenv": "^5.0.1", - "npm-package-arg": "^6.0.0", - "rimraf": "^2.6.2", - "safe-buffer": "^5.1.0", - "update-notifier": "^2.3.0", - "which": "^1.3.0", - "y18n": "^4.0.0", - "yargs": "^11.0.0" - } - }, - "locate-path": { - "version": "2.0.0", - "bundled": true, - "dev": true, - "requires": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - } - }, - "lock-verify": { - "version": "2.1.0", - "bundled": true, - "dev": true, - "requires": { - "npm-package-arg": "^6.1.0", - "semver": "^5.4.1" - } - }, - "lockfile": { - "version": "1.0.4", - "bundled": true, - "dev": true, - "requires": { - "signal-exit": "^3.0.2" - } - }, - "lodash._baseindexof": { - "version": "3.1.0", - "bundled": true, - "dev": true - }, - "lodash._baseuniq": { - "version": "4.6.0", - "bundled": true, - "dev": true, - "requires": { - "lodash._createset": "~4.0.0", - "lodash._root": "~3.0.0" - } - }, - "lodash._bindcallback": { - "version": "3.0.1", - "bundled": true, - "dev": true + "node_modules/execa/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==", + "license": "ISC" + }, + "node_modules/exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/expand-tilde": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", + "integrity": "sha512-A5EmesHW6rfnZ9ysHQjPdJRni0SRar0tjtG5MNtm9n5TUvsYU8oozprtRD4AqHxcZWWlVuAmQo2nWKfN9oyjTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "homedir-polyfill": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", + "license": "MIT", + "dependencies": { + "@jest/expect-utils": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/express": { + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", + "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", + "license": "MIT", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.3", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.7.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.3.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.12", + "proxy-addr": "~2.0.7", + "qs": "6.13.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.19.0", + "serve-static": "1.16.2", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dev": true, + "license": "MIT", + "dependencies": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/fast-content-type-parse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-2.0.1.tgz", + "integrity": "sha512-nGqtvLrj5w0naR6tDPfB4cUmYCqouzyQiz6C5y/LtcDllJdrcc6WaWW6iXyIIOErTa/XRybj28aasdn4LkVk6Q==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" }, - "lodash._cacheindexof": { - "version": "3.0.2", - "bundled": true, - "dev": true + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT" + }, + "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==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "license": "MIT" + }, + "node_modules/fast-uri": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz", + "integrity": "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" }, - "lodash._createcache": { - "version": "3.1.2", - "bundled": true, - "dev": true, - "requires": { - "lodash._getnative": "^3.0.0" - } - }, - "lodash._createset": { - "version": "4.0.3", - "bundled": true, - "dev": true - }, - "lodash._getnative": { - "version": "3.9.1", - "bundled": true, - "dev": true - }, - "lodash._root": { - "version": "3.0.1", - "bundled": true, - "dev": true - }, - "lodash.clonedeep": { - "version": "4.5.0", - "bundled": true, - "dev": true - }, - "lodash.restparam": { - "version": "3.6.1", - "bundled": true, - "dev": true - }, - "lodash.union": { - "version": "4.6.0", - "bundled": true, - "dev": true - }, - "lodash.uniq": { - "version": "4.5.0", - "bundled": true, - "dev": true - }, - "lodash.without": { - "version": "4.4.0", - "bundled": true, - "dev": true - }, - "lowercase-keys": { - "version": "1.0.1", - "bundled": true, - "dev": true - }, - "lru-cache": { - "version": "5.1.1", - "bundled": true, - "dev": true, - "requires": { - "yallist": "^3.0.2" - } - }, - "make-dir": { - "version": "1.3.0", - "bundled": true, - "dev": true, - "requires": { - "pify": "^3.0.0" - } - }, - "make-fetch-happen": { - "version": "5.0.2", - "bundled": true, - "dev": true, - "requires": { - "agentkeepalive": "^3.4.1", - "cacache": "^12.0.0", - "http-cache-semantics": "^3.8.1", - "http-proxy-agent": "^2.1.0", - "https-proxy-agent": "^2.2.3", - "lru-cache": "^5.1.1", - "mississippi": "^3.0.0", - "node-fetch-npm": "^2.0.2", - "promise-retry": "^1.1.1", - "socks-proxy-agent": "^4.0.0", - "ssri": "^6.0.0" - } - }, - "map-age-cleaner": { - "version": "0.1.3", - "bundled": true, - "dev": true, - "requires": { - "p-defer": "^1.0.0" - } - }, - "meant": { - "version": "1.0.1", - "bundled": true, - "dev": true - }, - "mem": { - "version": "4.3.0", - "bundled": true, - "dev": true, - "requires": { - "map-age-cleaner": "^0.1.1", - "mimic-fn": "^2.0.0", - "p-is-promise": "^2.0.0" - }, - "dependencies": { - "mimic-fn": { - "version": "2.1.0", - "bundled": true, - "dev": true - } - } - }, - "mime-db": { - "version": "1.35.0", - "bundled": true, - "dev": true - }, - "mime-types": { - "version": "2.1.19", - "bundled": true, - "dev": true, - "requires": { - "mime-db": "~1.35.0" - } - }, - "minimatch": { - "version": "3.0.4", - "bundled": true, - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "0.0.8", - "bundled": true, - "dev": true - }, - "minizlib": { - "version": "1.3.3", - "bundled": true, - "dev": true, - "requires": { - "minipass": "^2.9.0" - }, - "dependencies": { - "minipass": { - "version": "2.9.0", - "bundled": true, - "dev": true, - "requires": { - "safe-buffer": "^5.1.2", - "yallist": "^3.0.0" - } - } - } - }, - "mississippi": { - "version": "3.0.0", - "bundled": true, - "dev": true, - "requires": { - "concat-stream": "^1.5.0", - "duplexify": "^3.4.2", - "end-of-stream": "^1.1.0", - "flush-write-stream": "^1.0.0", - "from2": "^2.1.0", - "parallel-transform": "^1.1.0", - "pump": "^3.0.0", - "pumpify": "^1.3.3", - "stream-each": "^1.1.0", - "through2": "^2.0.0" - } - }, - "mkdirp": { - "version": "0.5.1", - "bundled": true, - "dev": true, - "requires": { - "minimist": "0.0.8" - } - }, - "move-concurrently": { - "version": "1.0.1", - "bundled": true, - "dev": true, - "requires": { - "aproba": "^1.1.1", - "copy-concurrently": "^1.0.0", - "fs-write-stream-atomic": "^1.0.8", - "mkdirp": "^0.5.1", - "rimraf": "^2.5.4", - "run-queue": "^1.0.3" - }, - "dependencies": { - "aproba": { - "version": "1.2.0", - "bundled": true, - "dev": true - } - } - }, - "ms": { - "version": "2.1.1", - "bundled": true, - "dev": true - }, - "mute-stream": { - "version": "0.0.7", - "bundled": true, - "dev": true - }, - "nice-try": { - "version": "1.0.5", - "bundled": true, - "dev": true - }, - "node-fetch-npm": { - "version": "2.0.2", - "bundled": true, - "dev": true, - "requires": { - "encoding": "^0.1.11", - "json-parse-better-errors": "^1.0.0", - "safe-buffer": "^5.1.1" - } - }, - "node-gyp": { - "version": "5.0.7", - "bundled": true, - "dev": true, - "requires": { - "env-paths": "^2.2.0", - "glob": "^7.1.4", - "graceful-fs": "^4.2.2", - "mkdirp": "^0.5.1", - "nopt": "^4.0.1", - "npmlog": "^4.1.2", - "request": "^2.88.0", - "rimraf": "^2.6.3", - "semver": "^5.7.1", - "tar": "^4.4.12", - "which": "^1.3.1" - } - }, - "nopt": { - "version": "4.0.1", - "bundled": true, - "dev": true, - "requires": { - "abbrev": "1", - "osenv": "^0.1.4" - } - }, - "normalize-package-data": { - "version": "2.5.0", - "bundled": true, - "dev": true, - "requires": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - }, - "dependencies": { - "resolve": { - "version": "1.10.0", - "bundled": true, - "dev": true, - "requires": { - "path-parse": "^1.0.6" - } - } - } - }, - "npm-audit-report": { - "version": "1.3.2", - "bundled": true, - "dev": true, - "requires": { - "cli-table3": "^0.5.0", - "console-control-strings": "^1.1.0" - } - }, - "npm-bundled": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "requires": { - "npm-normalize-package-bin": "^1.0.1" - } - }, - "npm-cache-filename": { - "version": "1.0.2", - "bundled": true, - "dev": true - }, - "npm-install-checks": { - "version": "3.0.2", - "bundled": true, - "dev": true, - "requires": { - "semver": "^2.3.0 || 3.x || 4 || 5" - } - }, - "npm-lifecycle": { - "version": "3.1.4", - "bundled": true, - "dev": true, - "requires": { - "byline": "^5.0.0", - "graceful-fs": "^4.1.15", - "node-gyp": "^5.0.2", - "resolve-from": "^4.0.0", - "slide": "^1.1.6", - "uid-number": "0.0.6", - "umask": "^1.1.0", - "which": "^1.3.1" - } - }, - "npm-logical-tree": { - "version": "1.2.1", - "bundled": true, - "dev": true - }, - "npm-normalize-package-bin": { - "version": "1.0.1", - "bundled": true, - "dev": true - }, - "npm-package-arg": { - "version": "6.1.1", - "bundled": true, - "dev": true, - "requires": { - "hosted-git-info": "^2.7.1", - "osenv": "^0.1.5", - "semver": "^5.6.0", - "validate-npm-package-name": "^3.0.0" - } - }, - "npm-packlist": { - "version": "1.4.7", - "bundled": true, - "dev": true, - "requires": { - "ignore-walk": "^3.0.1", - "npm-bundled": "^1.0.1" - } - }, - "npm-pick-manifest": { - "version": "3.0.2", - "bundled": true, - "dev": true, - "requires": { - "figgy-pudding": "^3.5.1", - "npm-package-arg": "^6.0.0", - "semver": "^5.4.1" - } - }, - "npm-profile": { - "version": "4.0.2", - "bundled": true, - "dev": true, - "requires": { - "aproba": "^1.1.2 || 2", - "figgy-pudding": "^3.4.1", - "npm-registry-fetch": "^4.0.0" - } - }, - "npm-registry-fetch": { - "version": "4.0.2", - "bundled": true, - "dev": true, - "requires": { - "JSONStream": "^1.3.4", - "bluebird": "^3.5.1", - "figgy-pudding": "^3.4.1", - "lru-cache": "^5.1.1", - "make-fetch-happen": "^5.0.0", - "npm-package-arg": "^6.1.0", - "safe-buffer": "^5.2.0" - }, - "dependencies": { - "safe-buffer": { - "version": "5.2.0", - "bundled": true, - "dev": true - } - } - }, - "npm-run-path": { - "version": "2.0.2", - "bundled": true, - "dev": true, - "requires": { - "path-key": "^2.0.0" - } - }, - "npm-user-validate": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "npmlog": { - "version": "4.1.2", - "bundled": true, - "dev": true, - "requires": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.3", - "set-blocking": "~2.0.0" - } - }, - "number-is-nan": { - "version": "1.0.1", - "bundled": true, - "dev": true - }, - "oauth-sign": { - "version": "0.9.0", - "bundled": true, - "dev": true - }, - "object-assign": { - "version": "4.1.1", - "bundled": true, - "dev": true - }, - "object-keys": { - "version": "1.0.12", - "bundled": true, - "dev": true - }, - "object.getownpropertydescriptors": { - "version": "2.0.3", - "bundled": true, - "dev": true, - "requires": { - "define-properties": "^1.1.2", - "es-abstract": "^1.5.1" - } - }, - "once": { - "version": "1.4.0", - "bundled": true, - "dev": true, - "requires": { - "wrappy": "1" - } - }, - "opener": { - "version": "1.5.1", - "bundled": true, - "dev": true - }, - "os-homedir": { - "version": "1.0.2", - "bundled": true, - "dev": true - }, - "os-locale": { - "version": "3.1.0", - "bundled": true, - "dev": true, - "requires": { - "execa": "^1.0.0", - "lcid": "^2.0.0", - "mem": "^4.0.0" - }, - "dependencies": { - "cross-spawn": { - "version": "6.0.5", - "bundled": true, - "dev": true, - "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, - "execa": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "requires": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - } - } - } - }, - "os-tmpdir": { - "version": "1.0.2", - "bundled": true, - "dev": true - }, - "osenv": { - "version": "0.1.5", - "bundled": true, - "dev": true, - "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" - } - }, - "p-defer": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "p-finally": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "p-is-promise": { - "version": "2.1.0", - "bundled": true, - "dev": true - }, - "p-limit": { - "version": "1.2.0", - "bundled": true, - "dev": true, - "requires": { - "p-try": "^1.0.0" - } - }, - "p-locate": { - "version": "2.0.0", - "bundled": true, - "dev": true, - "requires": { - "p-limit": "^1.1.0" - } - }, - "p-try": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "package-json": { - "version": "4.0.1", - "bundled": true, - "dev": true, - "requires": { - "got": "^6.7.1", - "registry-auth-token": "^3.0.1", - "registry-url": "^3.0.3", - "semver": "^5.1.0" - } - }, - "pacote": { - "version": "9.5.12", - "bundled": true, - "dev": true, - "requires": { - "bluebird": "^3.5.3", - "cacache": "^12.0.2", - "chownr": "^1.1.2", - "figgy-pudding": "^3.5.1", - "get-stream": "^4.1.0", - "glob": "^7.1.3", - "infer-owner": "^1.0.4", - "lru-cache": "^5.1.1", - "make-fetch-happen": "^5.0.0", - "minimatch": "^3.0.4", - "minipass": "^2.3.5", - "mississippi": "^3.0.0", - "mkdirp": "^0.5.1", - "normalize-package-data": "^2.4.0", - "npm-normalize-package-bin": "^1.0.0", - "npm-package-arg": "^6.1.0", - "npm-packlist": "^1.1.12", - "npm-pick-manifest": "^3.0.0", - "npm-registry-fetch": "^4.0.0", - "osenv": "^0.1.5", - "promise-inflight": "^1.0.1", - "promise-retry": "^1.1.1", - "protoduck": "^5.0.1", - "rimraf": "^2.6.2", - "safe-buffer": "^5.1.2", - "semver": "^5.6.0", - "ssri": "^6.0.1", - "tar": "^4.4.10", - "unique-filename": "^1.1.1", - "which": "^1.3.1" - }, - "dependencies": { - "minipass": { - "version": "2.9.0", - "bundled": true, - "dev": true, - "requires": { - "safe-buffer": "^5.1.2", - "yallist": "^3.0.0" - } - } - } - }, - "parallel-transform": { - "version": "1.1.0", - "bundled": true, - "dev": true, - "requires": { - "cyclist": "~0.2.2", - "inherits": "^2.0.3", - "readable-stream": "^2.1.5" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.6", - "bundled": true, - "dev": true, - "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" - } - }, - "string_decoder": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, - "path-exists": { - "version": "3.0.0", - "bundled": true, - "dev": true - }, - "path-is-absolute": { - "version": "1.0.1", - "bundled": true, - "dev": true - }, - "path-is-inside": { - "version": "1.0.2", - "bundled": true, - "dev": true - }, - "path-key": { - "version": "2.0.1", - "bundled": true, - "dev": true - }, - "path-parse": { - "version": "1.0.6", - "bundled": true, - "dev": true - }, - "performance-now": { - "version": "2.1.0", - "bundled": true, - "dev": true - }, - "pify": { - "version": "3.0.0", - "bundled": true, - "dev": true - }, - "prepend-http": { - "version": "1.0.4", - "bundled": true, - "dev": true - }, - "process-nextick-args": { - "version": "2.0.0", - "bundled": true, - "dev": true - }, - "promise-inflight": { - "version": "1.0.1", - "bundled": true, - "dev": true - }, - "promise-retry": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "requires": { - "err-code": "^1.0.0", - "retry": "^0.10.0" - }, - "dependencies": { - "retry": { - "version": "0.10.1", - "bundled": true, - "dev": true - } - } - }, - "promzard": { - "version": "0.3.0", - "bundled": true, - "dev": true, - "requires": { - "read": "1" - } - }, - "proto-list": { - "version": "1.2.4", - "bundled": true, - "dev": true - }, - "protoduck": { - "version": "5.0.1", - "bundled": true, - "dev": true, - "requires": { - "genfun": "^5.0.0" - } - }, - "prr": { - "version": "1.0.1", - "bundled": true, - "dev": true - }, - "pseudomap": { - "version": "1.0.2", - "bundled": true, - "dev": true - }, - "psl": { - "version": "1.1.29", - "bundled": true, - "dev": true - }, - "pump": { - "version": "3.0.0", - "bundled": true, - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "pumpify": { - "version": "1.5.1", - "bundled": true, - "dev": true, - "requires": { - "duplexify": "^3.6.0", - "inherits": "^2.0.3", - "pump": "^2.0.0" - }, - "dependencies": { - "pump": { - "version": "2.0.1", - "bundled": true, - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - } - } - }, - "punycode": { - "version": "1.4.1", - "bundled": true, - "dev": true - }, - "qrcode-terminal": { - "version": "0.12.0", - "bundled": true, - "dev": true - }, - "qs": { - "version": "6.5.2", - "bundled": true, - "dev": true - }, - "query-string": { - "version": "6.8.2", - "bundled": true, - "dev": true, - "requires": { - "decode-uri-component": "^0.2.0", - "split-on-first": "^1.0.0", - "strict-uri-encode": "^2.0.0" - } - }, - "qw": { - "version": "1.0.1", - "bundled": true, - "dev": true - }, - "rc": { - "version": "1.2.7", - "bundled": true, - "dev": true, - "requires": { - "deep-extend": "^0.5.1", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "bundled": true, - "dev": true - } - } - }, - "read": { - "version": "1.0.7", - "bundled": true, - "dev": true, - "requires": { - "mute-stream": "~0.0.4" - } - }, - "read-cmd-shim": { - "version": "1.0.5", - "bundled": true, - "dev": true, - "requires": { - "graceful-fs": "^4.1.2" - } - }, - "read-installed": { - "version": "4.0.3", - "bundled": true, - "dev": true, - "requires": { - "debuglog": "^1.0.1", - "graceful-fs": "^4.1.2", - "read-package-json": "^2.0.0", - "readdir-scoped-modules": "^1.0.0", - "semver": "2 || 3 || 4 || 5", - "slide": "~1.1.3", - "util-extend": "^1.0.1" - } - }, - "read-package-json": { - "version": "2.1.1", - "bundled": true, - "dev": true, - "requires": { - "glob": "^7.1.1", - "graceful-fs": "^4.1.2", - "json-parse-better-errors": "^1.0.1", - "normalize-package-data": "^2.0.0", - "npm-normalize-package-bin": "^1.0.0" - } - }, - "read-package-tree": { - "version": "5.3.1", - "bundled": true, - "dev": true, - "requires": { - "read-package-json": "^2.0.0", - "readdir-scoped-modules": "^1.0.0", - "util-promisify": "^2.1.0" - } - }, - "readable-stream": { - "version": "3.4.0", - "bundled": true, - "dev": true, - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - }, - "readdir-scoped-modules": { - "version": "1.1.0", - "bundled": true, - "dev": true, - "requires": { - "debuglog": "^1.0.1", - "dezalgo": "^1.0.0", - "graceful-fs": "^4.1.2", - "once": "^1.3.0" - } - }, - "registry-auth-token": { - "version": "3.3.2", - "bundled": true, - "dev": true, - "requires": { - "rc": "^1.1.6", - "safe-buffer": "^5.0.1" - } - }, - "registry-url": { - "version": "3.1.0", - "bundled": true, - "dev": true, - "requires": { - "rc": "^1.0.1" - } - }, - "request": { - "version": "2.88.0", - "bundled": true, - "dev": true, - "requires": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.0", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.4.3", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - } - }, - "require-directory": { - "version": "2.1.1", - "bundled": true, - "dev": true - }, - "require-main-filename": { - "version": "1.0.1", - "bundled": true, - "dev": true - }, - "resolve-from": { - "version": "4.0.0", - "bundled": true, - "dev": true - }, - "retry": { - "version": "0.12.0", - "bundled": true, - "dev": true - }, - "rimraf": { - "version": "2.6.3", - "bundled": true, - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, - "run-queue": { - "version": "1.0.3", - "bundled": true, - "dev": true, - "requires": { - "aproba": "^1.1.1" - }, - "dependencies": { - "aproba": { - "version": "1.2.0", - "bundled": true, - "dev": true - } - } - }, - "safe-buffer": { - "version": "5.1.2", - "bundled": true, - "dev": true - }, - "safer-buffer": { - "version": "2.1.2", - "bundled": true, - "dev": true - }, - "semver": { - "version": "5.7.1", - "bundled": true, - "dev": true - }, - "semver-diff": { - "version": "2.1.0", - "bundled": true, - "dev": true, - "requires": { - "semver": "^5.0.3" - } - }, - "set-blocking": { - "version": "2.0.0", - "bundled": true, - "dev": true - }, - "sha": { - "version": "3.0.0", - "bundled": true, - "dev": true, - "requires": { - "graceful-fs": "^4.1.2" - } - }, - "shebang-command": { - "version": "1.2.0", - "bundled": true, - "dev": true, - "requires": { - "shebang-regex": "^1.0.0" - } - }, - "shebang-regex": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "signal-exit": { - "version": "3.0.2", - "bundled": true, - "dev": true - }, - "slide": { - "version": "1.1.6", - "bundled": true, - "dev": true - }, - "smart-buffer": { - "version": "4.1.0", - "bundled": true, - "dev": true - }, - "socks": { - "version": "2.3.3", - "bundled": true, - "dev": true, - "requires": { - "ip": "1.1.5", - "smart-buffer": "^4.1.0" - } - }, - "socks-proxy-agent": { - "version": "4.0.2", - "bundled": true, - "dev": true, - "requires": { - "agent-base": "~4.2.1", - "socks": "~2.3.2" - }, - "dependencies": { - "agent-base": { - "version": "4.2.1", - "bundled": true, - "dev": true, - "requires": { - "es6-promisify": "^5.0.0" - } - } - } - }, - "sorted-object": { - "version": "2.0.1", - "bundled": true, - "dev": true - }, - "sorted-union-stream": { - "version": "2.1.3", - "bundled": true, - "dev": true, - "requires": { - "from2": "^1.3.0", - "stream-iterate": "^1.1.0" - }, - "dependencies": { - "from2": { - "version": "1.3.0", - "bundled": true, - "dev": true, - "requires": { - "inherits": "~2.0.1", - "readable-stream": "~1.1.10" - } - }, - "isarray": { - "version": "0.0.1", - "bundled": true, - "dev": true - }, - "readable-stream": { - "version": "1.1.14", - "bundled": true, - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" - } - }, - "string_decoder": { - "version": "0.10.31", - "bundled": true, - "dev": true - } - } - }, - "spdx-correct": { - "version": "3.0.0", - "bundled": true, - "dev": true, - "requires": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-exceptions": { - "version": "2.1.0", - "bundled": true, - "dev": true - }, - "spdx-expression-parse": { - "version": "3.0.0", - "bundled": true, - "dev": true, - "requires": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-license-ids": { - "version": "3.0.3", - "bundled": true, - "dev": true - }, - "split-on-first": { - "version": "1.1.0", - "bundled": true, - "dev": true - }, - "sshpk": { - "version": "1.14.2", - "bundled": true, - "dev": true, - "requires": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - } - }, - "ssri": { - "version": "6.0.1", - "bundled": true, - "dev": true, - "requires": { - "figgy-pudding": "^3.5.1" - } - }, - "stream-each": { - "version": "1.2.2", - "bundled": true, - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "stream-shift": "^1.0.0" - } - }, - "stream-iterate": { - "version": "1.2.0", - "bundled": true, - "dev": true, - "requires": { - "readable-stream": "^2.1.5", - "stream-shift": "^1.0.0" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.6", - "bundled": true, - "dev": true, - "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" - } - }, - "string_decoder": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, - "stream-shift": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "strict-uri-encode": { - "version": "2.0.0", - "bundled": true, - "dev": true - }, - "string-width": { - "version": "2.1.1", - "bundled": true, - "dev": true, - "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "bundled": true, - "dev": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "bundled": true, - "dev": true - }, - "strip-ansi": { - "version": "4.0.0", - "bundled": true, - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - } - } - }, - "string_decoder": { - "version": "1.2.0", - "bundled": true, - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - }, - "stringify-package": { - "version": "1.0.1", - "bundled": true, - "dev": true - }, - "strip-ansi": { - "version": "3.0.1", - "bundled": true, - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "strip-eof": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "strip-json-comments": { - "version": "2.0.1", - "bundled": true, - "dev": true - }, - "supports-color": { - "version": "5.4.0", - "bundled": true, - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - }, - "tar": { - "version": "4.4.13", - "bundled": true, - "dev": true, - "requires": { - "chownr": "^1.1.1", - "fs-minipass": "^1.2.5", - "minipass": "^2.8.6", - "minizlib": "^1.2.1", - "mkdirp": "^0.5.0", - "safe-buffer": "^5.1.2", - "yallist": "^3.0.3" - }, - "dependencies": { - "minipass": { - "version": "2.9.0", - "bundled": true, - "dev": true, - "requires": { - "safe-buffer": "^5.1.2", - "yallist": "^3.0.0" - } - } - } - }, - "term-size": { - "version": "1.2.0", - "bundled": true, - "dev": true, - "requires": { - "execa": "^0.7.0" - } - }, - "text-table": { - "version": "0.2.0", - "bundled": true, - "dev": true - }, - "through": { - "version": "2.3.8", - "bundled": true, - "dev": true - }, - "through2": { - "version": "2.0.3", - "bundled": true, - "dev": true, - "requires": { - "readable-stream": "^2.1.5", - "xtend": "~4.0.1" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.6", - "bundled": true, - "dev": true, - "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" - } - }, - "string_decoder": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, - "timed-out": { - "version": "4.0.1", - "bundled": true, - "dev": true - }, - "tiny-relative-date": { - "version": "1.3.0", - "bundled": true, - "dev": true - }, - "tough-cookie": { - "version": "2.4.3", - "bundled": true, - "dev": true, - "requires": { - "psl": "^1.1.24", - "punycode": "^1.4.1" - } - }, - "tunnel-agent": { - "version": "0.6.0", - "bundled": true, - "dev": true, - "requires": { - "safe-buffer": "^5.0.1" - } - }, - "tweetnacl": { - "version": "0.14.5", - "bundled": true, - "dev": true, - "optional": true - }, - "typedarray": { - "version": "0.0.6", - "bundled": true, - "dev": true - }, - "uid-number": { - "version": "0.0.6", - "bundled": true, - "dev": true - }, - "umask": { - "version": "1.1.0", - "bundled": true, - "dev": true - }, - "unique-filename": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "requires": { - "unique-slug": "^2.0.0" - } - }, - "unique-slug": { - "version": "2.0.0", - "bundled": true, - "dev": true, - "requires": { - "imurmurhash": "^0.1.4" - } - }, - "unique-string": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "requires": { - "crypto-random-string": "^1.0.0" - } - }, - "unpipe": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "unzip-response": { - "version": "2.0.1", - "bundled": true, - "dev": true - }, - "update-notifier": { - "version": "2.5.0", - "bundled": true, - "dev": true, - "requires": { - "boxen": "^1.2.1", - "chalk": "^2.0.1", - "configstore": "^3.0.0", - "import-lazy": "^2.1.0", - "is-ci": "^1.0.10", - "is-installed-globally": "^0.1.0", - "is-npm": "^1.0.0", - "latest-version": "^3.0.0", - "semver-diff": "^2.0.0", - "xdg-basedir": "^3.0.0" - } - }, - "url-parse-lax": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "requires": { - "prepend-http": "^1.0.1" - } - }, - "util-deprecate": { - "version": "1.0.2", - "bundled": true, - "dev": true - }, - "util-extend": { - "version": "1.0.3", - "bundled": true, - "dev": true - }, - "util-promisify": { - "version": "2.1.0", - "bundled": true, - "dev": true, - "requires": { - "object.getownpropertydescriptors": "^2.0.3" - } - }, - "uuid": { - "version": "3.3.3", - "bundled": true, - "dev": true - }, - "validate-npm-package-license": { - "version": "3.0.4", - "bundled": true, - "dev": true, - "requires": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, - "validate-npm-package-name": { - "version": "3.0.0", - "bundled": true, - "dev": true, - "requires": { - "builtins": "^1.0.3" - } - }, - "verror": { - "version": "1.10.0", - "bundled": true, - "dev": true, - "requires": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - } - }, - "wcwidth": { - "version": "1.0.1", - "bundled": true, - "dev": true, - "requires": { - "defaults": "^1.0.3" - } - }, - "which": { - "version": "1.3.1", - "bundled": true, - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "which-module": { - "version": "2.0.0", - "bundled": true, - "dev": true - }, - "wide-align": { - "version": "1.1.2", - "bundled": true, - "dev": true, - "requires": { - "string-width": "^1.0.2" - }, - "dependencies": { - "string-width": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - } - } - }, - "widest-line": { - "version": "2.0.0", - "bundled": true, - "dev": true, - "requires": { - "string-width": "^2.1.1" - } - }, - "worker-farm": { - "version": "1.7.0", - "bundled": true, - "dev": true, - "requires": { - "errno": "~0.1.7" - } - }, - "wrap-ansi": { - "version": "2.1.0", - "bundled": true, - "dev": true, - "requires": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1" - }, - "dependencies": { - "string-width": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - } - } - }, - "wrappy": { - "version": "1.0.2", - "bundled": true, - "dev": true - }, - "write-file-atomic": { - "version": "2.4.3", - "bundled": true, - "dev": true, - "requires": { - "graceful-fs": "^4.1.11", - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.2" - } - }, - "xdg-basedir": { - "version": "3.0.0", - "bundled": true, - "dev": true - }, - "xtend": { - "version": "4.0.1", - "bundled": true, - "dev": true - }, - "y18n": { - "version": "4.0.0", - "bundled": true, - "dev": true - }, - "yallist": { - "version": "3.0.3", - "bundled": true, - "dev": true - }, - "yargs": { - "version": "11.1.1", - "bundled": true, - "dev": true, - "requires": { - "cliui": "^4.0.0", - "decamelize": "^1.1.1", - "find-up": "^2.1.0", - "get-caller-file": "^1.0.1", - "os-locale": "^3.1.0", - "require-directory": "^2.1.1", - "require-main-filename": "^1.0.1", - "set-blocking": "^2.0.0", - "string-width": "^2.0.0", - "which-module": "^2.0.0", - "y18n": "^3.2.1", - "yargs-parser": "^9.0.2" - }, - "dependencies": { - "y18n": { - "version": "3.2.1", - "bundled": true, - "dev": true - } - } - }, - "yargs-parser": { - "version": "9.0.2", - "bundled": true, - "dev": true, - "requires": { - "camelcase": "^4.1.0" - } + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" } + ], + "license": "BSD-3-Clause", + "optional": true + }, + "node_modules/fastq": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.18.0.tgz", + "integrity": "sha512-QKHXPW0hD8g4UET03SdOdunzSouc9N4AuHdsX8XNcTsuz+yYFILVNIX4l9yHABMhiEI9Db0JTTIpu0wB+Y1QQw==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "license": "Apache-2.0", + "dependencies": { + "bser": "2.1.1" + } + }, + "node_modules/figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/filelist": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", + "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "minimatch": "^5.0.1" + } + }, + "node_modules/filelist/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, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", + "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/find-node-modules": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/find-node-modules/-/find-node-modules-2.1.3.tgz", + "integrity": "sha512-UC2I2+nx1ZuOBclWVNdcnbDR5dlrOdVb7xNjmT/lHE+LsgztWks3dG7boJ37yTS/venXw84B/mAW9uHVoC5QRg==", + "dev": true, + "license": "MIT", + "dependencies": { + "findup-sync": "^4.0.0", + "merge": "^2.1.1" + } + }, + "node_modules/find-root": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", + "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", + "dev": true, + "license": "MIT" + }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up-simple": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/find-up-simple/-/find-up-simple-1.0.0.tgz", + "integrity": "sha512-q7Us7kcjj2VMePAa02hDAF6d+MzsdsAWEwYyOpwUtlerRBkOEPBCRZrAV4XfcSN8fHAgaD0hP7miwoay6DCprw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/find-versions": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/find-versions/-/find-versions-6.0.0.tgz", + "integrity": "sha512-2kCCtc+JvcZ86IGAz3Z2Y0A1baIz9fL31pH/0S1IqZr9Iwnjq8izfPtrCyQKO6TLMPELLsQMre7VDqeIKCsHkA==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver-regex": "^4.0.5", + "super-regex": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/findup-sync": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-4.0.0.tgz", + "integrity": "sha512-6jvvn/12IC4quLBL1KNokxC7wWTvYncaVUYSoxWw7YykPLuRrnv4qdHcSOywOI5RpkOVGeQRtWM8/q+G6W6qfQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "detect-file": "^1.0.0", + "is-glob": "^4.0.0", + "micromatch": "^4.0.2", + "resolve-dir": "^1.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/foreground-child": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", + "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/from2": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", + "integrity": "sha512-OMcX/4IC/uqEPVgGeyfN22LJk6AZrMkRZHxcHBMBvHScDGgwTm2GT2Wkgtocyd3JfZffjj2kYUDXXII0Fk9W0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.0" + } + }, + "node_modules/from2/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==", + "dev": true, + "license": "MIT", + "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/from2/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==", + "dev": true, + "license": "MIT" + }, + "node_modules/from2/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==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "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==", + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/function-timeout": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/function-timeout/-/function-timeout-1.0.2.tgz", + "integrity": "sha512-939eZS4gJ3htTHAldmyyuzlrD58P03fHG49v2JfFXbV6OhvZKRC9j2yAtdHw/zrp2zXHuv05zMIy40F0ge7spA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "license": "MIT", + "engines": { + "node": ">=6.9.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==", + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.7.tgz", + "integrity": "sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "function-bind": "^1.1.2", + "get-proto": "^1.0.0", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/git-log-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/git-log-parser/-/git-log-parser-1.2.1.tgz", + "integrity": "sha512-PI+sPDvHXNPl5WNOErAK05s3j0lgwUzMN6o8cyQrDaKfT3qd7TmNJKeXX+SknI5I0QhG5fVPAEwSY4tRGDtYoQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "argv-formatter": "~1.0.0", + "spawn-error-forwarder": "~1.0.0", + "split2": "~1.0.0", + "stream-combiner2": "~1.1.1", + "through2": "~2.0.0", + "traverse": "0.6.8" + } + }, + "node_modules/glob": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.1.tgz", + "integrity": "sha512-zrQDm8XPnYEKawJScsnM0QzobJxlT/kHOOlRTio8IH/GrmxRE5fjllkzdaHclIuNjUQTJYH2xHNIGfdpJkDJUw==", + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^4.0.1", + "minimatch": "^10.0.0", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^2.0.0" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": "20 || >=22" + }, + "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, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/global-directory": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/global-directory/-/global-directory-4.0.1.tgz", + "integrity": "sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "ini": "4.1.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/global-modules": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", + "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", + "dev": true, + "license": "MIT", + "dependencies": { + "global-prefix": "^1.0.1", + "is-windows": "^1.0.1", + "resolve-dir": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/global-prefix": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", + "integrity": "sha512-5lsx1NUDHtSjfg0eHlmYvZKv8/nVqX4ckFbM+FrGcQ+04KWcWFo9P5MxPZYSzUvyzmdTbI7Eix8Q4IbELDqzKg==", + "dev": true, + "license": "MIT", + "dependencies": { + "expand-tilde": "^2.0.2", + "homedir-polyfill": "^1.0.1", + "ini": "^1.3.4", + "is-windows": "^1.0.1", + "which": "^1.2.14" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/global-prefix/node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true, + "license": "ISC" + }, + "node_modules/global-prefix/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/globby": { + "version": "14.0.2", + "resolved": "https://registry.npmjs.org/globby/-/globby-14.0.2.tgz", + "integrity": "sha512-s3Fq41ZVh7vbbe2PN3nrW7yC7U7MFVc5c98/iTl9c2GawNMKx/J648KQRW6WKkuU8GIbbh2IXfIRQjOZnXcTnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sindresorhus/merge-streams": "^2.1.0", + "fast-glob": "^3.3.2", + "ignore": "^5.2.4", + "path-type": "^5.0.0", + "slash": "^5.1.0", + "unicorn-magic": "^0.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby/node_modules/path-type": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-5.0.0.tgz", + "integrity": "sha512-5HviZNaZcfqP95rwpv+1HDgUamezbqdSYTyzjTvwtJSnIH+3vnbmWsItli8OFEndS984VT55M3jduxZbX351gg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby/node_modules/slash": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz", + "integrity": "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" + }, + "node_modules/handlebars": { + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", + "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.2", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, + "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==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/highlight.js": { + "version": "10.7.3", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", + "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": "*" + } + }, + "node_modules/homedir-polyfill": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", + "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "parse-passwd": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/hook-std": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hook-std/-/hook-std-3.0.0.tgz", + "integrity": "sha512-jHRQzjSDzMtFy34AGj1DN+vq54WVuhSvKgrHf0OMiFQTwDD4L/qqofVEWjLOBMTn5+lCD3fPg32W9yOfnEJTTw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/hosted-git-info": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-8.0.2.tgz", + "integrity": "sha512-sYKnA7eGln5ov8T8gnYlkSOxFJvywzEx9BueN6xo/GKO8PGiI6uK6xx+DIGe45T3bdVjLAQDQW1aicT8z8JwQg==", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^10.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/hosted-git-info/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "license": "MIT" + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "license": "MIT", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/http-proxy-agent/node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/http-proxy-agent/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, + "license": "MIT" + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/https-proxy-agent/node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/https-proxy-agent/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, + "license": "MIT" + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "license": "Apache-2.0", + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "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" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-fresh/node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/import-from-esm": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/import-from-esm/-/import-from-esm-2.0.0.tgz", + "integrity": "sha512-YVt14UZCgsX1vZQ3gKjkWVdBdHQ6eu3MPU1TBgL1H5orXe2+jWD006WCPPtOuwlQm10NuzOW5WawiF1Q9veW8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.4", + "import-meta-resolve": "^4.0.0" + }, + "engines": { + "node": ">=18.20" + } + }, + "node_modules/import-from-esm/node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/import-from-esm/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, + "license": "MIT" + }, + "node_modules/import-local": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", + "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", + "license": "MIT", + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-meta-resolve": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.1.0.tgz", + "integrity": "sha512-I6fiaX09Xivtk+THaMfAwnA3MVA5Big1WHF1Dfx9hFuvNIWpXnorlkzhcQf6ehrqQiiZECRt1poOAkPmer3ruw==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/indent-string": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz", + "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/index-to-position": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/index-to-position/-/index-to-position-0.1.2.tgz", + "integrity": "sha512-MWDKS3AS1bGCHLBA2VLImJz42f7bJh8wQsTGCzI3j519/CASStoDONUBVz2I/VID0MpiX3SGSnbOD2xUalbE5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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.", + "license": "ISC", + "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==", + "license": "ISC" + }, + "node_modules/ini": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.1.tgz", + "integrity": "sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==", + "dev": true, + "license": "ISC", + "optional": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/inquirer": { + "version": "8.2.5", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.5.tgz", + "integrity": "sha512-QAgPDQMEgrDssk1XiwwHoOGYF9BAbUcc1+j+FhEvaOt8/cKRqyLn0U5qA6F74fGhTMGxf92pOvPBeh29jQJDTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.1", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.21", + "mute-stream": "0.0.8", + "ora": "^5.4.1", + "run-async": "^2.4.0", + "rxjs": "^7.5.5", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/into-stream": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/into-stream/-/into-stream-7.0.0.tgz", + "integrity": "sha512-2dYz766i9HprMBasCMvHMuazJ7u4WzhJwo5kb3iPSiW/iRYV6uPari3zHoqZlnuaR7V1bEiNMxikhp37rdBXbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "from2": "^2.3.0", + "p-is-promise": "^3.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "license": "MIT" + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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, + "license": "MIT", + "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==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "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, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "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==", + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-utf8": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha512-rMYPYvCzsXywIsldgLaSoPlw5PfoB/ssr7hY4pLfcodrA5M/eArza1a9VmTiNIBNMjOGr1Ow9mTyU2o69U6U9Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, + "node_modules/issue-parser": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/issue-parser/-/issue-parser-7.0.1.tgz", + "integrity": "sha512-3YZcUUR2Wt1WsapF+S/WiA2WmlW0cWAoPccMqne7AxEBhCdFeTPjfv/Axb8V2gyCgY3nRw+ksZ3xSUX+R47iAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash.capitalize": "^4.2.1", + "lodash.escaperegexp": "^4.1.2", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.uniqby": "^4.7.0" + }, + "engines": { + "node": "^18.17 || >=20.6.1" + } + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", + "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", + "license": "BSD-3-Clause", + "dependencies": { + "@babel/core": "^7.23.9", + "@babel/parser": "^7.23.9", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-instrument/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "license": "BSD-3-Clause", + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "license": "BSD-3-Clause", + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps/node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/istanbul-lib-source-maps/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==", + "license": "MIT" + }, + "node_modules/istanbul-reports": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", + "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", + "license": "BSD-3-Clause", + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jackspeak": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.0.2.tgz", + "integrity": "sha512-bZsjR/iRjl1Nk1UkjGpAzLNfQtzuijhn2g+pbZb98HQ1Gk8vM9hfbxeMBP+M2/UUdwj0RqGG3mlvk2MsAqwvEw==", + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/jake": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.2.tgz", + "integrity": "sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "async": "^3.2.3", + "chalk": "^4.0.2", + "filelist": "^1.0.4", + "minimatch": "^3.1.2" + }, + "bin": { + "jake": "bin/cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jake/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/jake/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/java-properties": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/java-properties/-/java-properties-1.0.2.tgz", + "integrity": "sha512-qjdpeo2yKlYTH7nFdK0vbZWuTCesk4o63v5iVOlhMQPfuIZQfW/HI35SjfhA+4qpg36rnFSvUK5b1m+ckIblQQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", + "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", + "license": "MIT", + "dependencies": { + "@jest/core": "^29.7.0", + "@jest/types": "^29.6.3", + "import-local": "^3.0.2", + "jest-cli": "^29.7.0" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-changed-files": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", + "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", + "license": "MIT", + "dependencies": { + "execa": "^5.0.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-circus": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", + "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^1.0.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^29.7.0", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0", + "pretty-format": "^29.7.0", + "pure-rand": "^6.0.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-circus/node_modules/dedent": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.3.tgz", + "integrity": "sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ==", + "license": "MIT", + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" + }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/jest-cli": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", + "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", + "license": "MIT", + "dependencies": { + "@jest/core": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "create-jest": "^29.7.0", + "exit": "^0.1.2", + "import-local": "^3.0.2", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "yargs": "^17.3.1" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-config": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", + "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", + "license": "MIT", + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/test-sequencer": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-jest": "^29.7.0", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-circus": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@types/node": "*", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/jest-config/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/jest-config/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/jest-config/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/jest-diff": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", + "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-docblock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", + "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", + "license": "MIT", + "dependencies": { + "detect-newline": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-each": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", + "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "jest-util": "^29.7.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-environment-node": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", + "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-get-type": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-haste-map": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", + "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + } + }, + "node_modules/jest-leak-detector": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", + "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", + "license": "MIT", + "dependencies": { + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-matcher-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", + "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-message-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", + "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.6.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-mock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", + "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-pnp-resolver": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", + "license": "MIT", + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "jest-resolve": "*" + }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } + } + }, + "node_modules/jest-regex-util": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", + "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "resolve": "^1.20.0", + "resolve.exports": "^2.0.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve-dependencies": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", + "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", + "license": "MIT", + "dependencies": { + "jest-regex-util": "^29.6.3", + "jest-snapshot": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runner": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", + "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", + "license": "MIT", + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/environment": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "graceful-fs": "^4.2.9", + "jest-docblock": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-leak-detector": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-resolve": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-util": "^29.7.0", + "jest-watcher": "^29.7.0", + "jest-worker": "^29.7.0", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runtime": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", + "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/globals": "^29.7.0", + "@jest/source-map": "^29.6.3", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runtime/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/jest-runtime/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/jest-runtime/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/jest-snapshot": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", + "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", + "license": "MIT", + "dependencies": { + "@babel/core": "^7.11.6", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-jsx": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/types": "^7.3.3", + "@jest/expect-utils": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "natural-compare": "^1.4.0", + "pretty-format": "^29.7.0", + "semver": "^7.5.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-snapshot/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", + "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "leven": "^3.1.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-watcher": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", + "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", + "license": "MIT", + "dependencies": { + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "jest-util": "^29.7.0", + "string-length": "^4.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", + "license": "MIT", + "dependencies": { + "@types/node": "*", + "jest-util": "^29.7.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker/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==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/jiti": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.4.2.tgz", + "integrity": "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==", + "dev": true, + "license": "MIT", + "optional": true, + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" + }, + "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, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "license": "MIT" + }, + "node_modules/json-placeholder-replacer": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/json-placeholder-replacer/-/json-placeholder-replacer-2.1.0.tgz", + "integrity": "sha512-4thj3fpK8ZGmfYZ5Pe5VegiDXktz2XC/cpw0qZ/j/maCk/rRp83ftTVg9ffXx/4Ufc0g/Pv93nc1R6Bor+if3A==", + "license": "MIT", + "bin": { + "jpr": "dist/index.js", + "json-placeholder-replacer": "dist/index.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==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "license": "MIT" + }, + "node_modules/load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/load-json-file/node_modules/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", + "dev": true, + "license": "MIT", + "dependencies": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/load-json-file/node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.capitalize": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/lodash.capitalize/-/lodash.capitalize-4.2.1.tgz", + "integrity": "sha512-kZzYOKspf8XVX5AvmQF94gQW0lejFVgb80G85bU4ZWzoJ6C03PQg3coYAUpSTpQWelrZELd3XWgHzw4Ck5kaIw==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.escaperegexp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz", + "integrity": "sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.map": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.map/-/lodash.map-4.6.0.tgz", + "integrity": "sha512-worNHGKLDetmcEYDvh2stPCrrQRkP20E4l0iIS7F8EvzMqBBi7ltvFN5m1HvTf1P7Jk1txKhvFcmYsCr8O2F1Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/lodash.mergewith": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz", + "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/lodash.uniqby": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash.uniqby/-/lodash.uniqby-4.7.0.tgz", + "integrity": "sha512-e/zcLx6CSbmaEgFHCA7BnoQKyCtKMxnuWrJygbwPs/AIn+IMKl66L8/s+wBUn5LRw2pZx3bUHibiV1b6aTWIww==", + "dev": true, + "license": "MIT" + }, + "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, + "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/longest": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/longest/-/longest-2.0.1.tgz", + "integrity": "sha512-Ajzxb8CM6WAnFjgiloPsI3bF+WCxcvhdIG3KNA2KN962+tdBsHcuQ4k4qX/EcS/2CRkcc0iAkR956Nib6aXU/Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "devOptional": true, + "license": "ISC" + }, + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "license": "BSD-3-Clause", + "dependencies": { + "tmpl": "1.0.5" + } + }, + "node_modules/marked": { + "version": "12.0.2", + "resolved": "https://registry.npmjs.org/marked/-/marked-12.0.2.tgz", + "integrity": "sha512-qXUm7e/YKFoqFPYPa3Ukg9xlI5cyAtGmyEIzMfW//m6kXwCy2Ps9DYf5ioijFKQ8qyuscrHoY04iJGctu2Kg0Q==", + "dev": true, + "license": "MIT", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/marked-terminal": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/marked-terminal/-/marked-terminal-7.2.1.tgz", + "integrity": "sha512-rQ1MoMFXZICWNsKMiiHwP/Z+92PLKskTPXj+e7uwXmuMPkNn7iTqC+IvDekVm1MPeC9wYQeLxeFaOvudRR/XbQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-escapes": "^7.0.0", + "ansi-regex": "^6.1.0", + "chalk": "^5.3.0", + "cli-highlight": "^2.1.11", + "cli-table3": "^0.6.5", + "node-emoji": "^2.1.3", + "supports-hyperlinks": "^3.1.0" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "marked": ">=1 <15" + } + }, + "node_modules/marked-terminal/node_modules/ansi-escapes": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.0.0.tgz", + "integrity": "sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "environment": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/marked-terminal/node_modules/chalk": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", + "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/meow": { + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-13.2.0.tgz", + "integrity": "sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/merge/-/merge-2.1.1.tgz", + "integrity": "sha512-jz+Cfrg9GWOZbQAnDQ4hlVnQky+341Yk5ru8bZSe6sIDTCIg8n9i/u7hSQGSVOF3C7lH6mGtqjkiT9G4wFLL0w==", + "dev": true, + "license": "MIT" + }, + "node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "license": "MIT" + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/mime/-/mime-4.0.6.tgz", + "integrity": "sha512-4rGt7rvQHBbaSOF9POGkk1ocRP16Md1x36Xma8sz8h8/vfCUI2OtEIeCqe4Ofes853x4xDoPiFLIT47J5fI/7A==", + "dev": true, + "funding": [ + "https://github.com/sponsors/broofa" + ], + "license": "MIT", + "bin": { + "mime": "bin/cli.js" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/minimatch": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz", + "integrity": "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minimist": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz", + "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "dev": true, + "license": "ISC" + }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "license": "MIT" + }, + "node_modules/nconf": { + "version": "0.12.1", + "resolved": "https://registry.npmjs.org/nconf/-/nconf-0.12.1.tgz", + "integrity": "sha512-p2cfF+B3XXacQdswUYWZ0w6Vld0832A/tuqjLBu3H1sfUcby4N2oVbGhyuCkZv+t3iY3aiFEj7gZGqax9Q2c1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "async": "^3.0.0", + "ini": "^2.0.0", + "secure-keys": "^1.0.0", + "yargs": "^16.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/nconf/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, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/nconf/node_modules/ini": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz", + "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/nconf/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, + "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/nconf/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, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true, + "license": "MIT" + }, + "node_modules/nerf-dart": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/nerf-dart/-/nerf-dart-1.0.0.tgz", + "integrity": "sha512-EZSPZB70jiVsivaBLYDCyntd5eH8NTSMOn3rB+HxwdmKThGELLdYv8qVIMWvZEFy9w8ZZpW9h9OB32l1rGtj7g==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-emoji": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-2.2.0.tgz", + "integrity": "sha512-Z3lTE9pLaJF47NyMhd4ww1yFTAP8YhYI8SleJiHzM46Fgpm5cnNzSl9XfzFNqbaz+VlJrIj3fXQ4DeN1Rjm6cw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sindresorhus/is": "^4.6.0", + "char-regex": "^1.0.2", + "emojilib": "^2.4.0", + "skin-tone": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", + "license": "MIT" + }, + "node_modules/normalize-package-data": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-6.0.2.tgz", + "integrity": "sha512-V6gygoYb/5EmNI+MEGrWkC+e6+Rr7mTmfHrxDbLzxQogBkgzo76rkok0Am6thgSF7Mv2nLOajAJj5vDJZEFn7g==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^7.0.0", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/normalize-package-data/node_modules/hosted-git-info": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.2.tgz", + "integrity": "sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^10.0.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/normalize-package-data/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/normalize-package-data/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "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==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-url": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.0.1.tgz", + "integrity": "sha512-IO9QvjUMWxPQQhs60oOu10CRkWCiZzSUkzbXGGV9pviYl1fXYcvkzQ5jV9z8Y6un8ARoVRl4EtC6v6jNqbaJ/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/npm/-/npm-10.9.2.tgz", + "integrity": "sha512-iriPEPIkoMYUy3F6f3wwSZAU93E0Eg6cHwIR6jzzOXWSy+SD/rOODEs74cVONHKSx2obXtuUoyidVEhISrisgQ==", + "bundleDependencies": [ + "@isaacs/string-locale-compare", + "@npmcli/arborist", + "@npmcli/config", + "@npmcli/fs", + "@npmcli/map-workspaces", + "@npmcli/package-json", + "@npmcli/promise-spawn", + "@npmcli/redact", + "@npmcli/run-script", + "@sigstore/tuf", + "abbrev", + "archy", + "cacache", + "chalk", + "ci-info", + "cli-columns", + "fastest-levenshtein", + "fs-minipass", + "glob", + "graceful-fs", + "hosted-git-info", + "ini", + "init-package-json", + "is-cidr", + "json-parse-even-better-errors", + "libnpmaccess", + "libnpmdiff", + "libnpmexec", + "libnpmfund", + "libnpmhook", + "libnpmorg", + "libnpmpack", + "libnpmpublish", + "libnpmsearch", + "libnpmteam", + "libnpmversion", + "make-fetch-happen", + "minimatch", + "minipass", + "minipass-pipeline", + "ms", + "node-gyp", + "nopt", + "normalize-package-data", + "npm-audit-report", + "npm-install-checks", + "npm-package-arg", + "npm-pick-manifest", + "npm-profile", + "npm-registry-fetch", + "npm-user-validate", + "p-map", + "pacote", + "parse-conflict-json", + "proc-log", + "qrcode-terminal", + "read", + "semver", + "spdx-expression-parse", + "ssri", + "supports-color", + "tar", + "text-table", + "tiny-relative-date", + "treeverse", + "validate-npm-package-name", + "which", + "write-file-atomic" + ], + "dev": true, + "license": "Artistic-2.0", + "workspaces": [ + "docs", + "smoke-tests", + "mock-globals", + "mock-registry", + "workspaces/*" + ], + "dependencies": { + "@isaacs/string-locale-compare": "^1.1.0", + "@npmcli/arborist": "^8.0.0", + "@npmcli/config": "^9.0.0", + "@npmcli/fs": "^4.0.0", + "@npmcli/map-workspaces": "^4.0.2", + "@npmcli/package-json": "^6.1.0", + "@npmcli/promise-spawn": "^8.0.2", + "@npmcli/redact": "^3.0.0", + "@npmcli/run-script": "^9.0.1", + "@sigstore/tuf": "^3.0.0", + "abbrev": "^3.0.0", + "archy": "~1.0.0", + "cacache": "^19.0.1", + "chalk": "^5.3.0", + "ci-info": "^4.1.0", + "cli-columns": "^4.0.0", + "fastest-levenshtein": "^1.0.16", + "fs-minipass": "^3.0.3", + "glob": "^10.4.5", + "graceful-fs": "^4.2.11", + "hosted-git-info": "^8.0.2", + "ini": "^5.0.0", + "init-package-json": "^7.0.2", + "is-cidr": "^5.1.0", + "json-parse-even-better-errors": "^4.0.0", + "libnpmaccess": "^9.0.0", + "libnpmdiff": "^7.0.0", + "libnpmexec": "^9.0.0", + "libnpmfund": "^6.0.0", + "libnpmhook": "^11.0.0", + "libnpmorg": "^7.0.0", + "libnpmpack": "^8.0.0", + "libnpmpublish": "^10.0.1", + "libnpmsearch": "^8.0.0", + "libnpmteam": "^7.0.0", + "libnpmversion": "^7.0.0", + "make-fetch-happen": "^14.0.3", + "minimatch": "^9.0.5", + "minipass": "^7.1.1", + "minipass-pipeline": "^1.2.4", + "ms": "^2.1.2", + "node-gyp": "^11.0.0", + "nopt": "^8.0.0", + "normalize-package-data": "^7.0.0", + "npm-audit-report": "^6.0.0", + "npm-install-checks": "^7.1.1", + "npm-package-arg": "^12.0.0", + "npm-pick-manifest": "^10.0.0", + "npm-profile": "^11.0.1", + "npm-registry-fetch": "^18.0.2", + "npm-user-validate": "^3.0.0", + "p-map": "^4.0.0", + "pacote": "^19.0.1", + "parse-conflict-json": "^4.0.0", + "proc-log": "^5.0.0", + "qrcode-terminal": "^0.12.0", + "read": "^4.0.0", + "semver": "^7.6.3", + "spdx-expression-parse": "^4.0.0", + "ssri": "^12.0.0", + "supports-color": "^9.4.0", + "tar": "^6.2.1", + "text-table": "~0.2.0", + "tiny-relative-date": "^1.3.0", + "treeverse": "^3.0.0", + "validate-npm-package-name": "^6.0.0", + "which": "^5.0.0", + "write-file-atomic": "^6.0.0" + }, + "bin": { + "npm": "bin/npm-cli.js", + "npx": "bin/npx-cli.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "license": "MIT", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/@isaacs/cliui": { + "version": "8.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/npm/node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.1.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/npm/node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/npm/node_modules/@isaacs/fs-minipass": { + "version": "4.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.4" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/npm/node_modules/@isaacs/string-locale-compare": { + "version": "1.1.0", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/@npmcli/agent": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "agent-base": "^7.1.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.1", + "lru-cache": "^10.0.1", + "socks-proxy-agent": "^8.0.3" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/arborist": { + "version": "8.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@isaacs/string-locale-compare": "^1.1.0", + "@npmcli/fs": "^4.0.0", + "@npmcli/installed-package-contents": "^3.0.0", + "@npmcli/map-workspaces": "^4.0.1", + "@npmcli/metavuln-calculator": "^8.0.0", + "@npmcli/name-from-folder": "^3.0.0", + "@npmcli/node-gyp": "^4.0.0", + "@npmcli/package-json": "^6.0.1", + "@npmcli/query": "^4.0.0", + "@npmcli/redact": "^3.0.0", + "@npmcli/run-script": "^9.0.1", + "bin-links": "^5.0.0", + "cacache": "^19.0.1", + "common-ancestor-path": "^1.0.1", + "hosted-git-info": "^8.0.0", + "json-parse-even-better-errors": "^4.0.0", + "json-stringify-nice": "^1.1.4", + "lru-cache": "^10.2.2", + "minimatch": "^9.0.4", + "nopt": "^8.0.0", + "npm-install-checks": "^7.1.0", + "npm-package-arg": "^12.0.0", + "npm-pick-manifest": "^10.0.0", + "npm-registry-fetch": "^18.0.1", + "pacote": "^19.0.0", + "parse-conflict-json": "^4.0.0", + "proc-log": "^5.0.0", + "proggy": "^3.0.0", + "promise-all-reject-late": "^1.0.0", + "promise-call-limit": "^3.0.1", + "read-package-json-fast": "^4.0.0", + "semver": "^7.3.7", + "ssri": "^12.0.0", + "treeverse": "^3.0.0", + "walk-up-path": "^3.0.1" + }, + "bin": { + "arborist": "bin/index.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/config": { + "version": "9.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/map-workspaces": "^4.0.1", + "@npmcli/package-json": "^6.0.1", + "ci-info": "^4.0.0", + "ini": "^5.0.0", + "nopt": "^8.0.0", + "proc-log": "^5.0.0", + "semver": "^7.3.5", + "walk-up-path": "^3.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/fs": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/git": { + "version": "6.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/promise-spawn": "^8.0.0", + "ini": "^5.0.0", + "lru-cache": "^10.0.1", + "npm-pick-manifest": "^10.0.0", + "proc-log": "^5.0.0", + "promise-inflight": "^1.0.1", + "promise-retry": "^2.0.1", + "semver": "^7.3.5", + "which": "^5.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/installed-package-contents": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-bundled": "^4.0.0", + "npm-normalize-package-bin": "^4.0.0" + }, + "bin": { + "installed-package-contents": "bin/index.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/map-workspaces": { + "version": "4.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/name-from-folder": "^3.0.0", + "@npmcli/package-json": "^6.0.0", + "glob": "^10.2.2", + "minimatch": "^9.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/metavuln-calculator": { + "version": "8.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "cacache": "^19.0.0", + "json-parse-even-better-errors": "^4.0.0", + "pacote": "^20.0.0", + "proc-log": "^5.0.0", + "semver": "^7.3.5" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/metavuln-calculator/node_modules/pacote": { + "version": "20.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/git": "^6.0.0", + "@npmcli/installed-package-contents": "^3.0.0", + "@npmcli/package-json": "^6.0.0", + "@npmcli/promise-spawn": "^8.0.0", + "@npmcli/run-script": "^9.0.0", + "cacache": "^19.0.0", + "fs-minipass": "^3.0.0", + "minipass": "^7.0.2", + "npm-package-arg": "^12.0.0", + "npm-packlist": "^9.0.0", + "npm-pick-manifest": "^10.0.0", + "npm-registry-fetch": "^18.0.0", + "proc-log": "^5.0.0", + "promise-retry": "^2.0.1", + "sigstore": "^3.0.0", + "ssri": "^12.0.0", + "tar": "^6.1.11" + }, + "bin": { + "pacote": "bin/index.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/name-from-folder": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/node-gyp": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/package-json": { + "version": "6.1.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/git": "^6.0.0", + "glob": "^10.2.2", + "hosted-git-info": "^8.0.0", + "json-parse-even-better-errors": "^4.0.0", + "normalize-package-data": "^7.0.0", + "proc-log": "^5.0.0", + "semver": "^7.5.3" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/promise-spawn": { + "version": "8.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "which": "^5.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/query": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "postcss-selector-parser": "^6.1.2" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/redact": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/run-script": { + "version": "9.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/node-gyp": "^4.0.0", + "@npmcli/package-json": "^6.0.0", + "@npmcli/promise-spawn": "^8.0.0", + "node-gyp": "^11.0.0", + "proc-log": "^5.0.0", + "which": "^5.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/npm/node_modules/@sigstore/protobuf-specs": { + "version": "0.3.2", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@sigstore/tuf": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/protobuf-specs": "^0.3.2", + "tuf-js": "^3.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@tufjs/canonical-json": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/abbrev": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/agent-base": { + "version": "7.1.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/npm/node_modules/aggregate-error": { + "version": "3.1.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/ansi-regex": { + "version": "5.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/ansi-styles": { + "version": "6.2.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/npm/node_modules/aproba": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/archy": { + "version": "1.0.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/balanced-match": { + "version": "1.0.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/bin-links": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "cmd-shim": "^7.0.0", + "npm-normalize-package-bin": "^4.0.0", + "proc-log": "^5.0.0", + "read-cmd-shim": "^5.0.0", + "write-file-atomic": "^6.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/binary-extensions": { + "version": "2.3.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/brace-expansion": { + "version": "2.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/npm/node_modules/cacache": { + "version": "19.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/fs": "^4.0.0", + "fs-minipass": "^3.0.0", + "glob": "^10.2.2", + "lru-cache": "^10.0.1", + "minipass": "^7.0.3", + "minipass-collect": "^2.0.1", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "p-map": "^7.0.2", + "ssri": "^12.0.0", + "tar": "^7.4.3", + "unique-filename": "^4.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/cacache/node_modules/chownr": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/npm/node_modules/cacache/node_modules/minizlib": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.0.4", + "rimraf": "^5.0.5" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/npm/node_modules/cacache/node_modules/mkdirp": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "bin": { + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/cacache/node_modules/p-map": { + "version": "7.0.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/cacache/node_modules/tar": { + "version": "7.4.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.0.1", + "mkdirp": "^3.0.1", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/npm/node_modules/cacache/node_modules/yallist": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/npm/node_modules/chalk": { + "version": "5.3.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/npm/node_modules/chownr": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/ci-info": { + "version": "4.1.0", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/cidr-regex": { + "version": "4.1.1", + "dev": true, + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "ip-regex": "^5.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/npm/node_modules/clean-stack": { + "version": "2.2.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/npm/node_modules/cli-columns": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/npm/node_modules/cmd-shim": { + "version": "7.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/color-convert": { + "version": "2.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/npm/node_modules/color-name": { + "version": "1.1.4", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/common-ancestor-path": { + "version": "1.0.1", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/cross-spawn": { + "version": "7.0.6", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/cross-spawn/node_modules/which": { + "version": "2.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/cssesc": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm/node_modules/debug": { + "version": "4.3.7", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/npm/node_modules/diff": { + "version": "5.2.0", + "dev": true, + "inBundle": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/npm/node_modules/eastasianwidth": { + "version": "0.2.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/emoji-regex": { + "version": "8.0.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/encoding": { + "version": "0.1.13", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "iconv-lite": "^0.6.2" + } + }, + "node_modules/npm/node_modules/env-paths": { + "version": "2.2.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/npm/node_modules/err-code": { + "version": "2.0.3", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/exponential-backoff": { + "version": "3.1.1", + "dev": true, + "inBundle": true, + "license": "Apache-2.0" + }, + "node_modules/npm/node_modules/fastest-levenshtein": { + "version": "1.0.16", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 4.9.1" + } + }, + "node_modules/npm/node_modules/foreground-child": { + "version": "3.3.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/fs-minipass": { + "version": "3.0.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/glob": { + "version": "10.4.5", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/graceful-fs": { + "version": "4.2.11", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/hosted-git-info": { + "version": "8.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^10.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/http-cache-semantics": { + "version": "4.1.1", + "dev": true, + "inBundle": true, + "license": "BSD-2-Clause" + }, + "node_modules/npm/node_modules/http-proxy-agent": { + "version": "7.0.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/npm/node_modules/https-proxy-agent": { + "version": "7.0.5", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.0.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/npm/node_modules/iconv-lite": { + "version": "0.6.3", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm/node_modules/ignore-walk": { + "version": "7.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minimatch": "^9.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/imurmurhash": { + "version": "0.1.4", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/npm/node_modules/indent-string": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/ini": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/init-package-json": { + "version": "7.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/package-json": "^6.0.0", + "npm-package-arg": "^12.0.0", + "promzard": "^2.0.0", + "read": "^4.0.0", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4", + "validate-npm-package-name": "^6.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/ip-address": { + "version": "9.0.5", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "jsbn": "1.1.0", + "sprintf-js": "^1.1.3" + }, + "engines": { + "node": ">= 12" + } + }, + "node_modules/npm/node_modules/ip-regex": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/is-cidr": { + "version": "5.1.0", + "dev": true, + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "cidr-regex": "^4.1.1" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/npm/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/isexe": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/jackspeak": { + "version": "3.4.3", + "dev": true, + "inBundle": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/npm/node_modules/jsbn": { + "version": "1.1.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/json-parse-even-better-errors": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/json-stringify-nice": { + "version": "1.1.4", + "dev": true, + "inBundle": true, + "license": "ISC", + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/jsonparse": { + "version": "1.3.1", + "dev": true, + "engines": [ + "node >= 0.2.0" + ], + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/just-diff": { + "version": "6.0.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/just-diff-apply": { + "version": "5.5.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/libnpmaccess": { + "version": "9.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-package-arg": "^12.0.0", + "npm-registry-fetch": "^18.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/libnpmdiff": { + "version": "7.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/arborist": "^8.0.0", + "@npmcli/installed-package-contents": "^3.0.0", + "binary-extensions": "^2.3.0", + "diff": "^5.1.0", + "minimatch": "^9.0.4", + "npm-package-arg": "^12.0.0", + "pacote": "^19.0.0", + "tar": "^6.2.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/libnpmexec": { + "version": "9.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/arborist": "^8.0.0", + "@npmcli/run-script": "^9.0.1", + "ci-info": "^4.0.0", + "npm-package-arg": "^12.0.0", + "pacote": "^19.0.0", + "proc-log": "^5.0.0", + "read": "^4.0.0", + "read-package-json-fast": "^4.0.0", + "semver": "^7.3.7", + "walk-up-path": "^3.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/libnpmfund": { + "version": "6.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/arborist": "^8.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/libnpmhook": { + "version": "11.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "aproba": "^2.0.0", + "npm-registry-fetch": "^18.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/libnpmorg": { + "version": "7.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "aproba": "^2.0.0", + "npm-registry-fetch": "^18.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/libnpmpack": { + "version": "8.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/arborist": "^8.0.0", + "@npmcli/run-script": "^9.0.1", + "npm-package-arg": "^12.0.0", + "pacote": "^19.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/libnpmpublish": { + "version": "10.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "ci-info": "^4.0.0", + "normalize-package-data": "^7.0.0", + "npm-package-arg": "^12.0.0", + "npm-registry-fetch": "^18.0.1", + "proc-log": "^5.0.0", + "semver": "^7.3.7", + "sigstore": "^3.0.0", + "ssri": "^12.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/libnpmsearch": { + "version": "8.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-registry-fetch": "^18.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/libnpmteam": { + "version": "7.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "aproba": "^2.0.0", + "npm-registry-fetch": "^18.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/libnpmversion": { + "version": "7.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/git": "^6.0.1", + "@npmcli/run-script": "^9.0.1", + "json-parse-even-better-errors": "^4.0.0", + "proc-log": "^5.0.0", + "semver": "^7.3.7" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/lru-cache": { + "version": "10.4.3", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/make-fetch-happen": { + "version": "14.0.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/agent": "^3.0.0", + "cacache": "^19.0.1", + "http-cache-semantics": "^4.1.1", + "minipass": "^7.0.2", + "minipass-fetch": "^4.0.0", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^1.0.0", + "proc-log": "^5.0.0", + "promise-retry": "^2.0.1", + "ssri": "^12.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/make-fetch-happen/node_modules/negotiator": { + "version": "1.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/npm/node_modules/minimatch": { + "version": "9.0.5", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/minipass": { + "version": "7.1.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/npm/node_modules/minipass-collect": { + "version": "2.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/npm/node_modules/minipass-fetch": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.0.3", + "minipass-sized": "^1.0.3", + "minizlib": "^3.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + }, + "optionalDependencies": { + "encoding": "^0.1.13" + } + }, + "node_modules/npm/node_modules/minipass-fetch/node_modules/minizlib": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.0.4", + "rimraf": "^5.0.5" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/npm/node_modules/minipass-flush": { + "version": "1.0.5", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/minipass-flush/node_modules/minipass": { + "version": "3.3.6", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minipass-pipeline": { + "version": "1.2.4", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minipass-pipeline/node_modules/minipass": { + "version": "3.3.6", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minipass-sized": { + "version": "1.0.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minipass-sized/node_modules/minipass": { + "version": "3.3.6", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minizlib": { + "version": "2.1.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/minizlib/node_modules/minipass": { + "version": "3.3.6", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/mkdirp": { + "version": "1.0.4", + "dev": true, + "inBundle": true, + "license": "MIT", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/ms": { + "version": "2.1.3", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/mute-stream": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/node-gyp": { + "version": "11.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "env-paths": "^2.2.0", + "exponential-backoff": "^3.1.1", + "glob": "^10.3.10", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^14.0.3", + "nopt": "^8.0.0", + "proc-log": "^5.0.0", + "semver": "^7.3.5", + "tar": "^7.4.3", + "which": "^5.0.0" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/chownr": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/minizlib": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.0.4", + "rimraf": "^5.0.5" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/mkdirp": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "bin": { + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/tar": { + "version": "7.4.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.0.1", + "mkdirp": "^3.0.1", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/yallist": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/npm/node_modules/nopt": { + "version": "8.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "abbrev": "^2.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/nopt/node_modules/abbrev": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/normalize-package-data": { + "version": "7.0.0", + "dev": true, + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^8.0.0", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-audit-report": { + "version": "6.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-bundled": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-normalize-package-bin": "^4.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-install-checks": { + "version": "7.1.1", + "dev": true, + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "semver": "^7.1.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-normalize-package-bin": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-package-arg": { + "version": "12.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "hosted-git-info": "^8.0.0", + "proc-log": "^5.0.0", + "semver": "^7.3.5", + "validate-npm-package-name": "^6.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-packlist": { + "version": "9.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "ignore-walk": "^7.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-pick-manifest": { + "version": "10.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-install-checks": "^7.1.0", + "npm-normalize-package-bin": "^4.0.0", + "npm-package-arg": "^12.0.0", + "semver": "^7.3.5" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-profile": { + "version": "11.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-registry-fetch": "^18.0.0", + "proc-log": "^5.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-registry-fetch": { + "version": "18.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/redact": "^3.0.0", + "jsonparse": "^1.3.1", + "make-fetch-happen": "^14.0.0", + "minipass": "^7.0.2", + "minipass-fetch": "^4.0.0", + "minizlib": "^3.0.1", + "npm-package-arg": "^12.0.0", + "proc-log": "^5.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-registry-fetch/node_modules/minizlib": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.0.4", + "rimraf": "^5.0.5" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/npm/node_modules/npm-user-validate": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "BSD-2-Clause", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/p-map": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/package-json-from-dist": { + "version": "1.0.1", + "dev": true, + "inBundle": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/npm/node_modules/pacote": { + "version": "19.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/git": "^6.0.0", + "@npmcli/installed-package-contents": "^3.0.0", + "@npmcli/package-json": "^6.0.0", + "@npmcli/promise-spawn": "^8.0.0", + "@npmcli/run-script": "^9.0.0", + "cacache": "^19.0.0", + "fs-minipass": "^3.0.0", + "minipass": "^7.0.2", + "npm-package-arg": "^12.0.0", + "npm-packlist": "^9.0.0", + "npm-pick-manifest": "^10.0.0", + "npm-registry-fetch": "^18.0.0", + "proc-log": "^5.0.0", + "promise-retry": "^2.0.1", + "sigstore": "^3.0.0", + "ssri": "^12.0.0", + "tar": "^6.1.11" + }, + "bin": { + "pacote": "bin/index.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/parse-conflict-json": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "json-parse-even-better-errors": "^4.0.0", + "just-diff": "^6.0.0", + "just-diff-apply": "^5.2.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/path-key": { + "version": "3.1.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/path-scurry": { + "version": "1.11.1", + "dev": true, + "inBundle": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/postcss-selector-parser": { + "version": "6.1.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm/node_modules/proc-log": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/proggy": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/promise-all-reject-late": { + "version": "1.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/promise-call-limit": { + "version": "3.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/promise-inflight": { + "version": "1.0.1", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/promise-retry": { + "version": "2.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/promzard": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "read": "^4.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/qrcode-terminal": { + "version": "0.12.0", + "dev": true, + "inBundle": true, + "bin": { + "qrcode-terminal": "bin/qrcode-terminal.js" + } + }, + "node_modules/npm/node_modules/read": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "mute-stream": "^2.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/read-cmd-shim": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/read-package-json-fast": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "json-parse-even-better-errors": "^4.0.0", + "npm-normalize-package-bin": "^4.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/retry": { + "version": "0.12.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/npm/node_modules/rimraf": { + "version": "5.0.10", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "glob": "^10.3.7" + }, + "bin": { + "rimraf": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/safer-buffer": { + "version": "2.1.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true + }, + "node_modules/npm/node_modules/semver": { + "version": "7.6.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/shebang-command": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/shebang-regex": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/signal-exit": { + "version": "4.1.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/sigstore": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/bundle": "^3.0.0", + "@sigstore/core": "^2.0.0", + "@sigstore/protobuf-specs": "^0.3.2", + "@sigstore/sign": "^3.0.0", + "@sigstore/tuf": "^3.0.0", + "@sigstore/verify": "^2.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/sigstore/node_modules/@sigstore/bundle": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/protobuf-specs": "^0.3.2" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/sigstore/node_modules/@sigstore/core": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/sigstore/node_modules/@sigstore/sign": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/bundle": "^3.0.0", + "@sigstore/core": "^2.0.0", + "@sigstore/protobuf-specs": "^0.3.2", + "make-fetch-happen": "^14.0.1", + "proc-log": "^5.0.0", + "promise-retry": "^2.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/sigstore/node_modules/@sigstore/verify": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/bundle": "^3.0.0", + "@sigstore/core": "^2.0.0", + "@sigstore/protobuf-specs": "^0.3.2" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/smart-buffer": { + "version": "4.2.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/npm/node_modules/socks": { + "version": "2.8.3", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ip-address": "^9.0.5", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/npm/node_modules/socks-proxy-agent": { + "version": "8.0.4", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.1", + "debug": "^4.3.4", + "socks": "^2.8.3" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/npm/node_modules/spdx-correct": { + "version": "3.2.0", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/npm/node_modules/spdx-correct/node_modules/spdx-expression-parse": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/npm/node_modules/spdx-exceptions": { + "version": "2.5.0", + "dev": true, + "inBundle": true, + "license": "CC-BY-3.0" + }, + "node_modules/npm/node_modules/spdx-expression-parse": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/npm/node_modules/spdx-license-ids": { + "version": "3.0.20", + "dev": true, + "inBundle": true, + "license": "CC0-1.0" + }, + "node_modules/npm/node_modules/sprintf-js": { + "version": "1.1.3", + "dev": true, + "inBundle": true, + "license": "BSD-3-Clause" + }, + "node_modules/npm/node_modules/ssri": { + "version": "12.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/string-width": { + "version": "4.2.3", + "dev": true, + "inBundle": true, + "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/npm/node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "dev": true, + "inBundle": true, + "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/npm/node_modules/strip-ansi": { + "version": "6.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/supports-color": { + "version": "9.4.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/npm/node_modules/tar": { + "version": "6.2.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "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/npm/node_modules/tar/node_modules/fs-minipass": { + "version": "2.1.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/tar/node_modules/fs-minipass/node_modules/minipass": { + "version": "3.3.6", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/tar/node_modules/minipass": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/text-table": { + "version": "0.2.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/tiny-relative-date": { + "version": "1.3.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/treeverse": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/tuf-js": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "@tufjs/models": "3.0.1", + "debug": "^4.3.6", + "make-fetch-happen": "^14.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/tuf-js/node_modules/@tufjs/models": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "@tufjs/canonical-json": "2.0.0", + "minimatch": "^9.0.5" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/unique-filename": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "unique-slug": "^5.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/unique-slug": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/util-deprecate": { + "version": "1.0.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/validate-npm-package-license": { + "version": "3.0.4", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/npm/node_modules/validate-npm-package-license/node_modules/spdx-expression-parse": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/npm/node_modules/validate-npm-package-name": { + "version": "6.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/walk-up-path": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/which": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/which/node_modules/isexe": { + "version": "3.1.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=16" + } + }, + "node_modules/npm/node_modules/wrap-ansi": { + "version": "8.1.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/npm/node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "dev": true, + "inBundle": 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/npm/node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/npm/node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "6.1.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/npm/node_modules/wrap-ansi/node_modules/emoji-regex": { + "version": "9.2.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/wrap-ansi/node_modules/string-width": { + "version": "5.1.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "7.1.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/npm/node_modules/write-file-atomic": { + "version": "6.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/yallist": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.3.tgz", + "integrity": "sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "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==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/p-each-series": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-3.0.0.tgz", + "integrity": "sha512-lastgtAdoH9YaLyDa5i5z64q+kzOcQHsQ5SsZJD3q0VEyI8mq872S3geuNbRUQLVAE9siMfgKrpj7MloKFHruw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-filter": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-filter/-/p-filter-4.1.0.tgz", + "integrity": "sha512-37/tPdZ3oJwHaS3gNJdenCDB3Tz26i9sjhnguBtvN0vYlRIiDNnvTWkuh+0hETV9rLPdJ3rlL3yVOYPIAnM8rw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-map": "^7.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-is-promise": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-3.0.0.tgz", + "integrity": "sha512-Wo8VsW4IRQSKVXsJCn7TomUaVtyfjVDn3nUP7kE967BQk0CwFpdbZs0X0uk5sW9mkBa9eNM7hCMaG93WUAwxYQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-locate/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-map": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.3.tgz", + "integrity": "sha512-VkndIv2fIB99swvQoA65bm+fsmt6UNdGeIB0oxBs+WhAhdh08QA04JXpI7rbB9r08/nkbysKoya9rtDERYOYMA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-reduce": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-reduce/-/p-reduce-3.0.0.tgz", + "integrity": "sha512-xsrIUgI0Kn6iyDYm9StOpOeK29XM1aboGji26+QEortiFST1hGZaUQOLhtEbqHErPpGW/aSz6allwK2qcptp0Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "license": "BlueOak-1.0.0" + }, + "node_modules/pagedown": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/pagedown/-/pagedown-1.1.0.tgz", + "integrity": "sha512-rbaP3W0FLYGWc9+IY0OsBJzxDfpo21Y/B8gAnMAvQn1kp7WDhnzF1c3jJNscKjiAqFKe2Krs/xQn71P7A5XqbA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse-ms": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-4.0.0.tgz", + "integrity": "sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse-passwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", + "integrity": "sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" } }, - "npm-run-path": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "node_modules/parse5": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", + "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==", + "dev": true, + "license": "MIT" + }, + "node_modules/parse5-htmlparser2-tree-adapter": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz", + "integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==", "dev": true, - "requires": { - "path-key": "^2.0.0" + "license": "MIT", + "dependencies": { + "parse5": "^6.0.1" } }, - "number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "dev": true - }, - "nwsapi": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.1.4.tgz", - "integrity": "sha512-iGfd9Y6SFdTNldEy2L0GUhcarIutFmk+MPWIn9dmj8NMIup03G08uUF2KGbbmv/Ux4RT0VZJoP/sVbWA6d/VIw==", - "dev": true - }, - "oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" - }, - "object-copy": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", - "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", - "dev": true, - "requires": { - "copy-descriptor": "^0.1.0", - "define-property": "^0.2.5", - "kind-of": "^3.0.3" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } + "node_modules/parse5-htmlparser2-tree-adapter/node_modules/parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", + "dev": true, + "license": "MIT" + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" } }, - "object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true + "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==", + "license": "MIT", + "engines": { + "node": ">=8" + } }, - "object-visit": { + "node_modules/path-is-absolute": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", - "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", - "dev": true, - "requires": { - "isobject": "^3.0.0" + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" } }, - "object.getownpropertydescriptors": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz", - "integrity": "sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=", - "dev": true, - "requires": { - "define-properties": "^1.1.2", - "es-abstract": "^1.5.1" + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "license": "MIT", + "engines": { + "node": ">=8" } }, - "object.pick": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", - "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "license": "MIT" + }, + "node_modules/path-scurry": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz", + "integrity": "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==", + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "11.0.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.0.2.tgz", + "integrity": "sha512-123qHRfJBmo2jXDbo/a5YOQrJoHF/GNQTLzQ5+IdK5pWpceK17yRc6ozlWd25FxvGKQbIUs91fDFkXmDHTKcyA==", + "license": "ISC", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/path-to-regexp": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", + "license": "MIT" + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", "dev": true, - "requires": { - "isobject": "^3.0.1" + "license": "MIT", + "engines": { + "node": ">=8" } }, - "octokit-pagination-methods": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/octokit-pagination-methods/-/octokit-pagination-methods-1.1.0.tgz", - "integrity": "sha512-fZ4qZdQ2nxJvtcasX7Ghl+WlWS/d9IgnBIwFZXVNNZUmzpno91SX5bc5vuxiuKoCtK78XxGGNuSCrDC7xYB3OQ==", - "dev": true + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" }, - "on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", - "requires": { - "ee-first": "1.1.1" + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" } }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "requires": { - "wrappy": "1" + "node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" } }, - "onetime": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", - "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", + "node_modules/pirates": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", + "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-conf": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pkg-conf/-/pkg-conf-2.1.0.tgz", + "integrity": "sha512-C+VUP+8jis7EsQZIhDYmS5qlNtjv2yP4SNtjXK9AP1ZcTRlnSfuumaTnRfYZnYgUUYVIKqL0fRvmUGDV2fmp6g==", "dev": true, - "requires": { - "mimic-fn": "^1.0.0" + "license": "MIT", + "dependencies": { + "find-up": "^2.0.0", + "load-json-file": "^4.0.0" + }, + "engines": { + "node": ">=4" } }, - "optimist": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", - "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", + "node_modules/pkg-conf/node_modules/find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", "dev": true, - "requires": { - "minimist": "~0.0.1", - "wordwrap": "~0.0.2" + "license": "MIT", + "dependencies": { + "locate-path": "^2.0.0" }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pkg-conf/node_modules/locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", + "dev": true, + "license": "MIT", "dependencies": { - "minimist": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", - "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=", - "dev": true - } + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=4" } }, - "optionator": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", - "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", + "node_modules/pkg-conf/node_modules/p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", "dev": true, - "requires": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.4", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "wordwrap": "~1.0.0" + "license": "MIT", + "dependencies": { + "p-try": "^1.0.0" }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pkg-conf/node_modules/p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", + "dev": true, + "license": "MIT", "dependencies": { - "wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", - "dev": true - } + "p-limit": "^1.1.0" + }, + "engines": { + "node": ">=4" } }, - "os-locale": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", - "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", + "node_modules/pkg-conf/node_modules/p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==", "dev": true, - "requires": { - "execa": "^1.0.0", - "lcid": "^2.0.0", - "mem": "^4.0.0" + "license": "MIT", + "engines": { + "node": ">=4" } }, - "os-name": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/os-name/-/os-name-3.1.0.tgz", - "integrity": "sha512-h8L+8aNjNcMpo/mAIBPn5PXCM16iyPGjHNWo6U1YO8sJTMHtEtyczI6QJnLoplswm6goopQkqc7OAnjhWcugVg==", + "node_modules/pkg-conf/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", "dev": true, - "requires": { - "macos-release": "^2.2.0", - "windows-release": "^3.1.0" + "license": "MIT", + "engines": { + "node": ">=4" } }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", - "dev": true + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "license": "MIT", + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } }, - "p-defer": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", - "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=", - "dev": true + "node_modules/prettier": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.4.2.tgz", + "integrity": "sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } }, - "p-each-series": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-1.0.0.tgz", - "integrity": "sha1-kw89Et0fUOdDRFeiLNbwSsatf3E=", + "node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/pretty-ms": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-9.2.0.tgz", + "integrity": "sha512-4yf0QO/sllf/1zbZWYnvWw3NxCQwLXKzIj0G849LSufP15BXKM0rbD2Z3wVnkMfjdn/CB0Dpp444gYAACdsplg==", "dev": true, - "requires": { - "p-reduce": "^1.0.0" + "license": "MIT", + "dependencies": { + "parse-ms": "^4.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "p-filter": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-filter/-/p-filter-2.1.0.tgz", - "integrity": "sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw==", + "node_modules/prettyjson": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/prettyjson/-/prettyjson-1.2.5.tgz", + "integrity": "sha512-rksPWtoZb2ZpT5OVgtmy0KHVM+Dca3iVwWY9ifwhcexfjebtgjg3wmrUt9PvJ59XIYBcknQeYHD8IAnVlh9lAw==", + "license": "MIT", + "dependencies": { + "colors": "1.4.0", + "minimist": "^1.2.0" + }, + "bin": { + "prettyjson": "bin/prettyjson" + } + }, + "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==", "dev": true, - "requires": { - "p-map": "^2.0.0" + "license": "MIT" + }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "license": "MIT", + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" } }, - "p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", - "dev": true + "node_modules/proto-list": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", + "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==", + "dev": true, + "license": "ISC" }, - "p-is-promise": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.1.0.tgz", - "integrity": "sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==", - "dev": true + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/pure-rand": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", + "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "license": "MIT" + }, + "node_modules/qs": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.0.6" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "p-limit": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz", - "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==", + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", "dev": true, - "requires": { - "p-try": "^2.0.0" + "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/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" } }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "node_modules/raw-body": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", "dev": true, - "requires": { - "p-limit": "^2.0.0" + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" } }, - "p-map": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", - "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==", - "dev": true + "node_modules/rc/node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true, + "license": "ISC" }, - "p-reduce": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-reduce/-/p-reduce-1.0.0.tgz", - "integrity": "sha1-GMKw3ZNqRpClKfgjH1ig/bakffo=", - "dev": true + "node_modules/rc/node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } }, - "p-retry": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.1.0.tgz", - "integrity": "sha512-oepllyG9gX1qH4Sm20YAKxg1GA7L7puhvGnTfimi31P07zSIj7SDV6YtuAx9nbJF51DES+2CIIRkXs8GKqWJxA==", + "node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "license": "MIT" + }, + "node_modules/read-package-up": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/read-package-up/-/read-package-up-11.0.0.tgz", + "integrity": "sha512-MbgfoNPANMdb4oRBNg5eqLbB2t2r+o5Ua1pNt8BqGp4I0FJZhuVSOj3PaBPni4azWuSzEdNn2evevzVmEk1ohQ==", "dev": true, - "requires": { - "@types/retry": "^0.12.0", - "retry": "^0.12.0" + "license": "MIT", + "dependencies": { + "find-up-simple": "^1.0.0", + "read-pkg": "^9.0.0", + "type-fest": "^4.6.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true + "node_modules/read-package-up/node_modules/type-fest": { + "version": "4.33.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.33.0.tgz", + "integrity": "sha512-s6zVrxuyKbbAsSAD5ZPTB77q4YIdRctkTbJ2/Dqlinwz+8ooH2gd+YA7VA6Pa93KML9GockVvoxjZ2vHP+mu8g==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "pagedown": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/pagedown/-/pagedown-1.1.0.tgz", - "integrity": "sha1-K0bBGdXVtYPMPJIQoACz1A5Ob4A=", - "dev": true + "node_modules/read-pkg": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-9.0.1.tgz", + "integrity": "sha512-9viLL4/n1BJUCT1NXVTdS1jtm80yDEgR5T4yCelII49Mbj0v1rZdKqj7zCiYdbB0CuCgdrvHcNogAKTFPBocFA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/normalize-package-data": "^2.4.3", + "normalize-package-data": "^6.0.0", + "parse-json": "^8.0.0", + "type-fest": "^4.6.0", + "unicorn-magic": "^0.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "parse-github-url": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/parse-github-url/-/parse-github-url-1.0.2.tgz", - "integrity": "sha512-kgBf6avCbO3Cn6+RnzRGLkUsv4ZVqv/VfAYkRsyBcgkshNvVBkRn1FEZcW0Jb+npXQWm2vHPnnOqFteZxRRGNw==", - "dev": true + "node_modules/read-pkg/node_modules/parse-json": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-8.1.0.tgz", + "integrity": "sha512-rum1bPifK5SSar35Z6EKZuYPJx85pkNaFrxBK3mwdfSJ1/WKbYrjoW/zTPSjRRamfmVX1ACBIdFAO0VRErW/EA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.22.13", + "index-to-position": "^0.1.2", + "type-fest": "^4.7.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "node_modules/read-pkg/node_modules/type-fest": { + "version": "4.33.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.33.0.tgz", + "integrity": "sha512-s6zVrxuyKbbAsSAD5ZPTB77q4YIdRctkTbJ2/Dqlinwz+8ooH2gd+YA7VA6Pa93KML9GockVvoxjZ2vHP+mu8g==", "dev": true, - "requires": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "parse-passwd": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", - "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=", - "dev": true + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } }, - "parse5": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-4.0.0.tgz", - "integrity": "sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA==", - "dev": true + "node_modules/registry-auth-token": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-5.0.3.tgz", + "integrity": "sha512-1bpc9IyC+e+CNFRaWyn77tk4xGG4PPUyfakSmA6F6cvUDjrm58dfyJ3II+9yb10EDkHoy1LaPSmHaWLOH3m6HA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@pnpm/npm-conf": "^2.1.0" + }, + "engines": { + "node": ">=14" + } }, - "parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" + "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==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } }, - "pascalcase": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", - "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", - "dev": true + "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==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } }, - "path-dirname": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", - "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", - "dev": true + "node_modules/resolve": { + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "path-exists": { + "node_modules/resolve-cwd": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "license": "MIT", + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } }, - "path-is-absolute": { + "node_modules/resolve-dir": { "version": "1.0.1", - "resolved": "http://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" - }, - "path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", - "dev": true + "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", + "integrity": "sha512-R7uiTjECzvOsWSfdM0QKFNBVFcK27aHOUwdvK53BcW8zqnGdYp0Fbj82cy54+2A4P2tFM22J5kRfe1R+lM/1yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "expand-tilde": "^2.0.0", + "global-modules": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } }, - "path-parse": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", - "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", - "dev": true + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "license": "MIT", + "engines": { + "node": ">=8" + } }, - "path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + "node_modules/resolve.exports": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.3.tgz", + "integrity": "sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==", + "license": "MIT", + "engines": { + "node": ">=10" + } }, - "path-type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", - "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", "dev": true, - "requires": { - "pify": "^3.0.0" + "license": "MIT", + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" } }, - "performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" - }, - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true + "node_modules/restore-cursor/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==", + "dev": true, + "license": "ISC" }, - "pirates": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.1.tgz", - "integrity": "sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA==", + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", "dev": true, - "requires": { - "node-modules-regexp": "^1.0.0" + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" } }, - "pkg-conf": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/pkg-conf/-/pkg-conf-2.1.0.tgz", - "integrity": "sha1-ISZRTKbyq/69FoWW3xi6V4Z/AFg=", + "node_modules/run-async": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", "dev": true, - "requires": { - "find-up": "^2.0.0", - "load-json-file": "^4.0.0" - }, - "dependencies": { - "find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", - "dev": true, - "requires": { - "locate-path": "^2.0.0" - } - }, - "locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", - "dev": true, - "requires": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - } - }, - "p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", - "dev": true, - "requires": { - "p-try": "^1.0.0" - } + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" }, - "p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", - "dev": true, - "requires": { - "p-limit": "^1.1.0" - } + { + "type": "patreon", + "url": "https://www.patreon.com/feross" }, - "p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", - "dev": true + { + "type": "consulting", + "url": "https://feross.org/support" } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" } }, - "pkg-dir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", - "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "node_modules/rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", "dev": true, - "requires": { - "find-up": "^3.0.0" + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" } }, - "pn": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/pn/-/pn-1.1.0.tgz", - "integrity": "sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA==", - "dev": true + "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" + } + ], + "license": "MIT" }, - "posix-character-classes": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", - "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", - "dev": true + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" }, - "prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", - "dev": true + "node_modules/secure-keys": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/secure-keys/-/secure-keys-1.0.0.tgz", + "integrity": "sha512-nZi59hW3Sl5P3+wOO89eHBAAGwmCPd2aE1+dLZV5MO+ItQctIvAqihzaAXIQhvtH4KJPxM080HsnqltR2y8cWg==", + "dev": true, + "license": "MIT" }, - "pretty-format": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-24.8.0.tgz", - "integrity": "sha512-P952T7dkrDEplsR+TuY7q3VXDae5Sr7zmQb12JU/NDQa/3CH7/QW0yvqLcGN6jL+zQFKaoJcPc+yJxMTGmosqw==", + "node_modules/semantic-release": { + "version": "24.2.1", + "resolved": "https://registry.npmjs.org/semantic-release/-/semantic-release-24.2.1.tgz", + "integrity": "sha512-z0/3cutKNkLQ4Oy0HTi3lubnjTsdjjgOqmxdPjeYWe6lhFqUPfwslZxRHv3HDZlN4MhnZitb9SLihDkZNxOXfQ==", "dev": true, - "requires": { - "@jest/types": "^24.8.0", - "ansi-regex": "^4.0.0", - "ansi-styles": "^3.2.0", - "react-is": "^16.8.4" + "license": "MIT", + "dependencies": { + "@semantic-release/commit-analyzer": "^13.0.0-beta.1", + "@semantic-release/error": "^4.0.0", + "@semantic-release/github": "^11.0.0", + "@semantic-release/npm": "^12.0.0", + "@semantic-release/release-notes-generator": "^14.0.0-beta.1", + "aggregate-error": "^5.0.0", + "cosmiconfig": "^9.0.0", + "debug": "^4.0.0", + "env-ci": "^11.0.0", + "execa": "^9.0.0", + "figures": "^6.0.0", + "find-versions": "^6.0.0", + "get-stream": "^6.0.0", + "git-log-parser": "^1.2.0", + "hook-std": "^3.0.0", + "hosted-git-info": "^8.0.0", + "import-from-esm": "^2.0.0", + "lodash-es": "^4.17.21", + "marked": "^12.0.0", + "marked-terminal": "^7.0.0", + "micromatch": "^4.0.2", + "p-each-series": "^3.0.0", + "p-reduce": "^3.0.0", + "read-package-up": "^11.0.0", + "resolve-from": "^5.0.0", + "semver": "^7.3.2", + "semver-diff": "^4.0.0", + "signale": "^1.2.1", + "yargs": "^17.5.1" + }, + "bin": { + "semantic-release": "bin/semantic-release.js" + }, + "engines": { + "node": ">=20.8.1" } }, - "prettyjson": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prettyjson/-/prettyjson-1.2.1.tgz", - "integrity": "sha1-/P+rQdGcq0365eV15kJGYZsS0ok=", - "optional": true, - "requires": { - "colors": "^1.1.2", - "minimist": "^1.2.0" + "node_modules/semantic-release/node_modules/@sindresorhus/merge-streams": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-4.0.0.tgz", + "integrity": "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/semantic-release/node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dev": true, + "license": "MIT", "dependencies": { - "minimist": { - "version": "1.2.0", - "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { "optional": 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==", - "dev": true - }, - "prompts": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.1.0.tgz", - "integrity": "sha512-+x5TozgqYdOwWsQFZizE/Tra3fKvAoy037kOyU6cgz84n8f6zxngLOV4O32kTwt9FcLCxAqw0P/c8rOr9y+Gfg==", + "node_modules/semantic-release/node_modules/execa": { + "version": "9.5.2", + "resolved": "https://registry.npmjs.org/execa/-/execa-9.5.2.tgz", + "integrity": "sha512-EHlpxMCpHWSAh1dgS6bVeoLAXGnJNdR93aabr4QCGbzOM73o5XmRfM/e5FUqsw3aagP8S8XEWUWFAxnRBnAF0Q==", "dev": true, - "requires": { - "kleur": "^3.0.2", - "sisteransi": "^1.0.0" - } - }, - "proxy-addr": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.5.tgz", - "integrity": "sha512-t/7RxHXPH6cJtP0pRG6smSr9QJidhB+3kXu0KgXnbGYMgzEnUxRQ4/LDdfOwZEMyIh3/xHb8PX3t+lfL9z+YVQ==", - "requires": { - "forwarded": "~0.1.2", - "ipaddr.js": "1.9.0" + "license": "MIT", + "dependencies": { + "@sindresorhus/merge-streams": "^4.0.0", + "cross-spawn": "^7.0.3", + "figures": "^6.1.0", + "get-stream": "^9.0.0", + "human-signals": "^8.0.0", + "is-plain-obj": "^4.1.0", + "is-stream": "^4.0.1", + "npm-run-path": "^6.0.0", + "pretty-ms": "^9.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^4.0.0", + "yoctocolors": "^2.0.0" + }, + "engines": { + "node": "^18.19.0 || >=20.5.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, - "psl": { - "version": "1.1.31", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.31.tgz", - "integrity": "sha512-/6pt4+C+T+wZUieKR620OpzN/LlnNKuWjy1iFLQ/UG35JqHlR/89MP1d96dUfkf6Dne3TuLQzOYEYshJ+Hx8mw==" - }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "node_modules/semantic-release/node_modules/execa/node_modules/get-stream": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-9.0.1.tgz", + "integrity": "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==", "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" - }, - "q": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", - "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=", - "dev": true - }, - "qs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" - }, - "quick-lru": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-1.1.0.tgz", - "integrity": "sha1-Q2CxfGETatOAeDl/8RQW4Ybc+7g=", - "dev": true - }, - "range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" - }, - "raw-body": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", - "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", - "requires": { - "bytes": "3.1.0", - "http-errors": "1.7.2", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" + "license": "MIT", + "dependencies": { + "@sec-ant/readable-stream": "^0.4.1", + "is-stream": "^4.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "node_modules/semantic-release/node_modules/figures": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-6.1.0.tgz", + "integrity": "sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==", "dev": true, - "requires": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" + "license": "MIT", + "dependencies": { + "is-unicode-supported": "^2.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "react-is": { - "version": "16.8.6", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.6.tgz", - "integrity": "sha512-aUk3bHfZ2bRSVFFbbeVS4i+lNPZr3/WM5jT2J5omUVV1zzcs1nAaf3l51ctA5FFvCRbhrH0bdAsRRQddFJZPtA==", - "dev": true - }, - "read-pkg": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", - "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", + "node_modules/semantic-release/node_modules/human-signals": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-8.0.0.tgz", + "integrity": "sha512-/1/GPCpDUCCYwlERiYjxoczfP0zfvZMU/OWgQPMya9AbAE24vseigFdhAMObpc8Q4lc/kjutPfUddDYyAmejnA==", "dev": true, - "requires": { - "load-json-file": "^4.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^3.0.0" + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" } }, - "read-pkg-up": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-4.0.0.tgz", - "integrity": "sha512-6etQSH7nJGsK0RbG/2TeDzZFa8shjQ1um+SwQQ5cwKy0dhSXdOncEhb1CPpvQG4h7FyOV6EB6YlV0yJvZQNAkA==", + "node_modules/semantic-release/node_modules/is-stream": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-4.0.1.tgz", + "integrity": "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==", "dev": true, - "requires": { - "find-up": "^3.0.0", - "read-pkg": "^3.0.0" + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "readable-stream": { - "version": "2.3.6", - "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "node_modules/semantic-release/node_modules/is-unicode-supported": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", + "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", "dev": true, - "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" + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "realpath-native": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/realpath-native/-/realpath-native-1.1.0.tgz", - "integrity": "sha512-wlgPA6cCIIg9gKz0fgAPjnzh4yR/LnXovwuo9hvyGvx3h8nX4+/iLZplfUWasXpqD8BdnGnP5njOFjkUwPzvjA==", + "node_modules/semantic-release/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, - "requires": { - "util.promisify": "^1.0.0" - } + "license": "MIT" }, - "rechoir": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", - "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", + "node_modules/semantic-release/node_modules/npm-run-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-6.0.0.tgz", + "integrity": "sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==", "dev": true, - "requires": { - "resolve": "^1.1.6" + "license": "MIT", + "dependencies": { + "path-key": "^4.0.0", + "unicorn-magic": "^0.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "redent": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-2.0.0.tgz", - "integrity": "sha1-wbIAe0LVfrE4kHmzyDM2OdXhzKo=", + "node_modules/semantic-release/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", "dev": true, - "requires": { - "indent-string": "^3.0.0", - "strip-indent": "^2.0.0" + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "redeyed": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/redeyed/-/redeyed-2.1.1.tgz", - "integrity": "sha1-iYS1gV2ZyyIEacme7v/jiRPmzAs=", + "node_modules/semantic-release/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", "dev": true, - "requires": { - "esprima": "~4.0.0" + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" } }, - "regex-not": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", - "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "node_modules/semantic-release/node_modules/strip-final-newline": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-4.0.0.tgz", + "integrity": "sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==", "dev": true, - "requires": { - "extend-shallow": "^3.0.2", - "safe-regex": "^1.1.0" + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "registry-auth-token": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.1.1.tgz", - "integrity": "sha512-9bKS7nTl9+/A1s7tnPeGrUpRcVY+LUh7bfFgzpndALdPfXQBfQV77rQVtqgUV3ti4vc/Ik81Ex8UJDWDQ12zQA==", + "node_modules/semantic-release/node_modules/unicorn-magic": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz", + "integrity": "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==", "dev": true, - "requires": { - "rc": "^1.2.8" + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "remove-trailing-separator": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", - "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", - "dev": true - }, - "repeat-element": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", - "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", - "dev": true - }, - "repeat-string": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", - "dev": true - }, - "request": { - "version": "2.88.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", - "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", - "requires": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.0", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.4.3", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - }, - "dependencies": { - "punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" - }, - "tough-cookie": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", - "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", - "requires": { - "psl": "^1.1.24", - "punycode": "^1.4.1" - } - } + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" } }, - "request-promise-core": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.2.tgz", - "integrity": "sha512-UHYyq1MO8GsefGEt7EprS8UrXsm1TxEvFUX1IMTuSLU2Rh7fTIdFtl8xD7JiEYiWU2dl+NYAjCTksTehQUxPag==", + "node_modules/semver-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-4.0.0.tgz", + "integrity": "sha512-0Ju4+6A8iOnpL/Thra7dZsSlOHYAHIeMxfhWQRI1/VLcT3WDBZKKtQt/QkBOsiIN9ZpuvHE6cGZ0x4glCMmfiA==", "dev": true, - "requires": { - "lodash": "^4.17.11" + "license": "MIT", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "request-promise-native": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.7.tgz", - "integrity": "sha512-rIMnbBdgNViL37nZ1b3L/VfPOpSi0TqVDQPAvO6U14lMzOLrt5nilxCQqtDKhZeDiW0/hkCXGoQjhgJd/tCh6w==", + "node_modules/semver-diff/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", "dev": true, - "requires": { - "request-promise-core": "1.1.2", - "stealthy-require": "^1.1.1", - "tough-cookie": "^2.3.3" + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" } }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", - "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==" - }, - "require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", - "dev": true - }, - "resolve": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.10.0.tgz", - "integrity": "sha512-3sUr9aq5OfSg2S9pNtPA9hL1FVEAjvfOC4leW0SNf/mpnaakz2a9femSd6LqAww2RaFctwyf1lCqnTHuF1rxDg==", + "node_modules/semver-regex": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/semver-regex/-/semver-regex-4.0.5.tgz", + "integrity": "sha512-hunMQrEy1T6Jr2uEVjrAIqjwWcQTgOAcIM52C8MY1EZSD3DDNft04XzvYKPqjED65bNVVko0YI38nYeEHCX3yw==", "dev": true, - "requires": { - "path-parse": "^1.0.6" + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "resolve-cwd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz", - "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=", - "dev": true, - "requires": { - "resolve-from": "^3.0.0" + "node_modules/send": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", + "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" } }, - "resolve-dir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", - "integrity": "sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=", - "dev": true, - "requires": { - "expand-tilde": "^2.0.0", - "global-modules": "^1.0.0" + "node_modules/send/node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "license": "MIT", + "engines": { + "node": ">= 0.8" } }, - "resolve-from": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", - "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", - "dev": true - }, - "resolve-url": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", - "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", - "dev": true - }, - "restore-cursor": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", - "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", - "dev": true, - "requires": { - "onetime": "^2.0.0", - "signal-exit": "^3.0.2" + "node_modules/send/node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" } }, - "ret": { - "version": "0.1.15", - "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", - "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", - "dev": true - }, - "retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", - "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=", - "dev": true - }, - "right-pad": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/right-pad/-/right-pad-1.0.1.tgz", - "integrity": "sha1-jKCMLLtbVedNr6lr9/0aJ9VoyNA=", - "dev": true + "node_modules/send/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==", + "license": "MIT" }, - "rimraf": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", - "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", - "dev": true, - "requires": { - "glob": "^7.1.3" + "node_modules/serve-static": { + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", + "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", + "license": "MIT", + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.19.0" + }, + "engines": { + "node": ">= 0.8.0" } }, - "rsvp": { - "version": "4.8.5", - "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.5.tgz", - "integrity": "sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA==", - "dev": true + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" }, - "run-async": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz", - "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=", - "dev": true, - "requires": { - "is-promise": "^2.1.0" + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" } }, - "rxjs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.2.tgz", - "integrity": "sha512-HUb7j3kvb7p7eCUHE3FqjoDsC1xfZQ4AHFWfTKSpZ+sAhhz5X1WX0ZuUqWbzB2QhSLp3DoLUG+hMdEDKqWo2Zg==", - "dev": true, - "requires": { - "tslib": "^1.9.0" + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "license": "MIT", + "engines": { + "node": ">=8" } }, - "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==" - }, - "safe-regex": { + "node_modules/side-channel": { "version": "1.1.0", - "resolved": "http://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", - "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", - "dev": true, - "requires": { - "ret": "~0.1.10" + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "sane": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/sane/-/sane-4.1.0.tgz", - "integrity": "sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA==", - "dev": true, - "requires": { - "@cnakazawa/watch": "^1.0.3", - "anymatch": "^2.0.0", - "capture-exit": "^2.0.0", - "exec-sh": "^0.3.2", - "execa": "^1.0.0", - "fb-watchman": "^2.0.0", - "micromatch": "^3.1.4", - "minimist": "^1.1.1", - "walker": "~1.0.5" + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", - "dev": true - }, - "semantic-release": { - "version": "15.13.18", - "resolved": "https://registry.npmjs.org/semantic-release/-/semantic-release-15.13.18.tgz", - "integrity": "sha512-JtfdrhF1zRm91nJH/Rg3taftbWGwktJqqrJJdbmZGKYx63cfC4PoaS0jxRifGJUdmmgW/Kxz8f5bhtB+p1bu8A==", - "dev": true, - "requires": { - "@semantic-release/commit-analyzer": "^6.1.0", - "@semantic-release/error": "^2.2.0", - "@semantic-release/github": "^5.1.0", - "@semantic-release/npm": "^5.0.5", - "@semantic-release/release-notes-generator": "^7.1.2", - "aggregate-error": "^3.0.0", - "cosmiconfig": "^5.0.1", - "debug": "^4.0.0", - "env-ci": "^4.0.0", - "execa": "^1.0.0", - "figures": "^3.0.0", - "find-versions": "^3.0.0", - "get-stream": "^5.0.0", - "git-log-parser": "^1.2.0", - "hook-std": "^2.0.0", - "hosted-git-info": "^2.7.1", - "lodash": "^4.17.4", - "marked": "^0.6.0", - "marked-terminal": "^3.2.0", - "p-locate": "^4.0.0", - "p-reduce": "^2.0.0", - "read-pkg-up": "^6.0.0", - "resolve-from": "^5.0.0", - "semver": "^6.0.0", - "signale": "^1.2.1", - "yargs": "^13.1.0" - }, - "dependencies": { - "cliui": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", - "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", - "dev": true, - "requires": { - "string-width": "^3.1.0", - "strip-ansi": "^5.2.0", - "wrap-ansi": "^5.1.0" - } - }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "figures": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.0.0.tgz", - "integrity": "sha512-HKri+WoWoUgr83pehn/SIgLOMZ9nAWC6dcGj26RY2R4F50u4+RTUz0RCrUlOV3nKRAICW1UGzyb+kcX2qK1S/g==", - "dev": true, - "requires": { - "escape-string-regexp": "^1.0.5" - } - }, - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "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-stream": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz", - "integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==", - "dev": true, - "requires": { - "pump": "^3.0.0" - } - }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } - }, - "p-reduce": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-reduce/-/p-reduce-2.1.0.tgz", - "integrity": "sha512-2USApvnsutq8uoxZBGbbWM0JIYLiEMJ9RlaN7fAzVNb9OZN0SHjjTTfIcb667XynS5Y1VhwDJVDa72TnPzAYWw==", - "dev": true - }, - "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 - }, - "read-pkg": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.1.1.tgz", - "integrity": "sha512-dFcTLQi6BZ+aFUaICg7er+/usEoqFdQxiEBsEMNGoipenihtxxtdrQuBXvyANCEI8VuUIVYFgeHGx9sLLvim4w==", - "dev": true, - "requires": { - "@types/normalize-package-data": "^2.4.0", - "normalize-package-data": "^2.5.0", - "parse-json": "^4.0.0", - "type-fest": "^0.4.1" - }, - "dependencies": { - "type-fest": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.4.1.tgz", - "integrity": "sha512-IwzA/LSfD2vC1/YDYMv/zHP4rDF1usCwllsDpbolT3D4fUepIO7f9K70jjmUewU/LmGUKJcwcVtDCpnKk4BPMw==", - "dev": true - } - } - }, - "read-pkg-up": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-6.0.0.tgz", - "integrity": "sha512-odtTvLl+EXo1eTsMnoUHRmg/XmXdTkwXVxy4VFE9Kp6cCq7b3l7QMdBndND3eAFzrbSAXC/WCUOQQ9rLjifKZw==", - "dev": true, - "requires": { - "find-up": "^4.0.0", - "read-pkg": "^5.1.1", - "type-fest": "^0.5.0" - } - }, - "resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true - }, - "semver": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.1.2.tgz", - "integrity": "sha512-z4PqiCpomGtWj8633oeAdXm1Kn1W++3T8epkZYnwiVgIYIJ0QHszhInYSJTYxebByQH7KVCEAn8R9duzZW2PhQ==", - "dev": true - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "type-fest": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.5.2.tgz", - "integrity": "sha512-DWkS49EQKVX//Tbupb9TFa19c7+MK1XmzkrZUR8TAktmE/DizXoaoJV6TZ/tSIPXipqNiRI6CyAe7x69Jb6RSw==", - "dev": true - }, - "wrap-ansi": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", - "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.0", - "string-width": "^3.0.0", - "strip-ansi": "^5.0.0" - } - }, - "yargs": { - "version": "13.2.4", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.2.4.tgz", - "integrity": "sha512-HG/DWAJa1PAnHT9JAhNa8AbAv3FPaiLzioSjCcmuXXhP8MlpHO5vwls4g4j6n30Z74GVQj8Xa62dWVx1QCGklg==", - "dev": true, - "requires": { - "cliui": "^5.0.0", - "find-up": "^3.0.0", - "get-caller-file": "^2.0.1", - "os-locale": "^3.1.0", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^3.0.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^13.1.0" - }, - "dependencies": { - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "requires": { - "p-limit": "^2.0.0" - } - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true - } - } - }, - "yargs-parser": { - "version": "13.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.1.tgz", - "integrity": "sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ==", - "dev": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } - } + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "semver": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", - "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", - "dev": true - }, - "semver-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/semver-regex/-/semver-regex-2.0.0.tgz", - "integrity": "sha512-mUdIBBvdn0PLOeP3TEkMH7HHeUP3GjsXCwKarjv/kGmUFOYg1VqEemKhoQpWMu6X2I8kHeuVdGibLGkVK+/5Qw==", - "dev": true - }, - "send": { - "version": "0.17.1", - "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", - "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", - "requires": { - "debug": "2.6.9", - "depd": "~1.1.2", - "destroy": "~1.0.4", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "~1.7.2", - "mime": "1.6.0", - "ms": "2.1.1", - "on-finished": "~2.3.0", - "range-parser": "~1.2.1", - "statuses": "~1.5.0" - }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", "dependencies": { - "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" - } + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "serve-static": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", - "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", - "requires": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.17.1" + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", - "dev": true + "node_modules/signale": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/signale/-/signale-1.4.0.tgz", + "integrity": "sha512-iuh+gPf28RkltuJC7W5MRi6XAjTDCAPC/prJUpQoG4vIP3MJZ+GTydVnodXA7pwvTKb2cA0m9OFZW/cdWy/I/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^2.3.2", + "figures": "^2.0.0", + "pkg-conf": "^2.1.0" + }, + "engines": { + "node": ">=6" + } }, - "set-value": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", - "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.3", - "split-string": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } + "node_modules/signale/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" } }, - "setprototypeof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", - "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" - }, - "shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "node_modules/signale/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dev": true, - "requires": { - "shebang-regex": "^1.0.0" + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" } }, - "shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", - "dev": true - }, - "shellwords": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz", - "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==", - "dev": true - }, - "signal-exit": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", - "dev": true - }, - "signale": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/signale/-/signale-1.4.0.tgz", - "integrity": "sha512-iuh+gPf28RkltuJC7W5MRi6XAjTDCAPC/prJUpQoG4vIP3MJZ+GTydVnodXA7pwvTKb2cA0m9OFZW/cdWy/I/w==", + "node_modules/signale/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "dev": true, - "requires": { - "chalk": "^2.3.2", - "figures": "^2.0.0", - "pkg-conf": "^2.1.0" + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" } }, - "sisteransi": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.0.tgz", - "integrity": "sha512-N+z4pHB4AmUv0SjveWRd6q1Nj5w62m5jodv+GD8lvmbY/83T/rpbJGZOnK5T149OldDj4Db07BSv9xY4K6NTPQ==", - "dev": true + "node_modules/signale/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true, + "license": "MIT" }, - "slash": { + "node_modules/signale/node_modules/figures": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", - "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", - "dev": true - }, - "snapdragon": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", - "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", - "dev": true, - "requires": { - "base": "^0.11.1", - "debug": "^2.2.0", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "map-cache": "^0.2.2", - "source-map": "^0.5.6", - "source-map-resolve": "^0.5.0", - "use": "^3.1.0" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } + "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", + "integrity": "sha512-Oa2M9atig69ZkfwiApY8F2Yy+tzMbazyvqv21R0NsSC8floSOC09BbT1ITWAdoMGQvJ/aZnR1KMwdx9tvHnTNA==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=4" } }, - "snapdragon-node": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", - "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", - "dev": true, - "requires": { - "define-property": "^1.0.0", - "isobject": "^3.0.0", - "snapdragon-util": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } + "node_modules/signale/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" } }, - "snapdragon-util": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", - "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "node_modules/signale/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, - "requires": { - "kind-of": "^3.2.0" - }, + "license": "MIT", "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" } }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "license": "MIT" }, - "source-map-resolve": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz", - "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==", + "node_modules/skin-tone": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/skin-tone/-/skin-tone-2.0.0.tgz", + "integrity": "sha512-kUMbT1oBJCpgrnKoSr0o6wPtvRWT9W9UKvGLwfJYO2WuahZRHOpEyL1ckyMGgMWh0UdpmaoFqKKD29WTomNEGA==", "dev": true, - "requires": { - "atob": "^2.1.1", - "decode-uri-component": "^0.2.0", - "resolve-url": "^0.2.1", - "source-map-url": "^0.4.0", - "urix": "^0.1.0" + "license": "MIT", + "dependencies": { + "unicode-emoji-modifier-base": "^1.0.0" + }, + "engines": { + "node": ">=8" } }, - "source-map-support": { - "version": "0.5.10", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.10.tgz", - "integrity": "sha512-YfQ3tQFTK/yzlGJuX8pTwa4tifQj4QS2Mj7UegOu8jAz59MqIiMGPXxQhVQiIMNzayuUSF/jEuVnfFF5JqybmQ==", - "dev": true, - "requires": { + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "license": "MIT", + "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } } }, - "source-map-url": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", - "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", - "dev": true - }, - "spawn-error-forwarder": { + "node_modules/spawn-error-forwarder": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/spawn-error-forwarder/-/spawn-error-forwarder-1.0.0.tgz", - "integrity": "sha1-Gv2Uc46ZmwNG17n8NzvlXgdXcCk=", - "dev": true + "integrity": "sha512-gRjMgK5uFjbCvdibeGJuy3I5OYz6VLoVdsOJdA6wV0WlfQVLFueoqMxwwYD9RODdgb6oUIvlRlsyFSiQkMKu0g==", + "dev": true, + "license": "MIT" }, - "spdx-correct": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", - "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", + "node_modules/spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", "dev": true, - "requires": { + "license": "Apache-2.0", + "dependencies": { "spdx-expression-parse": "^3.0.0", "spdx-license-ids": "^3.0.0" } }, - "spdx-exceptions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", - "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==", - "dev": true + "node_modules/spdx-exceptions": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", + "dev": true, + "license": "CC-BY-3.0" }, - "spdx-expression-parse": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", - "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "spdx-exceptions": "^2.1.0", "spdx-license-ids": "^3.0.0" } }, - "spdx-license-ids": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.4.tgz", - "integrity": "sha512-7j8LYJLeY/Yb6ACbQ7F76qy5jHkp0U6jgBfJsk97bwWlVUnUWsAgpyaCvo17h0/RQGnQ036tVDomiwoI4pDkQA==", - "dev": true - }, - "split": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz", - "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==", - "dev": true, - "requires": { - "through": "2" - } - }, - "split-string": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", - "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "node_modules/spdx-license-ids": { + "version": "3.0.21", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.21.tgz", + "integrity": "sha512-Bvg/8F5XephndSK3JffaRqdT+gyhfqIPwDHpX80tJrF8QQRYMo8sNMeaZ2Dp5+jhwKnUmIOyFFQfHRkjJm5nXg==", "dev": true, - "requires": { - "extend-shallow": "^3.0.0" - } + "license": "CC0-1.0" }, - "split2": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/split2/-/split2-2.2.0.tgz", - "integrity": "sha512-RAb22TG39LhI31MbreBgIuKiIKhVsawfTgEGqKHTK87aG+ul/PB8Sqoi3I7kVdRWiCfrKxK3uo4/YUkpNvhPbw==", + "node_modules/split2": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-1.0.0.tgz", + "integrity": "sha512-NKywug4u4pX/AZBB1FCPzZ6/7O+Xhz1qMVbzTvvKvikjO99oPN87SkK08mEY9P63/5lWjK+wgOOgApnTg5r6qg==", "dev": true, - "requires": { - "through2": "^2.0.2" + "license": "ISC", + "dependencies": { + "through2": "~2.0.0" } }, - "sprintf-js": { + "node_modules/sprintf-js": { "version": "1.0.3", - "resolved": "http://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" - }, - "sshpk": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", - "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", - "requires": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - } - }, - "stack-utils": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-1.0.2.tgz", - "integrity": "sha512-MTX+MeG5U994cazkjd/9KNAapsHnibjMLnfXodlkXw76JEea0UiNzrqidzo1emMwk7w5Qhc9jd4Bn9TBb1MFwA==", - "dev": true + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "license": "BSD-3-Clause" }, - "static-extend": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", - "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", - "dev": true, - "requires": { - "define-property": "^0.2.5", - "object-copy": "^0.1.0" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - } + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" } }, - "statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" + "node_modules/stack-utils/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "license": "MIT", + "engines": { + "node": ">=8" + } }, - "stealthy-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", - "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=", - "dev": true + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } }, - "stream-combiner2": { + "node_modules/stream-combiner2": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/stream-combiner2/-/stream-combiner2-1.1.1.tgz", - "integrity": "sha1-+02KFCDqNidk4hrUeAOXvry0HL4=", + "integrity": "sha512-3PnJbYgS56AeWgtKF5jtJRT6uFJe56Z0Hc5Ngg/6sI6rIt8iiMBTa9cvdyFfpMQjaVHr8dusbNeFGIIonxOvKw==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "duplexer2": "~0.1.0", "readable-stream": "^2.0.2" } }, - "string-length": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-2.0.0.tgz", - "integrity": "sha1-1A27aGo6zpYMHP/KVivyxF+DY+0=", + "node_modules/stream-combiner2/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==", "dev": true, - "requires": { - "astral-regex": "^1.0.0", - "strip-ansi": "^4.0.0" - }, + "license": "MIT", "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - } + "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" } }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "node_modules/stream-combiner2/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==", "dev": true, - "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - } - } + "license": "MIT" }, - "string_decoder": { + "node_modules/stream-combiner2/node_modules/string_decoder": { "version": "1.1.1", - "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "safe-buffer": "~5.1.0" } }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "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==", "dev": true, - "requires": { - "ansi-regex": "^4.1.0" + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" } }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true + "node_modules/string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "license": "MIT", + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } }, - "strip-eof": { - "version": "1.0.0", - "resolved": "http://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", - "dev": true + "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==", + "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/string-width-cjs": { + "name": "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==", + "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", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/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==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-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==", + "license": "MIT", + "engines": { + "node": ">=8" + } }, - "strip-final-newline": { + "node_modules/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true - }, - "strip-indent": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-2.0.0.tgz", - "integrity": "sha1-XvjbKV0B5u1sv3qrlpmNeCJSe2g=", - "dev": true - }, - "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", - "dev": true + "license": "MIT", + "engines": { + "node": ">=6" + } }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "requires": { - "has-flag": "^3.0.0" + "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==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "supports-hyperlinks": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-1.0.1.tgz", - "integrity": "sha512-HHi5kVSefKaJkGYXbDuKbUGRVxqnWGn3J2e39CYcNJEfWciGq2zYtOhXLTlvrOZW1QU7VX67w7fMmWafHX9Pfw==", + "node_modules/super-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/super-regex/-/super-regex-1.0.0.tgz", + "integrity": "sha512-CY8u7DtbvucKuquCmOFEKhr9Besln7n9uN8eFbwcoGYWXOMW07u2o8njWaiXt11ylS3qoGF55pILjRmPlbodyg==", "dev": true, - "requires": { - "has-flag": "^2.0.0", - "supports-color": "^5.0.0" + "license": "MIT", + "dependencies": { + "function-timeout": "^1.0.1", + "time-span": "^5.1.0" }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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==", + "license": "MIT", "dependencies": { - "has-flag": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", - "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", - "dev": true - } + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, - "symbol-tree": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", - "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", - "dev": true + "node_modules/supports-hyperlinks": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-3.1.0.tgz", + "integrity": "sha512-2rn0BZ+/f7puLOHZm1HOJfwBggfaHXUpPUSSG/SWM4TWp5KCfmNYwnC3hruy2rZlMnmWZ+QAGpZfchu3f3695A==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + }, + "engines": { + "node": ">=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "temp-dir": { + "node_modules/supports-preserve-symlinks-flag": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-1.0.0.tgz", - "integrity": "sha1-CnwOom06Oa+n4OvqnB/AvE2qAR0=", - "dev": true + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "tempy": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/tempy/-/tempy-0.3.0.tgz", - "integrity": "sha512-WrH/pui8YCwmeiAoxV+lpRH9HpRtgBhSR2ViBPgpGb/wnYDzp21R4MN45fsCGvLROvY67o3byhJRYRONJyImVQ==", + "node_modules/temp-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-3.0.0.tgz", + "integrity": "sha512-nHc6S/bwIilKHNRgK/3jlhDoIHcp45YgyiwcAk46Tr0LfEqGBVpmiAyuiuxeVE44m3mXnEeVhaipLOEWmH+Njw==", "dev": true, - "requires": { - "temp-dir": "^1.0.0", - "type-fest": "^0.3.1", - "unique-string": "^1.0.0" - }, + "license": "MIT", + "engines": { + "node": ">=14.16" + } + }, + "node_modules/tempy": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/tempy/-/tempy-3.1.0.tgz", + "integrity": "sha512-7jDLIdD2Zp0bDe5r3D2qtkd1QOCacylBuL7oa4udvN6v2pqr4+LcCr67C8DR1zkpaZ8XosF5m1yQSabKAW6f2g==", + "dev": true, + "license": "MIT", "dependencies": { - "type-fest": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.3.1.tgz", - "integrity": "sha512-cUGJnCdr4STbePCgqNFbpVNCepa+kAVohJs1sLhxzdH+gnEoOd8VhbYa7pD3zZYGiURWM2xzEII3fQcRizDkYQ==", - "dev": true - } + "is-stream": "^3.0.0", + "temp-dir": "^3.0.0", + "type-fest": "^2.12.2", + "unique-string": "^3.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "test-exclude": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-5.2.3.tgz", - "integrity": "sha512-M+oxtseCFO3EDtAaGH7iiej3CBkzXqFMbzqYAACdzKui4eZA+pq3tZEwChvOdNfa7xxy8BfbmgJSIr43cC/+2g==", + "node_modules/tempy/node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", "dev": true, - "requires": { - "glob": "^7.1.3", - "minimatch": "^3.0.4", - "read-pkg-up": "^4.0.0", - "require-main-filename": "^2.0.0" + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "text-extensions": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-1.9.0.tgz", - "integrity": "sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ==", - "dev": true + "node_modules/tempy/node_modules/type-fest": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "throat": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/throat/-/throat-4.1.0.tgz", - "integrity": "sha1-iQN8vJLFarGJJua6TLsgDhVnKmo=", - "dev": true + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "license": "ISC", + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/test-exclude/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/test-exclude/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/test-exclude/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } }, - "through": { + "node_modules/through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", - "dev": true + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", + "dev": true, + "license": "MIT" }, - "through2": { + "node_modules/through2": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "readable-stream": "~2.3.6", "xtend": "~4.0.1" - } - }, - "tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "dev": true, - "requires": { - "os-tmpdir": "~1.0.2" - } - }, - "tmpl": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz", - "integrity": "sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE=", - "dev": true - }, - "to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", - "dev": true + } }, - "to-object-path": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", - "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "node_modules/through2/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==", "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, + "license": "MIT", "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } + "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" } }, - "to-regex": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", - "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "node_modules/through2/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==", "dev": true, - "requires": { - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "regex-not": "^1.0.2", - "safe-regex": "^1.1.0" - } + "license": "MIT" }, - "to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "node_modules/through2/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==", "dev": true, - "requires": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" } }, - "toidentifier": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", - "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" - }, - "tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "node_modules/time-span": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/time-span/-/time-span-5.1.0.tgz", + "integrity": "sha512-75voc/9G4rDIJleOo4jPvN4/YC4GRZrY8yy1uU4lwrB3XEQbWve8zXoO5No4eFrGcTAMYyoY67p8jRQdtA1HbA==", "dev": true, - "requires": { - "psl": "^1.1.28", - "punycode": "^2.1.1" + "license": "MIT", + "dependencies": { + "convert-hrtime": "^5.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "tr46": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", - "integrity": "sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=", + "node_modules/tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", "dev": true, - "requires": { - "punycode": "^2.1.0" + "license": "MIT", + "dependencies": { + "os-tmpdir": "~1.0.2" + }, + "engines": { + "node": ">=0.6.0" } }, - "traverse": { - "version": "0.6.6", - "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.6.6.tgz", - "integrity": "sha1-y99WD9e5r2MlAv7UD5GMFX6pcTc=", - "dev": true - }, - "trim-newlines": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-2.0.0.tgz", - "integrity": "sha1-tAPQuRvlDDMd/EuC7s6yLD3hbSA=", - "dev": true + "node_modules/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "license": "BSD-3-Clause" }, - "trim-off-newlines": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/trim-off-newlines/-/trim-off-newlines-1.0.1.tgz", - "integrity": "sha1-n5up2e+odkw4dpi8v+sshI8RrbM=", - "dev": true + "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==", + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } }, - "trim-right": { + "node_modules/toidentifier": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", - "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=", - "dev": true - }, - "ts-jest": { - "version": "24.0.2", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-24.0.2.tgz", - "integrity": "sha512-h6ZCZiA1EQgjczxq+uGLXQlNgeg02WWJBbeT8j6nyIBRQdglqbvzDoHahTEIiS6Eor6x8mK6PfZ7brQ9Q6tzHw==", - "dev": true, - "requires": { - "bs-logger": "0.x", - "buffer-from": "1.x", - "fast-json-stable-stringify": "2.x", - "json5": "2.x", - "make-error": "1.x", - "mkdirp": "0.x", - "resolve": "1.x", - "semver": "^5.5", - "yargs-parser": "10.x" - }, - "dependencies": { - "camelcase": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", - "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", - "dev": true - }, - "yargs-parser": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-10.1.0.tgz", - "integrity": "sha512-VCIyR1wJoEBZUqk5PA+oOBF6ypbwh5aNB3I50guxAL/quggdfs4TtNHQrSazFA3fYZ+tEqfs0zIGlv0c/rgjbQ==", - "dev": true, - "requires": { - "camelcase": "^4.1.0" - } - } + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" } }, - "ts-node": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-7.0.1.tgz", - "integrity": "sha512-BVwVbPJRspzNh2yfslyT1PSbl5uIk03EZlb493RKHN4qej/D06n1cEhjlOJG69oFsE7OT8XjpTUcYf6pKTLMhw==", + "node_modules/traverse": { + "version": "0.6.8", + "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.6.8.tgz", + "integrity": "sha512-aXJDbk6SnumuaZSANd21XAo15ucCDE38H4fkqiGsc3MhCK+wOlZvLP9cB/TvpHT0mOyWgC4Z8EwRlzqYSUzdsA==", "dev": true, - "requires": { - "arrify": "^1.0.0", - "buffer-from": "^1.1.0", - "diff": "^3.1.0", - "make-error": "^1.1.1", - "minimist": "^1.2.0", - "mkdirp": "^0.5.1", - "source-map-support": "^0.5.6", - "yn": "^2.0.0" + "license": "MIT", + "engines": { + "node": ">= 0.4" }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true - } + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "tslib": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", - "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==", - "dev": true - }, - "tslint": { - "version": "5.18.0", - "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.18.0.tgz", - "integrity": "sha512-Q3kXkuDEijQ37nXZZLKErssQVnwCV/+23gFEMROi8IlbaBG6tXqLPQJ5Wjcyt/yHPKBC+hD5SzuGaMora+ZS6w==", + "node_modules/ts-jest": { + "version": "29.2.5", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.2.5.tgz", + "integrity": "sha512-KD8zB2aAZrcKIdGk4OwpJggeLcH1FgrICqDSROWqlnJXGCXK4Mn6FcdK2B6670Xr73lHMG1kHw8R87A0ecZ+vA==", "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "builtin-modules": "^1.1.1", - "chalk": "^2.3.0", - "commander": "^2.12.1", - "diff": "^3.2.0", - "glob": "^7.1.1", - "js-yaml": "^3.13.1", - "minimatch": "^3.0.4", - "mkdirp": "^0.5.1", - "resolve": "^1.3.2", - "semver": "^5.3.0", - "tslib": "^1.8.0", - "tsutils": "^2.29.0" + "license": "MIT", + "dependencies": { + "bs-logger": "^0.2.6", + "ejs": "^3.1.10", + "fast-json-stable-stringify": "^2.1.0", + "jest-util": "^29.0.0", + "json5": "^2.2.3", + "lodash.memoize": "^4.1.2", + "make-error": "^1.3.6", + "semver": "^7.6.3", + "yargs-parser": "^21.1.1" + }, + "bin": { + "ts-jest": "cli.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.0 <8", + "@jest/transform": "^29.0.0", + "@jest/types": "^29.0.0", + "babel-jest": "^29.0.0", + "jest": "^29.0.0", + "typescript": ">=4.3 <6" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "@jest/transform": { + "optional": true + }, + "@jest/types": { + "optional": true + }, + "babel-jest": { + "optional": true + }, + "esbuild": { + "optional": true + } } }, - "tsutils": { - "version": "2.29.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz", - "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==", + "node_modules/ts-jest/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", "dev": true, - "requires": { - "tslib": "^1.8.1" + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" } }, - "tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "requires": { - "safe-buffer": "^5.0.1" + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } } }, - "tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" - }, - "type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "dev": true, - "requires": { - "prelude-ls": "~1.1.2" + "license": "0BSD" + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "license": "MIT", + "engines": { + "node": ">=4" } }, - "type-fest": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", - "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", - "dev": true + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "type-is": { + "node_modules/type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "requires": { + "license": "MIT", + "dependencies": { "media-typer": "0.3.0", "mime-types": "~2.1.24" }, - "dependencies": { - "mime-db": { - "version": "1.40.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", - "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==" - }, - "mime-types": { - "version": "2.1.24", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", - "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", - "requires": { - "mime-db": "1.40.0" - } - } + "engines": { + "node": ">= 0.6" } }, - "typescript": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.5.2.tgz", - "integrity": "sha512-7KxJovlYhTX5RaRbUdkAXN1KUZ8PwWlTzQdHV6xNqvuFOs7+WBo10TQUqT19Q/Jz2hk5v9TQDIhyLhhJY4p5AA==", - "dev": true + "node_modules/typescript": { + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz", + "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==", + "devOptional": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } }, - "uglify-js": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.6.0.tgz", - "integrity": "sha512-W+jrUHJr3DXKhrsS7NUVxn3zqMOFn0hL/Ei6v0anCIMoKC93TjcflTagwIHLW7SfMFfiQuktQyFVCFHGUE0+yg==", + "node_modules/uglify-js": { + "version": "3.19.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", + "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", "dev": true, + "license": "BSD-2-Clause", "optional": true, - "requires": { - "commander": "~2.20.0", - "source-map": "~0.6.1" + "bin": { + "uglifyjs": "bin/uglifyjs" }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "optional": true - } + "engines": { + "node": ">=0.8.0" } }, - "union-value": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", - "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", + "node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "license": "MIT" + }, + "node_modules/unicode-emoji-modifier-base": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unicode-emoji-modifier-base/-/unicode-emoji-modifier-base-1.0.0.tgz", + "integrity": "sha512-yLSH4py7oFH3oG/9K+XWrz1pSi3dfUrWEnInbxMfArOfc1+33BlGPQtLsOYwvdMy11AwUBetYuaRxSPqgkq+8g==", "dev": true, - "requires": { - "arr-union": "^3.1.0", - "get-value": "^2.0.6", - "is-extendable": "^0.1.1", - "set-value": "^2.0.1" + "license": "MIT", + "engines": { + "node": ">=4" } }, - "unique-string": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-1.0.0.tgz", - "integrity": "sha1-nhBXzKhRq7kzmPizOuGHuZyuwRo=", + "node_modules/unicorn-magic": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz", + "integrity": "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==", "dev": true, - "requires": { - "crypto-random-string": "^1.0.0" + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "universal-user-agent": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-2.1.0.tgz", - "integrity": "sha512-8itiX7G05Tu3mGDTdNY2fB4KJ8MgZLS54RdG6PkkfwMAavrXu1mV/lls/GABx9O3Rw4PnTtasxrvbMQoBYY92Q==", + "node_modules/unique-string": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-3.0.0.tgz", + "integrity": "sha512-VGXBUVwxKMBUznyffQweQABPRRW1vHZAbadFZud4pLFAqRGvv/96vafgjWFqzourzr8YonlQiPgH0YCJfawoGQ==", "dev": true, - "requires": { - "os-name": "^3.0.0" + "license": "MIT", + "dependencies": { + "crypto-random-string": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", - "dev": true + "node_modules/universal-user-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-7.0.2.tgz", + "integrity": "sha512-0JCqzSKnStlRRQfCdowvqy3cy0Dvtlb8xecj/H8JFZuCze4rwjPZQOgvFvn0Ws/usCHQFGpyr+pB9adaGwXn4Q==", + "dev": true, + "license": "ISC" + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } }, - "unpipe": { + "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } }, - "unset-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", - "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", - "dev": true, - "requires": { - "has-value": "^0.3.1", - "isobject": "^3.0.0" - }, - "dependencies": { - "has-value": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", - "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", - "dev": true, - "requires": { - "get-value": "^2.0.3", - "has-values": "^0.1.4", - "isobject": "^2.0.0" - }, - "dependencies": { - "isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", - "dev": true, - "requires": { - "isarray": "1.0.0" - } - } - } - }, - "has-values": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", - "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", - "dev": true + "node_modules/update-browserslist-db": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.2.tgz", + "integrity": "sha512-PPypAm5qvlD7XMZC3BujecnaOxwhrtoFR+Dqkk5Aa/6DssiH0ibKoketaj9w8LP7Bont1rYeoV5plxD7RTEPRg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" } }, - "uri-js": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", - "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", - "requires": { - "punycode": "^2.1.0" + "node_modules/url-join": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/url-join/-/url-join-5.0.0.tgz", + "integrity": "sha512-n2huDr9h9yzd6exQVnH/jU5mr+Pfx08LRXXZhkLLetAMESRj+anQsTAh940iMrIetKAmry9coFuZQ2jY8/p3WA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" } }, - "urix": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", - "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", - "dev": true - }, - "url-join": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.0.tgz", - "integrity": "sha1-TTNA6AfTdzvamZH4MFrNzCpmXSo=", - "dev": true - }, - "url-template": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/url-template/-/url-template-2.0.8.tgz", - "integrity": "sha1-/FZaPMy/93MMd19WQflVV5FDnyE=", - "dev": true - }, - "use": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", - "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", - "dev": true - }, - "util-deprecate": { + "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "dev": true - }, - "util.promisify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.0.tgz", - "integrity": "sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA==", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "dev": true, - "requires": { - "define-properties": "^1.1.2", - "object.getownpropertydescriptors": "^2.0.3" - } + "license": "MIT" }, - "utils-merge": { + "node_modules/utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } }, - "uuid": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", - "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/v8-to-istanbul": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", + "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", + "license": "ISC", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + }, + "engines": { + "node": ">=10.12.0" + } }, - "validate-npm-package-license": { + "node_modules/validate-npm-package-license": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", "dev": true, - "requires": { + "license": "Apache-2.0", + "dependencies": { "spdx-correct": "^3.0.0", "spdx-expression-parse": "^3.0.0" } }, - "vary": { + "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" - }, - "verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", - "requires": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" } }, - "w3c-hr-time": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.1.tgz", - "integrity": "sha1-gqwr/2PZUOqeMYmlimViX+3xkEU=", - "dev": true, - "requires": { - "browser-process-hrtime": "^0.1.2" + "node_modules/walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "license": "Apache-2.0", + "dependencies": { + "makeerror": "1.0.12" } }, - "walker": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.7.tgz", - "integrity": "sha1-L3+bj9ENZ3JisYqITijRlhjgKPs=", + "node_modules/wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", "dev": true, - "requires": { - "makeerror": "1.0.x" + "license": "MIT", + "dependencies": { + "defaults": "^1.0.3" } }, - "webidl-conversions": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", - "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==", - "dev": true - }, - "whatwg-encoding": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", - "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==", - "dev": true, - "requires": { - "iconv-lite": "0.4.24" + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" } }, - "whatwg-mimetype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", - "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==", - "dev": true - }, - "whatwg-url": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-6.5.0.tgz", - "integrity": "sha512-rhRZRqx/TLJQWUpQ6bmrt2UV4f0HCQ463yQuONJqC6fO2VoEb1pTYddbe59SkYq87aoM5A3bdhMZiUiVws+fzQ==", + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true, - "requires": { - "lodash.sortby": "^4.7.0", - "tr46": "^1.0.1", - "webidl-conversions": "^4.0.2" + "license": "MIT", + "engines": { + "node": ">=0.10.0" } }, - "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", "dev": true, - "requires": { - "isexe": "^2.0.0" - } + "license": "MIT" }, - "which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", - "dev": true - }, - "windows-release": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/windows-release/-/windows-release-3.2.0.tgz", - "integrity": "sha512-QTlz2hKLrdqukrsapKsINzqMgOUpQW268eJ0OaOpJN32h272waxR9fkB9VoWRtK7uKHG5EHJcTXQBD8XZVJkFA==", - "dev": true, - "requires": { - "execa": "^1.0.0" + "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==", + "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" } }, - "word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "dev": true - }, - "wordwrap": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", - "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=", - "dev": true - }, - "wrap-ansi": { - "version": "2.1.0", - "resolved": "http://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", - "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", - "dev": true, - "requires": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1" - }, + "node_modules/wrap-ansi-cjs": { + "name": "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==", + "license": "MIT", "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "dev": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "string-width": { - "version": "1.0.2", - "resolved": "http://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dev": true, - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - } + "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" } }, - "wrappy": { + "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" }, - "write-file-atomic": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.1.tgz", - "integrity": "sha512-TGHFeZEZMnv+gBFRfjAcxL5bPHrsGKtnb4qsFAws7/vlh+QfwAaySIw4AXP9ZskTTh5GWu3FLuJhsWVdiJPGvg==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.11", + "node_modules/write-file-atomic": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", + "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", + "license": "ISC", + "dependencies": { "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.2" + "signal-exit": "^3.0.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, - "ws": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-5.2.2.tgz", - "integrity": "sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA==", + "node_modules/write-file-atomic/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==", + "license": "ISC" + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", "dev": true, - "requires": { - "async-limiter": "~1.0.0" + "license": "MIT", + "engines": { + "node": ">=0.4" } }, - "xml-name-validator": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", - "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", - "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==", + "license": "ISC", + "engines": { + "node": ">=10" + } }, - "xtend": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", - "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", - "dev": true + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "license": "ISC" }, - "y18n": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", - "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", - "dev": true + "node_modules/yaml-lint": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/yaml-lint/-/yaml-lint-1.7.0.tgz", + "integrity": "sha512-zeBC/kskKQo4zuoGQ+IYjw6C9a/YILr2SXoEZA9jM0COrSwvwVbfTiFegT8qYBSBgOwLMWGL8sY137tOmFXGnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "consola": "^2.15.3", + "globby": "^11.1.0", + "js-yaml": "^4.1.0", + "nconf": "^0.12.0" + }, + "bin": { + "yamllint": "dist/cli.js" + } + }, + "node_modules/yaml-lint/node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "yamljs": { + "node_modules/yamljs": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/yamljs/-/yamljs-0.3.0.tgz", "integrity": "sha512-C/FsVVhht4iPQYXOInoxUM/1ELSf9EsgKH34FofQOp6hwCPrW4vG4w5++TED3xRUo8gD7l0P1J1dLlDYzODsTQ==", - "requires": { + "license": "MIT", + "dependencies": { "argparse": "^1.0.7", "glob": "^7.0.5" + }, + "bin": { + "json2yaml": "bin/json2yaml", + "yaml2json": "bin/yaml2json" } }, - "yargs": { - "version": "12.0.5", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz", - "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==", - "dev": true, - "requires": { - "cliui": "^4.0.0", - "decamelize": "^1.2.0", - "find-up": "^3.0.0", - "get-caller-file": "^1.0.1", - "os-locale": "^3.0.0", + "node_modules/yamljs/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/yamljs/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/yamljs/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/yamljs/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", - "require-main-filename": "^1.0.1", - "set-blocking": "^2.0.0", - "string-width": "^2.0.0", - "which-module": "^2.0.0", - "y18n": "^3.2.1 || ^4.0.0", - "yargs-parser": "^11.1.1" - }, - "dependencies": { - "require-main-filename": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", - "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", - "dev": true - } + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" } }, - "yargs-parser": { - "version": "11.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz", - "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==", - "dev": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "license": "ISC", + "engines": { + "node": ">=12" } }, - "yn": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/yn/-/yn-2.0.0.tgz", - "integrity": "sha1-5a2ryKz0CPY4X8dklWhMiOavaJo=", - "dev": true - }, - "yval": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/yval/-/yval-0.0.3.tgz", - "integrity": "sha1-7YS7H7s1CuQo8DUnkkQlD1Qf/28=", - "dev": true, - "requires": { - "argparse": ">=0", - "js-yaml": ">=0", - "lodash": ">=0", - "yamljs": ">=0" + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "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==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yoctocolors": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.1.1.tgz", + "integrity": "sha512-GQHQqAopRhwU8Kt1DDM8NjibDXHC8eoh1erhGAJPEyveY9qqVeXvVikNKrDz69sHowPMorbPUrH/mx8c50eiBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } } } diff --git a/package.json b/package.json index c37bec78..7e51618a 100644 --- a/package.json +++ b/package.json @@ -1,27 +1,26 @@ { "name": "enqueuer", - "version": "5.0.4", + "version": "6.0.3", "description": "Polyglot flow testing tool", - "main": "./js/enqueuer.js", - "typings": "./js/enqueuer.d.ts", + "main": "./dist/enqueuer.js", + "typings": "./dist/enqueuer.d.ts", "types": "enqueuer.d.ts", "directories": { - "lib": "js" + "lib": "dist" }, "homepage": "https://enqueuer.com", "scripts": { - "start": "node js/index.js", + "start": "node dist/index.js", "prepublishOnly": "npm run all", "test": "npm run codeCoverage && npm run examplesTest && npm run listsDescriptions", - "unitTest": "NODE_ENV=TEST node_modules/.bin/jest --silent .test.ts", - "examplesTest": "nqr -c conf/config-example.yml examples/requisition-navigation.yaml < misc/stdin && npm run validateOutput", - "validateOutput": "yval --from=json output/examples.json && yval --from=yaml output/examples.yml", + "unitTest": "NODE_ENV=TEST node_modules/.bin/jest --silent", + "examplesTest": "mkdir output; NODE_TLS_REJECT_UNAUTHORIZED=0 node dist/index.js -c conf/config-example.yml examples/task-navigation.yaml < misc/stdin && npm run validateOutput", + "validateOutput": "yamllint output/examples.yml", "codeCoverage": "NODE_ENV=TEST node_modules/.bin/jest --detectOpenHandles --coverage --silent", "listsDescriptions": "nqr -t > /dev/null && nqr -p > /dev/null && nqr -t > /dev/null && nqr -e > /dev/null", - "lint": "node_modules/.bin/tslint --project tsconfig.json", - "clean": "rm -rf js/ && find ./src -name '*.js' -type f -delete && rm -f temp/*", + "format": "prettier . '!**/*.{html}' --write", "updateHtml": "misc/mdToHtml.js", - "all": "npm run clean && npm run lint && npm run build && npm link && npm run test", + "all": "rm -rf dist ; npm run format ; npm run build && npm unlink -g && npm link && npm run test", "build": "tsc", "commit": "git-cz", "semantic-release": "semantic-release" @@ -51,42 +50,38 @@ }, "license": "MIT", "bin": { - "enqueuer": "js/index.js", - "nqr": "js/index.js" + "enqueuer": "dist/index.js", + "nqr": "dist/index.js" }, "dependencies": { - "commander": "^2.20.0", - "express": "^4.17.1", - "glob": "^7.1.4", - "json-placeholder-replacer": "^1.0.34", - "request": "^2.88.0", + "chalk": "^4.1.2", + "commander": "^12.1.0", + "express": "^4.19.2", + "glob": "^11.0.0", + "jest": "^29.7.0", + "json-placeholder-replacer": "^2.0.5", + "prettyjson": "^1.2.5", "require-from-string": "^2.0.2", "yamljs": "^0.3.0" }, "devDependencies": { - "@types/express": "^4.17.0", - "@types/glob": "^7.1.0", - "@types/jest": "^24.0.15", - "@types/node": "^11.13.17", - "@types/object-hash": "^1.3.0", - "@types/prettyjson": "0.0.28", - "@types/request": "^2.47.1", - "@types/require-from-string": "^1.2.0", - "@types/yamljs": "^0.2.30", - "commitizen": "^3.1.1", - "cz-conventional-changelog": "^2.1.0", - "jest": "^24.8.0", + "@types/commander": "^2.12.0", + "@types/express": "^4.17.21", + "@types/jest": "^29.5.12", + "@types/node": "^22.3.0", + "@types/object-hash": "^3.0.6", + "@types/prettyjson": "0.0.33", + "@types/require-from-string": "^1.2.3", + "@types/yamljs": "^0.2.34", + "commitizen": "^4.3.0", + "cz-conventional-changelog": "^3.3.0", "pagedown": "^1.1.0", - "semantic-release": "^15.13.18", - "ts-jest": "^24.0.2", - "ts-node": "^7.0.1", - "tslint": "^5.18.0", - "typescript": "^3.5.2", - "yval": "0.0.3" - }, - "optionalDependencies": { - "chalk": "^2.4.2", - "prettyjson": "^1.2.1" + "prettier": "^3.3.3", + "semantic-release": "^24.0.0", + "ts-jest": "^29.2.4", + "ts-node": "^10.9.2", + "typescript": "^5.5.4", + "yaml-lint": "^1.7.0" }, "config": { "commitizen": { @@ -94,16 +89,11 @@ } }, "jest": { - "globals": { - "ts-jest": { - "diagnostics": false - } - }, "collectCoverage": true, "collectCoverageFrom": [ "src/**/*.ts", - "!src/publishers/**", - "!src/subscriptions/**", + "!src/actuators/**", + "!src/sensors/**", "!src/index.ts" ], "coverageThreshold": { diff --git a/src/actuators/actuator.ts b/src/actuators/actuator.ts new file mode 100644 index 00000000..0a9ffca3 --- /dev/null +++ b/src/actuators/actuator.ts @@ -0,0 +1,39 @@ +import { ActuatorModel } from '../models/inputs/actuator-model'; +import { Event } from '../models/events/event'; +import { Logger } from '../loggers/logger'; + +export abstract class Actuator { + public type: string; + public payload: any; + public name: string; + public onMessageReceived?: Event; + public onInit?: Event; + public onFinish?: Event; + public messageReceived?: any; + public ignore: boolean = false; + + [propName: string]: any; + + protected constructor(actuatorAttributes: ActuatorModel) { + Object.keys(actuatorAttributes).forEach(key => { + this[key] = actuatorAttributes[key]; + }); + this.type = actuatorAttributes.type; + this.payload = actuatorAttributes.payload; + this.name = actuatorAttributes.name; + } + + public abstract act(): Promise; + + public registerHookEventExecutor(hookEventExecutor: (eventName: string, args: any) => void) { + this.hookEventExecutorFunctor = hookEventExecutor; + } + + protected executeHookEvent(hookName: string, args: any = {}) { + if (this.hookEventExecutorFunctor) { + this.hookEventExecutorFunctor(hookName, args); + } else { + Logger.warning(`Hook event executor not registered in actuator`); + } + } +} diff --git a/src/actuators/custom-actuator.ts b/src/actuators/custom-actuator.ts new file mode 100644 index 00000000..0dc85dbd --- /dev/null +++ b/src/actuators/custom-actuator.ts @@ -0,0 +1,32 @@ +import { Actuator } from './actuator'; +import { Store } from '../configurations/store'; +import { Logger } from '../loggers/logger'; +import { ActuatorModel } from '../models/inputs/actuator-model'; +import * as fs from 'fs'; +import requireFromString from 'require-from-string'; +import { MainInstance } from '../plugins/main-instance'; +import { ActuatorProtocol } from '../protocols/actuator-protocol'; + +class CustomActuator extends Actuator { + constructor(model: ActuatorModel) { + super(model); + this['model'] = model; + } + + public async act(): Promise { + try { + const moduleString: string = fs.readFileSync(this.module).toString(); + const module = requireFromString(moduleString); + const custom = new module.Actuator(this); + return await custom.act({ store: Store.getData(), logger: Logger }); + } catch (err) { + Logger.error(`Error loading module '${this.module}': ${err}`); + } + } +} + +export function entryPoint(mainInstance: MainInstance): void { + const protocol = new ActuatorProtocol('custom', (actuatorModel: ActuatorModel) => new CustomActuator(actuatorModel)); + + mainInstance.protocolManager.addProtocol(protocol); +} diff --git a/src/actuators/file-actuator.ts b/src/actuators/file-actuator.ts new file mode 100644 index 00000000..bb8587fd --- /dev/null +++ b/src/actuators/file-actuator.ts @@ -0,0 +1,74 @@ +import { Actuator } from './actuator'; +import { ActuatorModel } from '../models/inputs/actuator-model'; +import { IdGenerator } from '../strings/id-generator'; +import * as fs from 'fs'; +import { MainInstance } from '../plugins/main-instance'; +import { ActuatorProtocol } from '../protocols/actuator-protocol'; +import { DateController } from '../timers/date-controller'; + +class FileActuator extends Actuator { + constructor(actuatorAttributes: ActuatorModel) { + super(actuatorAttributes); + this['filenameExtension'] = this.filenameExtension || 'enq'; + } + + public act(): Promise { + const filename = this.getFileName(); + let value = this.payload; + + if (typeof value === 'object') { + value = JSON.stringify(value, null, 2); + } + + fs.writeFileSync(filename, value); + return Promise.resolve(); + } + + private getFileName() { + if (this.filename) { + return this.filename; + } + return this.createFileName(); + } + + private createFileName() { + let filename = + this.filenamePrefix + new IdGenerator(this.payload || new DateController().getStringOnlyNumbers()).generateId(); + const needsToInsertDot = filename.lastIndexOf('.') == -1 && this.filenameExtension.lastIndexOf('.') == -1; + if (needsToInsertDot) { + filename += '.'; + } + return filename + this.filenameExtension; + } +} + +export function entryPoint(mainInstance: MainInstance): void { + const protocol = new ActuatorProtocol('file', (actuatorModel: ActuatorModel) => new FileActuator(actuatorModel), { + description: 'The file actuator provides an implementation of filesystem writers', + libraryHomepage: 'https://nodejs.org/api/fs.html', + schema: { + attributes: { + filenameExtension: { + description: 'Used when there is no file name defined, succeeds the auto generated file name', + required: false, + type: 'string' + }, + filenamePrefix: { + description: 'Used when there is no file name defined, precedes the auto generated file name', + required: false, + type: 'string' + }, + filename: { + description: 'The file name', + required: false, + type: 'string' + }, + payload: { + type: 'text', + required: true + } + } + } + }); + mainInstance.protocolManager.addProtocol(protocol); +} diff --git a/src/actuators/http-actuator.ts b/src/actuators/http-actuator.ts new file mode 100644 index 00000000..23a5d21c --- /dev/null +++ b/src/actuators/http-actuator.ts @@ -0,0 +1,111 @@ +import { Actuator } from './actuator'; +import { Logger } from '../loggers/logger'; +import { ActuatorModel } from '../models/inputs/actuator-model'; +import { HttpActuatorFetcher } from '../pools/http-actuator-fetcher'; +import { MainInstance } from '../plugins/main-instance'; +import { ActuatorProtocol } from '../protocols/actuator-protocol'; +import { HttpAuthenticationFactory } from '../http-authentications/http-authentication-factory'; + +const DEFAULT_TIMEOUT = 5000; + +class HttpActuator extends Actuator { + constructor(model: ActuatorModel) { + super(model); + this['method'] = model.method || 'get'; + this.payload = this.payload || ''; + this['headers'] = this.headers || {}; + this['timeout'] = this.timeout || DEFAULT_TIMEOUT; + } + + public async act(): Promise { + this.insertAuthentication(); + + const response = await new HttpActuatorFetcher( + this.url, + this.method.toLowerCase(), + this.headers, + this.payload, + this.timeout + ).request(); + this.executeHookEvent('onResponseReceived', response); + return this.processResponseToBePrinted(response); + } + + private processResponseToBePrinted(response: any): object { + const toBePrinted: any = { + statusCode: response.statusCode, + headers: response.headers + }; + try { + if (response.body) { + toBePrinted.body = JSON.parse(response.body); + } + } catch (e) { + toBePrinted.body = response.body; + } + return toBePrinted; + } + + private insertAuthentication() { + if (this.authentication) { + const authenticator = new HttpAuthenticationFactory().create(this.authentication); + const authentication = authenticator.generate(); + if (authentication) { + this['headers'] = Object.assign(this.headers, authentication); + } else { + Logger.warning(`No http authentication method was generated from: ${this.authentication}`); + } + } + } +} + +export function entryPoint(mainInstance: MainInstance): void { + const protocol = new ActuatorProtocol('http', (actuatorModel: ActuatorModel) => new HttpActuator(actuatorModel), { + description: 'The HTTP actuator provides an implementation of http tasks', + libraryHomepage: 'https://nodejs.org/dist/latest-v18.x/docs/api/globals.html', + schema: { + attributes: { + url: { + required: true, + type: 'string', + example: 'https://github.com/enqueuer-land/enqueuer' + }, + method: { + required: false, + type: 'string', + defaultValue: 'GET', + listValues: ['GET', 'POST', 'PATCH', 'PUT', 'OPTIONS', 'HEAD', 'DELETE'] + }, + payload: { + required: true, + type: 'text' + }, + timeout: { + required: false, + type: 'int', + defaultValue: DEFAULT_TIMEOUT, + suffix: 'ms' + }, + headers: { + description: '', + type: 'object', + defaultValue: {} + } + }, + hooks: { + onResponseReceived: { + description: 'Hook called when the actuator gets a response from the server', + arguments: { + statusCode: {}, + headers: {}, + body: {} + } + } + } + } + }) + .addAlternativeName('http-client', 'https', 'https-client') + .setLibrary('fetch'); + + mainInstance.protocolManager.addProtocol(protocol); +} diff --git a/src/actuators/null-actuator.ts b/src/actuators/null-actuator.ts new file mode 100644 index 00000000..242f6e3a --- /dev/null +++ b/src/actuators/null-actuator.ts @@ -0,0 +1,12 @@ +import { Actuator } from './actuator'; +import { ActuatorModel } from '../models/inputs/actuator-model'; + +export class NullActuator extends Actuator { + public constructor(actuatorModel: ActuatorModel) { + super(actuatorModel); + } + + public act(): Promise { + return Promise.reject(`Undefined actuator: '${this.type}'`); + } +} diff --git a/src/actuators/standard-output-actuator.ts b/src/actuators/standard-output-actuator.ts new file mode 100644 index 00000000..e6c4b42a --- /dev/null +++ b/src/actuators/standard-output-actuator.ts @@ -0,0 +1,37 @@ +import { Actuator } from './actuator'; +import { ActuatorModel } from '../models/inputs/actuator-model'; +import { MainInstance } from '../plugins/main-instance'; +import { ActuatorProtocol } from '../protocols/actuator-protocol'; + +class StandardOutputActuator extends Actuator { + public constructor(actuatorProperties: ActuatorModel) { + super(actuatorProperties); + } + + public act(): Promise { + if (typeof this.payload === 'object') { + this.payload = JSON.stringify(this.payload, null, 2); + } + console.log(this.payload); + return Promise.resolve(); + } +} + +export function entryPoint(mainInstance: MainInstance): void { + const protocol = new ActuatorProtocol( + 'stdout', + (actuatorModel: ActuatorModel) => new StandardOutputActuator(actuatorModel), + { + schema: { + attributes: { + payload: { + type: 'text', + required: true + } + } + } + } + ).addAlternativeName('standard-output'); + + mainInstance.protocolManager.addProtocol(protocol); +} diff --git a/src/actuators/stream-actuator.ts b/src/actuators/stream-actuator.ts new file mode 100644 index 00000000..db2ea26a --- /dev/null +++ b/src/actuators/stream-actuator.ts @@ -0,0 +1,217 @@ +import { Actuator } from './actuator'; +import { ActuatorModel } from '../models/inputs/actuator-model'; +import * as net from 'net'; +import { Logger } from '../loggers/logger'; +import { Store } from '../configurations/store'; +import * as tls from 'tls'; +import { Timeout } from '../timers/timeout'; +import { MainInstance } from '../plugins/main-instance'; +import { ActuatorProtocol } from '../protocols/actuator-protocol'; +import { ProtocolDocumentation } from '../protocols/protocol-documentation'; + +class StreamActuator extends Actuator { + private readonly loadedStream: any; + + constructor(actuatorAttributes: ActuatorModel) { + super(actuatorAttributes); + this['timeout'] = this.timeout || 1000; + if (this.loadStream) { + Logger.debug(`Loading ${this.type} client: ${this.loadStream}`); + this.loadedStream = Store.getData()[this.loadStream]; + } + } + + public act(): Promise { + return new Promise((resolve, reject) => { + if (this.loadStream) { + this.sendReusingStream(resolve, reject); + } else { + this.sendCreatingStream(resolve, reject); + } + }); + } + + private sendReusingStream(resolve: any, reject: any) { + Logger.info(`${this.type} client is trying to reuse stream ${this.loadStream}`); + if (!this.loadedStream) { + Logger.error(`There is no ${this.type} stream able to be loaded named ${this.loadStream}`); + this.sendCreatingStream(resolve, reject); + } else { + Logger.debug(`Client is reusing ${this.type} stream`); + this.sendData(this.loadedStream, resolve, reject); + } + } + + private sendCreatingStream(resolve: any, reject: any) { + Logger.info(`${this.type} client trying to connect`); + this.createStream() + .then((stream: any) => { + Logger.debug(`${this.type} client connected to: ${this.serverAddress}:${this.port || this.path}`); + this.sendData(stream, resolve, reject); + }) + .catch(err => { + reject(err); + }); + } + + private createStream(): Promise { + return new Promise((resolve, reject) => { + if ('tcp' === (this.type || '').toLowerCase()) { + this.createTcpStream(resolve, reject); + } else if ('ssl' === (this.type || '').toLowerCase()) { + this.createSslStream(resolve, reject); + } else { + resolve(net.createConnection(this.path)); + } + }); + } + + private createSslStream(resolve: any, reject: any): void { + const stream: any = tls.connect(this.port, this.serverAddress, this.options, () => resolve(stream)); + stream.on('error', (error: any) => { + Logger.error(`${this.type} client error: ${error}`); + reject(error); + }); + } + + private createTcpStream(resolve: any, reject: any): void { + const stream = new net.Socket(); + stream.connect(this.port, this.serverAddress, () => resolve(stream)); + stream.on('error', (error: any) => { + Logger.error(`${this.type} client error: ${error} [${error.code}]`); + reject(error); + }); + } + + private sendData(stream: any, resolve: any, reject: any) { + Logger.debug(`${this.type} client sending message`); + stream.once('error', (data: any) => { + this.finalize(stream); + reject(data); + }); + const stringifyPayload = this.stringifyPayload(); + stream.write(stringifyPayload, () => { + Logger.debug(`${this.type} client sent message`); + this.registerEvents(stream, resolve); + if (this.saveStream) { + Logger.debug(`Persisting actuator stream ${this.saveStream}`); + Store.getData()[this.saveStream] = stream; + } + }); + } + + private registerEvents(stream: any, resolve: any) { + new Timeout(() => { + this.finalize(stream); + Logger.debug(`${this.type} client timed out`); + stream.removeAllListeners('data'); + resolve(); + }).start(this.timeout); + + stream + .once('end', () => { + Logger.debug(`${this.type} client ended`); + this.finalize(stream); + resolve(); + }) + .on('data', (msg: Buffer) => { + Logger.debug(`${this.type} client got data '${msg.toString()}'`); + if (!this.messageReceived) { + this.messageReceived = { + payload: '', + stream: stream.address() + }; + } + this.messageReceived.payload += msg; + }); + } + + private finalize(stream: any) { + if (this.messageReceived) { + this.executeHookEvent('onMessageReceived', this.messageReceived); + } + + if (!this.saveStream) { + Logger.trace(`Ending writable stream`); + stream.end(); + } + if (stream.close) { + Logger.trace(`Closing writable stream`); + stream.close(); + } + } + + private stringifyPayload() { + if (typeof this.payload != 'string' && !Buffer.isBuffer(this.payload)) { + return JSON.stringify(this.payload); + } + return this.payload; + } +} + +export function entryPoint(mainInstance: MainInstance): void { + const createFunction = (actuatorModel: ActuatorModel) => new StreamActuator(actuatorModel); + const docs: ProtocolDocumentation = { + description: 'The stream sensor provides implementations of TCP/UDS servers', + libraryHomepage: 'https://nodejs.org/api/net.html', + schema: { + attributes: { + payload: { + required: true, + type: 'text' + }, + serverAddress: { + required: false, + type: 'string' + }, + port: { + description: 'Defined when using TCP', + required: false, + type: 'int' + }, + path: { + description: 'Defined when using UDS', + required: false, + type: 'string' + }, + saveStream: { + description: 'Set it when you want to reuse this stream', + required: false, + type: 'string' + }, + loadStream: { + description: 'Set it when you want to reuse an opened stream', + required: false, + type: 'string' + }, + timeout: { + description: 'Timeout to stop listening after the first byte is read', + required: false, + type: 'int' + }, + options: { + description: + 'Defined when using SSL. https://nodejs.org/api/net.html#net_net_createserver_options_connectionlistener', + required: false, + type: 'object' + } + }, + hooks: { + onMessageReceived: { + arguments: { + payload: {}, + stream: {} + } + } + } + } + }; + + const tcp = new ActuatorProtocol('tcp', createFunction, docs); + const uds = new ActuatorProtocol('uds', createFunction, docs); + const ssl = new ActuatorProtocol('ssl', createFunction, docs); + + mainInstance.protocolManager.addProtocol(tcp); + mainInstance.protocolManager.addProtocol(uds); + mainInstance.protocolManager.addProtocol(ssl); +} diff --git a/src/actuators/udp-actuator.ts b/src/actuators/udp-actuator.ts new file mode 100644 index 00000000..d1a4b99e --- /dev/null +++ b/src/actuators/udp-actuator.ts @@ -0,0 +1,54 @@ +import { Actuator } from './actuator'; +import { ActuatorModel } from '../models/inputs/actuator-model'; +import { Logger } from '../loggers/logger'; +import * as dgram from 'dgram'; +import { ActuatorProtocol } from '../protocols/actuator-protocol'; +import { MainInstance } from '../plugins/main-instance'; + +class UdpActuator extends Actuator { + constructor(actuatorAttributes: ActuatorModel) { + super(actuatorAttributes); + } + + public act(): Promise { + return new Promise((resolve, reject) => { + const client = dgram.createSocket('udp4'); + Logger.debug('Udp client trying to send message'); + + client.send(Buffer.from(this.payload), this.port, this.serverAddress, (error: any) => { + if (error) { + client.close(); + reject(error); + return; + } + Logger.debug('Udp client sent message'); + resolve(); + }); + }); + } +} + +export function entryPoint(mainInstance: MainInstance): void { + const protocol = new ActuatorProtocol('udp', (actuatorModel: ActuatorModel) => new UdpActuator(actuatorModel), { + description: 'The udp actuator provides an implementation of UDP Datagram sockets clients', + libraryHomepage: 'https://nodejs.org/api/dgram.html', + schema: { + attributes: { + payload: { + required: true, + type: 'text' + }, + serverAddress: { + required: true, + type: 'string' + }, + port: { + required: true, + type: 'int' + } + } + } + }).addAlternativeName('udp-client'); + + mainInstance.protocolManager.addProtocol(protocol); +} diff --git a/src/asserters/asserter.ts b/src/asserters/asserter.ts index 7430c6b5..be6b1d79 100644 --- a/src/asserters/asserter.ts +++ b/src/asserters/asserter.ts @@ -1,6 +1,6 @@ -import {Assertion} from '../models/events/assertion'; -import {TestModel} from '../models/outputs/test-model'; +import { Assertion } from '../models/events/assertion'; +import { TestModel } from '../models/outputs/test-model'; export interface Asserter { - assert(assertion: Assertion, literal: any): TestModel; + assert(assertion: Assertion, literal: any): TestModel; } diff --git a/src/asserters/expect-to-be-any-of-asserter.test.ts b/src/asserters/expect-to-be-any-of-asserter.test.ts new file mode 100644 index 00000000..209aa9e4 --- /dev/null +++ b/src/asserters/expect-to-be-any-of-asserter.test.ts @@ -0,0 +1,183 @@ +import { entryPoint, ExpectToBeAnyOfAsserter } from './expect-to-be-any-of-asserter'; +import { Assertion } from '../models/events/assertion'; +import { MainInstance } from '../plugins/main-instance'; + +describe('ExpectToBeAnyOfAsserter', () => { + it('should compare equal primitives', () => { + const assertion: Assertion = { + name: 'assertion 0', + expect: 'equal', + toBeAnyOf: [1, 'equal', 3] + }; + + const literal = { + name: 'body.name', + expect: 'body.actual', + toBeAnyOf: 'body.expected' + }; + + const expectToBeEqualToAsserter = new ExpectToBeAnyOfAsserter().assert(assertion, literal); + expect(expectToBeEqualToAsserter.name).toBe('assertion 0'); + expect(expectToBeEqualToAsserter.valid).toBeTruthy(); + }); + + it('should compare not equal primitives', () => { + const assertion: Assertion = { + name: 'assertion 0', + expect: 2, + not: null, + toBeAnyOf: [0, 1] + }; + + const literal = { + name: 'body.name', + expect: 'body.actual', + toBeAnyOf: 'body.expected' + }; + + const expectToBeEqualToAsserter = new ExpectToBeAnyOfAsserter().assert(assertion, literal); + expect(expectToBeEqualToAsserter.name).toBe('assertion 0'); + expect(expectToBeEqualToAsserter.valid).toBeTruthy(); + expect(expectToBeEqualToAsserter.description).toBe( + `Expected 'body.actual' not to be any of '[0, 1]'. Received '2'` + ); + }); + + it('should compare equal primitives fail', () => { + const assertion: Assertion = { + name: 'assertion 0', + expect: 2, + toBeAnyOf: [3] + }; + + const literal = { + name: 'body.name', + expect: 'body.actual', + toBeAnyOf: 'body.expected' + }; + + const expectToBeEqualToAsserter = new ExpectToBeAnyOfAsserter().assert(assertion, literal); + expect(expectToBeEqualToAsserter.name).toBe('assertion 0'); + expect(expectToBeEqualToAsserter.valid).toBeFalsy(); + expect(expectToBeEqualToAsserter.description).toBeDefined(); + }); + + it('should compare equal objects', () => { + const assertion: Assertion = { + name: 'assertion 0', + expect: { value: 123, cycle: { nested: true, array: [4, 3, 2] } }, + toBeAnyOf: [1, { value: 123, cycle: { nested: true, array: [4, 3, 2] } }] + }; + + const literal = { + name: 'body.name', + expect: 'body.actual', + toBeAnyOf: 'body.expected' + }; + + const expectToBeEqualToAsserter = new ExpectToBeAnyOfAsserter().assert(assertion, literal); + expect(expectToBeEqualToAsserter.name).toBe('assertion 0'); + expect(expectToBeEqualToAsserter.valid).toBeTruthy(); + }); + + it('should compare equal objects reordering keys', () => { + const assertion: Assertion = { + name: 'assertion 0', + expect: { + cycle: { + nested: true, + array: [4, 3, 2] + }, + value: 123 + }, + toBeAnyOf: [ + 0, + 'string', + { + value: 123, + cycle: { + nested: true, + array: [4, 3, 2] + } + } + ] + }; + + const literal = { + name: 'body.name', + expect: 'body.actual', + toBeAnyOf: 'body.expected' + }; + + const expectToBeEqualToAsserter = new ExpectToBeAnyOfAsserter().assert(assertion, literal); + expect(expectToBeEqualToAsserter.name).toBe('assertion 0'); + expect(expectToBeEqualToAsserter.valid).toBeTruthy(); + }); + + it('should compare not equal objects', () => { + const assertion: Assertion = { + name: 'assertion 0', + expect: { value: 123, cycle: { nested: true, array: [4, 3, 2] } }, + not: null, + toBeAnyOf: ['string', { value: 123, cycle: { nested: true } }] + }; + + const literal = { + name: 'body.name', + expect: 'body.actual', + toBeAnyOf: 'body.expected' + }; + + const expectToBeEqualToAsserter = new ExpectToBeAnyOfAsserter().assert(assertion, literal); + expect(expectToBeEqualToAsserter.name).toBe('assertion 0'); + expect(expectToBeEqualToAsserter.valid).toBeTruthy(); + expect(expectToBeEqualToAsserter.description).toBeDefined(); + }); + + it('should compare equal objects fail', () => { + const assertion: Assertion = { + name: 'assertion 0', + expect: { value: 123, cycle: { nested: true, array: [4, 3, 2] } }, + toBeAnyOf: [{ value: 123, cycle: { nested: false, array: [4, 3, 2] } }] + }; + + const literal = { + name: 'body.name', + expect: 'body.actual', + toBeAnyOf: 'body.expected' + }; + + const expectToBeEqualToAsserter = new ExpectToBeAnyOfAsserter().assert(assertion, literal); + expect(expectToBeEqualToAsserter.name).toBe('assertion 0'); + expect(expectToBeEqualToAsserter.valid).toBeFalsy(); + expect(expectToBeEqualToAsserter.description).toBeDefined(); + }); + + it('Should export an entry point', done => { + const mainInstance: MainInstance = { + // @ts-ignore + asserterManager: { + addAsserter: (templateAssertion: object, createFunction: Function) => { + expect(templateAssertion).toEqual({ + expect: { + description: 'actual value', + type: 'number' + }, + toBeAnyOf: { + type: 'list', + description: 'expected value' + }, + not: { + required: false, + description: 'negates', + type: 'null' + } + }); + expect(createFunction()).toBeInstanceOf(ExpectToBeAnyOfAsserter); + done(); + } + } + }; + entryPoint(mainInstance); + }); +}); diff --git a/src/asserters/expect-to-be-any-of-asserter.ts b/src/asserters/expect-to-be-any-of-asserter.ts new file mode 100644 index 00000000..b3aed02d --- /dev/null +++ b/src/asserters/expect-to-be-any-of-asserter.ts @@ -0,0 +1,57 @@ +import { Assertion } from '../models/events/assertion'; +import { TestModel } from '../models/outputs/test-model'; +import { Asserter } from './asserter'; +import { MainInstance } from '../plugins/main-instance'; + +export class ExpectToBeAnyOfAsserter implements Asserter { + public assert(assertion: Assertion, literal: any): TestModel { + const actual = assertion.expect; + const isAnyOfList = assertion.toBeAnyOf.some((expected: string) => this.checkEquality(expected, actual)); + + return { + name: assertion.name, + valid: assertion.not === undefined ? isAnyOfList : !isAnyOfList, + description: `Expected '${literal.expect}'${ + assertion.not === undefined ? '' : ' not' + } to be any of '[${assertion.toBeAnyOf.join(', ')}]'. Received '${actual}'` + }; + } + + private checkEquality(expected: string, actual: string): boolean { + if (typeof actual === 'object' && typeof expected === 'object') { + return this.deepEqual(actual, expected); + } else { + return actual == expected; + } + } + + private deepEqual(x: any, y: any): boolean { + const ok = Object.keys, + tx = typeof x, + ty = typeof y; + return x && y && tx === 'object' && tx === ty + ? ok(x).length === ok(y).length && ok(x).every(key => this.deepEqual(x[key], y[key])) + : x === y && ((x != null && y != null) || x.constructor === y.constructor); + } +} + +export function entryPoint(mainInstance: MainInstance): void { + mainInstance.asserterManager.addAsserter( + { + expect: { + type: 'number', + description: 'actual value' + }, + not: { + required: false, + description: 'negates', + type: 'null' + }, + toBeAnyOf: { + type: 'list', + description: 'expected value' + } + }, + () => new ExpectToBeAnyOfAsserter() + ); +} diff --git a/src/asserters/expect-to-be-defined-asserter.test.ts b/src/asserters/expect-to-be-defined-asserter.test.ts index c8ecf0d1..c59ed76a 100644 --- a/src/asserters/expect-to-be-defined-asserter.test.ts +++ b/src/asserters/expect-to-be-defined-asserter.test.ts @@ -1,58 +1,55 @@ -import {Assertion} from '../models/events/assertion'; -import {MainInstance} from '../plugins/main-instance'; -import {entryPoint, ExpectToBeDefinedAsserter} from './expect-to-be-defined-asserter'; +import { Assertion } from '../models/events/assertion'; +import { MainInstance } from '../plugins/main-instance'; +import { entryPoint, ExpectToBeDefinedAsserter } from './expect-to-be-defined-asserter'; describe('ExpectToBeDefinedAsserter', () => { - it('should be defined', () => { - - const assertion: Assertion = { - name: 'assertion 0', - expectToBeDefined: 2, - }; - - const literal = { - name: 'body.name', - expectToBeDefined: 'body.expected', - }; - - const test = new ExpectToBeDefinedAsserter().assert(assertion, literal); - expect(test.name).toBe('assertion 0'); - expect(test.valid).toBeTruthy(); - }); - - it('should not be defined', () => { - - const assertion: Assertion = { - name: 'assertion 0', - }; - - const literal = { - name: 'body.name', - expectToBeDefined: 'body.expected', - }; - - const test = new ExpectToBeDefinedAsserter().assert(assertion, literal); - expect(test.name).toBe('assertion 0'); - expect(test.valid).toBeFalsy(); - expect(test.description).toBe("Expecting 'body.expected' to be defined"); - }); - - it('Should export an entry point', done => { - const mainInstance: MainInstance = { - // @ts-ignore - asserterManager: { - addAsserter: (templateAssertion: object, createFunction: Function) => { - expect(templateAssertion).toEqual({ - 'expectToBeDefined': { - 'description': 'stuff to be defined' - } - }); - expect(createFunction()).toBeInstanceOf(ExpectToBeDefinedAsserter); - done(); - } + it('should be defined', () => { + const assertion: Assertion = { + name: 'assertion 0', + expectToBeDefined: 2 + }; + + const literal = { + name: 'body.name', + expectToBeDefined: 'body.expected' + }; + + const test = new ExpectToBeDefinedAsserter().assert(assertion, literal); + expect(test.name).toBe('assertion 0'); + expect(test.valid).toBeTruthy(); + }); + + it('should not be defined', () => { + const assertion: Assertion = { + name: 'assertion 0' + }; + + const literal = { + name: 'body.name', + expectToBeDefined: 'body.expected' + }; + + const test = new ExpectToBeDefinedAsserter().assert(assertion, literal); + expect(test.name).toBe('assertion 0'); + expect(test.valid).toBeFalsy(); + expect(test.description).toBe("Expecting 'body.expected' to be defined"); + }); + + it('Should export an entry point', done => { + const mainInstance: MainInstance = { + // @ts-ignore + asserterManager: { + addAsserter: (templateAssertion: object, createFunction: Function) => { + expect(templateAssertion).toEqual({ + expectToBeDefined: { + description: 'stuff to be defined' } - }; - entryPoint(mainInstance); - }); - + }); + expect(createFunction()).toBeInstanceOf(ExpectToBeDefinedAsserter); + done(); + } + } + }; + entryPoint(mainInstance); + }); }); diff --git a/src/asserters/expect-to-be-defined-asserter.ts b/src/asserters/expect-to-be-defined-asserter.ts index 1bff4b2d..37231e16 100644 --- a/src/asserters/expect-to-be-defined-asserter.ts +++ b/src/asserters/expect-to-be-defined-asserter.ts @@ -1,24 +1,23 @@ -import {Assertion} from '../models/events/assertion'; -import {TestModel} from '../models/outputs/test-model'; -import {Asserter} from './asserter'; -import {MainInstance} from '../plugins/main-instance'; +import { Assertion } from '../models/events/assertion'; +import { TestModel } from '../models/outputs/test-model'; +import { Asserter } from './asserter'; +import { MainInstance } from '../plugins/main-instance'; export class ExpectToBeDefinedAsserter implements Asserter { - public assert(assertion: Assertion, literal: any): TestModel { - const name: string = assertion.name; - const expected = assertion.expectToBeDefined; + public assert(assertion: Assertion, literal: any): TestModel { + const expected = assertion.expectToBeDefined; - return { - name, - valid: expected !== undefined, - description: `Expecting '${literal.expectToBeDefined}' to be defined` - }; - } + return { + name: assertion.name, + valid: expected !== undefined, + description: `Expecting '${literal.expectToBeDefined}' to be defined` + }; + } } export function entryPoint(mainInstance: MainInstance): void { - mainInstance.asserterManager.addAsserter( - {expectToBeDefined: {description: 'stuff to be defined'}}, - () => new ExpectToBeDefinedAsserter() - ); + mainInstance.asserterManager.addAsserter( + { expectToBeDefined: { description: 'stuff to be defined' } }, + () => new ExpectToBeDefinedAsserter() + ); } diff --git a/src/asserters/expect-to-be-equal-to-asserter.test.ts b/src/asserters/expect-to-be-equal-to-asserter.test.ts index 94d1cec7..74444371 100644 --- a/src/asserters/expect-to-be-equal-to-asserter.test.ts +++ b/src/asserters/expect-to-be-equal-to-asserter.test.ts @@ -1,183 +1,177 @@ -import {entryPoint, ExpectToBeEqualToAsserter} from './expect-to-be-equal-to-asserter'; -import {Assertion} from '../models/events/assertion'; -import {MainInstance} from '../plugins/main-instance'; +import { entryPoint, ExpectToBeEqualToAsserter } from './expect-to-be-equal-to-asserter'; +import { Assertion } from '../models/events/assertion'; +import { MainInstance } from '../plugins/main-instance'; describe('ExpectToBeEqualToAsserter', () => { - it('should compare equal primitives', () => { - - const assertion: Assertion = { - name: 'assertion 0', - expect: 2, - toBeEqualTo: 2, - }; - - const literal = { - name: 'body.name', - expect: 'body.actual', - toBeEqualTo: 'body.expected', - }; - - const expectToBeEqualToAsserter = new ExpectToBeEqualToAsserter().assert(assertion, literal); - expect(expectToBeEqualToAsserter.name).toBe('assertion 0'); - expect(expectToBeEqualToAsserter.valid).toBeTruthy(); - }); - - it('should compare not equal primitives', () => { - - const assertion: Assertion = { - name: 'assertion 0', - expect: 2, - not: null, - toBeEqualTo: 0, - }; - - const literal = { - name: 'body.name', - expect: 'body.actual', - toBeEqualTo: 'body.expected', - }; - - const expectToBeEqualToAsserter = new ExpectToBeEqualToAsserter().assert(assertion, literal); - expect(expectToBeEqualToAsserter.name).toBe('assertion 0'); - expect(expectToBeEqualToAsserter.valid).toBeTruthy(); - expect(expectToBeEqualToAsserter.description).toBe(`Expected 'body.actual' not to be equal to '0'. Received '2'`); - }); - - it('should compare equal primitives fail', () => { - - const assertion: Assertion = { - name: 'assertion 0', - expect: 2, - toBeEqualTo: 3, - }; - - const literal = { - name: 'body.name', - expect: 'body.actual', - toBeEqualTo: 'body.expected', - }; - - const expectToBeEqualToAsserter = new ExpectToBeEqualToAsserter().assert(assertion, literal); - expect(expectToBeEqualToAsserter.name).toBe('assertion 0'); - expect(expectToBeEqualToAsserter.valid).toBeFalsy(); - expect(expectToBeEqualToAsserter.description).toBeDefined(); - }); - - it('should compare equal objects', () => { - - const assertion: Assertion = { - name: 'assertion 0', - expect: {value: 123, cycle: {nested: true, array: [4, 3, 2]}}, - toBeEqualTo: {value: 123, cycle: {nested: true, array: [4, 3, 2]}}, - }; - - const literal = { - name: 'body.name', - expect: 'body.actual', - toBeEqualTo: 'body.expected', - }; - - const expectToBeEqualToAsserter = new ExpectToBeEqualToAsserter().assert(assertion, literal); - expect(expectToBeEqualToAsserter.name).toBe('assertion 0'); - expect(expectToBeEqualToAsserter.valid).toBeTruthy(); - }); - - it('should compare equal objects reordering keys', () => { - - const assertion: Assertion = { - name: 'assertion 0', + it('should compare equal primitives', () => { + const assertion: Assertion = { + name: 'assertion 0', + expect: 2, + toBeEqualTo: 2 + }; + + const literal = { + name: 'body.name', + expect: 'body.actual', + toBeEqualTo: 'body.expected' + }; + + const expectToBeEqualToAsserter = new ExpectToBeEqualToAsserter().assert(assertion, literal); + expect(expectToBeEqualToAsserter.name).toBe('assertion 0'); + expect(expectToBeEqualToAsserter.valid).toBeTruthy(); + }); + + it('should compare not equal primitives', () => { + const assertion: Assertion = { + name: 'assertion 0', + expect: 2, + not: null, + toBeEqualTo: 0 + }; + + const literal = { + name: 'body.name', + expect: 'body.actual', + toBeEqualTo: 'body.expected' + }; + + const expectToBeEqualToAsserter = new ExpectToBeEqualToAsserter().assert(assertion, literal); + expect(expectToBeEqualToAsserter.name).toBe('assertion 0'); + expect(expectToBeEqualToAsserter.valid).toBeTruthy(); + expect(expectToBeEqualToAsserter.description).toBe(`Expected 'body.actual' not to be equal to '0'. Received '2'`); + }); + + it('should compare equal primitives fail', () => { + const assertion: Assertion = { + name: 'assertion 0', + expect: 2, + toBeEqualTo: 3 + }; + + const literal = { + name: 'body.name', + expect: 'body.actual', + toBeEqualTo: 'body.expected' + }; + + const expectToBeEqualToAsserter = new ExpectToBeEqualToAsserter().assert(assertion, literal); + expect(expectToBeEqualToAsserter.name).toBe('assertion 0'); + expect(expectToBeEqualToAsserter.valid).toBeFalsy(); + expect(expectToBeEqualToAsserter.description).toBeDefined(); + }); + + it('should compare equal objects', () => { + const assertion: Assertion = { + name: 'assertion 0', + expect: { value: 123, cycle: { nested: true, array: [4, 3, 2] } }, + toBeEqualTo: { value: 123, cycle: { nested: true, array: [4, 3, 2] } } + }; + + const literal = { + name: 'body.name', + expect: 'body.actual', + toBeEqualTo: 'body.expected' + }; + + const expectToBeEqualToAsserter = new ExpectToBeEqualToAsserter().assert(assertion, literal); + expect(expectToBeEqualToAsserter.name).toBe('assertion 0'); + expect(expectToBeEqualToAsserter.valid).toBeTruthy(); + }); + + it('should compare equal objects reordering keys', () => { + const assertion: Assertion = { + name: 'assertion 0', + expect: { + cycle: { + nested: true, + array: [4, 3, 2] + }, + value: 123 + }, + toBeEqualTo: { + value: 123, + cycle: { + nested: true, + array: [4, 3, 2] + } + } + }; + + const literal = { + name: 'body.name', + expect: 'body.actual', + toBeEqualTo: 'body.expected' + }; + + const expectToBeEqualToAsserter = new ExpectToBeEqualToAsserter().assert(assertion, literal); + expect(expectToBeEqualToAsserter.name).toBe('assertion 0'); + expect(expectToBeEqualToAsserter.valid).toBeTruthy(); + }); + + it('should compare not equal objects', () => { + const assertion: Assertion = { + name: 'assertion 0', + expect: { value: 123, cycle: { nested: true, array: [4, 3, 2] } }, + not: null, + toBeEqualTo: { value: 123, cycle: { nested: true } } + }; + + const literal = { + name: 'body.name', + expect: 'body.actual', + toBeEqualTo: 'body.expected' + }; + + const expectToBeEqualToAsserter = new ExpectToBeEqualToAsserter().assert(assertion, literal); + expect(expectToBeEqualToAsserter.name).toBe('assertion 0'); + expect(expectToBeEqualToAsserter.valid).toBeTruthy(); + expect(expectToBeEqualToAsserter.description).toBeDefined(); + }); + + it('should compare equal objects fail', () => { + const assertion: Assertion = { + name: 'assertion 0', + expect: { value: 123, cycle: { nested: true, array: [4, 3, 2] } }, + toBeEqualTo: { value: 123, cycle: { nested: false, array: [4, 3, 2] } } + }; + + const literal = { + name: 'body.name', + expect: 'body.actual', + toBeEqualTo: 'body.expected' + }; + + const expectToBeEqualToAsserter = new ExpectToBeEqualToAsserter().assert(assertion, literal); + expect(expectToBeEqualToAsserter.name).toBe('assertion 0'); + expect(expectToBeEqualToAsserter.valid).toBeFalsy(); + expect(expectToBeEqualToAsserter.description).toBeDefined(); + }); + + it('Should export an entry point', done => { + const mainInstance: MainInstance = { + // @ts-ignore + asserterManager: { + addAsserter: (templateAssertion: object, createFunction: Function) => { + expect(templateAssertion).toEqual({ expect: { - cycle: { - nested: true, array: [4, 3, 2] - }, - value: 123, + description: 'actual value', + type: 'number' }, toBeEqualTo: { - value: 123, - cycle: { - nested: true, array: [4, 3, 2] - } - } - }; - - const literal = { - name: 'body.name', - expect: 'body.actual', - toBeEqualTo: 'body.expected', - }; - - const expectToBeEqualToAsserter = new ExpectToBeEqualToAsserter().assert(assertion, literal); - expect(expectToBeEqualToAsserter.name).toBe('assertion 0'); - expect(expectToBeEqualToAsserter.valid).toBeTruthy(); - }); - - it('should compare not equal objects', () => { - - const assertion: Assertion = { - name: 'assertion 0', - expect: {value: 123, cycle: {nested: true, array: [4, 3, 2]}}, - not: null, - toBeEqualTo: {value: 123, cycle: {nested: true}}, - }; - - const literal = { - name: 'body.name', - expect: 'body.actual', - toBeEqualTo: 'body.expected', - }; - - const expectToBeEqualToAsserter = new ExpectToBeEqualToAsserter().assert(assertion, literal); - expect(expectToBeEqualToAsserter.name).toBe('assertion 0'); - expect(expectToBeEqualToAsserter.valid).toBeTruthy(); - expect(expectToBeEqualToAsserter.description).toBeDefined(); - }); - - it('should compare equal objects fail', () => { - - const assertion: Assertion = { - name: 'assertion 0', - expect: {value: 123, cycle: {nested: true, array: [4, 3, 2]}}, - toBeEqualTo: {value: 123, cycle: {nested: false, array: [4, 3, 2]}}, - }; - - const literal = { - name: 'body.name', - expect: 'body.actual', - toBeEqualTo: 'body.expected', - }; - - const expectToBeEqualToAsserter = new ExpectToBeEqualToAsserter().assert(assertion, literal); - expect(expectToBeEqualToAsserter.name).toBe('assertion 0'); - expect(expectToBeEqualToAsserter.valid).toBeFalsy(); - expect(expectToBeEqualToAsserter.description).toBeDefined(); - }); - - it('Should export an entry point', done => { - const mainInstance: MainInstance = { - // @ts-ignore - asserterManager: { - addAsserter: (templateAssertion: object, createFunction: Function) => { - expect(templateAssertion).toEqual({ - 'expect': { - 'description': 'actual value', - 'type': 'number' - }, - 'toBeEqualTo': { - 'description': 'expected value', - 'type': 'number' - }, not: { - required: false, - description: 'negates', - type: 'null' - } - }); - expect(createFunction()).toBeInstanceOf(ExpectToBeEqualToAsserter); - done(); - } + description: 'expected value', + type: 'number' + }, + not: { + required: false, + description: 'negates', + type: 'null' } - }; - entryPoint(mainInstance); - }); - -}) -; + }); + expect(createFunction()).toBeInstanceOf(ExpectToBeEqualToAsserter); + done(); + } + } + }; + entryPoint(mainInstance); + }); +}); diff --git a/src/asserters/expect-to-be-equal-to-asserter.ts b/src/asserters/expect-to-be-equal-to-asserter.ts index bfaca2cb..8bda0077 100644 --- a/src/asserters/expect-to-be-equal-to-asserter.ts +++ b/src/asserters/expect-to-be-equal-to-asserter.ts @@ -1,57 +1,58 @@ -import {Assertion} from '../models/events/assertion'; -import {TestModel} from '../models/outputs/test-model'; -import {Asserter} from './asserter'; -import {MainInstance} from '../plugins/main-instance'; +import { Assertion } from '../models/events/assertion'; +import { TestModel } from '../models/outputs/test-model'; +import { Asserter } from './asserter'; +import { MainInstance } from '../plugins/main-instance'; export class ExpectToBeEqualToAsserter implements Asserter { - public assert(assertion: Assertion, literal: any): TestModel { - const name: string = assertion.name; - const actual = assertion.expect; - const expected = assertion.toBeEqualTo; + public assert(assertion: Assertion, literal: any): TestModel { + const actual = assertion.expect; + const expected = assertion.toBeEqualTo; - if (typeof (actual) === 'object' && typeof (expected) === 'object') { - const areEquals = this.deepEqual(actual, expected); - return { - name, - valid: assertion.not === undefined ? areEquals : !areEquals, - description: `Expected '${JSON.stringify(literal.expect, null, 2)}'${assertion.not === undefined ? - '' : ' not'} to be equal to '${JSON - .stringify(expected, null, 2)}'. Received '${JSON - .stringify(actual, null, 2)}'` - }; - } else { - return { - name, - valid: assertion.not === undefined ? actual == expected : actual != expected, - description: `Expected '${literal.expect}'${assertion.not === undefined ? - '' : ' not'} to be equal to '${expected}'. Received '${actual}'` - }; - } + if (typeof actual === 'object' && typeof expected === 'object') { + const areEquals = this.deepEqual(actual, expected); + return { + name: assertion.name, + valid: assertion.not === undefined ? areEquals : !areEquals, + description: `Expected '${JSON.stringify(literal.expect, null, 2)}'${ + assertion.not === undefined ? '' : ' not' + } to be equal to '${JSON.stringify(expected, null, 2)}'. Received '${JSON.stringify(actual, null, 2)}'` + }; + } else { + return { + name: assertion.name, + valid: assertion.not === undefined ? actual == expected : actual != expected, + description: `Expected '${literal.expect}'${assertion.not === undefined ? '' : ' not'} to be equal to '${expected}'. Received '${actual}'` + }; } + } - private deepEqual(x: any, y: any): boolean { - const ok = Object.keys, tx = typeof x, ty = typeof y; - return x && y && tx === 'object' && tx === ty ? ( - ok(x).length === ok(y).length && - ok(x).every(key => this.deepEqual(x[key], y[key])) - ) : (x === y && (x != null && y != null || x.constructor === y.constructor)); - } + private deepEqual(x: any, y: any): boolean { + const ok = Object.keys, + tx = typeof x, + ty = typeof y; + return x && y && tx === 'object' && tx === ty + ? ok(x).length === ok(y).length && ok(x).every(key => this.deepEqual(x[key], y[key])) + : x === y && ((x != null && y != null) || x.constructor === y.constructor); + } } export function entryPoint(mainInstance: MainInstance): void { - mainInstance.asserterManager.addAsserter( - { - expect: { - type: 'number', - description: 'actual value' - }, not: { - required: false, - description: 'negates', - type: 'null' - }, toBeEqualTo: { - type: 'number', - description: 'expected value' - } - }, - () => new ExpectToBeEqualToAsserter()); + mainInstance.asserterManager.addAsserter( + { + expect: { + type: 'number', + description: 'actual value' + }, + not: { + required: false, + description: 'negates', + type: 'null' + }, + toBeEqualTo: { + type: 'number', + description: 'expected value' + } + }, + () => new ExpectToBeEqualToAsserter() + ); } diff --git a/src/asserters/expect-to-be-falsy-asserter.test.ts b/src/asserters/expect-to-be-falsy-asserter.test.ts index f11a1376..0e70e954 100644 --- a/src/asserters/expect-to-be-falsy-asserter.test.ts +++ b/src/asserters/expect-to-be-falsy-asserter.test.ts @@ -1,60 +1,56 @@ -import {Assertion} from '../models/events/assertion'; -import {MainInstance} from '../plugins/main-instance'; -import {entryPoint, ExpectToBeFalsyAsserter} from './expect-to-be-falsy-asserter'; -import {AssertionTemplate} from '../plugins/asserter-manager'; +import { Assertion } from '../models/events/assertion'; +import { MainInstance } from '../plugins/main-instance'; +import { entryPoint, ExpectToBeFalsyAsserter } from './expect-to-be-falsy-asserter'; +import { AssertionTemplate } from '../plugins/asserter-manager'; describe('ExpectToBeFalsyAsserter', () => { - it('should be falsy', () => { - - const assertion: Assertion = { - name: 'assertion 0', - expectToBeFalsy: false, - }; - - const literal = { - name: 'body.name', - expectToBeFalsy: 'body.expected', - }; - - const test = new ExpectToBeFalsyAsserter().assert(assertion, literal); - expect(test.name).toBe('assertion 0'); - expect(test.valid).toBeTruthy(); - }); - - it('should not be falsy', () => { - - const assertion: Assertion = { - name: 'assertion 0', - }; - - const literal = { - name: 'body.name', - expectToBeFalsy: 'body.expected', - }; - - const test = new ExpectToBeFalsyAsserter().assert(assertion, literal); - expect(test.name).toBe('assertion 0'); - expect(test.valid).toBeFalsy(); - expect(test.description).toBe("Expecting 'body.expected' to be false. Received: undefined"); - }); - - it('Should export an entry point', done => { - const mainInstance: MainInstance = { - // @ts-ignore - asserterManager: { - addAsserter: (templateAssertion: AssertionTemplate, createFunction: Function) => { - expect(templateAssertion).toEqual({ - expectToBeFalsy: { - 'description': - 'value expected to be false' - } - }); - expect(createFunction()).toBeInstanceOf(ExpectToBeFalsyAsserter); - done(); - } + it('should be falsy', () => { + const assertion: Assertion = { + name: 'assertion 0', + expectToBeFalsy: false + }; + + const literal = { + name: 'body.name', + expectToBeFalsy: 'body.expected' + }; + + const test = new ExpectToBeFalsyAsserter().assert(assertion, literal); + expect(test.name).toBe('assertion 0'); + expect(test.valid).toBeTruthy(); + }); + + it('should not be falsy', () => { + const assertion: Assertion = { + name: 'assertion 0' + }; + + const literal = { + name: 'body.name', + expectToBeFalsy: 'body.expected' + }; + + const test = new ExpectToBeFalsyAsserter().assert(assertion, literal); + expect(test.name).toBe('assertion 0'); + expect(test.valid).toBeFalsy(); + expect(test.description).toBe("Expecting 'body.expected' to be false. Received: undefined"); + }); + + it('Should export an entry point', done => { + const mainInstance: MainInstance = { + // @ts-ignore + asserterManager: { + addAsserter: (templateAssertion: AssertionTemplate, createFunction: Function) => { + expect(templateAssertion).toEqual({ + expectToBeFalsy: { + description: 'value expected to be false' } - }; - entryPoint(mainInstance); - }); - + }); + expect(createFunction()).toBeInstanceOf(ExpectToBeFalsyAsserter); + done(); + } + } + }; + entryPoint(mainInstance); + }); }); diff --git a/src/asserters/expect-to-be-falsy-asserter.ts b/src/asserters/expect-to-be-falsy-asserter.ts index c830efd7..a56e5fa8 100644 --- a/src/asserters/expect-to-be-falsy-asserter.ts +++ b/src/asserters/expect-to-be-falsy-asserter.ts @@ -1,23 +1,23 @@ -import {Assertion} from '../models/events/assertion'; -import {TestModel} from '../models/outputs/test-model'; -import {Asserter} from './asserter'; -import {MainInstance} from '../plugins/main-instance'; +import { Assertion } from '../models/events/assertion'; +import { TestModel } from '../models/outputs/test-model'; +import { Asserter } from './asserter'; +import { MainInstance } from '../plugins/main-instance'; export class ExpectToBeFalsyAsserter implements Asserter { - public assert(assertion: Assertion, literal: any): TestModel { - const name: string = assertion.name; - const expected = assertion.expectToBeFalsy; + public assert(assertion: Assertion, literal: any): TestModel { + const expected = assertion.expectToBeFalsy; - return { - name, - valid: expected === false, - description: `Expecting '${literal.expectToBeFalsy}' to be false. Received: ${expected}` - }; - } + return { + name: assertion.name, + valid: expected === false, + description: `Expecting '${literal.expectToBeFalsy}' to be false. Received: ${expected}` + }; + } } export function entryPoint(mainInstance: MainInstance): void { - mainInstance.asserterManager.addAsserter( - {expectToBeFalsy: {description: 'value expected to be false'}}, - () => new ExpectToBeFalsyAsserter()); + mainInstance.asserterManager.addAsserter( + { expectToBeFalsy: { description: 'value expected to be false' } }, + () => new ExpectToBeFalsyAsserter() + ); } diff --git a/src/asserters/expect-to-be-greater-than-asserter.test.ts b/src/asserters/expect-to-be-greater-than-asserter.test.ts index c7d0b637..9794964d 100644 --- a/src/asserters/expect-to-be-greater-than-asserter.test.ts +++ b/src/asserters/expect-to-be-greater-than-asserter.test.ts @@ -1,93 +1,90 @@ -import {Assertion} from '../models/events/assertion'; -import {MainInstance} from '../plugins/main-instance'; -import {entryPoint, ExpectToBeGreaterThanAsserter} from './expect-to-be-greater-than-asserter'; +import { Assertion } from '../models/events/assertion'; +import { MainInstance } from '../plugins/main-instance'; +import { entryPoint, ExpectToBeGreaterThanAsserter } from './expect-to-be-greater-than-asserter'; describe('ExpectToBeGreaterThanAsserter', () => { - it('should compare greater', () => { + it('should compare greater', () => { + const assertion: Assertion = { + name: 'assertion 0', + expect: 3, + toBeGreaterThan: 2 + }; - const assertion: Assertion = { - name: 'assertion 0', - expect: 3, - toBeGreaterThan: 2, - }; + const literal = { + name: 'body.name', + expect: 'body.actual', + toBeGreaterThan: 'body.expected' + }; - const literal = { - name: 'body.name', - expect: 'body.actual', - toBeGreaterThan: 'body.expected', - }; + const test = new ExpectToBeGreaterThanAsserter().assert(assertion, literal); + expect(test.name).toBe('assertion 0'); + expect(test.valid).toBeTruthy(); + }); - const test = new ExpectToBeGreaterThanAsserter().assert(assertion, literal); - expect(test.name).toBe('assertion 0'); - expect(test.valid).toBeTruthy(); - }); + it('should compare not greater', () => { + const assertion: Assertion = { + name: 'assertion 0', + expect: 3, + not: null, + toBeGreaterThan: 4 + }; - it('should compare not greater', () => { + const literal = { + name: 'body.name', + expect: 'body.actual', + toBeGreaterThan: 'body.expected' + }; - const assertion: Assertion = { - name: 'assertion 0', - expect: 3, - not: null, - toBeGreaterThan: 4, - }; + const test = new ExpectToBeGreaterThanAsserter().assert(assertion, literal); + expect(test.name).toBe('assertion 0'); + expect(test.valid).toBeTruthy(); + expect(test.valid).toBeDefined(); + }); - const literal = { - name: 'body.name', - expect: 'body.actual', - toBeGreaterThan: 'body.expected', - }; + it('should compare greater fail', () => { + const assertion: Assertion = { + name: 'assertion 0', + expect: 2, + toBeGreaterThan: 3 + }; - const test = new ExpectToBeGreaterThanAsserter().assert(assertion, literal); - expect(test.name).toBe('assertion 0'); - expect(test.valid).toBeTruthy(); - expect(test.valid).toBeDefined(); - }); + const literal = { + name: 'body.name', + expect: 'body.actual', + toBeGreaterThan: 'body.expected' + }; - it('should compare greater fail', () => { + const test = new ExpectToBeGreaterThanAsserter().assert(assertion, literal); + expect(test.name).toBe('assertion 0'); + expect(test.valid).toBeFalsy(); + expect(test.description).toBe("Expected 'body.actual' not to be greater than '3'. Received '2'"); + }); - const assertion: Assertion = { - name: 'assertion 0', - expect: 2, - toBeGreaterThan: 3, - }; - - const literal = { - name: 'body.name', - expect: 'body.actual', - toBeGreaterThan: 'body.expected', - }; - - const test = new ExpectToBeGreaterThanAsserter().assert(assertion, literal); - expect(test.name).toBe('assertion 0'); - expect(test.valid).toBeFalsy(); - expect(test.description).toBe("Expected 'body.actual' not to be greater than '3'. Received '2'"); - }); - - it('Should export an entry point', done => { - const mainInstance: MainInstance = { - // @ts-ignore - asserterManager: { - addAsserter: (templateAssertion: object, createFunction: Function) => { - expect(templateAssertion).toEqual({ - 'expect': { - 'description': 'actual value', - 'type': 'number' - }, - 'toBeGreaterThan': { - 'description': 'expected value', - 'type': 'number' - }, not: { - required: false, - description: 'negates', - type: 'null' - } - }); - expect(createFunction()).toBeInstanceOf(ExpectToBeGreaterThanAsserter); - done(); - } + it('Should export an entry point', done => { + const mainInstance: MainInstance = { + // @ts-ignore + asserterManager: { + addAsserter: (templateAssertion: object, createFunction: Function) => { + expect(templateAssertion).toEqual({ + expect: { + description: 'actual value', + type: 'number' + }, + toBeGreaterThan: { + description: 'expected value', + type: 'number' + }, + not: { + required: false, + description: 'negates', + type: 'null' } - }; - entryPoint(mainInstance); - }); - + }); + expect(createFunction()).toBeInstanceOf(ExpectToBeGreaterThanAsserter); + done(); + } + } + }; + entryPoint(mainInstance); + }); }); diff --git a/src/asserters/expect-to-be-greater-than-asserter.ts b/src/asserters/expect-to-be-greater-than-asserter.ts index 3e744f41..5075fb00 100644 --- a/src/asserters/expect-to-be-greater-than-asserter.ts +++ b/src/asserters/expect-to-be-greater-than-asserter.ts @@ -1,37 +1,38 @@ -import {Assertion} from '../models/events/assertion'; -import {TestModel} from '../models/outputs/test-model'; -import {Asserter} from './asserter'; -import {MainInstance} from '../plugins/main-instance'; +import { Assertion } from '../models/events/assertion'; +import { TestModel } from '../models/outputs/test-model'; +import { Asserter } from './asserter'; +import { MainInstance } from '../plugins/main-instance'; export class ExpectToBeGreaterThanAsserter implements Asserter { - public assert(assertion: Assertion, literal: any): TestModel { - const name: string = assertion.name; - const actual = assertion.expect; - const expected = assertion.toBeGreaterThan; + public assert(assertion: Assertion, literal: any): TestModel { + const actual = assertion.expect; + const expected = assertion.toBeGreaterThan; - return { - name, - valid: assertion.not === undefined ? actual > expected : actual <= expected, - description: `Expected '${literal.expect}'${assertion.not === undefined ? - ' not' : ''} to be greater than '${expected}'. Received '${actual}'` - }; - } + return { + name: assertion.name, + valid: assertion.not === undefined ? actual > expected : actual <= expected, + description: `Expected '${literal.expect}'${assertion.not === undefined ? ' not' : ''} to be greater than '${expected}'. Received '${actual}'` + }; + } } export function entryPoint(mainInstance: MainInstance): void { - mainInstance.asserterManager.addAsserter( - { - expect: { - type: 'number', - description: 'actual value' - }, not: { - required: false, - description: 'negates', - type: 'null' - }, toBeGreaterThan: { - type: 'number', - description: 'expected value' - } - }, - () => new ExpectToBeGreaterThanAsserter()); + mainInstance.asserterManager.addAsserter( + { + expect: { + type: 'number', + description: 'actual value' + }, + not: { + required: false, + description: 'negates', + type: 'null' + }, + toBeGreaterThan: { + type: 'number', + description: 'expected value' + } + }, + () => new ExpectToBeGreaterThanAsserter() + ); } diff --git a/src/asserters/expect-to-be-greater-than-or-equal-to-asserter.test.ts b/src/asserters/expect-to-be-greater-than-or-equal-to-asserter.test.ts index 86568fe7..26ba6b25 100644 --- a/src/asserters/expect-to-be-greater-than-or-equal-to-asserter.test.ts +++ b/src/asserters/expect-to-be-greater-than-or-equal-to-asserter.test.ts @@ -1,112 +1,108 @@ -import {Assertion} from '../models/events/assertion'; -import {MainInstance} from '../plugins/main-instance'; -import {entryPoint, ExpectToBeGreaterThanOrEqualToAsserter} from './expect-to-be-greater-than-or-equal-to-asserter'; +import { Assertion } from '../models/events/assertion'; +import { MainInstance } from '../plugins/main-instance'; +import { entryPoint, ExpectToBeGreaterThanOrEqualToAsserter } from './expect-to-be-greater-than-or-equal-to-asserter'; describe('ExpectToBeGreaterThanOrEqualToAsserter', () => { - it('should compare greater', () => { - - const assertion: Assertion = { - name: 'assertion 0', - expect: 3, - toBeGreaterThanOrEqualTo: 2, - }; - - const literal = { - name: 'body.name', - expect: 'body.actual', - toBeGreaterThanOrEqualTo: 'body.expected', - }; - - const test = new ExpectToBeGreaterThanOrEqualToAsserter().assert(assertion, literal); - expect(test.name).toBe('assertion 0'); - expect(test.valid).toBeTruthy(); - }); - - it('should compare not greater', () => { - - const assertion: Assertion = { - name: 'assertion 0', - expect: 3, - not: null, - toBeGreaterThanOrEqualTo: 4, - }; - - const literal = { - name: 'body.name', - expect: 'body.actual', - toBeGreaterThanOrEqualTo: 'body.expected', - }; - - const test = new ExpectToBeGreaterThanOrEqualToAsserter().assert(assertion, literal); - expect(test.name).toBe('assertion 0'); - expect(test.valid).toBeTruthy(); - expect(test.description).toBe("Expected 'body.actual' not to be greater than or equal to '4'. Received '3'"); - }); - - it('should compare equal', () => { - - const assertion: Assertion = { - name: 'assertion 0', - expect: 3, - toBeGreaterThanOrEqualTo: 3, - }; - - const literal = { - name: 'body.name', - expect: 'body.actual', - toBeGreaterThanOrEqualTo: 'body.expected', - }; - - const test = new ExpectToBeGreaterThanOrEqualToAsserter().assert(assertion, literal); - expect(test.name).toBe('assertion 0'); - expect(test.valid).toBeTruthy(); - }); - - it('should compare greater fail', () => { - - const assertion: Assertion = { - name: 'assertion 0', - expect: 2, - toBeGreaterThanOrEqualTo: 3, - }; - - const literal = { - name: 'body.name', - expect: 'body.actual', - toBeGreaterThanOrEqualTo: 'body.expected', - }; - - const test = new ExpectToBeGreaterThanOrEqualToAsserter().assert(assertion, literal); - expect(test.name).toBe('assertion 0'); - expect(test.valid).toBeFalsy(); - expect(test.description).toBe("Expected 'body.actual' to be greater than or equal to '3'. Received '2'"); - }); - - it('Should export an entry point', done => { - const mainInstance: MainInstance = { - // @ts-ignore - asserterManager: { - addAsserter: (templateAssertion: object, createFunction: Function) => { - expect(templateAssertion).toEqual({ - 'expect': { - 'description': 'actual value', - 'type': 'number' - }, - 'toBeGreaterThanOrEqualTo': { - 'description': 'expected value', - 'type': 'number' - }, not: { - required: false, - description: 'negates', - type: 'null' - } - }); - expect(createFunction()).toBeInstanceOf(ExpectToBeGreaterThanOrEqualToAsserter); - done(); - } + it('should compare greater', () => { + const assertion: Assertion = { + name: 'assertion 0', + expect: 3, + toBeGreaterThanOrEqualTo: 2 + }; + + const literal = { + name: 'body.name', + expect: 'body.actual', + toBeGreaterThanOrEqualTo: 'body.expected' + }; + + const test = new ExpectToBeGreaterThanOrEqualToAsserter().assert(assertion, literal); + expect(test.name).toBe('assertion 0'); + expect(test.valid).toBeTruthy(); + }); + + it('should compare not greater', () => { + const assertion: Assertion = { + name: 'assertion 0', + expect: 3, + not: null, + toBeGreaterThanOrEqualTo: 4 + }; + + const literal = { + name: 'body.name', + expect: 'body.actual', + toBeGreaterThanOrEqualTo: 'body.expected' + }; + + const test = new ExpectToBeGreaterThanOrEqualToAsserter().assert(assertion, literal); + expect(test.name).toBe('assertion 0'); + expect(test.valid).toBeTruthy(); + expect(test.description).toBe("Expected 'body.actual' not to be greater than or equal to '4'. Received '3'"); + }); + + it('should compare equal', () => { + const assertion: Assertion = { + name: 'assertion 0', + expect: 3, + toBeGreaterThanOrEqualTo: 3 + }; + + const literal = { + name: 'body.name', + expect: 'body.actual', + toBeGreaterThanOrEqualTo: 'body.expected' + }; + + const test = new ExpectToBeGreaterThanOrEqualToAsserter().assert(assertion, literal); + expect(test.name).toBe('assertion 0'); + expect(test.valid).toBeTruthy(); + }); + + it('should compare greater fail', () => { + const assertion: Assertion = { + name: 'assertion 0', + expect: 2, + toBeGreaterThanOrEqualTo: 3 + }; + + const literal = { + name: 'body.name', + expect: 'body.actual', + toBeGreaterThanOrEqualTo: 'body.expected' + }; + + const test = new ExpectToBeGreaterThanOrEqualToAsserter().assert(assertion, literal); + expect(test.name).toBe('assertion 0'); + expect(test.valid).toBeFalsy(); + expect(test.description).toBe("Expected 'body.actual' to be greater than or equal to '3'. Received '2'"); + }); + + it('Should export an entry point', done => { + const mainInstance: MainInstance = { + // @ts-ignore + asserterManager: { + addAsserter: (templateAssertion: object, createFunction: Function) => { + expect(templateAssertion).toEqual({ + expect: { + description: 'actual value', + type: 'number' + }, + toBeGreaterThanOrEqualTo: { + description: 'expected value', + type: 'number' + }, + not: { + required: false, + description: 'negates', + type: 'null' } - }; - entryPoint(mainInstance); - }); - + }); + expect(createFunction()).toBeInstanceOf(ExpectToBeGreaterThanOrEqualToAsserter); + done(); + } + } + }; + entryPoint(mainInstance); + }); }); diff --git a/src/asserters/expect-to-be-greater-than-or-equal-to-asserter.ts b/src/asserters/expect-to-be-greater-than-or-equal-to-asserter.ts index 8da4f2a8..bac4636b 100644 --- a/src/asserters/expect-to-be-greater-than-or-equal-to-asserter.ts +++ b/src/asserters/expect-to-be-greater-than-or-equal-to-asserter.ts @@ -1,37 +1,40 @@ -import {Assertion} from '../models/events/assertion'; -import {TestModel} from '../models/outputs/test-model'; -import {Asserter} from './asserter'; -import {MainInstance} from '../plugins/main-instance'; +import { Assertion } from '../models/events/assertion'; +import { TestModel } from '../models/outputs/test-model'; +import { Asserter } from './asserter'; +import { MainInstance } from '../plugins/main-instance'; export class ExpectToBeGreaterThanOrEqualToAsserter implements Asserter { - public assert(assertion: Assertion, literal: any): TestModel { - const name: string = assertion.name; - const actual = assertion.expect; - const expected = assertion.toBeGreaterThanOrEqualTo; + public assert(assertion: Assertion, literal: any): TestModel { + const actual = assertion.expect; + const expected = assertion.toBeGreaterThanOrEqualTo; - return { - name, - valid: assertion.not === undefined ? actual >= expected : actual < expected, - description: `Expected '${literal.expect}'${assertion.not !== undefined ? - ' not' : ''} to be greater than or equal to '${expected}'. Received '${actual}'` - }; - } + return { + name: assertion.name, + valid: assertion.not === undefined ? actual >= expected : actual < expected, + description: `Expected '${literal.expect}'${ + assertion.not !== undefined ? ' not' : '' + } to be greater than or equal to '${expected}'. Received '${actual}'` + }; + } } export function entryPoint(mainInstance: MainInstance): void { - mainInstance.asserterManager.addAsserter( - { - expect: { - type: 'number', - description: 'actual value' - }, not: { - required: false, - description: 'negates', - type: 'null' - }, toBeGreaterThanOrEqualTo: { - type: 'number', - description: 'expected value' - } - }, - () => new ExpectToBeGreaterThanOrEqualToAsserter()); + mainInstance.asserterManager.addAsserter( + { + expect: { + type: 'number', + description: 'actual value' + }, + not: { + required: false, + description: 'negates', + type: 'null' + }, + toBeGreaterThanOrEqualTo: { + type: 'number', + description: 'expected value' + } + }, + () => new ExpectToBeGreaterThanOrEqualToAsserter() + ); } diff --git a/src/asserters/expect-to-be-less-than-asserter.test.ts b/src/asserters/expect-to-be-less-than-asserter.test.ts index 141dd65b..ac01b39f 100644 --- a/src/asserters/expect-to-be-less-than-asserter.test.ts +++ b/src/asserters/expect-to-be-less-than-asserter.test.ts @@ -1,112 +1,109 @@ -import {Assertion} from '../models/events/assertion'; -import {MainInstance} from '../plugins/main-instance'; -import {entryPoint, ExpectToBeLessThanAsserter} from './expect-to-be-less-than-asserter'; -import {AssertionTemplate} from '../plugins/asserter-manager'; +import { Assertion } from '../models/events/assertion'; +import { MainInstance } from '../plugins/main-instance'; +import { entryPoint, ExpectToBeLessThanAsserter } from './expect-to-be-less-than-asserter'; +import { AssertionTemplate } from '../plugins/asserter-manager'; describe('ExpectToBeLessThanAsserter', () => { - it('should compare less', () => { - - const assertion: Assertion = { - name: 'assertion 0', - expect: 1, - toBeLessThan: 3, - }; - - const literal = { - name: 'body.name', - expect: 'body.actual', - toBeLessThan: 'body.expected', - }; - - const test = new ExpectToBeLessThanAsserter().assert(assertion, literal); - expect(test.name).toBe('assertion 0'); - expect(test.valid).toBeTruthy(); - }); - - it('should compare not less', () => { - - const assertion: Assertion = { - name: 'assertion 0', - expect: 4, - not: null, - toBeLessThan: 3, - }; - - const literal = { - name: 'body.name', - expect: 'body.actual', - toBeLessThan: 'body.expected', - }; - - const test = new ExpectToBeLessThanAsserter().assert(assertion, literal); - expect(test.valid).toBeTruthy(); - }); - - it('should compare less fail', () => { - - const assertion: Assertion = { - name: 'assertion 0', - expect: 4, - toBeLessThan: 3, - }; - - const literal = { - name: 'body.name', - expect: 'body.actual', - toBeLessThan: 'body.expected', - }; - - const test = new ExpectToBeLessThanAsserter().assert(assertion, literal); - expect(test.name).toBe('assertion 0'); - expect(test.valid).toBeFalsy(); - expect(test.description).toBe("Expected 'body.actual' to be less than '3'. Received '4'"); - }); - - it('should compare not less fail', () => { - - const assertion: Assertion = { - name: 'assertion 0', - expect: 2, - not: null, - toBeLessThan: 3, - }; - - const literal = { - name: 'body.name', - expect: 'body.actual', - toBeLessThan: 'body.expected', - }; - - const test = new ExpectToBeLessThanAsserter().assert(assertion, literal); - expect(test.name).toBe('assertion 0'); - expect(test.valid).toBeFalsy(); - expect(test.description).toBe("Expected 'body.actual' not to be less than '3'. Received '2'"); - }); - - it('Should export an entry point', done => { - const mainInstance: MainInstance = { - // @ts-ignore - asserterManager: { - addAsserter: (templateAssertion: AssertionTemplate, createFunction: Function) => { - expect(templateAssertion).toEqual({ - expect: { - type: 'number', - description: 'actual value' - }, toBeLessThan: { - type: 'number', - description: 'expected value' - }, not: { - required: false, - description: 'negates', - type: 'null' - } - }); - expect(createFunction()).toBeInstanceOf(ExpectToBeLessThanAsserter); - done(); - } + it('should compare less', () => { + const assertion: Assertion = { + name: 'assertion 0', + expect: 1, + toBeLessThan: 3 + }; + + const literal = { + name: 'body.name', + expect: 'body.actual', + toBeLessThan: 'body.expected' + }; + + const test = new ExpectToBeLessThanAsserter().assert(assertion, literal); + expect(test.name).toBe('assertion 0'); + expect(test.valid).toBeTruthy(); + }); + + it('should compare not less', () => { + const assertion: Assertion = { + name: 'assertion 0', + expect: 4, + not: null, + toBeLessThan: 3 + }; + + const literal = { + name: 'body.name', + expect: 'body.actual', + toBeLessThan: 'body.expected' + }; + + const test = new ExpectToBeLessThanAsserter().assert(assertion, literal); + expect(test.valid).toBeTruthy(); + }); + + it('should compare less fail', () => { + const assertion: Assertion = { + name: 'assertion 0', + expect: 4, + toBeLessThan: 3 + }; + + const literal = { + name: 'body.name', + expect: 'body.actual', + toBeLessThan: 'body.expected' + }; + + const test = new ExpectToBeLessThanAsserter().assert(assertion, literal); + expect(test.name).toBe('assertion 0'); + expect(test.valid).toBeFalsy(); + expect(test.description).toBe("Expected 'body.actual' to be less than '3'. Received '4'"); + }); + + it('should compare not less fail', () => { + const assertion: Assertion = { + name: 'assertion 0', + expect: 2, + not: null, + toBeLessThan: 3 + }; + + const literal = { + name: 'body.name', + expect: 'body.actual', + toBeLessThan: 'body.expected' + }; + + const test = new ExpectToBeLessThanAsserter().assert(assertion, literal); + expect(test.name).toBe('assertion 0'); + expect(test.valid).toBeFalsy(); + expect(test.description).toBe("Expected 'body.actual' not to be less than '3'. Received '2'"); + }); + + it('Should export an entry point', done => { + const mainInstance: MainInstance = { + // @ts-ignore + asserterManager: { + addAsserter: (templateAssertion: AssertionTemplate, createFunction: Function) => { + expect(templateAssertion).toEqual({ + expect: { + type: 'number', + description: 'actual value' + }, + toBeLessThan: { + type: 'number', + description: 'expected value' + }, + not: { + required: false, + description: 'negates', + type: 'null' } - }; - entryPoint(mainInstance); - }); - + }); + expect(createFunction()).toBeInstanceOf(ExpectToBeLessThanAsserter); + done(); + } + } + }; + entryPoint(mainInstance); + }); }); diff --git a/src/asserters/expect-to-be-less-than-asserter.ts b/src/asserters/expect-to-be-less-than-asserter.ts index b16a9eb5..83d604f1 100644 --- a/src/asserters/expect-to-be-less-than-asserter.ts +++ b/src/asserters/expect-to-be-less-than-asserter.ts @@ -1,37 +1,38 @@ -import {Assertion} from '../models/events/assertion'; -import {TestModel} from '../models/outputs/test-model'; -import {Asserter} from './asserter'; -import {MainInstance} from '../plugins/main-instance'; +import { Assertion } from '../models/events/assertion'; +import { TestModel } from '../models/outputs/test-model'; +import { Asserter } from './asserter'; +import { MainInstance } from '../plugins/main-instance'; export class ExpectToBeLessThanAsserter implements Asserter { - public assert(assertion: Assertion, literal: any): TestModel { - const name: string = assertion.name; - const actual = assertion.expect; - const expected = assertion.toBeLessThan; + public assert(assertion: Assertion, literal: any): TestModel { + const actual = assertion.expect; + const expected = assertion.toBeLessThan; - return { - name, - valid: assertion.not === undefined ? actual < expected : actual >= expected, - description: `Expected '${literal.expect}'${assertion.not !== undefined ? - ' not' : ''} to be less than '${expected}'. Received '${actual}'` - }; - } + return { + name: assertion.name, + valid: assertion.not === undefined ? actual < expected : actual >= expected, + description: `Expected '${literal.expect}'${assertion.not !== undefined ? ' not' : ''} to be less than '${expected}'. Received '${actual}'` + }; + } } export function entryPoint(mainInstance: MainInstance): void { - mainInstance.asserterManager.addAsserter( - { - expect: { - type: 'number', - description: 'actual value' - }, not: { - required: false, - description: 'negates', - type: 'null' - }, toBeLessThan: { - type: 'number', - description: 'expected value' - } - }, - () => new ExpectToBeLessThanAsserter()); + mainInstance.asserterManager.addAsserter( + { + expect: { + type: 'number', + description: 'actual value' + }, + not: { + required: false, + description: 'negates', + type: 'null' + }, + toBeLessThan: { + type: 'number', + description: 'expected value' + } + }, + () => new ExpectToBeLessThanAsserter() + ); } diff --git a/src/asserters/expect-to-be-less-than-or-equal-to-asserter.test.ts b/src/asserters/expect-to-be-less-than-or-equal-to-asserter.test.ts index 749228d8..ee80df9c 100644 --- a/src/asserters/expect-to-be-less-than-or-equal-to-asserter.test.ts +++ b/src/asserters/expect-to-be-less-than-or-equal-to-asserter.test.ts @@ -1,113 +1,108 @@ -import {Assertion} from '../models/events/assertion'; -import {MainInstance} from '../plugins/main-instance'; -import {entryPoint, ExpectToBeLessThanOrEqualToAsserter} from './expect-to-be-less-than-or-equal-to-asserter'; +import { Assertion } from '../models/events/assertion'; +import { MainInstance } from '../plugins/main-instance'; +import { entryPoint, ExpectToBeLessThanOrEqualToAsserter } from './expect-to-be-less-than-or-equal-to-asserter'; describe('ExpectToBeLessThanOrEqualToAsserter', () => { - it('should compare less', () => { - - const assertion: Assertion = { - name: 'assertion 0', - expect: 1, - toBeLessThanOrEqualTo: 2, - }; - - const literal = { - name: 'body.name', - expect: 'body.actual', - toBeLessThanOrEqualTo: 'body.expected', - }; - - const test = new ExpectToBeLessThanOrEqualToAsserter().assert(assertion, literal); - expect(test.name).toBe('assertion 0'); - expect(test.valid).toBeTruthy(); - }); - - it('should compare not less', () => { - - const assertion: Assertion = { - name: 'assertion 0', - expect: 1, - not: null, - toBeLessThanOrEqualTo: 2, - }; - - const literal = { - name: 'body.name', - expect: 'body.actual', - toBeLessThanOrEqualTo: 'body.expected', - }; - - const test = new ExpectToBeLessThanOrEqualToAsserter().assert(assertion, literal); - expect(test.name).toBe('assertion 0'); - expect(test.valid).toBeFalsy(); - expect(test.description).toBe("Expected 'body.actual' not to be less than or equal to '2'. Received '1'"); - }); - - it('should compare equal', () => { - - const assertion: Assertion = { - name: 'assertion 0', - expect: 3, - toBeLessThanOrEqualTo: 3, - }; - - const literal = { - name: 'body.name', - expect: 'body.actual', - toBeLessThanOrEqualTo: 'body.expected', - }; - - const test = new ExpectToBeLessThanOrEqualToAsserter().assert(assertion, literal); - expect(test.name).toBe('assertion 0'); - expect(test.valid).toBeTruthy(); - }); - - it('should compare less false', () => { - - const assertion: Assertion = { - name: 'assertion 0', - expect: 4, - toBeLessThanOrEqualTo: 3, - }; - - const literal = { - name: 'body.name', - expect: 'body.actual', - toBeLessThanOrEqualTo: 'body.expected', - }; - - const test = new ExpectToBeLessThanOrEqualToAsserter().assert(assertion, literal); - expect(test.name).toBe('assertion 0'); - expect(test.valid).toBeFalsy(); - expect(test.description).toBe("Expected 'body.actual' to be less than or equal to '3'. Received '4'"); - }); - - it('Should export an entry point', done => { - const mainInstance: MainInstance = { - // @ts-ignore - asserterManager: { - addAsserter: (templateAssertion: object, createFunction: Function) => { - expect(templateAssertion).toEqual({ - 'expect': { - 'description': 'actual value', - 'type': 'number' - }, - 'toBeLessThanOrEqualTo': { - 'description': 'expected value', - 'type': 'number' - }, - not: { - required: false, - description: 'negates', - type: 'null' - } - }); - expect(createFunction()).toBeInstanceOf(ExpectToBeLessThanOrEqualToAsserter); - done(); - } + it('should compare less', () => { + const assertion: Assertion = { + name: 'assertion 0', + expect: 1, + toBeLessThanOrEqualTo: 2 + }; + + const literal = { + name: 'body.name', + expect: 'body.actual', + toBeLessThanOrEqualTo: 'body.expected' + }; + + const test = new ExpectToBeLessThanOrEqualToAsserter().assert(assertion, literal); + expect(test.name).toBe('assertion 0'); + expect(test.valid).toBeTruthy(); + }); + + it('should compare not less', () => { + const assertion: Assertion = { + name: 'assertion 0', + expect: 1, + not: null, + toBeLessThanOrEqualTo: 2 + }; + + const literal = { + name: 'body.name', + expect: 'body.actual', + toBeLessThanOrEqualTo: 'body.expected' + }; + + const test = new ExpectToBeLessThanOrEqualToAsserter().assert(assertion, literal); + expect(test.name).toBe('assertion 0'); + expect(test.valid).toBeFalsy(); + expect(test.description).toBe("Expected 'body.actual' not to be less than or equal to '2'. Received '1'"); + }); + + it('should compare equal', () => { + const assertion: Assertion = { + name: 'assertion 0', + expect: 3, + toBeLessThanOrEqualTo: 3 + }; + + const literal = { + name: 'body.name', + expect: 'body.actual', + toBeLessThanOrEqualTo: 'body.expected' + }; + + const test = new ExpectToBeLessThanOrEqualToAsserter().assert(assertion, literal); + expect(test.name).toBe('assertion 0'); + expect(test.valid).toBeTruthy(); + }); + + it('should compare less false', () => { + const assertion: Assertion = { + name: 'assertion 0', + expect: 4, + toBeLessThanOrEqualTo: 3 + }; + + const literal = { + name: 'body.name', + expect: 'body.actual', + toBeLessThanOrEqualTo: 'body.expected' + }; + + const test = new ExpectToBeLessThanOrEqualToAsserter().assert(assertion, literal); + expect(test.name).toBe('assertion 0'); + expect(test.valid).toBeFalsy(); + expect(test.description).toBe("Expected 'body.actual' to be less than or equal to '3'. Received '4'"); + }); + + it('Should export an entry point', done => { + const mainInstance: MainInstance = { + // @ts-ignore + asserterManager: { + addAsserter: (templateAssertion: object, createFunction: Function) => { + expect(templateAssertion).toEqual({ + expect: { + description: 'actual value', + type: 'number' + }, + toBeLessThanOrEqualTo: { + description: 'expected value', + type: 'number' + }, + not: { + required: false, + description: 'negates', + type: 'null' } - }; - entryPoint(mainInstance); - }); - + }); + expect(createFunction()).toBeInstanceOf(ExpectToBeLessThanOrEqualToAsserter); + done(); + } + } + }; + entryPoint(mainInstance); + }); }); diff --git a/src/asserters/expect-to-be-less-than-or-equal-to-asserter.ts b/src/asserters/expect-to-be-less-than-or-equal-to-asserter.ts index 1b7c0a2c..de503a4d 100644 --- a/src/asserters/expect-to-be-less-than-or-equal-to-asserter.ts +++ b/src/asserters/expect-to-be-less-than-or-equal-to-asserter.ts @@ -1,37 +1,40 @@ -import {Assertion} from '../models/events/assertion'; -import {TestModel} from '../models/outputs/test-model'; -import {Asserter} from './asserter'; -import {MainInstance} from '../plugins/main-instance'; +import { Assertion } from '../models/events/assertion'; +import { TestModel } from '../models/outputs/test-model'; +import { Asserter } from './asserter'; +import { MainInstance } from '../plugins/main-instance'; export class ExpectToBeLessThanOrEqualToAsserter implements Asserter { - public assert(assertion: Assertion, literal: any): TestModel { - const name: string = assertion.name; - const actual = assertion.expect; - const expected = assertion.toBeLessThanOrEqualTo; + public assert(assertion: Assertion, literal: any): TestModel { + const actual = assertion.expect; + const expected = assertion.toBeLessThanOrEqualTo; - return { - name, - valid: assertion.not === undefined ? actual <= expected : actual > expected, - description: `Expected '${literal.expect}'${assertion.not !== undefined ? - ' not' : ''} to be less than or equal to '${expected}'. Received '${actual}'` - }; - } + return { + name: assertion.name, + valid: assertion.not === undefined ? actual <= expected : actual > expected, + description: `Expected '${literal.expect}'${ + assertion.not !== undefined ? ' not' : '' + } to be less than or equal to '${expected}'. Received '${actual}'` + }; + } } export function entryPoint(mainInstance: MainInstance): void { - mainInstance.asserterManager.addAsserter( - { - expect: { - type: 'number', - description: 'actual value' - }, not: { - required: false, - description: 'negates', - type: 'null' - }, toBeLessThanOrEqualTo: { - type: 'number', - description: 'expected value' - } - }, - () => new ExpectToBeLessThanOrEqualToAsserter()); + mainInstance.asserterManager.addAsserter( + { + expect: { + type: 'number', + description: 'actual value' + }, + not: { + required: false, + description: 'negates', + type: 'null' + }, + toBeLessThanOrEqualTo: { + type: 'number', + description: 'expected value' + } + }, + () => new ExpectToBeLessThanOrEqualToAsserter() + ); } diff --git a/src/asserters/expect-to-be-truthy-asserter.test.ts b/src/asserters/expect-to-be-truthy-asserter.test.ts index b4a3312d..6b7ab824 100644 --- a/src/asserters/expect-to-be-truthy-asserter.test.ts +++ b/src/asserters/expect-to-be-truthy-asserter.test.ts @@ -1,59 +1,55 @@ -import {Assertion} from '../models/events/assertion'; -import {MainInstance} from '../plugins/main-instance'; -import {entryPoint, ExpectToBeTruthyAsserter} from './expect-to-be-truthy-asserter'; +import { Assertion } from '../models/events/assertion'; +import { MainInstance } from '../plugins/main-instance'; +import { entryPoint, ExpectToBeTruthyAsserter } from './expect-to-be-truthy-asserter'; describe('ExpectToBeTruthyAsserter', () => { - it('should be truthy', () => { - - const assertion: Assertion = { - name: 'assertion 0', - expectToBeTruthy: true, - }; - - const literal = { - name: 'body.name', - expectToBeTruthy: 'body.expected', - }; - - const test = new ExpectToBeTruthyAsserter().assert(assertion, literal); - expect(test.name).toBe('assertion 0'); - expect(test.valid).toBeTruthy(); - }); - - it('should not be truthy', () => { - - const assertion: Assertion = { - name: 'assertion 0', - }; - - const literal = { - name: 'body.name', - expectToBeTruthy: 'body.expected', - }; - - const test = new ExpectToBeTruthyAsserter().assert(assertion, literal); - expect(test.name).toBe('assertion 0'); - expect(test.valid).toBeFalsy(); - expect(test.description).toBe("Expecting 'body.expected' to be true. Received: undefined"); - }); - - it('Should export an entry point', done => { - const mainInstance: MainInstance = { - // @ts-ignore - asserterManager: { - addAsserter: (templateAssertion: object, createFunction: Function) => { - expect(templateAssertion).toEqual({ - 'expectToBeTruthy': { - 'description': - 'value expected to be true' - } - }); - expect(createFunction()).toBeInstanceOf(ExpectToBeTruthyAsserter); - done(); - } + it('should be truthy', () => { + const assertion: Assertion = { + name: 'assertion 0', + expectToBeTruthy: true + }; + + const literal = { + name: 'body.name', + expectToBeTruthy: 'body.expected' + }; + + const test = new ExpectToBeTruthyAsserter().assert(assertion, literal); + expect(test.name).toBe('assertion 0'); + expect(test.valid).toBeTruthy(); + }); + + it('should not be truthy', () => { + const assertion: Assertion = { + name: 'assertion 0' + }; + + const literal = { + name: 'body.name', + expectToBeTruthy: 'body.expected' + }; + + const test = new ExpectToBeTruthyAsserter().assert(assertion, literal); + expect(test.name).toBe('assertion 0'); + expect(test.valid).toBeFalsy(); + expect(test.description).toBe("Expecting 'body.expected' to be true. Received: undefined"); + }); + + it('Should export an entry point', done => { + const mainInstance: MainInstance = { + // @ts-ignore + asserterManager: { + addAsserter: (templateAssertion: object, createFunction: Function) => { + expect(templateAssertion).toEqual({ + expectToBeTruthy: { + description: 'value expected to be true' } - }; - entryPoint(mainInstance); - }); - + }); + expect(createFunction()).toBeInstanceOf(ExpectToBeTruthyAsserter); + done(); + } + } + }; + entryPoint(mainInstance); + }); }); diff --git a/src/asserters/expect-to-be-truthy-asserter.ts b/src/asserters/expect-to-be-truthy-asserter.ts index ceeee740..41f6ffe4 100644 --- a/src/asserters/expect-to-be-truthy-asserter.ts +++ b/src/asserters/expect-to-be-truthy-asserter.ts @@ -1,23 +1,23 @@ -import {Assertion} from '../models/events/assertion'; -import {TestModel} from '../models/outputs/test-model'; -import {Asserter} from './asserter'; -import {MainInstance} from '../plugins/main-instance'; +import { Assertion } from '../models/events/assertion'; +import { TestModel } from '../models/outputs/test-model'; +import { Asserter } from './asserter'; +import { MainInstance } from '../plugins/main-instance'; export class ExpectToBeTruthyAsserter implements Asserter { - public assert(assertion: Assertion, literal: any): TestModel { - const name: string = assertion.name; - const expected = assertion.expectToBeTruthy; + public assert(assertion: Assertion, literal: any): TestModel { + const expected = assertion.expectToBeTruthy; - return { - name, - valid: expected === true, - description: `Expecting '${literal.expectToBeTruthy}' to be true. Received: ${expected}` - }; - } + return { + name: assertion.name, + valid: expected === true, + description: `Expecting '${literal.expectToBeTruthy}' to be true. Received: ${expected}` + }; + } } export function entryPoint(mainInstance: MainInstance): void { - mainInstance.asserterManager.addAsserter( - {expectToBeTruthy: {description: 'value expected to be true'}}, - () => new ExpectToBeTruthyAsserter()); + mainInstance.asserterManager.addAsserter( + { expectToBeTruthy: { description: 'value expected to be true' } }, + () => new ExpectToBeTruthyAsserter() + ); } diff --git a/src/asserters/expect-to-be-undefined-asserter.test.ts b/src/asserters/expect-to-be-undefined-asserter.test.ts index 0c0012b8..659d9610 100644 --- a/src/asserters/expect-to-be-undefined-asserter.test.ts +++ b/src/asserters/expect-to-be-undefined-asserter.test.ts @@ -1,58 +1,55 @@ -import {Assertion} from '../models/events/assertion'; -import {MainInstance} from '../plugins/main-instance'; -import {entryPoint, ExpectToBeUndefinedAsserter} from './expect-to-be-undefined-asserter'; +import { Assertion } from '../models/events/assertion'; +import { MainInstance } from '../plugins/main-instance'; +import { entryPoint, ExpectToBeUndefinedAsserter } from './expect-to-be-undefined-asserter'; describe('ExpectToBeUndefinedAsserter', () => { - it('should be undefined', () => { - - const assertion: Assertion = { - name: 'assertion 0', - }; - - const literal = { - name: 'body.name', - expectToBeUndefined: 'body.expected', - }; - - const test = new ExpectToBeUndefinedAsserter().assert(assertion, literal); - expect(test.name).toBe('assertion 0'); - expect(test.valid).toBeTruthy(); - }); - - it('should not be undefined', () => { - - const assertion: Assertion = { - name: 'assertion 0', - expectToBeUndefined: 2, - }; - - const literal = { - name: 'body.name', - expectToBeUndefined: 'body.expected', - }; - - const test = new ExpectToBeUndefinedAsserter().assert(assertion, literal); - expect(test.name).toBe('assertion 0'); - expect(test.valid).toBeFalsy(); - expect(test.description).toBe("Expecting 'body.expected' to be undefined. Received: 2"); - }); - - it('Should export an entry point', done => { - const mainInstance: MainInstance = { - // @ts-ignore - asserterManager: { - addAsserter: (templateAssertion: object, createFunction: Function) => { - expect(templateAssertion).toEqual({ - 'expectToBeUndefined': { - description: 'value expected to be undefined' - }, - }); - expect(createFunction()).toBeInstanceOf(ExpectToBeUndefinedAsserter); - done(); - } + it('should be undefined', () => { + const assertion: Assertion = { + name: 'assertion 0' + }; + + const literal = { + name: 'body.name', + expectToBeUndefined: 'body.expected' + }; + + const test = new ExpectToBeUndefinedAsserter().assert(assertion, literal); + expect(test.name).toBe('assertion 0'); + expect(test.valid).toBeTruthy(); + }); + + it('should not be undefined', () => { + const assertion: Assertion = { + name: 'assertion 0', + expectToBeUndefined: 2 + }; + + const literal = { + name: 'body.name', + expectToBeUndefined: 'body.expected' + }; + + const test = new ExpectToBeUndefinedAsserter().assert(assertion, literal); + expect(test.name).toBe('assertion 0'); + expect(test.valid).toBeFalsy(); + expect(test.description).toBe("Expecting 'body.expected' to be undefined. Received: 2"); + }); + + it('Should export an entry point', done => { + const mainInstance: MainInstance = { + // @ts-ignore + asserterManager: { + addAsserter: (templateAssertion: object, createFunction: Function) => { + expect(templateAssertion).toEqual({ + expectToBeUndefined: { + description: 'value expected to be undefined' } - }; - entryPoint(mainInstance); - }); - + }); + expect(createFunction()).toBeInstanceOf(ExpectToBeUndefinedAsserter); + done(); + } + } + }; + entryPoint(mainInstance); + }); }); diff --git a/src/asserters/expect-to-be-undefined-asserter.ts b/src/asserters/expect-to-be-undefined-asserter.ts index d8f97f43..57c7d77e 100644 --- a/src/asserters/expect-to-be-undefined-asserter.ts +++ b/src/asserters/expect-to-be-undefined-asserter.ts @@ -1,23 +1,23 @@ -import {Assertion} from '../models/events/assertion'; -import {TestModel} from '../models/outputs/test-model'; -import {Asserter} from './asserter'; -import {MainInstance} from '../plugins/main-instance'; +import { Assertion } from '../models/events/assertion'; +import { TestModel } from '../models/outputs/test-model'; +import { Asserter } from './asserter'; +import { MainInstance } from '../plugins/main-instance'; export class ExpectToBeUndefinedAsserter implements Asserter { - public assert(assertion: Assertion, literal: any): TestModel { - const name: string = assertion.name; - const expected = assertion.expectToBeUndefined; + public assert(assertion: Assertion, literal: any): TestModel { + const expected = assertion.expectToBeUndefined; - return { - name, - valid: expected === undefined, - description: `Expecting '${literal.expectToBeUndefined}' to be undefined. Received: ${expected}` - }; - } + return { + name: assertion.name, + valid: expected === undefined, + description: `Expecting '${literal.expectToBeUndefined}' to be undefined. Received: ${expected}` + }; + } } export function entryPoint(mainInstance: MainInstance): void { - mainInstance.asserterManager.addAsserter( - {expectToBeUndefined: {description: 'value expected to be undefined'}}, - () => new ExpectToBeUndefinedAsserter()); + mainInstance.asserterManager.addAsserter( + { expectToBeUndefined: { description: 'value expected to be undefined' } }, + () => new ExpectToBeUndefinedAsserter() + ); } diff --git a/src/asserters/expect-to-contain-asserter.test.ts b/src/asserters/expect-to-contain-asserter.test.ts index 873a7df5..0b61ce15 100644 --- a/src/asserters/expect-to-contain-asserter.test.ts +++ b/src/asserters/expect-to-contain-asserter.test.ts @@ -1,179 +1,165 @@ -import {ExpectToContainAsserter, entryPoint} from './expect-to-contain-asserter'; -import {Assertion} from '../models/events/assertion'; -import {MainInstance} from '../plugins/main-instance'; +import { ExpectToContainAsserter, entryPoint } from './expect-to-contain-asserter'; +import { Assertion } from '../models/events/assertion'; +import { MainInstance } from '../plugins/main-instance'; describe('ExpectToContainAsserter', () => { - it('should contain char in string', () => { - - const assertion: Assertion = { - name: 'assertion 0', - expect: 'enqueuer', - toContain: 'e', - }; - - const literal = { - name: 'body.name', - expect: 'body.actual', - toContain: 'body.expected', - }; - - const test = new ExpectToContainAsserter().assert(assertion, literal); - expect(test.name).toBe('assertion 0'); - expect(test.valid).toBeTruthy(); - }); - - it('should not contain char in string', () => { - - const assertion: Assertion = { - name: 'assertion 0', - expect: 'enqueuer', - not: null, - toContain: 'y', - }; - - const literal = { - name: 'body.name', - expect: 'body.actual', - toContain: 'body.expected', - }; - - const test = new ExpectToContainAsserter().assert(assertion, literal); - expect(test.valid).toBeTruthy(); - expect(test.description).toBe("Expecting 'enqueuer' (body.actual) not to contain 'y'"); - }); - - it('should handle contain with different types', () => { - - const assertion: Assertion = { - name: 'assertion 0', - expect: 'enqueuer', - toContain: 4, - }; - - const literal = { - name: 'body.name', - expect: 'body.actual', - toContain: 'body.expected', - }; - - const test = new ExpectToContainAsserter().assert(assertion, literal); - expect(test.name).toBe('assertion 0'); - expect(test.valid).toBeFalsy(); - expect(test.description).toBe("Expecting 'toContain' to be a 'string'. Received a 'number' instead"); - }); - - it('should contain char in array', () => { - - const assertion: Assertion = { - name: 'assertion 0', - expect: [0, 'enqueuer', true], - toContain: true, - }; - - const literal = { - name: 'body.name', - expect: 'body.actual', - toContain: 'body.expected', - }; - - const test = new ExpectToContainAsserter().assert(assertion, literal); - expect(test.name).toBe('assertion 0'); - expect(test.valid).toBeTruthy(); - }); - - it('should not contain element in array', () => { - - const assertion: Assertion = { - name: 'assertion 0', - expect: [0, 'enqueuer', true], - not: 'null', - toContain: 'y', - }; - - const literal = { - name: 'body.name', - expect: 'body.actual', - toContain: 'body.expected', - }; - - const test = new ExpectToContainAsserter().assert(assertion, literal); - expect(test.name).toBe('assertion 0'); - expect(test.valid).toBeTruthy(); - expect(test.description).toBe("Expecting '0,enqueuer,true' (body.actual) not to contain 'y'"); - }); - - it('should contain element in string fail', () => { - - const assertion: Assertion = { - name: 'assertion 0', - expect: [0, 'enqueuer', true], - toContain: false, - }; - - const literal = { - name: 'body.name', - expect: 'body.actual', - toContain: 'body.expected', - }; - - const test = new ExpectToContainAsserter().assert(assertion, literal); - expect(test.name).toBe('assertion 0'); - expect(test.valid).toBeFalsy(); - expect(test.description).toBe("Expecting '0,enqueuer,true' (body.actual) to contain 'false'"); - }); - - it('should handle contain with type being not string nor array', () => { - - const assertion: Assertion = { - name: 'assertion 0', - expect: 7, - toContain: 4, - }; - - const literal = { - name: 'body.name', - expect: 'body.actual', - toContain: 'body.expected', - }; - - const test = new ExpectToContainAsserter().assert(assertion, literal); - expect(test.name).toBe('assertion 0'); - expect(test.valid).toBeFalsy(); - expect(test.description).toBe("Expecting 'body.actual' to be a string or an array. Received a 'number'"); - }); - - it('Should export an entry point', done => { - const mainInstance: MainInstance = { - // @ts-ignore - asserterManager: { - addAsserter: (templateAssertion: object, createFunction: Function) => { - expect(templateAssertion).toEqual({ - 'expect': { - 'description': 'actual value', - 'type': [ - 'string', - 'array' - ] - }, - 'toContain': { - 'description': 'element', - 'type': [ - 'string', - 'any' - ] - }, not: { - required: false, - description: 'negates', - type: 'null' - } - - }); - expect(createFunction()).toBeInstanceOf(ExpectToContainAsserter); - done(); - } + it('should contain char in string', () => { + const assertion: Assertion = { + name: 'assertion 0', + expect: 'enqueuer', + toContain: 'e' + }; + + const literal = { + name: 'body.name', + expect: 'body.actual', + toContain: 'body.expected' + }; + + const test = new ExpectToContainAsserter().assert(assertion, literal); + expect(test.name).toBe('assertion 0'); + expect(test.valid).toBeTruthy(); + }); + + it('should not contain char in string', () => { + const assertion: Assertion = { + name: 'assertion 0', + expect: 'enqueuer', + not: null, + toContain: 'y' + }; + + const literal = { + name: 'body.name', + expect: 'body.actual', + toContain: 'body.expected' + }; + + const test = new ExpectToContainAsserter().assert(assertion, literal); + expect(test.valid).toBeTruthy(); + expect(test.description).toBe("Expecting 'enqueuer' (body.actual) not to contain 'y'"); + }); + + it('should handle contain with different types', () => { + const assertion: Assertion = { + name: 'assertion 0', + expect: 'enqueuer', + toContain: 4 + }; + + const literal = { + name: 'body.name', + expect: 'body.actual', + toContain: 'body.expected' + }; + + const test = new ExpectToContainAsserter().assert(assertion, literal); + expect(test.name).toBe('assertion 0'); + expect(test.valid).toBeFalsy(); + expect(test.description).toBe("Expecting 'toContain' to be a 'string'. Received a 'number' instead"); + }); + + it('should contain char in array', () => { + const assertion: Assertion = { + name: 'assertion 0', + expect: [0, 'enqueuer', true], + toContain: true + }; + + const literal = { + name: 'body.name', + expect: 'body.actual', + toContain: 'body.expected' + }; + + const test = new ExpectToContainAsserter().assert(assertion, literal); + expect(test.name).toBe('assertion 0'); + expect(test.valid).toBeTruthy(); + }); + + it('should not contain element in array', () => { + const assertion: Assertion = { + name: 'assertion 0', + expect: [0, 'enqueuer', true], + not: 'null', + toContain: 'y' + }; + + const literal = { + name: 'body.name', + expect: 'body.actual', + toContain: 'body.expected' + }; + + const test = new ExpectToContainAsserter().assert(assertion, literal); + expect(test.name).toBe('assertion 0'); + expect(test.valid).toBeTruthy(); + expect(test.description).toBe("Expecting '0,enqueuer,true' (body.actual) not to contain 'y'"); + }); + + it('should contain element in string fail', () => { + const assertion: Assertion = { + name: 'assertion 0', + expect: [0, 'enqueuer', true], + toContain: false + }; + + const literal = { + name: 'body.name', + expect: 'body.actual', + toContain: 'body.expected' + }; + + const test = new ExpectToContainAsserter().assert(assertion, literal); + expect(test.name).toBe('assertion 0'); + expect(test.valid).toBeFalsy(); + expect(test.description).toBe("Expecting '0,enqueuer,true' (body.actual) to contain 'false'"); + }); + + it('should handle contain with type being not string nor array', () => { + const assertion: Assertion = { + name: 'assertion 0', + expect: 7, + toContain: 4 + }; + + const literal = { + name: 'body.name', + expect: 'body.actual', + toContain: 'body.expected' + }; + + const test = new ExpectToContainAsserter().assert(assertion, literal); + expect(test.name).toBe('assertion 0'); + expect(test.valid).toBeFalsy(); + expect(test.description).toBe("Expecting 'body.actual' to be a string or an array. Received a 'number'"); + }); + + it('Should export an entry point', done => { + const mainInstance: MainInstance = { + // @ts-ignore + asserterManager: { + addAsserter: (templateAssertion: object, createFunction: Function) => { + expect(templateAssertion).toEqual({ + expect: { + description: 'actual value', + type: ['string', 'array'] + }, + toContain: { + description: 'element', + type: ['string', 'any'] + }, + not: { + required: false, + description: 'negates', + type: 'null' } - }; - entryPoint(mainInstance); - }); - + }); + expect(createFunction()).toBeInstanceOf(ExpectToContainAsserter); + done(); + } + } + }; + entryPoint(mainInstance); + }); }); diff --git a/src/asserters/expect-to-contain-asserter.ts b/src/asserters/expect-to-contain-asserter.ts index f51d7289..b510ca9b 100644 --- a/src/asserters/expect-to-contain-asserter.ts +++ b/src/asserters/expect-to-contain-asserter.ts @@ -1,61 +1,61 @@ -import {Assertion} from '../models/events/assertion'; -import {TestModel} from '../models/outputs/test-model'; -import {Asserter} from './asserter'; -import {MainInstance} from '../plugins/main-instance'; +import { Assertion } from '../models/events/assertion'; +import { TestModel } from '../models/outputs/test-model'; +import { Asserter } from './asserter'; +import { MainInstance } from '../plugins/main-instance'; export class ExpectToContainAsserter implements Asserter { - public assert(assertion: Assertion, literal: any): TestModel { - const name: string = assertion.name; - const actual = assertion.expect; - const expected = assertion.toContain; - const not = assertion.not !== undefined; + public assert(assertion: Assertion, literal: any): TestModel { + const actual = assertion.expect; + const expected = assertion.toContain; + const not = assertion.not !== undefined; - if (typeof (actual) === 'string') { - if (typeof (expected) === 'string') { - return { - name, - valid: not ? actual.indexOf(expected) === -1 : actual.indexOf(expected) !== -1, - description: `Expecting '${actual}' (${literal.expect})${not ? ' not' : ''} to contain '${expected}'` - }; - } else { - return { - name, - valid: false, - description: `Expecting 'toContain' to be a 'string'. Received a '${typeof (expected)}' instead` - }; - } - } else if (Array.isArray((actual))) { - return { - name, - valid: not ? !actual.includes(expected) : actual.includes(expected), - description: `Expecting '${actual}' (${literal.expect})${not ? ' not' : ''} to contain '${expected}'` - }; - } else { - return { - name, - valid: false, - description: `Expecting '${literal.expect}' to be a string or an array. Received a '${typeof (actual)}'` - }; - } + if (typeof actual === 'string') { + if (typeof expected === 'string') { + return { + name: assertion.name, + valid: not ? actual.indexOf(expected) === -1 : actual.indexOf(expected) !== -1, + description: `Expecting '${actual}' (${literal.expect})${not ? ' not' : ''} to contain '${expected}'` + }; + } else { + return { + name: assertion.name, + valid: false, + description: `Expecting 'toContain' to be a 'string'. Received a '${typeof expected}' instead` + }; + } + } else if (Array.isArray(actual)) { + return { + name: assertion.name, + valid: not ? !actual.includes(expected) : actual.includes(expected), + description: `Expecting '${actual}' (${literal.expect})${not ? ' not' : ''} to contain '${expected}'` + }; + } else { + return { + name: assertion.name, + valid: false, + description: `Expecting '${literal.expect}' to be a string or an array. Received a '${typeof actual}'` + }; } + } } export function entryPoint(mainInstance: MainInstance): void { - mainInstance.asserterManager.addAsserter( - { - expect: { - description: 'actual value', - type: ['string', 'array'] - }, - not: { - required: false, - description: 'negates', - type: 'null' - }, - toContain: { - description: 'element', - type: ['string', 'any'] - }, - }, - () => new ExpectToContainAsserter()); + mainInstance.asserterManager.addAsserter( + { + expect: { + description: 'actual value', + type: ['string', 'array'] + }, + not: { + required: false, + description: 'negates', + type: 'null' + }, + toContain: { + description: 'element', + type: ['string', 'any'] + } + }, + () => new ExpectToContainAsserter() + ); } diff --git a/src/asserters/null-asserter.test.ts b/src/asserters/null-asserter.test.ts index edad2843..746782d6 100644 --- a/src/asserters/null-asserter.test.ts +++ b/src/asserters/null-asserter.test.ts @@ -1,20 +1,21 @@ -import {Assertion} from '../models/events/assertion'; -import {NullAsserter} from './null-asserter'; +import { Assertion } from '../models/events/assertion'; +import { NullAsserter } from './null-asserter'; describe('NullAsserter', () => { - it('should return invalid', () => { + it('should return invalid', () => { + const assertion: Assertion = { + name: 'assertion 0', + expect: 2, + unknown: 2 + }; - const assertion: Assertion = { - name: 'assertion 0', - expect: 2, - unknown: 2, - }; - - const expectToBeEqualToAsserter = new NullAsserter().assert(assertion, {name: '123'}); - expect(expectToBeEqualToAsserter).toEqual({ - 'description': 'Undefined asserter: [expect; unknown]', - 'name': 'Not known asserter', - 'valid': false - }); + const expectToBeEqualToAsserter = new NullAsserter().assert(assertion, { + name: '123' + }); + expect(expectToBeEqualToAsserter).toEqual({ + description: 'Undefined asserter: [expect; unknown]', + name: 'Not known asserter', + valid: false }); + }); }); diff --git a/src/asserters/null-asserter.ts b/src/asserters/null-asserter.ts index 65f0265a..1c334194 100644 --- a/src/asserters/null-asserter.ts +++ b/src/asserters/null-asserter.ts @@ -1,14 +1,14 @@ -import {Assertion} from '../models/events/assertion'; -import {TestModel} from '../models/outputs/test-model'; -import {Asserter} from './asserter'; +import { Assertion } from '../models/events/assertion'; +import { TestModel } from '../models/outputs/test-model'; +import { Asserter } from './asserter'; export class NullAsserter implements Asserter { - public assert(assertion: Assertion, literal: any): TestModel { - delete assertion.name; - return { - name: 'Not known asserter', - valid: false, - description: 'Undefined asserter: [' + Object.keys(assertion).join('; ') + ']' - }; - } + public assert(assertion: Assertion, literal: any): TestModel { + delete assertion.name; + return { + name: 'Not known asserter', + valid: false, + description: 'Undefined asserter: [' + Object.keys(assertion).join('; ') + ']' + }; + } } diff --git a/src/code-generators/assertion-code-generator.test.ts b/src/code-generators/assertion-code-generator.test.ts index 8f2249f9..f5f70476 100644 --- a/src/code-generators/assertion-code-generator.test.ts +++ b/src/code-generators/assertion-code-generator.test.ts @@ -1,31 +1,30 @@ -import {AssertionCodeGenerator} from './assertion-code-generator'; +import { AssertionCodeGenerator } from './assertion-code-generator'; describe('AssertionCodeGenerator', () => { + it('Should pass tests instance name', () => { + const assertionCodeGenerator: AssertionCodeGenerator = new AssertionCodeGenerator('tests', 'asserter', 'assertion'); + const code: string = assertionCodeGenerator.generate(); - it('Should pass tests instance name', () => { - const assertionCodeGenerator: AssertionCodeGenerator = - new AssertionCodeGenerator('tests', 'asserter', 'assertion'); - const code: string = assertionCodeGenerator.generate(); - - expect(code).toBe('try {\n' + - ' const evaluated = {name: assertion.name};\n' + - ' (Object.keys(assertion) || [])\n' + - ' .filter(key => key !== \'name\')\n' + - ' .forEach(key => evaluated[key] = eval(assertion[key]) );\n' + - ' const testResult = asserter.assert(evaluated, assertion);\n' + - ' if (assertion.ignore !== undefined && assertion.ignore !== false) {\n' + - ' testResult.ignored = true;\n' + - ' }\n' + - ' tests.push(testResult);\n' + - ' } catch (err) {\n' + - ' const msg = `Error executing assertion: \'${err}\'`;\n' + - ' Logger.error(msg);\n' + - ' tests.push({\n' + - ' name: assertion.name,\n' + - ' description: msg,\n' + - ' valid: false\n' + - ' });\n' + - ' }'); - }); - + expect(code).toBe( + 'try {\n' + + ' const evaluated = {name: assertion.name};\n' + + ' (Object.keys(assertion) || [])\n' + + " .filter(key => key !== 'name')\n" + + ' .forEach(key => evaluated[key] = eval(assertion[key]) );\n' + + ' const testResult = asserter.assert(evaluated, assertion);\n' + + ' if (assertion.ignore !== undefined && assertion.ignore !== false) {\n' + + ' testResult.ignored = true;\n' + + ' }\n' + + ' tests.push(testResult);\n' + + ' } catch (err) {\n' + + " const msg = `Error executing assertion: '${err}'`;\n" + + ' Logger.error(msg);\n' + + ' tests.push({\n' + + ' name: assertion.name,\n' + + ' description: msg,\n' + + ' valid: false\n' + + ' });\n' + + ' }' + ); + }); }); diff --git a/src/code-generators/assertion-code-generator.ts b/src/code-generators/assertion-code-generator.ts index b161de3e..72b1c95a 100644 --- a/src/code-generators/assertion-code-generator.ts +++ b/src/code-generators/assertion-code-generator.ts @@ -1,16 +1,16 @@ export class AssertionCodeGenerator { - private readonly testsName: string; - private readonly assertionName: string; - private readonly asserterInstanceName: string; + private readonly testsName: string; + private readonly assertionName: string; + private readonly asserterInstanceName: string; - public constructor(testsName: string, asserterInstanceName: string, assertionName: string) { - this.testsName = testsName; - this.asserterInstanceName = asserterInstanceName; - this.assertionName = assertionName; - } + public constructor(testsName: string, asserterInstanceName: string, assertionName: string) { + this.testsName = testsName; + this.asserterInstanceName = asserterInstanceName; + this.assertionName = assertionName; + } - public generate(): string { - return `try { + public generate(): string { + return `try { const evaluated = {name: assertion.name}; (Object.keys(${this.assertionName}) || []) .filter(key => key !== 'name') @@ -29,6 +29,5 @@ export class AssertionCodeGenerator { valid: false }); }`; - } - + } } diff --git a/src/code-generators/event-code-generator.ts b/src/code-generators/event-code-generator.ts index ea4d7304..50d4dbf6 100644 --- a/src/code-generators/event-code-generator.ts +++ b/src/code-generators/event-code-generator.ts @@ -1,61 +1,68 @@ -import {AssertionCodeGenerator} from './assertion-code-generator'; -import {Assertion} from '../models/events/assertion'; -import {StoreCodeGenerator} from './store-code-generator'; -import {TestModel} from '../models/outputs/test-model'; -import {DynamicFunctionController} from '../dynamic-functions/dynamic-function-controller'; -import {Store} from '../configurations/store'; -import {Logger} from '../loggers/logger'; -import {DynamicModulesManager} from '../plugins/dynamic-modules-manager'; +import { AssertionCodeGenerator } from './assertion-code-generator'; +import { Assertion } from '../models/events/assertion'; +import { StoreCodeGenerator } from './store-code-generator'; +import { TestModel } from '../models/outputs/test-model'; +import { DynamicFunctionController } from '../dynamic-functions/dynamic-function-controller'; +import { Store } from '../configurations/store'; +import { Logger } from '../loggers/logger'; +import { DynamicModulesManager } from '../plugins/dynamic-modules-manager'; //TODO test it export class EventCodeGenerator { - private readonly thisArg: any; - private readonly tests: TestModel[] = []; - private readonly testsInstanceName = 'tests'; - private readonly asserterInstanceName = 'asserter'; - private readonly storeInstanceName = 'store'; - private readonly script: string; - private readonly store: { [propName: string]: any }; - private readonly name: string; - private assertions: Assertion[]; - - public constructor(thisArg: any, eventName: string) { - this.thisArg = thisArg; - this.name = eventName; - const eventValue: any = this.thisArg[eventName] || {}; - this.store = eventValue.store || {}; - this.script = eventValue.script || ''; - this.assertions = eventValue.assertions || []; - } - - public run(functionArguments: { name: string; value: any }[]): TestModel[] { - this.runScriptAndStore(functionArguments); - this.runAssertions(functionArguments); - return this.tests; - } - - private runScriptAndStore(functionArguments: { name: string; value: any }[]) { - const dynamicFunction = new DynamicFunctionController(this.getScriptSnippet() + this.getStoreSnippet(), this.thisArg); - - dynamicFunction.addArgument(this.storeInstanceName, Store.getData()); - dynamicFunction.addArgument(this.testsInstanceName, this.tests); - dynamicFunction.addArgument('Logger', Logger); - - functionArguments.forEach(argument => { - dynamicFunction.addArgument(argument.name, argument.value); - }); - - try { - dynamicFunction.execute(); - } catch (err) { - const message = `Error running event '${this.name}': ${err}`; - Logger.error(message); - this.tests.push({valid: false, name: 'Event ran', description: message}); - } + private readonly thisArg: any; + private readonly tests: TestModel[] = []; + private readonly testsInstanceName = 'tests'; + private readonly asserterInstanceName = 'asserter'; + private readonly storeInstanceName = 'store'; + private readonly script: string; + private readonly store: { [propName: string]: any }; + private readonly name: string; + private assertions: Assertion[]; + + public constructor(thisArg: any, eventName: string) { + this.thisArg = thisArg; + this.name = eventName; + const eventValue: any = this.thisArg[eventName] || {}; + this.store = eventValue.store || {}; + this.script = eventValue.script || ''; + this.assertions = eventValue.assertions || []; + } + + public run(functionArguments: { name: string; value: any }[]): TestModel[] { + this.runScriptAndStore(functionArguments); + this.runAssertions(functionArguments); + return this.tests; + } + + private runScriptAndStore(functionArguments: { name: string; value: any }[]) { + const dynamicFunction = new DynamicFunctionController( + this.getScriptSnippet() + this.getStoreSnippet(), + this.thisArg + ); + + dynamicFunction.addArgument(this.storeInstanceName, Store.getData()); + dynamicFunction.addArgument(this.testsInstanceName, this.tests); + dynamicFunction.addArgument('Logger', Logger); + + functionArguments.forEach(argument => { + dynamicFunction.addArgument(argument.name, argument.value); + }); + + try { + dynamicFunction.execute(); + } catch (err) { + const message = `Error running event '${this.name}': ${err}`; + Logger.error(message); + this.tests.push({ + valid: false, + name: 'Event ran', + description: message + }); } + } - private getScriptSnippet(): string { - return `try { + private getScriptSnippet(): string { + return `try { ${this.script} } catch (err) { const msg = \`Error executing '${this.name}' script: '\${err}'\`; @@ -66,39 +73,46 @@ export class EventCodeGenerator { name: "Valid 'script' code" }); }\n`; - } - - private getStoreSnippet(): string { - return new StoreCodeGenerator(this.testsInstanceName, this.storeInstanceName).generate(this.store); - } - - private runAssertions(functionArguments: { name: string; value: any }[]): void { - this.assertions.forEach((assertion: any) => { - const assertionCodeGenerator: AssertionCodeGenerator = - new AssertionCodeGenerator(this.testsInstanceName, this.asserterInstanceName, 'assertion'); - - const dynamicFunction = new DynamicFunctionController(assertionCodeGenerator.generate(), this.thisArg); - - dynamicFunction.addArgument(this.asserterInstanceName, - DynamicModulesManager.getInstance().getAsserterManager().createAsserter(assertion)); - dynamicFunction.addArgument(this.storeInstanceName, Store.getData()); - dynamicFunction.addArgument(this.testsInstanceName, this.tests); - dynamicFunction.addArgument('assertion', assertion); - dynamicFunction.addArgument('Logger', Logger); - - functionArguments.forEach(argument => { - dynamicFunction.addArgument(argument.name, argument.value); - }); - - try { - dynamicFunction.execute(); - } catch (err) { - const message = `Error running event '${this.name}' '${assertion.name}': ${err}`; - Logger.error(message); - this.tests.push({valid: false, name: 'Assertion ran', description: message}); - } - + } + + private getStoreSnippet(): string { + return new StoreCodeGenerator(this.testsInstanceName, this.storeInstanceName).generate(this.store); + } + + private runAssertions(functionArguments: { name: string; value: any }[]): void { + this.assertions.forEach((assertion: any) => { + const assertionCodeGenerator: AssertionCodeGenerator = new AssertionCodeGenerator( + this.testsInstanceName, + this.asserterInstanceName, + 'assertion' + ); + + const dynamicFunction = new DynamicFunctionController(assertionCodeGenerator.generate(), this.thisArg); + + dynamicFunction.addArgument( + this.asserterInstanceName, + DynamicModulesManager.getInstance().getAsserterManager().createAsserter(assertion) + ); + dynamicFunction.addArgument(this.storeInstanceName, Store.getData()); + dynamicFunction.addArgument(this.testsInstanceName, this.tests); + dynamicFunction.addArgument('assertion', assertion); + dynamicFunction.addArgument('Logger', Logger); + + functionArguments.forEach(argument => { + dynamicFunction.addArgument(argument.name, argument.value); + }); + + try { + dynamicFunction.execute(); + } catch (err) { + const message = `Error running event '${this.name}' '${assertion.name}': ${err}`; + Logger.error(message); + this.tests.push({ + valid: false, + name: 'Assertion ran', + description: message }); - } - + } + }); + } } diff --git a/src/code-generators/store-code-generator.test.ts b/src/code-generators/store-code-generator.test.ts index 2d457fa4..ec973a39 100644 --- a/src/code-generators/store-code-generator.test.ts +++ b/src/code-generators/store-code-generator.test.ts @@ -1,41 +1,41 @@ -import {StoreCodeGenerator} from './store-code-generator'; -import {Event} from '../models/events/event'; +import { StoreCodeGenerator } from './store-code-generator'; +import { Event } from '../models/events/event'; const event: Event = { - store: { - first: 'firstValue', - second: 'secondValue', - } + store: { + first: 'firstValue', + second: 'secondValue' + } }; describe('StoreCodeGenerator', () => { + it('Should create code', () => { + const storeCodeGenerator: StoreCodeGenerator = new StoreCodeGenerator('tests', 'storeName'); + const code: string = storeCodeGenerator.generate(event.store); - it('Should create code', () => { - const storeCodeGenerator: StoreCodeGenerator = new StoreCodeGenerator('tests', 'storeName'); - const code: string = storeCodeGenerator.generate(event.store); - - expect(code).toBe('try {\n' + - ' storeName[\'first\'] = firstValue;\n' + - ' } catch (err) {\n' + - ' const msg = `Error executing store \'first\' code: \'${err}\'`;\n' + - ' Logger.error(msg);\n' + - ' tests.push({\n' + - ' description: msg,\n' + - ' valid: false,\n' + - ' name: \"Valid \'store\' in event code\"\n' + - ' });\n' + - ' }\n' + - 'try {\n' + - ' storeName[\'second\'] = secondValue;\n' + - ' } catch (err) {\n' + - ' const msg = `Error executing store \'second\' code: \'${err}\'`;\n' + - ' Logger.error(msg);\n' + - ' tests.push({\n' + - ' description: msg,\n' + - ' valid: false,\n' + - ' name: \"Valid \'store\' in event code\"\n' + - ' });\n' + - ' }\n'); - }); - + expect(code).toBe( + 'try {\n' + + " storeName['first'] = firstValue;\n" + + ' } catch (err) {\n' + + " const msg = `Error executing store 'first' code: '${err}'`;\n" + + ' Logger.error(msg);\n' + + ' tests.push({\n' + + ' description: msg,\n' + + ' valid: false,\n' + + ' name: "Valid \'store\' in event code"\n' + + ' });\n' + + ' }\n' + + 'try {\n' + + " storeName['second'] = secondValue;\n" + + ' } catch (err) {\n' + + " const msg = `Error executing store 'second' code: '${err}'`;\n" + + ' Logger.error(msg);\n' + + ' tests.push({\n' + + ' description: msg,\n' + + ' valid: false,\n' + + ' name: "Valid \'store\' in event code"\n' + + ' });\n' + + ' }\n' + ); + }); }); diff --git a/src/code-generators/store-code-generator.ts b/src/code-generators/store-code-generator.ts index 5479c899..557e4040 100644 --- a/src/code-generators/store-code-generator.ts +++ b/src/code-generators/store-code-generator.ts @@ -1,16 +1,16 @@ export class StoreCodeGenerator { - private readonly testsName: string; - private readonly storeInstanceName: string; + private readonly testsName: string; + private readonly storeInstanceName: string; - public constructor(testsName: string, storeInstanceName: string) { - this.storeInstanceName = storeInstanceName; - this.testsName = testsName; - } + public constructor(testsName: string, storeInstanceName: string) { + this.storeInstanceName = storeInstanceName; + this.testsName = testsName; + } - public generate(store: any): string { - let code = ''; - Object.keys(store).forEach(key => { - code += `try { + public generate(store: any): string { + let code = ''; + Object.keys(store).forEach(key => { + code += `try { ${this.storeInstanceName}['${key}'] = ${store[key]}; } catch (err) { const msg = \`Error executing store '${key}' code: '\${err}'\`; @@ -21,9 +21,7 @@ export class StoreCodeGenerator { name: "Valid 'store' in event code" }); }\n`; - - }); - return code; - } - + }); + return code; + } } diff --git a/src/components/component-parent-backupper.test.ts b/src/components/component-parent-backupper.test.ts index 3cbf6d2b..d306e6aa 100644 --- a/src/components/component-parent-backupper.test.ts +++ b/src/components/component-parent-backupper.test.ts @@ -1,65 +1,64 @@ -import {ComponentParentBackupper} from './component-parent-backupper'; +import { ComponentParentBackupper } from './component-parent-backupper'; describe('ComponentParentBackupper', () => { - it('should remove parents', () => { + it('should remove parents', () => { + const task: any = { + name: 'task' + }; + const child = { + name: 'actuator', + parent: task + }; + const actuator = { + name: 'actuator', + parent: task + }; + const sensor = { + name: 'sensor', + parent: task + }; - const requisition: any = { - name: 'requisition' - }; - const child = { - name: 'publisher', - parent: requisition, - }; - const publisher = { - name: 'publisher', - parent: requisition, - }; - const subscription = { - name: 'subscription', - parent: requisition, - }; + task.tasks = [child]; + task.actuators = [actuator]; + task.sensors = [sensor]; - requisition.requisitions = [child]; - requisition.publishers = [publisher]; - requisition.subscriptions = [subscription]; + new ComponentParentBackupper().removeParents(task); - new ComponentParentBackupper().removeParents(requisition); + expect(task.tasks[0].parent).toBeUndefined(); + expect(task.actuators[0].parent).toBeUndefined(); + expect(task.sensors[0].parent).toBeUndefined(); + }); - expect(requisition.requisitions[0].parent).toBeUndefined(); - expect(requisition.publishers[0].parent).toBeUndefined(); - expect(requisition.subscriptions[0].parent).toBeUndefined(); - }); + it('should put parents back', () => { + const task: any = { + name: 'task', + id: 'task' + }; + const child = { + name: 'child', + id: 'child', + parent: task + }; + const actuator = { + name: 'actuator', + id: 'actuator', + parent: task + }; + const sensor = { + name: 'sensor', + id: 'sensor', + parent: task + }; + task.tasks = [child]; + task.actuators = [actuator]; + task.sensors = [sensor]; - it('should put parents back', () => { - const requisition: any = { - name: 'requisition', - id: 'requisition' - }; - const child = { - name: 'child', - id: 'child', - parent: requisition, - }; - const publisher = { - name: 'publisher', - id: 'publisher', - parent: requisition, - }; - const subscription = { - name: 'subscription', - id: 'subscription', - parent: requisition, - }; - requisition.requisitions = [child]; - requisition.publishers = [publisher]; - requisition.subscriptions = [subscription]; + const componentParentBackupper = new ComponentParentBackupper(); + componentParentBackupper.removeParents(task); + componentParentBackupper.putParentsBack(task); - const componentParentBackupper = new ComponentParentBackupper(); - componentParentBackupper.removeParents(requisition); - componentParentBackupper.putParentsBack(requisition); - - expect(requisition.requisitions[0].parent.name).toBe(requisition.name); - expect(requisition.publishers[0].parent.name).toBe(requisition.name); - expect(requisition.subscriptions[0].parent.name).toBe(requisition.name); - }); + expect(task.tasks[0].parent.name).toBe(task.name); + expect(task.actuators[0].parent.name).toBe(task.name); + expect(task.sensors[0].parent.name).toBe(task.name); + }); }); diff --git a/src/components/component-parent-backupper.ts b/src/components/component-parent-backupper.ts index 1178f23a..850c00e9 100644 --- a/src/components/component-parent-backupper.ts +++ b/src/components/component-parent-backupper.ts @@ -1,28 +1,21 @@ -import {RequisitionModel} from '../models/inputs/requisition-model'; +import { TaskModel } from '../models/inputs/task-model'; export class ComponentParentBackupper { - private readonly parentMap: any = {}; + private readonly parentMap: any = {}; - public removeParents(requisition: RequisitionModel): void { - this.parentMap[requisition.id] = requisition.parent; - delete requisition.parent; - (requisition.requisitions || []) - .map(child => this.removeParents(child)); - (requisition.publishers || []) - .concat(requisition.subscriptions || []) - .map(leaf => { - this.parentMap[leaf.id] = leaf.parent; - delete leaf.parent; - }); - } - - public putParentsBack(requisition: RequisitionModel): void { - requisition.parent = this.parentMap[requisition.id]; - (requisition.requisitions || []) - .map(child => this.putParentsBack(child)); - (requisition.publishers || []) - .concat(requisition.subscriptions || []) - .map(leaf => leaf.parent = this.parentMap[leaf.id]); - } + public removeParents(task: TaskModel): void { + this.parentMap[task.id] = task.parent; + delete task.parent; + (task.tasks || []).map(child => this.removeParents(child)); + (task.actuators || []).concat(task.sensors || []).map(leaf => { + this.parentMap[leaf.id] = leaf.parent; + delete leaf.parent; + }); + } + public putParentsBack(task: TaskModel): void { + task.parent = this.parentMap[task.id]; + (task.tasks || []).map(child => this.putParentsBack(child)); + (task.actuators || []).concat(task.sensors || []).map(leaf => (leaf.parent = this.parentMap[leaf.id])); + } } diff --git a/src/components/requisition-adopter.test.ts b/src/components/requisition-adopter.test.ts deleted file mode 100644 index c0e2ad29..00000000 --- a/src/components/requisition-adopter.test.ts +++ /dev/null @@ -1,174 +0,0 @@ -import {RequisitionAdopter} from './requisition-adopter'; - -describe('RequisitionAdopter', () => { - - it('Should set default values', () => { - const parent = new RequisitionAdopter({}).getRequisition(); - - expect(parent.name).toBe('Requisition #0'); - expect(parent.id).toBeDefined(); - expect(parent.parent).toBeUndefined(); - expect(parent.requisitions).toEqual([]); - expect(parent.publishers).toEqual([]); - expect(parent.subscriptions).toEqual([]); - expect(parent.delay).toBe(0); - expect(parent.level).toBe(0); - expect(parent.iterations).toBe(1); - }); - - it('Should override default values', () => { - const name = 'specialName'; - - const parent = new RequisitionAdopter({ - name: name, - id: 'otherId', - value: 123, - delay: 10000, - level: 5, - iterations: 9, - }).getRequisition(); - - expect(parent.value).toBe(parent.value); - expect(parent.name).toBe(parent.name); - expect(parent.id).toBe(parent.id); - expect(parent.publishers).toEqual([]); - expect(parent.subscriptions).toEqual([]); - expect(parent.requisitions).toEqual([]); - expect(parent.delay).toBe(parent.delay); - expect(parent.level).toBe(parent.level); - expect(parent.iterations).toBe(parent.iterations); - }); - - it('Should initialize publishers', () => { - const name = 'specialName'; - - const publishers = [ - { - type: 'first', - name: 'firstName', - extraValue: 123 - }, - {}, - ]; - const parent = new RequisitionAdopter({ - name, - publishers: publishers, - }).getRequisition(); - - expect(parent.publishers[0].name).toBe(publishers[0].name); - expect(parent.publishers[0].type).toBe(publishers[0].type); - expect(parent.publishers[0].extraValue).toBe(publishers[0].extraValue); - expect(parent.publishers[0].parent.name).toBe(name); - expect(parent.publishers[0].parent.publishers[0].name).toBe(publishers[0].name); - - expect(parent.publishers[1].name).toBe('Publisher #1'); - expect(parent.publishers[1].type).toBe(publishers[1].type); - expect(parent.publishers[1].extraValue).toBe(publishers[1].extraValue); - expect(parent.publishers[1].parent.name).toBe(name); - expect(parent.publishers[1].parent.publishers[1].name).toBe('Publisher #1'); - }); - - it('Should initialize subscriptions', () => { - const name = 'specialName'; - - const subscriptions = [ - { - type: 'first', - extraValue: 123 - }, - { - name: 'secondName' - }, - ]; - const parent = new RequisitionAdopter({ - name, - subscriptions: subscriptions, - }).getRequisition(); - - expect(parent.subscriptions[0].name).toBe('Subscription #0'); - expect(parent.subscriptions[0].type).toBe(subscriptions[0].type); - expect(parent.subscriptions[0].extraValue).toBe(subscriptions[0].extraValue); - expect(parent.subscriptions[0].parent.name).toBe(name); - expect(parent.subscriptions[0].parent.subscriptions[0].name).toBe('Subscription #0'); - - expect(parent.subscriptions[1].name).toBe(subscriptions[1].name); - expect(parent.subscriptions[1].type).toBe(subscriptions[1].type); - expect(parent.subscriptions[1].extraValue).toBe(subscriptions[1].extraValue); - expect(parent.subscriptions[1].parent.name).toBe(name); - expect(parent.subscriptions[1].parent.subscriptions[1].name).toBe(subscriptions[1].name); - }); - - it('Should initialize requisitions', () => { - const name = 'specialName'; - - const requisitions = [ - { - extraValue: 123 - }, - {}, - ]; - const parent = new RequisitionAdopter({name, requisitions}).getRequisition(); - - expect(parent.name).toBe(name); - expect(parent.requisitions[0].name).toBe('Requisition #0'); - expect(parent.requisitions[0].extraValue).toBe(requisitions[0].extraValue); - - expect(parent.requisitions[1].name).toBe('Requisition #1'); - expect(parent.requisitions[1].extraValue).toBe(requisitions[1].extraValue); - expect(parent.requisitions[1].parent!.name).toBe(name); - expect(parent.requisitions[1].parent!.requisitions[1].name).toBe('Requisition #1'); - }); - - it('should merge with default values', () => { - const requisitions = [ - { - 'delay': 200, - 'requisitions': [ - { - 'delay': 100 - } - ], - 'name': 'examples/no-tests.yml' - } - ]; - - const requisition = new RequisitionAdopter({name: 'name', requisitions, level: 6}).getRequisition(); - - expect(requisition.name).toBe('name'); - expect(requisition.id).toBeDefined(); - expect(requisition.parent).toBeUndefined(); - expect(requisition.publishers).toEqual([]); - expect(requisition.subscriptions).toEqual([]); - expect(requisition.delay).toBe(0); - expect(requisition.level).toBe(6); - expect(requisition.iterations).toBe(1); - - expect(requisition.requisitions[0].name).toBe('examples/no-tests.yml'); - expect(requisition.requisitions[0].id).toBeDefined(); - expect(requisition.requisitions[0].parent!.name).toBe('name'); - expect(requisition.requisitions[0].requisitions[0].delay).toBe(100); - expect(requisition.requisitions[0].requisitions[0].level).toBe(8); - expect(requisition.requisitions[0].publishers).toEqual([]); - expect(requisition.requisitions[0].subscriptions).toEqual([]); - expect(requisition.requisitions[0].delay).toBe(200); - expect(requisition.requisitions[0].level).toBe(7); - expect(requisition.requisitions[0].iterations).toBe(1); - - }); - - it('should set parents', () => { - const parent = { - name: 'parent', - requisitions: [{name: 'req'}], - publishers: [{name: 'pub'}], - subscriptions: [{name: 'sub'}], - }; - - const requisition = new RequisitionAdopter(parent).getRequisition(); - - expect(requisition.requisitions[0].parent!.name).toBe('parent'); - expect(requisition.publishers[0].parent.name).toBe('parent'); - expect(requisition.subscriptions[0].parent.name).toBe('parent'); - }); - -}); diff --git a/src/components/requisition-adopter.ts b/src/components/requisition-adopter.ts deleted file mode 100644 index 843cf649..00000000 --- a/src/components/requisition-adopter.ts +++ /dev/null @@ -1,56 +0,0 @@ -import {IdGenerator} from '../strings/id-generator'; -import {RequisitionModel} from '../models/inputs/requisition-model'; -import {PublisherModel} from '../models/inputs/publisher-model'; -import {SubscriptionModel} from '../models/inputs/subscription-model'; - -export class RequisitionAdopter { - private readonly requisition: RequisitionModel; - private defaultModel: any = { - subscriptions: [], - publishers: [], - requisitions: [], - delay: 0, - iterations: 1, - level: 0, - parallel: false, - ignore: false, - }; - - constructor(node: any) { - this.requisition = this.baptiseRequisition(node, node.name ? node.name : `Requisition #0`); - } - - public getRequisition(): RequisitionModel { - return this.requisition; - } - - private baptiseRequisition(requisition: RequisitionModel, name?: string, parent?: RequisitionModel): RequisitionModel { - requisition = Object.assign({}, this.defaultModel, requisition) as RequisitionModel; - this.putNameAndId(requisition, name, parent); - requisition.requisitions = requisition.requisitions - .map((child, index) => { - child.level = requisition.level + 1; - return this.baptiseRequisition(child, `Requisition #${index}`, requisition) as RequisitionModel; - }); - requisition.publishers = requisition.publishers - .map((publisher, index) => this.putNameAndId(publisher, `Publisher #${index}`, requisition) as PublisherModel); - requisition.subscriptions = requisition.subscriptions - .map((subscription, index) => this.putNameAndId(subscription, `Subscription #${index}`, requisition) as SubscriptionModel); - return requisition; - } - - private putNameAndId(component: RequisitionModel | PublisherModel | SubscriptionModel, name?: string, parent?: RequisitionModel) { - if (!component.name && name) { - component.name = name; - } - if (!component.id) { - component.id = new IdGenerator(component).generateId(); - } - if (parent) { - component.parent = parent; - } - - return component; - } - -} diff --git a/src/components/task-adopter.test.ts b/src/components/task-adopter.test.ts new file mode 100644 index 00000000..4c5f5eed --- /dev/null +++ b/src/components/task-adopter.test.ts @@ -0,0 +1,178 @@ +import { TaskAdopter } from './task-adopter'; + +describe('TaskAdopter', () => { + it('Should set default values', () => { + const parent = new TaskAdopter({}).getTask(); + + expect(parent.name).toBe('Task #0'); + expect(parent.id).toBeDefined(); + expect(parent.parent).toBeUndefined(); + expect(parent.tasks).toEqual([]); + expect(parent.actuators).toEqual([]); + expect(parent.sensors).toEqual([]); + expect(parent.delay).toBe(0); + expect(parent.level).toBe(0); + expect(parent.iterations).toBe(1); + }); + + it('Should override default values', () => { + const name = 'specialName'; + + const parent = new TaskAdopter({ + name: name, + id: 'otherId', + value: 123, + delay: 10000, + level: 5, + iterations: 9 + }).getTask(); + + expect(parent.value).toBe(parent.value); + expect(parent.name).toBe(parent.name); + expect(parent.id).toBe(parent.id); + expect(parent.actuators).toEqual([]); + expect(parent.sensors).toEqual([]); + expect(parent.tasks).toEqual([]); + expect(parent.delay).toBe(parent.delay); + expect(parent.level).toBe(parent.level); + expect(parent.iterations).toBe(parent.iterations); + }); + + it('Should initialize actuators', () => { + const name = 'specialName'; + + const actuators = [ + { + type: 'first', + name: 'firstName', + extraValue: 123 + }, + {} + ]; + const parent = new TaskAdopter({ + name, + actuators: actuators + }).getTask(); + + expect(parent.actuators[0].name).toBe(actuators[0].name); + expect(parent.actuators[0].type).toBe(actuators[0].type); + expect(parent.actuators[0].extraValue).toBe(actuators[0].extraValue); + expect(parent.actuators[0].parent.name).toBe(name); + expect(parent.actuators[0].parent.actuators[0].name).toBe(actuators[0].name); + + expect(parent.actuators[1].name).toBe('Actuator #1'); + expect(parent.actuators[1].type).toBe(actuators[1].type); + expect(parent.actuators[1].extraValue).toBe(actuators[1].extraValue); + expect(parent.actuators[1].parent.name).toBe(name); + expect(parent.actuators[1].parent.actuators[1].name).toBe('Actuator #1'); + }); + + it('Should initialize sensors', () => { + const name = 'specialName'; + + const sensors = [ + { + type: 'first', + extraValue: 123 + }, + { + name: 'secondName' + } + ]; + const parent = new TaskAdopter({ + name, + sensors: sensors + }).getTask(); + + expect(parent.sensors[0].name).toBe('Sensor #0'); + expect(parent.sensors[0].type).toBe(sensors[0].type); + expect(parent.sensors[0].extraValue).toBe(sensors[0].extraValue); + expect(parent.sensors[0].parent.name).toBe(name); + expect(parent.sensors[0].parent.sensors[0].name).toBe('Sensor #0'); + + expect(parent.sensors[1].name).toBe(sensors[1].name); + expect(parent.sensors[1].type).toBe(sensors[1].type); + expect(parent.sensors[1].extraValue).toBe(sensors[1].extraValue); + expect(parent.sensors[1].parent.name).toBe(name); + expect(parent.sensors[1].parent.sensors[1].name).toBe(sensors[1].name); + }); + + it('Should initialize tasks', () => { + const name = 'specialName'; + + const tasks = [ + { + extraValue: 123 + }, + {} + ]; + const parent = new TaskAdopter({ + name, + tasks + }).getTask(); + + expect(parent.name).toBe(name); + expect(parent.tasks[0].name).toBe('Task #0'); + expect(parent.tasks[0].extraValue).toBe(tasks[0].extraValue); + + expect(parent.tasks[1].name).toBe('Task #1'); + expect(parent.tasks[1].extraValue).toBe(tasks[1].extraValue); + expect(parent.tasks[1].parent!.name).toBe(name); + expect(parent.tasks[1].parent!.tasks[1].name).toBe('Task #1'); + }); + + it('should merge with default values', () => { + const tasks = [ + { + delay: 200, + tasks: [ + { + delay: 100 + } + ], + name: 'examples/no-tests.yml' + } + ]; + + const task = new TaskAdopter({ + name: 'name', + tasks, + level: 6 + }).getTask(); + + expect(task.name).toBe('name'); + expect(task.id).toBeDefined(); + expect(task.parent).toBeUndefined(); + expect(task.actuators).toEqual([]); + expect(task.sensors).toEqual([]); + expect(task.delay).toBe(0); + expect(task.level).toBe(6); + expect(task.iterations).toBe(1); + + expect(task.tasks[0].name).toBe('examples/no-tests.yml'); + expect(task.tasks[0].id).toBeDefined(); + expect(task.tasks[0].parent!.name).toBe('name'); + expect(task.tasks[0].tasks[0].delay).toBe(100); + expect(task.tasks[0].tasks[0].level).toBe(8); + expect(task.tasks[0].actuators).toEqual([]); + expect(task.tasks[0].sensors).toEqual([]); + expect(task.tasks[0].delay).toBe(200); + expect(task.tasks[0].level).toBe(7); + expect(task.tasks[0].iterations).toBe(1); + }); + + it('should set parents', () => { + const parent = { + name: 'parent', + tasks: [{ name: 'req' }], + actuators: [{ name: 'pub' }], + sensors: [{ name: 'sub' }] + }; + + const task = new TaskAdopter(parent).getTask(); + + expect(task.tasks[0].parent!.name).toBe('parent'); + expect(task.actuators[0].parent.name).toBe('parent'); + expect(task.sensors[0].parent.name).toBe('parent'); + }); +}); diff --git a/src/components/task-adopter.ts b/src/components/task-adopter.ts new file mode 100644 index 00000000..58c85a34 --- /dev/null +++ b/src/components/task-adopter.ts @@ -0,0 +1,56 @@ +import { IdGenerator } from '../strings/id-generator'; +import { TaskModel } from '../models/inputs/task-model'; +import { ActuatorModel } from '../models/inputs/actuator-model'; +import { SensorModel } from '../models/inputs/sensor-model'; + +export class TaskAdopter { + private readonly task: TaskModel; + private defaultModel: any = { + sensors: [], + actuators: [], + tasks: [], + delay: 0, + iterations: 1, + level: 0, + parallel: false, + ignore: false + }; + + constructor(node: any) { + this.task = this.baptiseTask(node, node.name ? node.name : `Task #0`); + } + + public getTask(): TaskModel { + return this.task; + } + + private baptiseTask(task: TaskModel, name?: string, parent?: TaskModel): TaskModel { + task = Object.assign({}, this.defaultModel, task) as TaskModel; + this.putNameAndId(task, name, parent); + task.tasks = task.tasks.map((child, index) => { + child.level = task.level + 1; + return this.baptiseTask(child, `Task #${index}`, task) as TaskModel; + }); + task.actuators = task.actuators.map( + (actuator, index) => this.putNameAndId(actuator, `Actuator #${index}`, task) as ActuatorModel + ); + task.sensors = task.sensors.map( + (sensor, index) => this.putNameAndId(sensor, `Sensor #${index}`, task) as SensorModel + ); + return task; + } + + private putNameAndId(component: TaskModel | ActuatorModel | SensorModel, name?: string, parent?: TaskModel) { + if (!component.name && name) { + component.name = name; + } + if (!component.id) { + component.id = new IdGenerator(component).generateId(); + } + if (parent) { + component.parent = parent; + } + + return component; + } +} diff --git a/src/configurations/command-line-configuration.test.ts b/src/configurations/command-line-configuration.test.ts index 2aae95df..6514c86e 100644 --- a/src/configurations/command-line-configuration.test.ts +++ b/src/configurations/command-line-configuration.test.ts @@ -1,5 +1,8 @@ -import {CommandLineConfiguration} from './command-line-configuration'; -import {DynamicModulesManager} from '../plugins/dynamic-modules-manager'; +import { CommandLineConfiguration } from './command-line-configuration'; +import { DynamicModulesManager } from '../plugins/dynamic-modules-manager'; + +const consoleLogMock = jest.fn(message => console.warn(message)); +console.log = consoleLogMock; jest.mock('../plugins/dynamic-modules-manager'); @@ -10,304 +13,319 @@ const describeAssertersMock = jest.fn(() => false); const describeLoadedModulesMock = jest.fn(() => false); // @ts-ignore DynamicModulesManager.getInstance.mockImplementation(() => ({ - getProtocolManager: () => { - return { - describeMatchingProtocols: describeProtocolsMock - }; - }, - getReportFormatterManager: () => { - return { - describeMatchingReportFormatters: describeReportFormattersMock - }; - }, - getObjectParserManager: () => { - return { - describeMatchingObjectParsers: describeObjectParsersMock - }; - }, - getAsserterManager: () => { - return { - describeMatchingAsserters: describeAssertersMock - }; - }, - describeLoadedModules: describeLoadedModulesMock + getProtocolManager: () => { + return { + describeMatchingProtocols: describeProtocolsMock + }; + }, + getReportFormatterManager: () => { + return { + describeMatchingReportFormatters: describeReportFormattersMock + }; + }, + getObjectParserManager: () => { + return { + describeMatchingObjectParsers: describeObjectParsersMock + }; + }, + getAsserterManager: () => { + return { + describeMatchingAsserters: describeAssertersMock + }; + }, + describeLoadedModules: describeLoadedModulesMock })); const exitMock = jest.fn(); describe('CommandLineConfiguration', () => { - beforeEach(() => { - describeProtocolsMock.mockClear(); - describeReportFormattersMock.mockClear(); - describeObjectParsersMock.mockClear(); - describeLoadedModulesMock.mockClear(); - - exitMock.mockClear(); - // @ts-ignore - process.exit = exitMock; - }); + beforeEach(() => { + describeProtocolsMock.mockClear(); + describeReportFormattersMock.mockClear(); + describeObjectParsersMock.mockClear(); + describeLoadedModulesMock.mockClear(); + consoleLogMock.mockClear(); - it('should not throw', () => { - // @ts-ignore - expect(() => new CommandLineConfiguration(undefined)).not.toThrow(); - }); + exitMock.mockClear(); + // @ts-ignore + process.exit = exitMock; + }); - it('verbosity -b', () => { - const logLevel = 'info'; - const commandLineConfiguration = new CommandLineConfiguration(['node', 'test', '-b', logLevel]); + it('should not throw', () => { + // @ts-ignore + expect(() => new CommandLineConfiguration(undefined)).not.toThrow(); + }); - expect(commandLineConfiguration.getVerbosity()).toBe(logLevel); - }); + it('verbosity -b', () => { + const logLevel = 'info'; + const commandLineConfiguration = new CommandLineConfiguration(['node', 'test', '-b', logLevel]); - it('verbosity --verbosity', () => { - const logLevel = 'info'; - const commandLineConfiguration = new CommandLineConfiguration(['node', 'test', '--verbosity', logLevel]); + expect(commandLineConfiguration.getVerbosity()).toBe(logLevel); + }); - expect(commandLineConfiguration.getVerbosity()).toBe(logLevel); - }); + it('verbosity --verbosity', () => { + const logLevel = 'info'; + const commandLineConfiguration = new CommandLineConfiguration(['node', 'test', '--verbosity', logLevel]); - it('verbosity -m', () => { - const maxLevel = '3'; - const commandLineConfiguration = new CommandLineConfiguration(['node', 'test', '-m', maxLevel]); + expect(commandLineConfiguration.getVerbosity()).toBe(logLevel); + }); - expect(commandLineConfiguration.getMaxReportLevelPrint()).toBe(maxLevel); - }); + it('verbosity -m', () => { + const maxLevel = 3; + const commandLineConfiguration = new CommandLineConfiguration(['node', 'test', '-m', maxLevel.toString()]); - it('verbosity --max-report-level-print', () => { - const maxLevel = '3'; - const commandLineConfiguration = new CommandLineConfiguration(['node', 'test', '--max-report-level-print', maxLevel]); + expect(commandLineConfiguration.getMaxReportLevelPrint()).toBe(maxLevel); + }); - expect(commandLineConfiguration.getMaxReportLevelPrint()).toBe(maxLevel); - }); + it('verbosity --max-report-level-print', () => { + const maxLevel = 3; + const commandLineConfiguration = new CommandLineConfiguration([ + 'node', + 'test', + '--max-report-level-print', + maxLevel.toString() + ]); - it('verbosity --max-report-level-print default', () => { - const commandLineConfiguration = new CommandLineConfiguration(['node', 'test']); + expect(commandLineConfiguration.getMaxReportLevelPrint()).toBe(maxLevel); + }); - expect(commandLineConfiguration.getMaxReportLevelPrint()).toBeUndefined(); - }); + it('verbosity --max-report-level-print default', () => { + const commandLineConfiguration = new CommandLineConfiguration(['node', 'test']); - it('undefined logLevel', () => { - const commandLineConfiguration = new CommandLineConfiguration(['node', 'test']); + expect(commandLineConfiguration.getMaxReportLevelPrint()).toBeUndefined(); + }); - expect(commandLineConfiguration.getVerbosity()).toBe('warn'); - }); + it('undefined logLevel', () => { + const commandLineConfiguration = new CommandLineConfiguration(['node', 'test']); - it('default console output', () => { - const commandLineConfiguration = new CommandLineConfiguration(['node', 'test']); + expect(commandLineConfiguration.getVerbosity()).toBe('warn'); + }); - expect(commandLineConfiguration.getStdoutRequisitionOutput()).toBeFalsy(); - }); + it('default console output', () => { + const commandLineConfiguration = new CommandLineConfiguration(['node', 'test']); - it('set console output -o', () => { - const commandLineConfiguration = new CommandLineConfiguration(['node', 'test', '-o']); + expect(commandLineConfiguration.getStdoutTaskOutput()).toBeFalsy(); + }); - expect(commandLineConfiguration.getStdoutRequisitionOutput()).toBeTruthy(); - }); + it('set console output -o', () => { + const commandLineConfiguration = new CommandLineConfiguration(['node', 'test', '-o']); - it('set console output --stdout-requisition-output', () => { - const commandLineConfiguration = new CommandLineConfiguration(['node', 'test', '--stdout-requisition-output']); + expect(commandLineConfiguration.getStdoutTaskOutput()).toBeTruthy(); + }); - expect(commandLineConfiguration.getStdoutRequisitionOutput()).toBeTruthy(); - }); + it('set console output --stdout-task-output', () => { + const commandLineConfiguration = new CommandLineConfiguration(['node', 'test', '--stdout-task-output']); - it('default passing tests', () => { - const commandLineConfiguration = new CommandLineConfiguration(['node', 'test']); + expect(commandLineConfiguration.getStdoutTaskOutput()).toBeTruthy(); + }); - expect(commandLineConfiguration.getShowPassingTests()).toBeFalsy(); - }); + it('default passing tests', () => { + const commandLineConfiguration = new CommandLineConfiguration(['node', 'test']); - it('set passing tests -i', () => { - const commandLineConfiguration = new CommandLineConfiguration(['node', 'test', '-i']); + expect(commandLineConfiguration.getShowPassingTests()).toBeFalsy(); + }); - expect(commandLineConfiguration.getShowPassingTests()).toBeTruthy(); - }); + it('set passing tests -i', () => { + const commandLineConfiguration = new CommandLineConfiguration(['node', 'test', '-i']); - it('set passing tests --show-passing-tests', () => { - const commandLineConfiguration = new CommandLineConfiguration(['node', 'test', '--show-passing-tests']); + expect(commandLineConfiguration.getShowPassingTests()).toBeTruthy(); + }); - expect(commandLineConfiguration.getShowPassingTests()).toBeTruthy(); - }); + it('set passing tests --show-passing-tests', () => { + const commandLineConfiguration = new CommandLineConfiguration(['node', 'test', '--show-passing-tests']); - it('getConfigFileName -c', () => { - const configFile = 'minusC'; - const commandLineConfiguration = new CommandLineConfiguration(['node', 'test', '-c', configFile]); + expect(commandLineConfiguration.getShowPassingTests()).toBeTruthy(); + }); - expect(commandLineConfiguration.getConfigFileName()).toBe(configFile); - }); - - it('getConfigFileName --config-file', () => { - const configFile = 'configFile'; - const commandLineConfiguration = new CommandLineConfiguration(['node', 'test', '--config-file', configFile]); - - expect(commandLineConfiguration.getConfigFileName()).toBe(configFile); - }); + it('getConfigFileName -c', () => { + const configFile = 'minusC'; + const commandLineConfiguration = new CommandLineConfiguration(['node', 'test', '-c', configFile]); - it('describe protocols -p', () => { - const commandLineConfiguration = new CommandLineConfiguration(['node', 'test', '-p']); - commandLineConfiguration.verifyPrematureActions(); + expect(commandLineConfiguration.getConfigFileName()).toBe(configFile); + }); - expect(exitMock).toHaveBeenCalledWith(0); - expect(describeProtocolsMock).toHaveBeenCalledWith(undefined); - }); + it('getConfigFileName --config-file', () => { + const configFile = 'configFile'; + const commandLineConfiguration = new CommandLineConfiguration(['node', 'test', '--config-file', configFile]); - it('describe protocols --protocols-description', () => { - const commandLineConfiguration = new CommandLineConfiguration(['node', 'test', '--protocols-description']); + expect(commandLineConfiguration.getConfigFileName()).toBe(configFile); + }); - commandLineConfiguration.verifyPrematureActions(); + it('describe protocols -p', () => { + const commandLineConfiguration = new CommandLineConfiguration(['node', 'test', '-p']); + commandLineConfiguration.verifyPrematureActions(); - expect(exitMock).toHaveBeenCalledWith(0); - expect(describeProtocolsMock).toHaveBeenCalledWith(undefined); - }); + expect(exitMock).toHaveBeenCalledWith(0); + expect(describeProtocolsMock).toHaveBeenCalledWith(undefined); + }); - it('describe protocols --protocols-description http', () => { - const params = 'http'; - const commandLineConfiguration = new CommandLineConfiguration(['node', 'test', '--protocols-description', params]); + it('describe protocols --protocols-description', () => { + const commandLineConfiguration = new CommandLineConfiguration(['node', 'test', '--protocols-description']); - commandLineConfiguration.verifyPrematureActions(); + commandLineConfiguration.verifyPrematureActions(); - expect(exitMock).toHaveBeenCalledWith(0); - expect(describeProtocolsMock).toHaveBeenCalledWith(params); - }); + expect(exitMock).toHaveBeenCalledWith(0); + expect(describeProtocolsMock).toHaveBeenCalledWith(undefined); + }); - it('describe formatters -f', () => { - const params = 'json'; - const commandLineConfiguration = new CommandLineConfiguration(['node', 'test', '-f', params]); + it('describe protocols --protocols-description http', () => { + const params = 'http'; + const commandLineConfiguration = new CommandLineConfiguration(['node', 'test', '--protocols-description', params]); - commandLineConfiguration.verifyPrematureActions(); + commandLineConfiguration.verifyPrematureActions(); - expect(exitMock).toHaveBeenCalledWith(0); - expect(describeReportFormattersMock).toHaveBeenCalledWith(params); - }); + expect(exitMock).toHaveBeenCalledWith(0); + expect(describeProtocolsMock).toHaveBeenCalledWith(params); + }); - it('describe formatters --formatters-description', () => { - const commandLineConfiguration = new CommandLineConfiguration(['node', 'test', '--formatters-description']); + it('describe formatters -f', () => { + const params = 'json'; + const commandLineConfiguration = new CommandLineConfiguration(['node', 'test', '-f', params]); - commandLineConfiguration.verifyPrematureActions(); + commandLineConfiguration.verifyPrematureActions(); - expect(exitMock).toHaveBeenCalledWith(0); - expect(describeReportFormattersMock).toHaveBeenCalledWith(true); - }); + expect(exitMock).toHaveBeenCalledWith(0); + expect(describeReportFormattersMock).toHaveBeenCalledWith(params); + }); - it('describe parsers list -e', () => { - const commandLineConfiguration = new CommandLineConfiguration(['node', 'test', '-e']); + it('describe formatters --formatters-description', () => { + const commandLineConfiguration = new CommandLineConfiguration(['node', 'test', '--formatters-description']); - commandLineConfiguration.verifyPrematureActions(); + commandLineConfiguration.verifyPrematureActions(); - expect(exitMock).toHaveBeenCalledWith(1); - expect(describeObjectParsersMock).toHaveBeenCalledWith(true); - }); + expect(exitMock).toHaveBeenCalledWith(0); + expect(describeReportFormattersMock).toHaveBeenCalledWith(true); + }); - it('describe --parsers-list', () => { - const params = 'csv'; - const commandLineConfiguration = new CommandLineConfiguration(['node', 'test', '--parsers-list', params]); + it('describe parsers list -e', () => { + const commandLineConfiguration = new CommandLineConfiguration(['node', 'test', '-e']); - commandLineConfiguration.verifyPrematureActions(); + commandLineConfiguration.verifyPrematureActions(); - expect(exitMock).toHaveBeenCalledWith(1); - expect(describeObjectParsersMock).toHaveBeenCalledWith(params); - }); + expect(exitMock).toHaveBeenCalledWith(1); + expect(describeObjectParsersMock).toHaveBeenCalledWith(true); + }); - it('describe assertions -t', () => { - const params = 'expect'; - const commandLineConfiguration = new CommandLineConfiguration(['node', 'test', '-t', params]); + it('describe --parsers-list', () => { + const params = 'csv'; + const commandLineConfiguration = new CommandLineConfiguration(['node', 'test', '--parsers-list', params]); - commandLineConfiguration.verifyPrematureActions(); + commandLineConfiguration.verifyPrematureActions(); - expect(exitMock).toHaveBeenCalledWith(1); - expect(describeAssertersMock).toHaveBeenCalledWith(params); - }); + expect(exitMock).toHaveBeenCalledWith(1); + expect(describeObjectParsersMock).toHaveBeenCalledWith(params); + }); - it('describe assertions --tests-list', () => { - const params = 'expect'; - const commandLineConfiguration = new CommandLineConfiguration(['node', 'test', '--tests-list', params]); + it('describe assertions -t', () => { + const params = 'expect'; + const commandLineConfiguration = new CommandLineConfiguration(['node', 'test', '-t', params]); - commandLineConfiguration.verifyPrematureActions(); + commandLineConfiguration.verifyPrematureActions(); - expect(exitMock).toHaveBeenCalledWith(1); - expect(describeAssertersMock).toHaveBeenCalledWith(params); - }); + expect(exitMock).toHaveBeenCalledWith(1); + expect(describeAssertersMock).toHaveBeenCalledWith(params); + }); - it('describe loaded modules -u', () => { - const commandLineConfiguration = new CommandLineConfiguration(['node', 'test', '-u']); + it('describe assertions --tests-list', () => { + const params = 'expect'; + const commandLineConfiguration = new CommandLineConfiguration(['node', 'test', '--tests-list', params]); - commandLineConfiguration.verifyPrematureActions(); + commandLineConfiguration.verifyPrematureActions(); - expect(exitMock).toHaveBeenCalledWith(0); - expect(describeLoadedModulesMock).toHaveBeenCalledWith(); - }); + expect(exitMock).toHaveBeenCalledWith(1); + expect(describeAssertersMock).toHaveBeenCalledWith(params); + }); - it('describe loaded modules --loaded-modules-list', () => { - const commandLineConfiguration = new CommandLineConfiguration(['node', 'test', '--loaded-modules-list']); + it('describe loaded modules -u', () => { + const commandLineConfiguration = new CommandLineConfiguration(['node', 'test', '-u']); - commandLineConfiguration.verifyPrematureActions(); + commandLineConfiguration.verifyPrematureActions(); - expect(exitMock).toHaveBeenCalledWith(0); - expect(describeLoadedModulesMock).toHaveBeenCalledWith(); - }); + expect(exitMock).toHaveBeenCalledWith(0); + expect(describeLoadedModulesMock).toHaveBeenCalledWith(); + }); - it('no file', () => { - const commandLineConfiguration = new CommandLineConfiguration(['node', 'test']); + it('describe loaded modules --loaded-modules-list', () => { + const commandLineConfiguration = new CommandLineConfiguration(['node', 'test', '--loaded-modules-list']); - expect(commandLineConfiguration.getTestFiles()).toEqual([]); - expect(commandLineConfiguration.getTestFilesIgnoringOthers()).toEqual([]); - }); + commandLineConfiguration.verifyPrematureActions(); - it('add file ', () => { - const testFile1 = 'filename1'; - const testFile2 = 'filename2'; - const commandLineConfiguration = new CommandLineConfiguration( - ['node', 'test', '--some', 'test', testFile1, '--other', 'stuff', testFile2]); + expect(exitMock).toHaveBeenCalledWith(0); + expect(describeLoadedModulesMock).toHaveBeenCalledWith(); + }); - expect(commandLineConfiguration.getTestFiles().sort()).toEqual([testFile2, testFile1].sort()); - }); + it('should run parallely', () => { + const commandLineConfiguration = new CommandLineConfiguration(['node', 'test', '-q']); - it('add test file', () => { - const commandLineConfiguration = new CommandLineConfiguration(['node', 'test', '-a', 'file', '--add-file', 'file2']); + expect(commandLineConfiguration.isParallelExecution).toBeTruthy(); + }); - expect(commandLineConfiguration.getTestFiles()).toEqual(['file', 'file2']); - }); + it('no file', () => { + const commandLineConfiguration = new CommandLineConfiguration(['node', 'test']); - it('add plugin', () => { - const commandLineConfiguration = new CommandLineConfiguration(['node', 'test', '-l', 'plugin1', '--add-plugin', 'plugin2']); + expect(commandLineConfiguration.getTestFiles()).toEqual([]); + }); - expect(commandLineConfiguration.getPlugins()).toEqual(['plugin1', 'plugin2']); - }); + it('add file ', () => { + const testFile1 = 'filename1'; + const testFile2 = 'filename2'; + const commandLineConfiguration = new CommandLineConfiguration([ + 'node', + 'test', + '-e', + '-b', + 'debug', + testFile1, + testFile2 + ]); - it('render help', () => { - const consoleMock = jest.fn(); - console.log = consoleMock; - const commandLineConfiguration = new CommandLineConfiguration(['node', 'test', '-h']); - expect(consoleMock).toHaveBeenCalled(); - }); + expect(commandLineConfiguration.getTestFiles().sort()).toEqual([testFile1, testFile2].sort()); + }); - it('get version', () => { - const packageJson = require('../../package.json'); + it('add test file', () => { + const commandLineConfiguration = new CommandLineConfiguration(['node', 'test', 'file', 'file2']); - const commandLineConfiguration = new CommandLineConfiguration(['node', 'test', '-v']); + expect(commandLineConfiguration.getTestFiles()).toEqual(['file', 'file2']); + }); - expect(commandLineConfiguration.getVersion()).toBe(packageJson.version); - }); + it('add plugin', () => { + const commandLineConfiguration = new CommandLineConfiguration([ + 'node', + 'test', + '-l', + 'plugin1', + '--add-plugin', + 'plugin2' + ]); - it('add test file ignoring', () => { - const commandLineConfiguration = new CommandLineConfiguration(['node', 'test', '-A', 'file', '--add-file-and-ignore-others', 'file2']); + expect(commandLineConfiguration.getPlugins()).toEqual(['plugin1', 'plugin2']); + }); - expect(commandLineConfiguration.getTestFilesIgnoringOthers()).toEqual(['file', 'file2']); - }); + it('render help', () => { + new CommandLineConfiguration(['node', 'test', '-h']); + expect(consoleLogMock).toHaveBeenCalled(); + }); - it('getStore -s', () => { - const option = ['-s', '--store']; - const store: any = { - key: 'value', - 'composed-name': 'stuff', - number: '10' - }; - const newArguments = ['node', 'test']; - Object.keys(store).forEach((key, index) => { - newArguments.push(option[index % option.length]); - newArguments.push(key + '=' + store[key]); - }); - const commandLineConfiguration = new CommandLineConfiguration(newArguments); - - expect(commandLineConfiguration.getStore()).toEqual(store); - }); + it('get version', () => { + const packageJson = require('../../package.json'); + const commandLineConfiguration = new CommandLineConfiguration(['node', 'test', '-v']); + expect(commandLineConfiguration.getVersion()).toBe(packageJson.version); + }); + + it('getStore -s', () => { + const option = ['-s', '--store']; + const store: any = { + key: 'value', + 'composed-name': 'stuff', + number: '10' + }; + const newArguments = ['node', 'test']; + Object.keys(store).forEach((key, index) => { + newArguments.push(option[index % option.length]); + newArguments.push(key + '=' + store[key]); + }); + const commandLineConfiguration = new CommandLineConfiguration(newArguments); + + expect(commandLineConfiguration.getStore()).toEqual(store); + }); }); diff --git a/src/configurations/command-line-configuration.ts b/src/configurations/command-line-configuration.ts index 87709d7a..e7dcd93f 100644 --- a/src/configurations/command-line-configuration.ts +++ b/src/configurations/command-line-configuration.ts @@ -1,150 +1,167 @@ -import {Command} from 'commander'; -import {DynamicModulesManager} from '../plugins/dynamic-modules-manager'; -import {Logger} from '../loggers/logger'; +import { Command, Option } from 'commander'; +import { DynamicModulesManager } from '../plugins/dynamic-modules-manager'; +import { Logger } from '../loggers/logger'; const packageJson = require('../../package.json'); export class CommandLineConfiguration { - private parsedCommandLine: any; - private readonly commandLineStore: any = {}; - private readonly plugins: string[] = []; - private readonly testFiles: string[] = []; - private readonly testFilesIgnoringOthers: string[] = []; - - public constructor(commandLineArguments: string[]) { - const commander = new Command() - .version(process.env.npm_package_version || packageJson.version, '-v, --version') - .allowUnknownOption() - .usage('[options] [other-test-files...]') - .description('Take a look at the full documentation: https://enqueuer.com') - .option('-b, --verbosity ', 'set verbosity', /^(trace|debug|info|warn|error|fatal)$/i, 'warn') - .option('-c, --config-file ', 'set configurationFile') - .option('-e, --parsers-list [parser]', 'list available object parsers') - .option('-f, --formatters-description [formatter]', 'describe report formatters') - .option('-o, --stdout-requisition-output', 'add stdout as requisition output', false) - .option('-m, --max-report-level-print ', 'set max report level print', /^(\d)$/i) - .option('-p, --protocols-description [protocol]', 'describe protocols') - .option('-t, --tests-list [expectedField]', 'list available tests assertions') - .option('-u, --loaded-modules-list', 'list loaded modules') - .option('-i, --show-passing-tests', 'show passing tests') - .option('-s, --store [store]', 'add variables values to this session', - (val: string, memo: string[]) => this.storeCommandLineAction(val, memo), []) - .option('-l, --add-plugin [plugin]', 'add plugin', - (val: string) => this.plugins.push(val), []) - .option('-a, --add-file ', 'add file to be tested', - (val: string) => this.testFiles.push(val), []) - .option('-A, --add-file-and-ignore-others ', 'add file to be tested and ignore others', - (val: string) => this.testFilesIgnoringOthers.push(val), []); - commander.on('--help', () => this.helpDescription()); - try { - this.parsedCommandLine = commander.parse(commandLineArguments || ['path', 'enqueuer']); - } catch (err) { - Logger.error(`Error parsing command line: ${err}`); - this.parsedCommandLine = {}; - } - + private options: any; + private readonly commandLineStore: any = {}; + private readonly plugins: string[] = []; + private readonly testFiles: string[] = []; + + public constructor(commandLineArguments: string[]) { + const commander = new Command() + .version(process.env.npm_package_version || packageJson.version, '-v, --version', 'output the current version') + .allowUnknownOption() + .usage('[options] [test-files...]') + .description('Take a look at the full documentation: https://enqueuer.com') + .addOption( + new Option('-b, --verbosity ', 'set verbosity') + .choices(['trace', 'debug', 'info', 'warn', 'error', 'fatal']) + .default('warn') + ) + .option('-c, --config-file ', 'set configurationFile') + .option('-d, --show-explicit-tests-only', 'show explicit tests only', false) + .option('-o, --stdout-task-output', 'add stdout as task output', false) + .option('-m, --max-report-level-print ', 'set max report level print', parseInt) + .option('-i, --show-passing-tests', 'show passing tests') + .option( + '-s, --store [store]', + 'add variables values to this session', + (val: string, memo: string[]) => this.storeCommandLineAction(val, memo), + [] + ) + .option('-l, --add-plugin [plugin]', 'add plugin', (val: string) => { + this.plugins.push(val); + return val; + }) + .option('-e, --parsers-list [parser]', 'list available object parsers') + .option('-q, --parallel', 'should run tests files parallely', false) + .option('-f, --formatters-description [formatter]', 'describe report formatters', false) + .option('-p, --protocols-description [protocol]', 'describe protocols', false) + .option('-t, --tests-list [expectedField]', 'list available tests assertions', false) + .option('-u, --loaded-modules-list', 'list loaded modules', false) + .argument('[test-files...]', 'other files to be tested') + .action((testFiles: string[]) => { + this.testFiles.push(...(testFiles || [])); + }); + + commander.on('--help', () => this.helpDescription()); + try { + commander.parse(commandLineArguments || ['path', 'enqueuer']); + this.options = commander.opts(); + } catch (err) { + Logger.error(`Error parsing command line: ${err}`); + this.options = {}; } - - public verifyPrematureActions(): void { - let exitCode: boolean | undefined; - if (this.parsedCommandLine.protocolsDescription) { - const protocolsMatcherArg = typeof this.parsedCommandLine.protocolsDescription === 'string' ? - this.parsedCommandLine.protocolsDescription : - undefined; - exitCode = DynamicModulesManager.getInstance().getProtocolManager() - .describeMatchingProtocols(protocolsMatcherArg); - } else if (this.parsedCommandLine.formattersDescription) { - exitCode = DynamicModulesManager.getInstance().getReportFormatterManager() - .describeMatchingReportFormatters(this.parsedCommandLine.formattersDescription); - } else if (this.parsedCommandLine.parsersList) { - exitCode = DynamicModulesManager.getInstance().getObjectParserManager() - .describeMatchingObjectParsers(this.parsedCommandLine.parsersList); - } else if (this.parsedCommandLine.testsList) { - exitCode = DynamicModulesManager.getInstance().getAsserterManager() - .describeMatchingAsserters(this.parsedCommandLine.testsList); - } else if (this.parsedCommandLine.loadedModulesList) { - DynamicModulesManager.getInstance().describeLoadedModules(); - exitCode = true; - } - - if (exitCode !== undefined) { - process.exit(exitCode ? 0 : 1); - } + } + + public verifyPrematureActions(): void { + let exitCode: boolean | undefined; + if (this.options.protocolsDescription) { + const protocolsMatcherArg = + typeof this.options.protocolsDescription === 'string' ? this.options.protocolsDescription : undefined; + exitCode = DynamicModulesManager.getInstance() + .getProtocolManager() + .describeMatchingProtocols(protocolsMatcherArg); + } else if (this.options.formattersDescription) { + exitCode = DynamicModulesManager.getInstance() + .getReportFormatterManager() + .describeMatchingReportFormatters(this.options.formattersDescription); + } else if (this.options.parsersList) { + exitCode = DynamicModulesManager.getInstance() + .getObjectParserManager() + .describeMatchingObjectParsers(this.options.parsersList); + } else if (this.options.testsList) { + exitCode = DynamicModulesManager.getInstance() + .getAsserterManager() + .describeMatchingAsserters(this.options.testsList); + } else if (this.options.loadedModulesList) { + DynamicModulesManager.getInstance().describeLoadedModules(); + exitCode = true; } - public getVerbosity(): string { - return this.parsedCommandLine.verbosity; + if (exitCode !== undefined) { + process.exit(exitCode ? 0 : 1); } + } - public getStdoutRequisitionOutput(): boolean { - return !!this.parsedCommandLine.stdoutRequisitionOutput; - } + public getVerbosity(): string { + return this.options.verbosity; + } - public getConfigFileName(): string | undefined { - return this.parsedCommandLine.configFile; - } + public getStdoutTaskOutput(): boolean { + return !!this.options.stdoutTaskOutput; + } - public getShowPassingTests(): boolean { - return this.parsedCommandLine.showPassingTests; - } + public getShowExplicitTestsOnly(): boolean { + return !!this.options.showExplicitTestsOnly; + } - public getStore(): any { - return this.commandLineStore; - } + public getConfigFileName(): string | undefined { + return this.options.configFile; + } - public getTestFiles(): string[] { - const testFiles = this.testFiles; - if (testFiles.length > 0) { - return testFiles; - } - const args = this.parsedCommandLine.args; - if (args && args.length > 0) { - return args; - } - return []; - } + public getShowPassingTests(): boolean { + return this.options.showPassingTests; + } - public getTestFilesIgnoringOthers(): string[] { - return this.testFilesIgnoringOthers; - } + public isParallelExecution(): boolean { + return !!this.options.parallel; + } - public getPlugins(): string[] { - return this.plugins; - } + public getStore(): any { + return this.commandLineStore; + } - public getMaxReportLevelPrint(): number | undefined { - return this.parsedCommandLine.maxReportLevelPrint; + public getTestFiles(): string[] { + const testFiles = this.testFiles; + if (testFiles.length > 0) { + return testFiles; } - - public getVersion(): string { - return this.parsedCommandLine._version; + const args = this.options.args; + if (args && args.length > 0) { + return args; } + return []; + } - private storeCommandLineAction(val: string, memo: string[]) { - const split = val.split('='); - if (split.length == 2) { - this.commandLineStore[split[0]] = split[1]; - } - memo.push(val); - return memo; - } + public getPlugins(): string[] { + return this.plugins; + } - private helpDescription(): void { - console.log(''); - console.log('Examples:'); - console.log(' $ nqr --config-file config-file.yml --verbosity error --store key=value'); - console.log(' $ enqueuer -c config-file.yml test-file.yml --add-file another-test-file.yml -b info'); - console.log(' $ enqueuer test-file.yml --store someKey=true --store someOtherKey=false'); - console.log(' $ nqr --protocols-description -s key=value'); - console.log(' $ nqr -t expect'); - console.log(' $ nqr -l my-enqueuer-plugin-name -p plugin-protocol'); - console.log(' $ nqr -p http'); - console.log(' $ nqr --formatters-description json'); - - console.log(''); - console.log('Contributing:'); - console.log(' https://github.com/enqueuer-land/enqueuer'); + public getMaxReportLevelPrint(): number | undefined { + return this.options.maxReportLevelPrint; + } - } + public getVersion(): string { + return process.env.npm_package_version || packageJson.version; + } + private storeCommandLineAction(val: string, memo: string[]) { + const split = val.split('='); + if (split.length == 2) { + this.commandLineStore[split[0]] = split[1]; + } + memo.push(val); + return memo; + } + + private helpDescription(): void { + console.log(''); + console.log('Examples:'); + console.log(' $ nqr --config-file config-file.yml --verbosity error --store key=value'); + console.log(' $ enqueuer -c config-file.yml test-file.yml another-test-file.yml -b info'); + console.log(' $ enqueuer test-file.yml --store someKey=true --store someOtherKey=false'); + console.log(' $ nqr --protocols-description -s key=value'); + console.log(' $ nqr -t expect'); + console.log(' $ nqr -l my-enqueuer-plugin-name -p plugin-protocol'); + console.log(' $ nqr -p http'); + console.log(' $ nqr --formatters-description json'); + + console.log(''); + console.log('Contributing:'); + console.log(' https://github.com/enqueuer-land/enqueuer'); + } } diff --git a/src/configurations/configuration.test.ts b/src/configurations/configuration.test.ts index 88aee386..7a61508c 100644 --- a/src/configurations/configuration.test.ts +++ b/src/configurations/configuration.test.ts @@ -1,235 +1,219 @@ -import {Configuration} from './configuration'; -import {FileConfiguration} from './file-configuration'; -import {CommandLineConfiguration} from './command-line-configuration'; +import { Configuration } from './configuration'; +import { FileConfiguration } from './file-configuration'; +import { CommandLineConfiguration } from './command-line-configuration'; import prettyjson from 'prettyjson'; +const mockedCommandLineConfiguration = CommandLineConfiguration as jest.Mock; + jest.mock('./file-configuration'); jest.mock('./command-line-configuration'); jest.mock('prettyjson'); describe('Configuration', () => { - beforeEach(() => { - // @ts-ignore - Configuration.loaded = false; + beforeEach(() => { + // @ts-ignore + Configuration.loaded = false; + + mockedCommandLineConfiguration.mockClear(); + }); + + it('should addFiles', () => { + const commandLine = createEmptyCommandLine(); + mockedCommandLineConfiguration.mockImplementationOnce(() => commandLine); + + const instance = Configuration.getInstance(); + instance.addFiles('file1', 'file2'); + + expect(instance.getFiles().sort()).toEqual(['file1', 'file2'].sort()); + }); + + it('should call verifyPrematureActions', () => { + const commandLine = createEmptyCommandLine(); + mockedCommandLineConfiguration.mockImplementationOnce(() => commandLine); + commandLine.verifyPrematureActions = jest.fn(() => true); + + const instance = Configuration.getInstance(); + + expect(commandLine.verifyPrematureActions).toBeCalled(); + }); + + it('should check default values', () => { + mockedCommandLineConfiguration.mockImplementationOnce(() => createEmptyCommandLine()); + + const instance = Configuration.getInstance(); + + expect(instance.isParallel()).toBeFalsy(); + expect(instance.getFiles()).toEqual([]); + expect(instance.getLogLevel()).toBe('warn'); + expect(instance.getMaxReportLevelPrint()).toBe(1); + expect(instance.getStore()).toEqual({}); + expect(instance.getPlugins()).toEqual([]); + expect(instance.getOutputs()).toEqual([]); + expect(instance.getMaxReportLevelPrint()).toEqual(1); + }); + + it('should work with only command line', () => { + const commandLine = createCommandLine(); + mockedCommandLineConfiguration.mockImplementationOnce(() => commandLine); + + const instance = Configuration.getInstance(); + + expect(instance.getFiles()).toEqual(['cli-firstFile', 'cli-secondFile']); + expect(instance.getLogLevel()).toBe('cli-debug'); + expect(instance.getStore()).toEqual({ cliKey: 'value' }); + expect(instance.getPlugins()).toEqual(['cli-amqp-plugin', 'common-plugin']); + expect(instance.isParallel()).toBeFalsy(); + expect(instance.getMaxReportLevelPrint()).toBe(5); + }); + + it('should work with only conf file', () => { + mockedCommandLineConfiguration.mockImplementationOnce(() => createEmptyCommandLine('confFile')); + (FileConfiguration as jest.Mock).mockImplementationOnce(() => createFileConfiguration()); + + const instance = Configuration.getInstance(); + + expect(instance.isParallel()).toBeTruthy(); + expect(instance.getFiles()).toEqual(['confFile-1', 'confFile-2']); + expect(instance.getLogLevel()).toBe('confFile-fatal'); + expect(instance.getMaxReportLevelPrint()).toBe(13); + expect(instance.getStore()).toEqual({ + confFileStore: 'yml', + confFileKey: 'file report output' }); - - it('should addFiles', () => { - const commandLine = createEmptyCommandLine(); - // @ts-ignore - CommandLineConfiguration.mockImplementationOnce(() => commandLine); - - const instance = Configuration.getInstance(); - instance.addFiles('file1', 'file2'); - - expect(instance.getFiles().sort()).toEqual(['file1', 'file2'].sort()); + expect(instance.getPlugins()).toEqual(['confFile-plugin', 'confFile-plugin-2', 'common-plugin']); + }); + + it('should handle file not found', () => { + mockedCommandLineConfiguration.mockImplementationOnce(() => createEmptyCommandLine('not to throw')); + // @ts-ignore + FileConfiguration.mockImplementationOnce(() => { + throw 'error'; }); - it('should call verifyPrematureActions', () => { - const commandLine = createEmptyCommandLine(); - // @ts-ignore - CommandLineConfiguration.mockImplementationOnce(() => commandLine); - commandLine.verifyPrematureActions = jest.fn(); - - const instance = Configuration.getInstance(); - - expect(commandLine.verifyPrematureActions).toBeCalled(); - }); - - it('should check default values', () => { - // @ts-ignore - CommandLineConfiguration.mockImplementationOnce(() => createEmptyCommandLine()); - - const instance = Configuration.getInstance(); - - expect(instance.isParallel()).toBeFalsy(); - expect(instance.getFiles()).toEqual([]); - expect(instance.getLogLevel()).toBe('warn'); - expect(instance.getMaxReportLevelPrint()).toBe(1); - expect(instance.getStore()).toEqual({}); - expect(instance.getPlugins()).toEqual([]); - expect(instance.getOutputs()).toEqual([]); - expect(instance.getMaxReportLevelPrint()).toEqual(1); - }); - - it('should work with only command line', () => { - const commandLine = createCommandLine(); - // @ts-ignore - CommandLineConfiguration.mockImplementationOnce(() => commandLine); - - const instance = Configuration.getInstance(); - - expect(instance.getFiles()).toEqual(['cli-firstFile', 'cli-secondFile']); - expect(instance.getLogLevel()).toBe('cli-debug'); - expect(instance.getStore()).toEqual({cliKey: 'value'}); - expect(instance.getPlugins()).toEqual(['cli-amqp-plugin', 'common-plugin']); - expect(instance.isParallel()).toBeFalsy(); - expect(instance.getMaxReportLevelPrint()).toBe(5); - }); - - it('should work with only conf file', () => { - // @ts-ignore - CommandLineConfiguration.mockImplementationOnce(() => createEmptyCommandLine('confFile')); - // @ts-ignore - FileConfiguration.mockImplementationOnce(() => createFileConfiguration()); - - const instance = Configuration.getInstance(); - - expect(instance.isParallel()).toBeTruthy(); - expect(instance.getFiles()).toEqual(['confFile-1', 'confFile-2']); - expect(instance.getLogLevel()).toBe('confFile-fatal'); - expect(instance.getMaxReportLevelPrint()).toBe(13); - expect(instance.getStore()).toEqual({confFileStore: 'yml', confFileKey: 'file report output'}); - expect(instance.getPlugins()).toEqual(['confFile-plugin', 'confFile-plugin-2', 'common-plugin']); - }); - - it('should handle file not found', () => { - // @ts-ignore - CommandLineConfiguration.mockImplementationOnce(() => createEmptyCommandLine('not to throw')); - // @ts-ignore - FileConfiguration.mockImplementationOnce(() => { - throw 'error'; - }); - - expect(() => Configuration.getInstance()).not.toThrow(); - }); - - it('should merge command line with conf file', () => { - const commandLine = createCommandLine('conf-file'); - const fileConfiguration = createFileConfiguration(); - // @ts-ignore - CommandLineConfiguration.mockImplementationOnce(() => commandLine); - // @ts-ignore - FileConfiguration.mockImplementationOnce(() => fileConfiguration); - - const instance = Configuration.getInstance(); - - expect(instance.isParallel()).toBeTruthy(); - expect(instance.getFiles()).toEqual(fileConfiguration.getFiles().concat(commandLine.getTestFiles())); - expect(instance.getLogLevel()).toBe(commandLine.getVerbosity()); - expect(instance.getMaxReportLevelPrint()).toBe(commandLine.getMaxReportLevelPrint()); - expect(instance.getStore()).toEqual(Object.assign({}, fileConfiguration.getStore(), commandLine.getStore())); - }); - - it('should ignore files', () => { - const uniqueFiles = ['unique-file1', 'unique-file2']; - const fileConfiguration = createFileConfiguration(); - const commandLine = createCommandLine('conf-file'); - // @ts-ignore - commandLine.getTestFilesIgnoringOthers = () => uniqueFiles; - // @ts-ignore - CommandLineConfiguration.mockImplementationOnce(() => commandLine); - // @ts-ignore - FileConfiguration.mockImplementationOnce(() => fileConfiguration); - - const instance = Configuration.getInstance(); - - expect(instance.getFiles()).toEqual(uniqueFiles); - }); - - it('should combine plugins', () => { - const commandLine = createCommandLine('conf-file'); - const fileConfiguration = createFileConfiguration(); - const manuallyAddedPlugins = ['common-plugin', 'manuallyAddedPlugin']; - // @ts-ignore - CommandLineConfiguration.mockImplementationOnce(() => commandLine); - // @ts-ignore - FileConfiguration.mockImplementationOnce(() => fileConfiguration); - - const configuration = Configuration.getInstance(); - manuallyAddedPlugins.forEach(plugin => configuration.addPlugin(plugin)); - - const confPlugins = configuration.getPlugins(); - const uniquePlugins = [...new Set(commandLine.getPlugins() - .concat(fileConfiguration.getPlugins()) - .concat(manuallyAddedPlugins))]; - expect(confPlugins.length).toBe(uniquePlugins.length); - confPlugins.forEach(confPlugin => expect(uniquePlugins).toContainEqual(confPlugin)); - }); - - it('should create cli output formatter', () => { - const commandLine = createCommandLine(); - commandLine.getStdoutRequisitionOutput = () => true; - // @ts-ignore - CommandLineConfiguration.mockImplementationOnce(() => commandLine); - - const instance = Configuration.getInstance(); - - expect(instance.getOutputs()).toEqual([{format: 'console', name: 'command line report output', type: 'standard-output'}]); - }); - - it('should print configuration', () => { - const commandLine = createCommandLine(); - commandLine.getVerbosity = () => 'trace'; - // @ts-ignore - CommandLineConfiguration.mockImplementationOnce(() => commandLine); - const render = jest.fn(); - // @ts-ignore - prettyjson.render.mockImplementation(render); - - const instance = Configuration.getInstance(); - - expect(render).toHaveBeenCalledWith({ - 'configuration': { - 'files': ['cli-firstFile', 'cli-secondFile'], - 'logLevel': 'trace', - 'maxReportLevelPrint': 5, - 'outputs': [{'format': 'console', 'name': 'command line report output', 'type': 'standard-output'}], - 'parallel': false, - 'showPassingTests': true, - 'plugins': ['cli-amqp-plugin', 'common-plugin'], - 'store': {'cliKey': 'value'} + expect(() => Configuration.getInstance()).toThrow(); + }); + + it('should merge command line with conf file', () => { + const commandLine = createCommandLine('conf-file'); + const fileConfiguration = createFileConfiguration(); + mockedCommandLineConfiguration.mockImplementationOnce(() => commandLine); + // @ts-ignore + FileConfiguration.mockImplementationOnce(() => fileConfiguration); + + const instance = Configuration.getInstance(); + + expect(instance.isParallel()).toBeTruthy(); + expect(instance.getFiles()).toEqual(fileConfiguration.getFiles().concat(commandLine.getTestFiles())); + expect(instance.getLogLevel()).toBe(commandLine.getVerbosity()); + expect(instance.getMaxReportLevelPrint()).toBe(commandLine.getMaxReportLevelPrint()); + expect(instance.getStore()).toEqual(Object.assign({}, fileConfiguration.getStore(), commandLine.getStore())); + }); + + it('should create cli output formatter', () => { + const commandLine = createCommandLine(); + commandLine.getStdoutTaskOutput = () => true; + mockedCommandLineConfiguration.mockImplementationOnce(() => commandLine); + + const instance = Configuration.getInstance(); + + expect(instance.getOutputs()).toEqual([ + { + format: 'console', + name: 'command line report output', + type: 'standard-output' + } + ]); + }); + + it('should print configuration', () => { + const commandLine = createCommandLine(); + commandLine.getVerbosity = () => 'trace'; + mockedCommandLineConfiguration.mockImplementationOnce(() => commandLine); + const render = jest.fn(); + // @ts-ignore + prettyjson.render.mockImplementation(render); + + Configuration.getInstance(); + + expect(render).toHaveBeenCalledWith( + { + configuration: { + files: ['cli-firstFile', 'cli-secondFile'], + logLevel: 'trace', + maxReportLevelPrint: 5, + outputs: [ + { + format: 'console', + name: 'command line report output', + type: 'standard-output' } - }, expect.anything()); - }); - - const createEmptyCommandLine = (filename?: string) => { - return { - verifyPrematureActions: () => true, - getConfigFileName: () => filename, - getTestFiles: () => undefined, - getVerbosity: () => undefined, - getPlugins: () => undefined, - getShowPassingTests: () => undefined, - getStore: () => undefined, - getTestFilesIgnoringOthers: () => undefined, - getStdoutRequisitionOutput: () => false, - getMaxReportLevelPrint: () => undefined - }; + ], + parallel: false, + showExplicitTestsOnly: false, + showPassingTests: true, + plugins: ['cli-amqp-plugin', 'common-plugin'], + store: { cliKey: 'value' } + } + }, + expect.anything() + ); + }); + + const createEmptyCommandLine = (filename?: string) => { + return { + verifyPrematureActions: () => true, + getConfigFileName: () => filename, + getTestFiles: () => undefined, + isParallelExecution: () => undefined, + getVerbosity: () => undefined, + getPlugins: () => undefined, + getShowPassingTests: () => undefined, + getShowExplicitTestsOnly: () => undefined, + getStore: () => undefined, + getTestFilesIgnoringOthers: () => undefined, + getStdoutTaskOutput: () => false, + getMaxReportLevelPrint: () => undefined }; - - const createCommandLine = (filename?: string) => { + }; + + const createCommandLine = (filename?: string) => { + return { + verifyPrematureActions: () => true, + getConfigFileName: () => filename, + getTestFiles: () => ['cli-firstFile', 'cli-secondFile'], + getVerbosity: () => 'cli-debug', + getPlugins: () => ['cli-amqp-plugin', 'common-plugin'], + getStore: () => { return { - verifyPrematureActions: () => true, - getConfigFileName: () => filename, - getTestFiles: () => ['cli-firstFile', 'cli-secondFile'], - getVerbosity: () => 'cli-debug', - getPlugins: () => ['cli-amqp-plugin', 'common-plugin'], - getStore: () => { - return { - cliKey: 'value' - }; - }, - getShowPassingTests: () => true, - getTestFilesIgnoringOthers: () => undefined, - getStdoutRequisitionOutput: () => true, - getMaxReportLevelPrint: () => 5 + cliKey: 'value' }; + }, + isParallelExecution: () => false, + getShowPassingTests: () => true, + getShowExplicitTestsOnly: () => false, + getTestFilesIgnoringOthers: () => undefined, + getStdoutTaskOutput: () => true, + getMaxReportLevelPrint: () => 5 }; + }; - const createFileConfiguration = () => { + const createFileConfiguration = () => { + return { + getLogLevel: () => 'confFile-fatal', + getOutputs: () => { return { - getLogLevel: () => 'confFile-fatal', - getOutputs: () => { - return {type: 'confFile-type', format: 'yml', name: 'confFile report output'}; - }, - getStore: () => { - return {confFileStore: 'yml', confFileKey: 'file report output'}; - }, - getPlugins: () => ['confFile-plugin', 'confFile-plugin-2', 'common-plugin'], - isParallelExecution: () => true, - getFiles: () => ['confFile-1', 'confFile-2'], - getMaxReportLevelPrint: () => 13, + type: 'confFile-type', + format: 'yml', + name: 'confFile report output' }; + }, + getStore: () => { + return { confFileStore: 'yml', confFileKey: 'file report output' }; + }, + getPlugins: () => ['confFile-plugin', 'confFile-plugin-2', 'common-plugin'], + isParallelExecution: () => true, + getFiles: () => ['confFile-1', 'confFile-2'], + getMaxReportLevelPrint: () => 13 }; - + }; }); diff --git a/src/configurations/configuration.ts b/src/configurations/configuration.ts index bc57045a..e4e71f48 100644 --- a/src/configurations/configuration.ts +++ b/src/configurations/configuration.ts @@ -1,150 +1,151 @@ -import {CommandLineConfiguration} from './command-line-configuration'; -import {FileConfiguration} from './file-configuration'; -import {PublisherModel} from '../models/inputs/publisher-model'; -import {Logger} from '../loggers/logger'; -import {DynamicModulesManager} from '../plugins/dynamic-modules-manager'; -import {LogLevel} from '../loggers/log-level'; -import {prettifyJson} from '../outputs/prettify-json'; +import { CommandLineConfiguration } from './command-line-configuration'; +import { FileConfiguration } from './file-configuration'; +import { ActuatorModel } from '../models/inputs/actuator-model'; +import { Logger } from '../loggers/logger'; +import { DynamicModulesManager } from '../plugins/dynamic-modules-manager'; +import { LogLevel } from '../loggers/log-level'; +import { prettifyJson } from '../outputs/prettify-json'; process.setMaxListeners(30); export class Configuration { - private static instance: Configuration; - private static loaded: boolean = false; - - private parallel: boolean = false; - private files: string[] = []; - private logLevel: string = 'warn'; - private outputs: PublisherModel[] = []; - private maxReportLevelPrint: number = 1; - private store: any = {}; - private plugins: string[] = []; - private commandLineConfiguration: CommandLineConfiguration; - private showPassingTests: boolean = false; - - private constructor() { - this.commandLineConfiguration = new CommandLineConfiguration(process.argv); - const fileName = this.commandLineConfiguration.getConfigFileName(); - this.adjustFromFile(fileName); - this.adjustFromCommandLine(); - } - - public static getInstance(): Configuration { - if (Configuration.loaded === false) { - Configuration.loaded = true; - Configuration.instance = new Configuration(); - Logger.setLoggerLevel(LogLevel.buildFromString(Configuration.instance.getLogLevel())); - Configuration.instance.commandLineConfiguration.verifyPrematureActions(); - if (Configuration.instance.logLevel === 'trace') { - this.printConfiguration(); - } - } - return Configuration.instance; - } - - public getValues(): Configuration { - const copy = Object.assign({}, Configuration.instance); - delete copy.commandLineConfiguration; - return copy; - } - - public addPlugin(pluginName: string): boolean { - Logger.info(`Plugin added to the list: ${pluginName}`); - const plugins: Set = new Set(this.plugins); - plugins.add(pluginName); - this.plugins = Array.from(plugins.values()); - return DynamicModulesManager.getInstance().loadModuleExplicitly(pluginName); - } - - public isParallel(): boolean { - return this.parallel; - } - - public getShowPassingTests(): boolean { - return this.showPassingTests; - } - - public addFiles(...files: string[]): void { - this.files = this.files.concat(files); - } - - public getFiles(): string[] { - return this.files; - } - - public getLogLevel(): string { - return this.logLevel; - } - - public getOutputs(): PublisherModel[] { - return this.outputs; - } - - public setMaxReportLevelPrint(level: number) { - this.maxReportLevelPrint = level; - } - - public getMaxReportLevelPrint(): number { - return this.maxReportLevelPrint; - } - - public getStore(): any { - return this.store; - } - - public getPlugins(): string[] { - return this.plugins; - } - - public addPlugins(...plugins: string[]): void { - this.plugins = this.plugins.concat(plugins); - } - - private adjustFromCommandLine(): void { - this.files = this.files.concat(this.commandLineConfiguration.getTestFiles() || []); - - this.logLevel = this.commandLineConfiguration.getVerbosity() || this.logLevel; - this.plugins = [...new Set(this.plugins.concat(this.commandLineConfiguration.getPlugins() || []))]; - this.store = Object.assign({}, this.store, this.commandLineConfiguration.getStore()); - this.showPassingTests = this.commandLineConfiguration.getShowPassingTests(); - const filesIgnoringOthers = this.commandLineConfiguration.getTestFilesIgnoringOthers(); - if (filesIgnoringOthers && filesIgnoringOthers.length > 0) { - this.files = filesIgnoringOthers; - } - if (this.commandLineConfiguration.getStdoutRequisitionOutput()) { - this.outputs.push({type: 'standard-output', format: 'console', name: 'command line report output'}); - } - const fileMaxReportLevelPrint = this.commandLineConfiguration.getMaxReportLevelPrint(); + private static instance: Configuration; + private static loaded: boolean = false; + + private parallel: boolean = false; + private files: string[] = []; + private logLevel: string = 'warn'; + private outputs: ActuatorModel[] = []; + private maxReportLevelPrint: number = 1; + private store: any = {}; + private plugins: string[] = []; + private commandLineConfiguration: CommandLineConfiguration; + private showPassingTests: boolean = false; + private showExplicitTestsOnly: boolean = false; + + private constructor() { + this.commandLineConfiguration = new CommandLineConfiguration(process.argv); + const fileName = this.commandLineConfiguration.getConfigFileName(); + this.adjustFromFile(fileName); + this.adjustFromCommandLine(); + } + + public static getInstance(): Configuration { + if (Configuration.loaded === false) { + Configuration.loaded = true; + Configuration.instance = new Configuration(); + Logger.setLoggerLevel(LogLevel.buildFromString(Configuration.instance.getLogLevel())); + Configuration.instance.commandLineConfiguration.verifyPrematureActions(); + if (Configuration.instance.logLevel === 'trace') { + this.printConfiguration(); + } + } + return Configuration.instance; + } + + public getValues(): Configuration { + return Object.assign({}, Configuration.instance, { + commandLineConfiguration: undefined + }); + } + + public addPlugin(pluginName: string): boolean { + Logger.info(`Plugin added to the list: ${pluginName}`); + const plugins: Set = new Set(this.plugins); + plugins.add(pluginName); + this.plugins = Array.from(plugins.values()); + return DynamicModulesManager.getInstance().loadModuleExplicitly(pluginName); + } + + public isParallel(): boolean { + return this.parallel; + } + + public getShowPassingTests(): boolean { + return this.showPassingTests; + } + + public getShowExplicitTestsOnly(): boolean { + return this.showExplicitTestsOnly; + } + + public addFiles(...files: string[]): void { + this.files = this.files.concat(files); + } + + public getFiles(): string[] { + return this.files; + } + + public getLogLevel(): string { + return this.logLevel; + } + + public getOutputs(): ActuatorModel[] { + return this.outputs; + } + + public setMaxReportLevelPrint(level: number) { + this.maxReportLevelPrint = level; + } + + public getMaxReportLevelPrint(): number { + return this.maxReportLevelPrint; + } + + public getStore(): any { + return this.store; + } + + public getPlugins(): string[] { + return this.plugins; + } + + public addPlugins(...plugins: string[]): void { + this.plugins = this.plugins.concat(plugins); + } + + private adjustFromCommandLine(): void { + this.files = this.files.concat(this.commandLineConfiguration.getTestFiles() || []); + this.parallel = this.commandLineConfiguration.isParallelExecution() || this.parallel; + + this.logLevel = this.commandLineConfiguration.getVerbosity() || this.logLevel; + this.plugins = [...new Set(this.plugins.concat(this.commandLineConfiguration.getPlugins() || []))]; + this.store = Object.assign({}, this.store, this.commandLineConfiguration.getStore()); + this.showPassingTests = this.commandLineConfiguration.getShowPassingTests(); + this.showExplicitTestsOnly = this.commandLineConfiguration.getShowExplicitTestsOnly(); + if (this.commandLineConfiguration.getStdoutTaskOutput()) { + this.outputs.push({ + type: 'standard-output', + format: 'console', + name: 'command line report output' + }); + } + const fileMaxReportLevelPrint = this.commandLineConfiguration.getMaxReportLevelPrint(); + if (fileMaxReportLevelPrint !== undefined) { + this.maxReportLevelPrint = fileMaxReportLevelPrint; + } + } + + private adjustFromFile(filename?: string) { + if (filename !== undefined) { + const fileConfiguration = new FileConfiguration(filename); + if (fileConfiguration) { + this.parallel = fileConfiguration.isParallelExecution() || this.parallel; + this.logLevel = fileConfiguration.getLogLevel() || this.logLevel; + this.files = this.files.concat(fileConfiguration.getFiles()); + this.outputs = this.outputs.concat(fileConfiguration.getOutputs()); + this.plugins = this.plugins.concat(fileConfiguration.getPlugins()); + this.store = Object.assign({}, fileConfiguration.getStore(), this.store); + const fileMaxReportLevelPrint = fileConfiguration.getMaxReportLevelPrint(); if (fileMaxReportLevelPrint !== undefined) { - this.maxReportLevelPrint = fileMaxReportLevelPrint; - } - - } - - private adjustFromFile(filename?: string) { - if (filename !== undefined) { - try { - const fileConfiguration = new FileConfiguration(filename); - if (fileConfiguration) { - this.parallel = fileConfiguration.isParallelExecution() || this.parallel; - this.logLevel = fileConfiguration.getLogLevel() || this.logLevel; - this.files = this.files.concat(fileConfiguration.getFiles()); - this.outputs = this.outputs.concat(fileConfiguration.getOutputs()); - this.plugins = this.plugins.concat(fileConfiguration.getPlugins()); - this.store = Object.assign({}, fileConfiguration.getStore(), this.store); - const fileMaxReportLevelPrint = fileConfiguration.getMaxReportLevelPrint(); - if (fileMaxReportLevelPrint !== undefined) { - this.maxReportLevelPrint = fileMaxReportLevelPrint; - } - } - } catch (err) { - Logger.error(err); - } + this.maxReportLevelPrint = fileMaxReportLevelPrint; } + } } + } - private static printConfiguration() { - console.log(prettifyJson({configuration: this.getInstance().getValues()})); - } - + private static printConfiguration() { + console.log(prettifyJson({ configuration: this.getInstance().getValues() })); + } } diff --git a/src/configurations/file-configuration.test.ts b/src/configurations/file-configuration.test.ts index 9c3906e1..ff4febca 100644 --- a/src/configurations/file-configuration.test.ts +++ b/src/configurations/file-configuration.test.ts @@ -1,80 +1,78 @@ -import {FileConfiguration} from './file-configuration'; -import {YmlObjectParser} from '../object-parser/yml-object-parser'; +import { FileConfiguration } from './file-configuration'; +import { YmlObjectParser } from '../object-parser/yml-object-parser'; import * as fs from 'fs'; jest.mock('../object-parser/yml-object-parser'); jest.mock('fs'); +// @ts-expect-error fs.readFileSync.mockImplementation(() => 'fileContent'); describe('FileConfiguration', () => { + it('Throw error', () => { + // @ts-ignore + YmlObjectParser.mockImplementationOnce(() => { + return { + parse: () => { + throw 'error'; + } + }; + }); - it('Throw error', () => { - // @ts-ignore - YmlObjectParser.mockImplementationOnce(() => { - return { - parse: () => { - throw 'error'; - } - }; - }); + const filename = 'filename'; - const filename = 'filename'; + expect(() => new FileConfiguration(filename)).toThrow(); + }); - expect(() => new FileConfiguration(filename)).toThrow(); + it('get values', () => { + // @ts-expect-error + YmlObjectParser.mockImplementationOnce(() => { + return { + parse: () => { + return { + 'log-level': 'logEnqueuer', + outputs: ['outputs'], + parallel: true, + files: ['1', '2'], + plugins: ['plugin1', 'plugin2'], + maxReportLevelPrint: 10, + store: { + key: 'value', + otherKey: 123 + } + }; + } + }; }); + const fileConfiguration = new FileConfiguration('itDoesNotMatter'); - it('get values', () => { - YmlObjectParser.mockImplementationOnce(() => { - return { - parse: () => { - return { - 'log-level': 'logEnqueuer', - outputs: ['outputs'], - parallel: true, - files: ['1', '2'], - plugins: ['plugin1', 'plugin2'], - maxReportLevelPrint: 10, - store: { - key: 'value', - otherKey: 123 - } - }; - } - }; - }); - const fileConfiguration = new FileConfiguration('itDoesNotMatter'); - - expect(fileConfiguration.getOutputs()).toEqual(['outputs']); - expect(fileConfiguration.isParallelExecution()).toBeTruthy(); - expect(fileConfiguration.getFiles()).toEqual(['1', '2']); - expect(fileConfiguration.getMaxReportLevelPrint()).toBe(10); - expect(fileConfiguration.getPlugins()).toEqual(['plugin1', 'plugin2']); - expect(fileConfiguration.getStore()).toEqual( - { - key: 'value', - otherKey: 123 - }); + expect(fileConfiguration.getOutputs()).toEqual(['outputs']); + expect(fileConfiguration.isParallelExecution()).toBeTruthy(); + expect(fileConfiguration.getFiles()).toEqual(['1', '2']); + expect(fileConfiguration.getMaxReportLevelPrint()).toBe(10); + expect(fileConfiguration.getPlugins()).toEqual(['plugin1', 'plugin2']); + expect(fileConfiguration.getStore()).toEqual({ + key: 'value', + otherKey: 123 }); + }); - it('get default', () => { - // @ts-ignore - YmlObjectParser.mockImplementationOnce(() => { - return { - parse: () => { - return {}; - } - }; - }); - const fileConfiguration = new FileConfiguration('itDoesNotMatter'); - - expect(fileConfiguration.getOutputs()).toEqual([]); - expect(fileConfiguration.isParallelExecution()).toBeFalsy(); - expect(fileConfiguration.getFiles()).toEqual([]); - expect(fileConfiguration.getMaxReportLevelPrint()).toBeUndefined(); - expect(fileConfiguration.getPlugins()).toEqual([]); - expect(fileConfiguration.getStore()).toEqual({}); - + it('get default', () => { + // @ts-ignore + YmlObjectParser.mockImplementationOnce(() => { + return { + parse: () => { + return {}; + } + }; }); + const fileConfiguration = new FileConfiguration('itDoesNotMatter'); + expect(fileConfiguration.getOutputs()).toEqual([]); + expect(fileConfiguration.isParallelExecution()).toBeFalsy(); + expect(fileConfiguration.getFiles()).toEqual([]); + expect(fileConfiguration.getMaxReportLevelPrint()).toBeUndefined(); + expect(fileConfiguration.getPlugins()).toEqual([]); + expect(fileConfiguration.getStore()).toEqual({}); + }); }); diff --git a/src/configurations/file-configuration.ts b/src/configurations/file-configuration.ts index 37325fc2..150bef88 100644 --- a/src/configurations/file-configuration.ts +++ b/src/configurations/file-configuration.ts @@ -1,45 +1,45 @@ -import {PublisherModel} from '../models/inputs/publisher-model'; -import {YmlObjectParser} from '../object-parser/yml-object-parser'; +import { ActuatorModel } from '../models/inputs/actuator-model'; +import { YmlObjectParser } from '../object-parser/yml-object-parser'; import * as fs from 'fs'; export class FileConfiguration { - private readonly parsedFile: any; - - public constructor(filename: string) { - try { - const fileContent = fs.readFileSync(filename).toString(); - const ymlObjectParser = new YmlObjectParser(); - this.parsedFile = ymlObjectParser.parse(fileContent); - } catch (err) { - throw (`Error loading configuration file: ${err}`); - } - } + private readonly parsedFile: any; - public getLogLevel(): string { - return this.parsedFile['log-level']; + public constructor(filename: string) { + try { + const fileContent = fs.readFileSync(filename.trim()).toString(); + const ymlObjectParser = new YmlObjectParser(); + this.parsedFile = ymlObjectParser.parse(fileContent); + } catch (err) { + throw `Error loading configuration file [${filename}]: ${err}`; } + } - public getOutputs(): PublisherModel[] { - return this.parsedFile.outputs || []; - } + public getLogLevel(): string { + return this.parsedFile['log-level']; + } - public getStore(): any { - return this.parsedFile.store || {}; - } + public getOutputs(): ActuatorModel[] { + return this.parsedFile.outputs || []; + } - public getPlugins(): string[] { - return this.parsedFile.plugins || []; - } + public getStore(): any { + return this.parsedFile.store || {}; + } - public isParallelExecution(): boolean { - return !!this.parsedFile.parallel; - } + public getPlugins(): string[] { + return this.parsedFile.plugins || []; + } - public getFiles(): string[] { - return this.parsedFile.files || []; - } + public isParallelExecution(): boolean { + return !!this.parsedFile.parallel; + } - public getMaxReportLevelPrint(): number { - return this.parsedFile.maxReportLevelPrint; - } + public getFiles(): string[] { + return this.parsedFile.files || []; + } + + public getMaxReportLevelPrint(): number { + return this.parsedFile.maxReportLevelPrint; + } } diff --git a/src/configurations/file-content-map-creator.test.ts b/src/configurations/file-content-map-creator.test.ts index 29a4f989..50dbfbb7 100644 --- a/src/configurations/file-content-map-creator.test.ts +++ b/src/configurations/file-content-map-creator.test.ts @@ -1,196 +1,203 @@ -import {FileContentMapCreator} from './file-content-map-creator'; -import {DynamicModulesManager} from '../plugins/dynamic-modules-manager'; +import { FileContentMapCreator } from './file-content-map-creator'; +import { DynamicModulesManager } from '../plugins/dynamic-modules-manager'; import * as fs from 'fs'; jest.mock('../plugins/dynamic-modules-manager'); jest.mock('fs'); describe('FileContentMapCreator', () => { - - it('Handle exceptions', () => { - const readFileSyncMock = jest.fn(() => { - throw 'err'; - }); - // @ts-ignore - fs.readFileSync.mockImplementationOnce(readFileSyncMock); - - const tag = 'any'; - const filename = 'examples/file-content.any'; - const replaceableKey = tag + '://' + filename; - const requisition = {value: '<<' + replaceableKey + '>>'}; - - // @ts-ignore - const fileMap = new FileContentMapCreator(requisition); - - const expected: any = {}; - expected[replaceableKey] = 'err'; - expect(fileMap.getMap()).toEqual(expected); - expect(readFileSyncMock).toHaveBeenCalledWith(filename); + it('Handle exceptions', () => { + const readFileSyncMock = jest.fn(() => { + throw 'err'; }); - - it('Handle exceptions undefined field', () => { - const requisition = {value: undefined}; - - // @ts-ignore - expect(() => new FileContentMapCreator(requisition)).not.toThrow(); - + // @ts-ignore + fs.readFileSync.mockImplementationOnce(readFileSyncMock); + + const tag = 'any'; + const filename = 'examples/file-content.any'; + const replaceableKey = tag + '://' + filename; + const task = { value: '<<' + replaceableKey + '>>' }; + + // @ts-ignore + const fileMap = new FileContentMapCreator(task); + + const expected: any = {}; + expected[replaceableKey] = 'err'; + expect(fileMap.getMap()).toEqual(expected); + expect(readFileSyncMock).toHaveBeenCalledWith(filename); + }); + + it('Handle exceptions undefined field', () => { + const task = { value: undefined }; + + // @ts-ignore + expect(() => new FileContentMapCreator(task)).not.toThrow(); + }); + + it('Parse from file with right parser', () => { + const fileContent = 'fileContent'; + const readFileSyncMock = jest.fn(() => fileContent); + // @ts-ignore + fs.readFileSync.mockImplementationOnce(readFileSyncMock); + + const tag = 'tag'; + const filename = 'examples/file-content.tag'; + const replaceableKey = tag + '://' + filename; + const task = { value: '<<' + replaceableKey + '>>' }; + + // @ts-expect-error + DynamicModulesManager.getInstance.mockImplementation(() => { + return { + getObjectParserManager: () => { + return { + createParser: () => { + return { + parse: () => fileContent + }; + } + }; + } + }; }); - it('Parse from file with right parser', () => { - const fileContent = 'fileContent'; - const readFileSyncMock = jest.fn(() => fileContent); - // @ts-ignore - fs.readFileSync.mockImplementationOnce(readFileSyncMock); - - const tag = 'tag'; - const filename = 'examples/file-content.tag'; - const replaceableKey = tag + '://' + filename; - const requisition = {value: '<<' + replaceableKey + '>>'}; - - DynamicModulesManager.getInstance.mockImplementation(() => { - return { - getObjectParserManager: () => { - return { - createParser: () => { - return { - parse: () => fileContent - }; - } - }; + // @ts-ignore + const fileMap = new FileContentMapCreator(task); + + const expected: any = {}; + expected[replaceableKey] = fileContent; + expect(fileMap.getMap()).toEqual(expected); + expect(readFileSyncMock).toHaveBeenCalledWith(filename); + }); + + it('Load unknown tag as a regular file', () => { + const fileContent = 'fileContent'; + const readFileSync = jest.fn(() => Buffer.from(fileContent)); + // @ts-ignore + fs.readFileSync.mockImplementationOnce(readFileSync); + + const tag = 'unknown'; + const filename = 'examples/file-content.unknown'; + const replaceableKey = tag + '://' + filename; + const task = { value: '<<' + replaceableKey + '>>' }; + + // @ts-expect-error + DynamicModulesManager.getInstance.mockImplementation(() => { + return { + getObjectParserManager: () => { + return { + createParser: () => { + return { + parse: () => { + throw 'not parsed'; } - }; - }); - - // @ts-ignore - const fileMap = new FileContentMapCreator(requisition); - - const expected: any = {}; - expected[replaceableKey] = fileContent; - expect(fileMap.getMap()).toEqual(expected); - expect(readFileSyncMock).toHaveBeenCalledWith(filename); + }; + } + }; + } + }; }); - it('Load unknown tag as a regular file', () => { - const fileContent = 'fileContent'; - const readFileSync = jest.fn(() => Buffer.from(fileContent)); - // @ts-ignore - fs.readFileSync.mockImplementationOnce(readFileSync); - - const tag = 'unknown'; - const filename = 'examples/file-content.unknown'; - const replaceableKey = tag + '://' + filename; - const requisition = {value: '<<' + replaceableKey + '>>'}; - - DynamicModulesManager.getInstance.mockImplementation(() => { - return { - getObjectParserManager: () => { - return { - createParser: () => { - return { - parse: () => { - throw 'not parsed'; - } - }; - } - }; + // @ts-ignore + const fileMap = new FileContentMapCreator(task); + + const expected: any = {}; + expected[replaceableKey] = fileContent; + expect(fileMap.getMap()).toEqual(expected); + expect(readFileSync).toHaveBeenCalledWith(filename); + }); + + it('should parse query', () => { + const fileContent = 'fileContent'; + const readFileSync = jest.fn(() => Buffer.from(fileContent)); + let text; + let query; + + // @ts-ignore + fs.readFileSync.mockImplementationOnce(readFileSync); + + const task = { + value: '<>' + }; + + // @ts-expect-error + DynamicModulesManager.getInstance.mockImplementation(() => { + return { + getObjectParserManager: () => { + return { + createParser: (tag: string) => { + expect(tag).toBe('some'); + return { + parse: (receivedText: string, receivedQuery: any) => { + text = receivedText; + query = receivedQuery; } - }; - }); - - // @ts-ignore - const fileMap = new FileContentMapCreator(requisition); - - const expected: any = {}; - expected[replaceableKey] = fileContent; - expect(fileMap.getMap()).toEqual(expected); - expect(readFileSync).toHaveBeenCalledWith(filename); + }; + } + }; + } + }; }); - it('should parse query', () => { - const fileContent = 'fileContent'; - const readFileSync = jest.fn(() => Buffer.from(fileContent)); - let text; - let query; - - // @ts-ignore - fs.readFileSync.mockImplementationOnce(readFileSync); - - const requisition = {value: '<>'}; - - // @ts-ignore - DynamicModulesManager.getInstance.mockImplementation(() => { - return { - getObjectParserManager: () => { - return { - createParser: (tag: string) => { - expect(tag).toBe('some'); - return { - parse: (receivedText: string, receivedQuery: any) => { - text = receivedText; - query = receivedQuery; - } - }; - } - }; - } - }; - }); - - // @ts-ignore - new FileContentMapCreator(requisition).getMap(); - expect(text).toBe(fileContent); - expect(query).toEqual({delimiter: ';', header: 'false', other: true, tag: 'some', filename: 'filename'}); + // @ts-ignore + new FileContentMapCreator(task).getMap(); + expect(text).toBe(fileContent); + expect(query).toEqual({ + delimiter: ';', + header: 'false', + other: true, + tag: 'some', + filename: 'filename' }); - - it('Load each key just once', () => { - const fileContent = 'fileContent'; - const readFileSyncMock = jest.fn(() => Buffer.from(fileContent)); - // @ts-ignore - fs.readFileSync.mockImplementation(readFileSyncMock); - - const tag = 'tag'; - const filename = 'examples/file-content'; - const replaceableKey = tag + '://' + filename; - const requisition = { - value: '<<' + replaceableKey + '>>', - second: '{{' + replaceableKey + '}}', - third: '<<' + replaceableKey + '>>', - }; - - // @ts-ignore - DynamicModulesManager.getInstance.mockImplementation(() => { - return { - getObjectParserManager: () => { - return { - createParser: () => { - return { - parse: () => fileContent - }; - } - }; - } - }; - }); - - // @ts-ignore - const fileMap = new FileContentMapCreator(requisition); - - const expected: any = {}; - expected[replaceableKey] = fileContent; - expect(fileMap.getMap()).toEqual(expected); - expect(readFileSyncMock).toHaveBeenCalledWith(filename); - expect(readFileSyncMock).toHaveBeenCalledTimes(1); + }); + + it('Load each key just once', () => { + const fileContent = 'fileContent'; + const readFileSyncMock = jest.fn(() => Buffer.from(fileContent)); + // @ts-ignore + fs.readFileSync.mockImplementation(readFileSyncMock); + + const tag = 'tag'; + const filename = 'examples/file-content'; + const replaceableKey = tag + '://' + filename; + const task = { + value: '<<' + replaceableKey + '>>', + second: '{{' + replaceableKey + '}}', + third: '<<' + replaceableKey + '>>' + }; + + // @ts-ignore + DynamicModulesManager.getInstance.mockImplementation(() => { + return { + getObjectParserManager: () => { + return { + createParser: () => { + return { + parse: () => fileContent + }; + } + }; + } + }; }); - it('Handle empty matches', () => { - const requisition = { - key: '((I am not a file))', - }; + // @ts-ignore + const fileMap = new FileContentMapCreator(task); - // @ts-ignore - const fileMap = new FileContentMapCreator(requisition); + const expected: any = {}; + expected[replaceableKey] = fileContent; + expect(fileMap.getMap()).toEqual(expected); + expect(readFileSyncMock).toHaveBeenCalledWith(filename); + expect(readFileSyncMock).toHaveBeenCalledTimes(1); + }); - const expected: any = {}; - expect(fileMap.getMap()).toEqual(expected); - }); + it('Handle empty matches', () => { + const task = { + key: '((I am not a file))' + }; + + // @ts-ignore + const fileMap = new FileContentMapCreator(task); + const expected: any = {}; + expect(fileMap.getMap()).toEqual(expected); + }); }); diff --git a/src/configurations/file-content-map-creator.ts b/src/configurations/file-content-map-creator.ts index a514f66c..6e7fe4ef 100644 --- a/src/configurations/file-content-map-creator.ts +++ b/src/configurations/file-content-map-creator.ts @@ -1,96 +1,100 @@ -import {Logger} from '../loggers/logger'; -import {RequisitionModel} from '../models/inputs/requisition-model'; -import {DynamicModulesManager} from '../plugins/dynamic-modules-manager'; +import { Logger } from '../loggers/logger'; +import { TaskModel } from '../models/inputs/task-model'; +import { DynamicModulesManager } from '../plugins/dynamic-modules-manager'; import * as fs from 'fs'; -import {ObjectParser} from '../object-parser/object-parser'; +import { ObjectParser } from '../object-parser/object-parser'; export class FileContentMapCreator { + private map: any = {}; - private map: any = {}; + public constructor(value: TaskModel) { + this.checkChildren(value); + } - public constructor(value: RequisitionModel) { - this.checkChildren(value); - } + public getMap(): {} { + return this.map; + } - public getMap(): {} { - return this.map; + private checkChildren(node: any): void { + for (const key in node) { + const attribute = node[key]; + if (typeof attribute === 'object') { + this.checkChildren(attribute); + } else if (attribute !== undefined) { + this.findTags(JSON.stringify(attribute)); + } } + } - private checkChildren(node: any): void { - for (const key in node) { - const attribute = node[key]; - if (typeof attribute === 'object') { - this.checkChildren(attribute); - } else if (attribute !== undefined) { - this.findTags(JSON.stringify(attribute)); - } - } - } + private findTags(node: string) { + const angleBrackets = /<<[\w\s]+:\/\/[^>>]+>>/g; + const curlyBrackets = /{{[\w\s]+:\/\/[^}}]+}}/g; + const curly: string[] = node.match(curlyBrackets) || []; + const angle: string[] = node.match(angleBrackets) || []; + angle.concat(...curly).forEach((value: string) => { + const placeholderContent: string = value.substring(2, value.length - 2); + this.map[placeholderContent] = this.insertIntoMap(placeholderContent); + }); + } - private findTags(node: string) { - const angleBrackets = /<<[\w\s]+:\/\/[^>>]+>>/g; - const curlyBrackets = /{{[\w\s]+:\/\/[^}}]+}}/g; - const match = (node.match(angleBrackets) || []).concat(node.match(curlyBrackets) || []); - match.forEach((value: string) => { - const key: string = value.substr(2, value.length - 4); - this.map[key] = this.insertIntoMap(key); - }); - } - - private insertIntoMap(key: string): object | string { - if (!this.map[key]) { - try { - const query = this.parsePlaceHolder(key); - const fileContent = fs.readFileSync(query.filename).toString(); - const objectParser = DynamicModulesManager.getInstance().getObjectParserManager().createParser(query.tag); - if (objectParser !== undefined) { - Logger.trace(`Trying to parse content as '${query.tag}' parser`); - return this.getValue(objectParser, fileContent, query); - } - return fileContent; - } catch (err) { - Logger.warning(err.toString()); - return err.toString(); - } + private insertIntoMap(key: string): object | string { + if (!this.map[key]) { + try { + const options = this.parsePlaceholder(key); + const fileContent = fs.readFileSync(options.filename).toString(); + const objectParser = DynamicModulesManager.getInstance().getObjectParserManager().createParser(options.tag); + if (objectParser !== undefined) { + Logger.trace(`Trying to parse content as '${options.tag}' parser`); + return this.getValue(objectParser, fileContent, options); } - return this.map[key]; + return fileContent; + } catch (err) { + Logger.warning('FileContentMapCreator: ' + err); + return '' + err; + } } + return this.map[key]; + } - private parsePlaceHolder(key: string) { - const separator: string = '://'; - const separatorIndex = key.indexOf(separator); - const tag = key.substring(0, separatorIndex); - const afterDoubleSlash = key.substring(separatorIndex + 3); - const parseQuery = this.parseQuery(afterDoubleSlash); - parseQuery.tag = tag; - return parseQuery; - } + private parsePlaceholder(key: string): { + tag: string; + filename: string; + [propname: string]: string; + } { + const separator: string = '://'; + const separatorIndex = key.indexOf(separator); + const tag = key.substring(0, separatorIndex); + const afterDoubleSlash = key.substring(separatorIndex + 3); + const parseQuery = this.parseQuery(afterDoubleSlash); + parseQuery.tag = tag; + return parseQuery; + } - private parseQuery(tag: string) { - const strings = tag.split('?'); - const query: any = { - filename: strings[0] - }; - if (strings.length > 1) { - const pairs = strings[1].split('&'); - for (let i = 0; i < pairs.length; i++) { - const pair = pairs[i].split('='); - let value: any = pair[1]; - if (value === undefined) { - value = true; - } - query[pair[0]] = value; - } + private parseQuery(tag: string) { + const strings = tag.split('?'); + const query: any = { + filename: strings[0] + }; + if (strings.length > 1) { + const pairs = strings[1].split('&'); + for (let i = 0; i < pairs.length; i++) { + const pair = pairs[i].split('='); + let value: any = pair[1]; + if (value === undefined) { + value = true; } - return query; + query[pair[0]] = value; + } } + return query; + } - private getValue(objectParser: ObjectParser, fileContent: string, query: any): object | string { - try { - return objectParser.parse(fileContent, query); - } catch (err) { - Logger.error(err.toString()); - return fileContent; - } + private getValue(objectParser: ObjectParser, fileContent: string, query: any): object | string { + try { + return objectParser.parse(fileContent, query); + } catch (err) { + Logger.error('' + err); + return fileContent; } + } } diff --git a/src/configurations/store.test.ts b/src/configurations/store.test.ts index 2ce0a950..9b132176 100644 --- a/src/configurations/store.test.ts +++ b/src/configurations/store.test.ts @@ -1,87 +1,85 @@ -import {Store} from './store'; -import {Configuration} from './configuration'; +import { Store } from './store'; +import { Configuration } from './configuration'; jest.mock('./configuration'); describe('Store', () => { - - beforeEach(() => { - // @ts-ignore - Store.data = undefined; + beforeEach(() => { + // @ts-ignore + Store.data = undefined; + }); + + it('Catch getData exception', () => { + // @ts-ignore + Configuration.getInstance.mockImplementationOnce(() => { + throw `err`; }); - it('Catch getData exception', () => { - // @ts-ignore - Configuration.getInstance.mockImplementationOnce(() => { - throw `err`; - }); - - expect(() => Store.getData()).not.toThrow(); + expect(() => Store.getData()).not.toThrow(); + }); + + it('should get data from configuration', () => { + // @ts-ignore + Configuration.getInstance.mockImplementationOnce(() => { + return { + getStore: () => { + return { + key: 'value' + }; + } + }; }); - it('should get data from configuration', () => { - // @ts-ignore - Configuration.getInstance.mockImplementationOnce(() => { - return { - getStore: () => { - return { - key: 'value' - }; - } - }; - }); - - expect(Store.getData().key).toBe('value'); + expect(Store.getData().key).toBe('value'); + }); + + it('should refresh data', () => { + // @ts-ignore + Configuration.getInstance.mockImplementationOnce(() => { + return { + getStore: () => { + return {}; + } + }; }); - it('should refresh data', () => { - // @ts-ignore - Configuration.getInstance.mockImplementationOnce(() => { - return { - getStore: () => { - return {}; - } - }; - }); + Store.getData().key = 5; + expect(Store.getData().key).toBe(5); - Store.getData().key = 5; - expect(Store.getData().key).toBe(5); + Store.refreshData(); - Store.refreshData(); + expect(Store.getData().key).toBeUndefined(); + }); - expect(Store.getData().key).toBeUndefined(); + it('data call just once', () => { + const getValuesMock = jest.fn(() => { + return { + getStore: () => { + return {}; + } + }; }); - - it('data call just once', () => { - const getValuesMock = jest.fn(() => { - return { - getStore: () => { - return {}; - } - }; - }); - // @ts-ignore - Configuration.getInstance.mockImplementationOnce(getValuesMock); - - Store.getData(); - Store.getData(); - Store.getData(); - - expect(getValuesMock).toBeCalledTimes(1); - }); - - it('should get env var', () => { - const getValuesMock = jest.fn(() => { - return { - getStore: () => { - return {}; - } - }; - }); - // @ts-ignore - Configuration.getInstance.mockImplementationOnce(getValuesMock); - - expect(Store.getData().PATH).toBeDefined(); + // @ts-ignore + Configuration.getInstance.mockImplementationOnce(getValuesMock); + + Store.getData(); + Store.getData(); + Store.getData(); + + expect(getValuesMock).toBeCalledTimes(1); + }); + + it('should get env var', () => { + const getValuesMock = jest.fn(() => { + return { + getStore: () => { + return {}; + } + }; }); + // @ts-ignore + Configuration.getInstance.mockImplementationOnce(getValuesMock); + expect(Store.getData().PATH).toBeDefined(); + }); }); diff --git a/src/configurations/store.ts b/src/configurations/store.ts index 8808a40c..6da49903 100644 --- a/src/configurations/store.ts +++ b/src/configurations/store.ts @@ -1,28 +1,28 @@ -import {Configuration} from './configuration'; -import {Logger} from '../loggers/logger'; +import { Configuration } from './configuration'; +import { Logger } from '../loggers/logger'; export class Store { - private static data: any = undefined; + private static data: any = undefined; - private constructor() { - //private - } + private constructor() { + //private + } - public static getData(): any { - if (Store.data === undefined) { - try { - Store.data = process.env; - const configurationStore = Configuration.getInstance().getStore(); - Store.data = Object.assign({}, configurationStore, Store.data); - } catch (err) { - Logger.warning(err); - } - } - return Store.data; + public static getData(): any { + if (Store.data === undefined) { + try { + Store.data = process.env; + const configurationStore = Configuration.getInstance().getStore(); + Store.data = Object.assign({}, configurationStore, Store.data); + } catch (err) { + Logger.warning(`Store errored: ` + err); + } } + return Store.data; + } - public static refreshData(): any { - Store.data = undefined; - return Store.getData(); - } + public static refreshData(): any { + Store.data = undefined; + return Store.getData(); + } } diff --git a/src/dynamic-functions/dynamic-function-controller.test.ts b/src/dynamic-functions/dynamic-function-controller.test.ts index 816d2d9f..a9f3f0d2 100644 --- a/src/dynamic-functions/dynamic-function-controller.test.ts +++ b/src/dynamic-functions/dynamic-function-controller.test.ts @@ -1,40 +1,37 @@ -import {DynamicFunctionController} from './dynamic-function-controller'; +import { DynamicFunctionController } from './dynamic-function-controller'; describe('DynamicFunctionController', () => { + it('Should add argument and pass it to the function', () => { + const testerExecutor: DynamicFunctionController = new DynamicFunctionController('name.value++;'); + const arg = { value: 2 }; - it('Should add argument and pass it to the function', () => { - const testerExecutor: DynamicFunctionController = new DynamicFunctionController('name.value++;'); - const arg = {value: 2}; + testerExecutor.addArgument('name', arg); + testerExecutor.execute(); - testerExecutor.addArgument('name', arg); - testerExecutor.execute(); + expect(arg.value).toBe(3); + }); - expect(arg.value).toBe(3); - }); + it('Should bind this', done => { + const self = { + method(value: number) { + expect(value).toBe(3); + done(); + } + }; + const testerExecutor: DynamicFunctionController = new DynamicFunctionController('this.method(3);', self); - it('Should bind this', done => { - const self = { - method(value: number) { - expect(value).toBe(3); - done(); - } - }; - const testerExecutor: DynamicFunctionController = new DynamicFunctionController('this.method(3);', self); + testerExecutor.execute(); + }); - testerExecutor.execute(); - }); + it('Should throw function creation error', () => { + const testerExecutor: DynamicFunctionController = new DynamicFunctionController('inv;alid statement'); - it('Should throw function creation error', () => { - const testerExecutor: DynamicFunctionController = new DynamicFunctionController('inv;alid statement'); + expect(() => testerExecutor.execute()).toThrow(); + }); - expect(() => testerExecutor.execute()).toThrow(); - }); - - it('Should throw function execution error', () => { - const testerExecutor: DynamicFunctionController = new DynamicFunctionController('notDefined++'); - - expect(() => testerExecutor.execute()).toThrow(); - }); + it('Should throw function execution error', () => { + const testerExecutor: DynamicFunctionController = new DynamicFunctionController('notDefined++'); + expect(() => testerExecutor.execute()).toThrow(); + }); }); - diff --git a/src/dynamic-functions/dynamic-function-controller.ts b/src/dynamic-functions/dynamic-function-controller.ts index f5acfca3..bfe5ec8d 100644 --- a/src/dynamic-functions/dynamic-function-controller.ts +++ b/src/dynamic-functions/dynamic-function-controller.ts @@ -1,45 +1,43 @@ -import {Logger} from '../loggers/logger'; +import { Logger } from '../loggers/logger'; export class DynamicFunctionController { - - private readonly functionBody: string; - private readonly thisArg: any; - private arguments: { name: string, value: any }[] = []; - - public constructor(functionBody: string, thisArg: any = null) { - this.functionBody = functionBody; - this.thisArg = thisArg; - this.addArgument('require', require); - this.addArgument('Logger', Logger); - } - - public addArgument(name: string, value: any): void { - this.arguments.push({name: name, value: value}); - } - - public execute(): any { - const createdFunction = this.createFunction(); - return this.executeFunction(createdFunction); + private readonly functionBody: string; + private readonly thisArg: any; + private arguments: { name: string; value: any }[] = []; + + public constructor(functionBody: string, thisArg: any = null) { + this.functionBody = functionBody; + this.thisArg = thisArg; + this.addArgument('require', require); + this.addArgument('Logger', Logger); + } + + public addArgument(name: string, value: any): void { + this.arguments.push({ name: name, value: value }); + } + + public execute(): any { + const createdFunction = this.createFunction(); + return this.executeFunction(createdFunction); + } + + private createFunction(): Function { + try { + const constructorArgs = this.arguments.map(arg => arg.name).concat(this.functionBody); + return ((...args: string[]) => new Function(...args)).apply(this.thisArg, constructorArgs); + } catch (err) { + Logger.error(`Error creating function '${err}'`); + throw err; } - - private createFunction(): Function { - try { - const constructorArgs = this.arguments.map(arg => arg.name).concat(this.functionBody); - return ((...args: string[]) => new Function(...args)).apply(this.thisArg, constructorArgs); - } catch (err) { - Logger.error(`Error creating function '${err}'`); - throw err; - } + } + + private executeFunction(dynamicFunction: Function): any { + try { + const callArgs = this.arguments.map(arg => arg.value); + return dynamicFunction.apply(this.thisArg, callArgs); + } catch (err) { + Logger.error(`Error running function '${err}'}`); + throw err; } - - private executeFunction(dynamicFunction: Function): any { - try { - const callArgs = this.arguments.map(arg => arg.value); - return dynamicFunction.apply(this.thisArg, callArgs); - } catch (err) { - Logger.error(`Error running function '${err}'}`); - throw err; - } - } - + } } diff --git a/src/enqueuer-as-node-child-runner.test.ts b/src/enqueuer-as-node-child-runner.test.ts index 77df38f7..1e33bad3 100644 --- a/src/enqueuer-as-node-child-runner.test.ts +++ b/src/enqueuer-as-node-child-runner.test.ts @@ -1,19 +1,20 @@ -import {NotificationEmitter} from './notifications/notification-emitter'; -import {EnqueuerAsNodeChildRunner} from './enqueuer-as-node-child-runner'; -import {Notifications} from './notifications/notifications'; -import {ChildRequisitionRunner} from './run-as-child/child-requisition-runner'; -import {AssertDescriber} from './run-as-child/assert-describer'; -import {ModuleAdder} from './run-as-child/module-adder'; -import {ProtocolDescriber} from './run-as-child/protocol-describer'; -import {StoreCleaner} from './run-as-child/store-cleaner'; - +import { NotificationEmitter } from './notifications/notification-emitter'; +import { EnqueuerAsNodeChildRunner } from './enqueuer-as-node-child-runner'; +import { Notifications } from './notifications/notifications'; +import { ChildTaskRunner } from './run-as-child/child-task-runner'; +import { AssertDescriber } from './run-as-child/assert-describer'; +import { ModuleAdder } from './run-as-child/module-adder'; +import { ProtocolDescriber } from './run-as-child/protocol-describer'; +import { StoreCleaner } from './run-as-child/store-cleaner'; +import { StoreSetter } from './run-as-child/store-setter'; jest.mock('./notifications/notification-emitter'); -jest.mock('./run-as-child/child-requisition-runner'); +jest.mock('./run-as-child/child-task-runner'); jest.mock('./run-as-child/assert-describer'); jest.mock('./run-as-child/module-adder'); jest.mock('./run-as-child/protocol-describer'); jest.mock('./run-as-child/store-cleaner'); +jest.mock('./run-as-child/store-setter'); const processOnMock = jest.fn(); process.on = processOnMock; @@ -22,9 +23,9 @@ const processSendMock = jest.fn(); process.send = processSendMock; const replierProcessMock = jest.fn(); -const replierConstructorMock = jest.fn(() => ({process: replierProcessMock})); +const replierConstructorMock = jest.fn(() => ({ process: replierProcessMock })); // @ts-ignore -ChildRequisitionRunner.mockImplementation(replierConstructorMock); +ChildTaskRunner.mockImplementation(replierConstructorMock); // @ts-ignore AssertDescriber.mockImplementation(replierConstructorMock); // @ts-ignore @@ -33,121 +34,121 @@ ModuleAdder.mockImplementation(replierConstructorMock); ProtocolDescriber.mockImplementation(replierConstructorMock); // @ts-ignore StoreCleaner.mockImplementation(replierConstructorMock); +// @ts-ignore +StoreSetter.mockImplementation(replierConstructorMock); let notificationEmitterOnMock = jest.fn(); // @ts-ignore NotificationEmitter.on.mockImplementation(notificationEmitterOnMock); describe('EnqueuerAsNodeChildRunner', () => { - beforeEach(() => { - processOnMock.mockClear(); - processSendMock.mockClear(); - replierConstructorMock.mockClear(); - replierProcessMock.mockClear(); - notificationEmitterOnMock.mockClear(); + beforeEach(() => { + processOnMock.mockClear(); + processSendMock.mockClear(); + replierConstructorMock.mockClear(); + replierProcessMock.mockClear(); + notificationEmitterOnMock.mockClear(); + }); + + it('should listen to message', async () => { + const enqueuerAsNodeChildRunner = new EnqueuerAsNodeChildRunner(); + const statusCode = await enqueuerAsNodeChildRunner.execute(); + + expect(statusCode).toBe(0); + expect(processOnMock.mock.calls[0][0]).toBe('message'); + }); + + it('should send exit message', async () => { + const statusCode = 0; + await new EnqueuerAsNodeChildRunner().execute(); + + expect(processOnMock.mock.calls[1][0]).toBe('exit'); + + const onExitCallback = processOnMock.mock.calls[1][1]; + onExitCallback(statusCode); + + expect(processSendMock).toHaveBeenCalledWith({ + event: 'PROCESS_EXIT', + value: statusCode }); + }); - it('should listen to message', async () => { - const enqueuerAsNodeChildRunner = new EnqueuerAsNodeChildRunner(); - const statusCode = await enqueuerAsNodeChildRunner.execute(); + it('should do nothing when message is unknown', async () => { + const message = { event: 'UNKNOWN' }; - expect(statusCode).toBe(0); - expect(processOnMock.mock.calls[0][0]).toBe('message'); - }); + await new EnqueuerAsNodeChildRunner().execute(); - it('should send exit message', async () => { - const statusCode = 0; - await new EnqueuerAsNodeChildRunner().execute(); + const onMessageCallback = processOnMock.mock.calls[0][1]; + onMessageCallback(message); + expect(replierProcessMock).not.toHaveBeenCalled(); + }); - expect(processOnMock.mock.calls[1][0]).toBe('exit'); + it('should add modules list when requested', async () => { + const message = { event: 'ADD_MODULE', value: 'value' }; - const onExitCallback = processOnMock.mock.calls[1][1]; - onExitCallback(statusCode); + await new EnqueuerAsNodeChildRunner().execute(); - expect(processSendMock).toHaveBeenCalledWith({ - event: 'PROCESS_EXIT', - value: statusCode - }); - }); + const onMessageCallback = processOnMock.mock.calls[0][1]; + onMessageCallback(message); + expect(replierConstructorMock).toHaveBeenCalled(); + expect(replierProcessMock).toHaveBeenCalledWith(message); + }); - it('should do nothing when message is unknown', async () => { - const message = {event: 'UNKNOWN'}; + it('should get protocols list when requested', async () => { + const message = { event: 'GET_PROTOCOLS', value: 'value' }; - await new EnqueuerAsNodeChildRunner().execute(); + await new EnqueuerAsNodeChildRunner().execute(); - const onMessageCallback = processOnMock.mock.calls[0][1]; - onMessageCallback(message); - expect(replierProcessMock).not.toHaveBeenCalled(); - }); + const onMessageCallback = processOnMock.mock.calls[0][1]; + onMessageCallback(message); + expect(replierConstructorMock).toHaveBeenCalled(); + expect(replierProcessMock).toHaveBeenCalledWith(message); + }); - it('should add modules list when requested', async () => { - const message = {event: 'ADD_MODULE', value: 'value'}; + it('should get asserters list when requested', async () => { + const message = { event: 'GET_ASSERTERS', value: 'value' }; - await new EnqueuerAsNodeChildRunner().execute(); + await new EnqueuerAsNodeChildRunner().execute(); - const onMessageCallback = processOnMock.mock.calls[0][1]; - onMessageCallback(message); - expect(replierConstructorMock).toHaveBeenCalled(); - expect(replierProcessMock).toHaveBeenCalledWith(message); - }); + const onMessageCallback = processOnMock.mock.calls[0][1]; + onMessageCallback(message); + expect(replierConstructorMock).toHaveBeenCalled(); + expect(replierProcessMock).toHaveBeenCalledWith(message); + }); - it('should get protocols list when requested', async () => { - const message = {event: 'GET_PROTOCOLS', value: 'value'}; + it('should run enqueuer runner when a message arrives', async () => { + const message = { event: 'RUN_REQUISITION', value: 'value' }; - await new EnqueuerAsNodeChildRunner().execute(); + await new EnqueuerAsNodeChildRunner().execute(); - const onMessageCallback = processOnMock.mock.calls[0][1]; - onMessageCallback(message); - expect(replierConstructorMock).toHaveBeenCalled(); - expect(replierProcessMock).toHaveBeenCalledWith(message); - }); + const onMessageCallback = processOnMock.mock.calls[0][1]; + onMessageCallback(message); + expect(replierConstructorMock).toHaveBeenCalled(); + expect(replierProcessMock).toHaveBeenCalledWith(message); + }); - it('should get asserters list when requested', async () => { - const message = {event: 'GET_ASSERTERS', value: 'value'}; + it('should register senders to every notification', async () => { + await new EnqueuerAsNodeChildRunner().execute(); - await new EnqueuerAsNodeChildRunner().execute(); + const everyNotificationKey = Object.keys(Notifications) + .map((key: any) => Notifications[key]) + .filter((key: any) => typeof key === 'number'); - const onMessageCallback = processOnMock.mock.calls[0][1]; - onMessageCallback(message); - expect(replierConstructorMock).toHaveBeenCalled(); - expect(replierProcessMock).toHaveBeenCalledWith(message); - }); + expect(notificationEmitterOnMock.mock.calls.map((call: any) => call[0])).toEqual(everyNotificationKey); + }); - it('should run enqueuer runner when a message arrives', async () => { - const message = {event: 'RUN_REQUISITION', value: 'value'}; + it('should proxy message when notification is emitted', async () => { + await new EnqueuerAsNodeChildRunner().execute(); - await new EnqueuerAsNodeChildRunner().execute(); + const report = { cycle: {} }; + report.cycle = report; - const onMessageCallback = processOnMock.mock.calls[0][1]; - onMessageCallback(message); - expect(replierConstructorMock).toHaveBeenCalled(); - expect(replierProcessMock).toHaveBeenCalledWith(message); + notificationEmitterOnMock.mock.calls[0][1]({ report }); + expect(processSendMock).toHaveBeenCalledWith({ + event: 'REQUISITION_FINISHED', + value: { + report: {} + } }); - - it('should register senders to every notification', async () => { - await new EnqueuerAsNodeChildRunner().execute(); - - const everyNotificationKey = Object.keys(Notifications) - .map((key: any) => Notifications[key]) - .filter((key: any) => typeof key === 'number'); - - expect(notificationEmitterOnMock.mock.calls.map((call: any) => call[0])) - .toEqual(everyNotificationKey); - }); - - it('should proxy message when notification is emitted', async () => { - await new EnqueuerAsNodeChildRunner().execute(); - - const report = {cycle: {}}; - report.cycle = report; - - notificationEmitterOnMock.mock.calls[0][1]({report}); - expect(processSendMock).toHaveBeenCalledWith({ - event: 'REQUISITION_FINISHED', - value: { - report: {} - } - }); - }); - + }); }); diff --git a/src/enqueuer-as-node-child-runner.ts b/src/enqueuer-as-node-child-runner.ts index ff910d9f..f995cfb3 100644 --- a/src/enqueuer-as-node-child-runner.ts +++ b/src/enqueuer-as-node-child-runner.ts @@ -1,72 +1,73 @@ -import {NotificationEmitter} from './notifications/notification-emitter'; -import {ObjectDecycler} from './object-parser/object-decycler'; -import {Notifications} from './notifications/notifications'; -import {ParentReplier} from './run-as-child/parent-replier'; -import {ChildReceivingEvents} from './run-as-child/child-receiving-events'; -import {ChildRequisitionRunner} from './run-as-child/child-requisition-runner'; -import {ModuleAdder} from './run-as-child/module-adder'; -import {ProtocolDescriber} from './run-as-child/protocol-describer'; -import {AssertDescriber} from './run-as-child/assert-describer'; -import {StoreCleaner} from './run-as-child/store-cleaner'; -import {ChildSendingEvents} from './run-as-child/child-sending-events'; +import { Logger } from './loggers/logger'; +import { ModuleAdder } from './run-as-child/module-adder'; +import { StoreSetter } from './run-as-child/store-setter'; +import { StoreCleaner } from './run-as-child/store-cleaner'; +import { ParentReplier } from './run-as-child/parent-replier'; +import { Notifications } from './notifications/notifications'; +import { ObjectDecycler } from './object-parser/object-decycler'; +import { AssertDescriber } from './run-as-child/assert-describer'; +import { ProtocolDescriber } from './run-as-child/protocol-describer'; +import { ChildSendingEvents } from './run-as-child/child-sending-events'; +import { NotificationEmitter } from './notifications/notification-emitter'; +import { ChildReceivingEvents } from './run-as-child/child-receiving-events'; +import { ChildTaskRunner } from './run-as-child/child-task-runner'; export class EnqueuerAsNodeChildRunner { - private readonly processors: { - [key: string]: ParentReplier - }; - - public constructor() { - this.processors = { - [ChildReceivingEvents.ADD_MODULE]: new ModuleAdder(), - [ChildReceivingEvents.CLEAN_STORE]: new StoreCleaner(), - [ChildReceivingEvents.GET_ASSERTERS]: new AssertDescriber(), - [ChildReceivingEvents.GET_PROTOCOLS]: new ProtocolDescriber(), - [ChildReceivingEvents.RUN_REQUISITION]: new ChildRequisitionRunner(), - }; - } - - public execute(): Promise { - console.log('Child is rocking and rolling'); - this.registerInternProxies(); - this.registerListeners(); - return new Promise(resolve => { - if (process.env.NODE_ENV && process.env.NODE_ENV.toUpperCase() === 'TEST') { - resolve(0); - } - }); - } + private readonly processors: { + [key: string]: ParentReplier; + }; - private registerInternProxies() { - Object.keys(Notifications) - .map((key: any) => Notifications[key]) - .filter((key: any) => typeof key === 'number') - .forEach((notificationNameIndex: any) => { - const notification = Notifications[notificationNameIndex]; - // @ts-ignore - NotificationEmitter.on(notificationNameIndex, (report: any) => { - process.send!( - { - event: notification, - value: new ObjectDecycler().decycle(report) - }); - }); + public constructor() { + this.processors = { + [ChildReceivingEvents.ADD_MODULE]: new ModuleAdder(), + [ChildReceivingEvents.CLEAN_STORE]: new StoreCleaner(), + [ChildReceivingEvents.SET_STORE]: new StoreSetter(), + [ChildReceivingEvents.GET_ASSERTERS]: new AssertDescriber(), + [ChildReceivingEvents.GET_PROTOCOLS]: new ProtocolDescriber(), + [ChildReceivingEvents.RUN_REQUISITION]: new ChildTaskRunner() + }; + } - }); - } + public execute(): Promise { + Logger.info('Enqueuer is rocking and rolling'); + this.registerInternProxies(); + this.registerListeners(); + return new Promise(resolve => { + if (process.env.NODE_ENV && process.env.NODE_ENV.toUpperCase() === 'TEST') { + resolve(0); + } + }); + } - public registerListeners(): void { - process.on('message', async message => { - const processor = this.processors[message.event]; - if (processor) { - await processor.process(message); - } - }); - process.on('exit', (code) => { - process.send!({ - event: ChildSendingEvents.PROCESS_EXIT, - value: code - }); + private registerInternProxies() { + Object.keys(Notifications) + .map((key: any) => Notifications[key]) + .filter((key: any) => typeof key === 'number') + .forEach((notificationNameIndex: any) => { + const notification = Notifications[notificationNameIndex]; + // @ts-ignore + NotificationEmitter.on(notificationNameIndex, (report: any) => { + process.send!({ + event: notification, + value: new ObjectDecycler().decycle(report) + }); }); - } + }); + } + public registerListeners(): void { + process.on('message', async (message: { event: string }) => { + Logger.debug(`Received from parent: ${message.event}: ${JSON.stringify(message)}`); + const processor = this.processors[message.event]; + if (processor) { + await processor.process(message); + } + }); + process.on('exit', code => { + process.send!({ + event: ChildSendingEvents.PROCESS_EXIT, + value: code + }); + }); + } } diff --git a/src/enqueuer-runner.test.ts b/src/enqueuer-runner.test.ts index b98058bf..404ba57e 100644 --- a/src/enqueuer-runner.test.ts +++ b/src/enqueuer-runner.test.ts @@ -1,119 +1,119 @@ -import {EnqueuerRunner} from './enqueuer-runner'; -import {Configuration} from './configurations/configuration'; -import {RequisitionFilePatternParser} from './requisition-runners/requisition-file-pattern-parser'; -import {RequisitionRunner} from './requisition-runners/requisition-runner'; -import {SummaryTestOutput} from './outputs/summary-test-output'; -import {NotificationEmitter} from './notifications/notification-emitter'; -import {Logger} from './loggers/logger'; -import {LogLevel} from './loggers/log-level'; -import {Notifications} from './notifications/notifications'; +import { EnqueuerRunner } from './enqueuer-runner'; +import { Configuration } from './configurations/configuration'; +import { TaskFilePatternParser } from './task-runners/task-file-pattern-parser'; +import { TaskRunner } from './task-runners/task-runner'; +import { SummaryTestOutput } from './outputs/summary-test-output'; +import { NotificationEmitter } from './notifications/notification-emitter'; +import { Logger } from './loggers/logger'; +import { LogLevel } from './loggers/log-level'; +import { Notifications } from './notifications/notifications'; jest.mock('./outputs/summary-test-output'); jest.mock('./configurations/configuration'); -jest.mock('./requisition-runners/requisition-file-pattern-parser'); -jest.mock('./requisition-runners/requisition-runner'); +jest.mock('./task-runners/task-file-pattern-parser'); +jest.mock('./task-runners/task-runner'); jest.mock('./loggers/logger'); const loggerLevel = 'enqueuer-starter-level'; describe('EnqueuerRunner', () => { - let configurationMethodsMock: any; - let parsedRequisitions = [{name: 'I am fake'}]; - let requisitionRunnerMethods = { - run: jest.fn(async () => { - const report = { - name: 'mocked report', - valid: true, - hooks: {} - }; - NotificationEmitter.emit(Notifications.REQUISITION_FINISHED, {requisition: report}); - return [report]; - }) + let configurationMethodsMock: any; + let parsedTasks = [{ name: 'I am fake' }]; + let taskRunnerMethods = { + run: jest.fn(async () => { + const report = { + name: 'mocked report', + valid: true, + hooks: {} + }; + NotificationEmitter.emit(Notifications.REQUISITION_FINISHED, { + task: report + }); + return [report]; + }) + }; + + let taskRunnerMock = jest.fn(() => taskRunnerMethods); + + let parallel = true; + beforeEach(() => { + configurationMethodsMock = { + isParallel: jest.fn(() => parallel), + getOutputs: jest.fn(), + getFiles: jest.fn(() => ['src/*.ts', 'not-matching-pattern', true]), + getMaxReportLevelPrint: () => true, + getShowPassingTests: () => 2, + getLogLevel: jest.fn(() => loggerLevel) }; - - let requisitionRunnerMock = jest.fn(() => requisitionRunnerMethods); - - let parallel = true; - beforeEach(() => { - configurationMethodsMock = { - isParallel: jest.fn(() => parallel), - getOutputs: jest.fn(), - getFiles: jest.fn(() => ['src/*.ts', 'not-matching-pattern', true]), - getMaxReportLevelPrint: () => true, - getShowPassingTests: () => 2, - getLogLevel: jest.fn(() => loggerLevel) - }; - // @ts-ignore - Configuration.getInstance.mockImplementation(() => configurationMethodsMock); - - // @ts-ignore - RequisitionFilePatternParser.mockImplementationOnce(() => { - return { - parse: () => parsedRequisitions, - getFilesErrors: () => [] - }; - }); - - // @ts-ignore - RequisitionRunner.mockImplementationOnce(requisitionRunnerMock); - requisitionRunnerMethods.run.mockClear(); - requisitionRunnerMock.mockClear(); + // @ts-ignore + Configuration.getInstance.mockImplementation(() => configurationMethodsMock); + + // @ts-ignore + TaskFilePatternParser.mockImplementationOnce(() => { + return { + parse: () => parsedTasks, + getFilesErrors: () => [] + }; }); - it('Should set logger level', () => { - const loggerLevelMock = jest.fn(); - // @ts-ignore - Logger.setLoggerLevel.mockImplementation(loggerLevelMock); - - const runner = new EnqueuerRunner().execute(); - - expect(loggerLevelMock).toHaveBeenNthCalledWith(1, LogLevel.INFO); - expect(loggerLevelMock).toHaveBeenNthCalledWith(2, LogLevel.WARN); - }); - - it('should call configuration methods once', async () => { - - await new EnqueuerRunner().execute(); - - expect(configurationMethodsMock.isParallel).toHaveBeenCalledTimes(1); - expect(configurationMethodsMock.getFiles).toHaveBeenCalledTimes(1); - expect(configurationMethodsMock.getOutputs).toHaveBeenCalledTimes(1); - expect(configurationMethodsMock.getLogLevel).toHaveBeenCalledTimes(1); - }); - - it('should call Summary', async () => { - - const printMock = jest.fn(); - // @ts-ignore - SummaryTestOutput.mockImplementationOnce(() => ({ - print: printMock - })); - - await new EnqueuerRunner().execute(); - expect(SummaryTestOutput).toHaveBeenCalledWith({ - hooks: - { - 'onParsed': {'tests': [], 'valid': true} - }, - 'name': 'mocked report', - 'valid': true - }, { - 'maxLevel': true, - 'showPassingTests': 2, - printChildren: true - }); - expect(printMock).toHaveBeenCalledTimes(1); - }); - - it('should log Summary error', () => { - - // @ts-ignore - SummaryTestOutput.mockImplementationOnce(() => { - throw 'error'; - }); - - expect(async () => await new EnqueuerRunner().execute()).not.toThrowError(); + // @ts-ignore + TaskRunner.mockImplementationOnce(taskRunnerMock); + taskRunnerMethods.run.mockClear(); + taskRunnerMock.mockClear(); + }); + + it('Should set logger level', () => { + const loggerLevelMock = jest.fn(); + // @ts-ignore + Logger.setLoggerLevel.mockImplementation(loggerLevelMock); + + const runner = new EnqueuerRunner().execute(); + + expect(loggerLevelMock).toHaveBeenNthCalledWith(1, LogLevel.INFO); + expect(loggerLevelMock).toHaveBeenNthCalledWith(2, LogLevel.WARN); + }); + + it('should call configuration methods once', async () => { + await new EnqueuerRunner().execute(); + + expect(configurationMethodsMock.isParallel).toHaveBeenCalledTimes(1); + expect(configurationMethodsMock.getFiles).toHaveBeenCalledTimes(1); + expect(configurationMethodsMock.getOutputs).toHaveBeenCalledTimes(1); + expect(configurationMethodsMock.getLogLevel).toHaveBeenCalledTimes(1); + }); + + it('should call Summary', async () => { + const printMock = jest.fn(); + // @ts-ignore + SummaryTestOutput.mockImplementationOnce(() => ({ + print: printMock + })); + + await new EnqueuerRunner().execute(); + expect(SummaryTestOutput).toHaveBeenCalledWith( + { + hooks: { + onParsed: { tests: [], valid: true } + }, + name: 'mocked report', + valid: true + }, + { + maxLevel: true, + showPassingTests: 2, + printChildren: true + } + ); + expect(printMock).toHaveBeenCalledTimes(1); + }); + + it('should log Summary error', () => { + // @ts-ignore + SummaryTestOutput.mockImplementationOnce(() => { + throw 'error'; }); + expect(async () => await new EnqueuerRunner().execute()).not.toThrowError(); + }); }); diff --git a/src/enqueuer-runner.ts b/src/enqueuer-runner.ts index d17ae5f7..05588cc4 100644 --- a/src/enqueuer-runner.ts +++ b/src/enqueuer-runner.ts @@ -1,85 +1,89 @@ -import {Logger} from './loggers/logger'; -import {MultiTestsOutput} from './outputs/multi-tests-output'; -import * as input from './models/inputs/requisition-model'; -import * as output from './models/outputs/requisition-model'; -import {DateController} from './timers/date-controller'; -import {RequisitionFilePatternParser} from './requisition-runners/requisition-file-pattern-parser'; -import {RequisitionRunner} from './requisition-runners/requisition-runner'; -import {Configuration} from './configurations/configuration'; -import {RequisitionAdopter} from './components/requisition-adopter'; -import {NotificationEmitter} from './notifications/notification-emitter'; -import {SummaryTestOutput} from './outputs/summary-test-output'; -import {PublisherModel} from './models/inputs/publisher-model'; -import {TestModel} from './models/outputs/test-model'; -import {LogLevel} from './loggers/log-level'; -import {Notifications} from './notifications/notifications'; +import { Logger } from './loggers/logger'; +import { MultiTestsOutput } from './outputs/multi-tests-output'; +import * as input from './models/inputs/task-model'; +import * as output from './models/outputs/task-model'; +import { DateController } from './timers/date-controller'; +import { TaskFilePatternParser } from './task-runners/task-file-pattern-parser'; +import { TaskRunner } from './task-runners/task-runner'; +import { Configuration } from './configurations/configuration'; +import { TaskAdopter } from './components/task-adopter'; +import { NotificationEmitter } from './notifications/notification-emitter'; +import { SummaryTestOutput } from './outputs/summary-test-output'; +import { ActuatorModel } from './models/inputs/actuator-model'; +import { TestModel } from './models/outputs/test-model'; +import { LogLevel } from './loggers/log-level'; +import { Notifications } from './notifications/notifications'; export class EnqueuerRunner { - private static reportName: string = 'enqueuer'; + private static reportName: string = 'enqueuer'; - private readonly startTime: DateController; - private enqueuerRequisition?: input.RequisitionModel; + private enqueuerTask?: input.TaskModel; - constructor() { - this.startTime = new DateController(); - NotificationEmitter.on(Notifications.REQUISITION_FINISHED, (report: any) => EnqueuerRunner.printReport(report.requisition)); - } + constructor() { + NotificationEmitter.on( + Notifications.REQUISITION_FINISHED, + async (report: any) => await EnqueuerRunner.printReport(report.task) + ); + } - public async execute(): Promise { - const configuration = Configuration.getInstance(); - Logger.setLoggerLevel(LogLevel.INFO); - Logger.info('Rocking and rolling'); - Logger.setLoggerLevel(LogLevel.buildFromString(configuration.getLogLevel())); - const requisitionFileParser = new RequisitionFilePatternParser(configuration.getFiles()); - const requisitions = requisitionFileParser.parse(); - this.enqueuerRequisition = new RequisitionAdopter( - { - requisitions, - name: EnqueuerRunner.reportName, - timeout: -1, - parallel: configuration.isParallel() - }).getRequisition(); - const parsingErrors = requisitionFileParser.getFilesErrors(); - const finalReports = await new RequisitionRunner(this.enqueuerRequisition).run(); - await this.publishReports(configuration.getOutputs(), finalReports, parsingErrors); - return finalReports; - } + public async execute(): Promise { + const configuration = Configuration.getInstance(); + Logger.setLoggerLevel(LogLevel.INFO); + Logger.info('Rocking and rolling'); + Logger.setLoggerLevel(LogLevel.buildFromString(configuration.getLogLevel())); + const taskFileParser = new TaskFilePatternParser(configuration.getFiles()); + const tasks = taskFileParser.parse(); + this.enqueuerTask = new TaskAdopter({ + tasks, + name: EnqueuerRunner.reportName, + timeout: -1, + parallel: configuration.isParallel() + }).getTask(); + const parsingErrors = taskFileParser.getFilesErrors(); + const finalReports = await new TaskRunner(this.enqueuerTask).run(); + await this.publishReports(configuration.getOutputs(), finalReports, parsingErrors); + return finalReports; + } - private async publishReports(configurationOutputs: PublisherModel[], finalReports: output.RequisitionModel[], parsingErrors: TestModel[]) { - Logger.info('Publishing reports'); - const valid = parsingErrors.length === 0; - const outputs = new MultiTestsOutput(configurationOutputs); - await finalReports.map(async report => { - report.hooks!.onParsed = { - valid: valid, - tests: parsingErrors - }; - report.valid = report.valid && valid; - await outputs.publishReport(report); - }); - return finalReports; - } + private async publishReports( + configurationOutputs: ActuatorModel[], + finalReports: output.TaskModel[], + parsingErrors: TestModel[] + ) { + Logger.info('Publishing reports'); + const valid = parsingErrors.length === 0; + const outputs = new MultiTestsOutput(configurationOutputs); + //TODO fix this useless await + await finalReports.map(async report => { + report.hooks!.onParsed = { + valid: valid, + tests: parsingErrors + }; + report.valid = report.valid && valid; + await outputs.publishReport(report); + }); + return finalReports; + } - private static printReport(report: output.RequisitionModel): void { - const configuration = Configuration.getInstance(); - if (report.level === undefined || report.level <= configuration.getMaxReportLevelPrint()) { - try { - let printChildren = true; - if (report.level === 0) { - console.log(` ----------------`); - printChildren = false; - } - - new SummaryTestOutput(report, { - maxLevel: configuration.getMaxReportLevelPrint(), - showPassingTests: configuration.getShowPassingTests(), - printChildren: printChildren - }).print(); - } catch (e) { - Logger.warning(e); - } + private static async printReport(report: output.TaskModel): Promise { + const configuration = Configuration.getInstance(); + if (report.level === undefined || report.level <= configuration.getMaxReportLevelPrint()) { + try { + let printChildren = true; + if (report.level === 0) { + console.log(` ----------------`); + printChildren = false; } + const summaryTestOutput = new SummaryTestOutput(report, { + maxLevel: configuration.getMaxReportLevelPrint(), + showPassingTests: configuration.getShowPassingTests(), + printChildren: printChildren + }); + await summaryTestOutput.print(); + } catch (err) { + Logger.warning(`Runner errored: ` + err); + } } - + } } diff --git a/src/enqueuer-starter.test.ts b/src/enqueuer-starter.test.ts index 0a3f817e..f6db1513 100644 --- a/src/enqueuer-starter.test.ts +++ b/src/enqueuer-starter.test.ts @@ -1,36 +1,36 @@ -import {EnqueuerStarter} from './enqueuer-starter'; -import {EnqueuerRunner} from './enqueuer-runner'; +import { EnqueuerStarter } from './enqueuer-starter'; +import { EnqueuerRunner } from './enqueuer-runner'; jest.mock('./enqueuer-runner'); jest.mock('./configurations/configuration'); describe('EnqueuerStarter', () => { - it('Should translate true to 0', async () => { - // @ts-ignore - EnqueuerRunner.mockImplementationOnce(() => ({execute: () => []})); - - expect(await new EnqueuerStarter().start()).toBe(0); - }); - - it('Should translate false to 1', async () => { - // @ts-ignore - EnqueuerRunner.mockImplementationOnce(() => ({execute: () => [{valid: false}]})); - - expect(await new EnqueuerStarter().start()).toBe(1); + it('Should translate true to 0', async () => { + // @ts-ignore + EnqueuerRunner.mockImplementationOnce(() => ({ execute: () => [] })); + + expect(await new EnqueuerStarter().start()).toBe(0); + }); + + it('Should translate false to 1', async () => { + // @ts-ignore + EnqueuerRunner.mockImplementationOnce(() => ({ + execute: () => [{ valid: false }] + })); + + expect(await new EnqueuerStarter().start()).toBe(1); + }); + + it('Should translate error to -1', async () => { + // @ts-ignore + EnqueuerRunner.mockImplementationOnce(() => { + return { + execute: () => { + throw `error`; + } + }; }); - it('Should translate error to -1', async () => { - // @ts-ignore - EnqueuerRunner.mockImplementationOnce(() => { - return { - execute: () => { - throw `error`; - } - }; - }); - - expect(await new EnqueuerStarter().start()).toBe(-1); - }); - - + expect(await new EnqueuerStarter().start()).toBe(-1); + }); }); diff --git a/src/enqueuer-starter.ts b/src/enqueuer-starter.ts index 12b94fcf..48a33abc 100644 --- a/src/enqueuer-starter.ts +++ b/src/enqueuer-starter.ts @@ -1,28 +1,29 @@ -import {Logger} from './loggers/logger'; -import {EnqueuerRunner} from './enqueuer-runner'; -import {LogLevel} from './loggers/log-level'; +import { Logger } from './loggers/logger'; +import { EnqueuerRunner } from './enqueuer-runner'; +import { LogLevel } from './loggers/log-level'; export class EnqueuerStarter { + private enqueuerRunner: EnqueuerRunner; - private enqueuerRunner: EnqueuerRunner; + constructor() { + this.enqueuerRunner = new EnqueuerRunner(); + } - constructor() { - this.enqueuerRunner = new EnqueuerRunner(); + public async start(): Promise { + let statusCode = 1; + try { + const reports = await this.enqueuerRunner.execute(); + statusCode = reports.every(report => report.valid) ? 0 : 1; + } catch (error) { + Logger.fatal(`Execution error: ${error}`); + statusCode = -1; } - - public async start(): Promise { - let statusCode = 1; - try { - const reports = await this.enqueuerRunner.execute(); - statusCode = reports.every(report => report.valid) ? 0 : 1; - } catch (error) { - Logger.fatal(`Execution error: ${error}`); - statusCode = -1; - } - Logger.info('Enqueuer execution is over (' + (statusCode === 0) + ')'); - Logger.setLoggerLevel(LogLevel.INFO); - Logger.info(`Hope you had a great time`); - return statusCode; - + if (statusCode === 0) { + Logger.info('Enqueuer execution status was succesfull'); } + Logger.info('There was a failure in enqueuer execution tests'); + Logger.setLoggerLevel(LogLevel.INFO); + Logger.info(`Hope you had a great time`); + return statusCode; + } } diff --git a/src/enqueuer.ts b/src/enqueuer.ts index 7cff7b12..6cbcb80e 100644 --- a/src/enqueuer.ts +++ b/src/enqueuer.ts @@ -1,14 +1,14 @@ -export {RequisitionModel as InputRequisitionModel} from './models/inputs/requisition-model'; -export {PublisherModel as InputPublisherModel} from './models/inputs/publisher-model'; -export {SubscriptionModel as InputSubscriptionModel} from './models/inputs/subscription-model'; - -export {ReportModel as OutputReportModel} from './models/outputs/report-model'; -export {HookModel as OutputHookModel} from './models/outputs/hook-model'; -export {RequisitionModel as OutputRequisitionModel} from './models/outputs/requisition-model'; -export {PublisherModel as OutputPublisherModel} from './models/outputs/publisher-model'; -export {SubscriptionModel as OutputSubscriptionModel} from './models/outputs/subscription-model'; -export {TimeModel as OutputTimeModel} from './models/outputs/time-model'; -export {TestModel as OutputTestModel} from './models/outputs/test-model'; +export { TaskModel as InputTaskModel } from './models/inputs/task-model'; +export { ActuatorModel as InputActuatorModel } from './models/inputs/actuator-model'; +export { SensorModel as InputSensorModel } from './models/inputs/sensor-model'; + +export { ReportModel as OutputReportModel } from './models/outputs/report-model'; +export { HookModel as OutputHookModel } from './models/outputs/hook-model'; +export { TaskModel as OutputTaskModel } from './models/outputs/task-model'; +export { ActuatorModel as OutputActuatorModel } from './models/outputs/actuator-model'; +export { SensorModel as OutputSensorModel } from './models/outputs/sensor-model'; +export { TimeModel as OutputTimeModel } from './models/outputs/time-model'; +export { TestModel as OutputTestModel } from './models/outputs/test-model'; export * from './models/events/assertion'; export * from './models/events/event'; @@ -27,10 +27,10 @@ export * from './configurations/store'; export * from './loggers/logger'; export * from './loggers/log-level'; -export * from './requisition-runners/requisition-parser'; -export * from './requisition-runners/requisition-file-parser'; -export * from './requisition-runners/requisition-file-pattern-parser'; -export * from './requisition-runners/requisition-runner'; +export * from './task-runners/task-parser'; +export * from './task-runners/task-file-parser'; +export * from './task-runners/task-file-pattern-parser'; +export * from './task-runners/task-runner'; export * from './strings/id-generator'; @@ -44,12 +44,12 @@ export * from './plugins/main-instance'; export * from './protocols/protocol'; export * from './protocols/protocol-documentation'; -export * from './protocols/publisher-protocol'; -export * from './protocols/subscription-protocol'; +export * from './protocols/actuator-protocol'; +export * from './protocols/sensor-protocol'; -export * from './publishers/publisher'; +export * from './actuators/actuator'; -export * from './subscriptions/subscription'; +export * from './sensors/sensor'; export * from './object-parser/object-parser'; diff --git a/src/events/event-executor.test.ts b/src/events/event-executor.test.ts index 7165ccd4..90b2b6a2 100644 --- a/src/events/event-executor.test.ts +++ b/src/events/event-executor.test.ts @@ -1,47 +1,45 @@ -import {EventExecutor} from './event-executor'; +import { EventExecutor } from './event-executor'; let initializable: any; describe('EventExecutor', () => { - - beforeEach(() => { - initializable = { - value: 4, - onInit: { - store: { - key: 'value' - }, - script: 'this.method(this.value)', - assertions: [ - { - name: 'equalName', - expected: 2, - isEqualTo: 2 - }, - { - isDefined: 'x' - }] - } - }; - }); - - it('Should add argument and pass it to the script executor', done => { - const eventExecutor: EventExecutor = new EventExecutor(initializable, 'onInit'); - initializable.method = (value: number) => { - expect(value).toBe(initializable.value); - done(); - }; - - eventExecutor.execute(); - - }); - - it('Should return empty array if no event is passed', () => { - const eventExecutor: EventExecutor = new EventExecutor(initializable, 'noInit'); - - const testModels = eventExecutor.execute(); - - expect(testModels.length).toBe(0); - }); - + beforeEach(() => { + initializable = { + value: 4, + onInit: { + store: { + key: 'value' + }, + script: 'this.method(this.value)', + assertions: [ + { + name: 'equalName', + expected: 2, + isEqualTo: 2 + }, + { + isDefined: 'x' + } + ] + } + }; + }); + + it('Should add argument and pass it to the script executor', done => { + const eventExecutor: EventExecutor = new EventExecutor(initializable, 'onInit'); + initializable.method = (value: number) => { + expect(value).toBe(initializable.value); + done(); + }; + + eventExecutor.execute(); + }); + + it('Should return empty array if no event is passed', () => { + const eventExecutor: EventExecutor = new EventExecutor(initializable, 'noInit'); + + const testModels = eventExecutor.execute(); + + expect(testModels.length).toBe(0); + }); }); diff --git a/src/events/event-executor.ts b/src/events/event-executor.ts index dad2159d..616f029d 100644 --- a/src/events/event-executor.ts +++ b/src/events/event-executor.ts @@ -1,56 +1,69 @@ -import {Event} from '../models/events/event'; -import {Assertion} from '../models/events/assertion'; -import {Logger} from '../loggers/logger'; -import {TestModel} from '../models/outputs/test-model'; -import {EventCodeGenerator} from '../code-generators/event-code-generator'; +import { Event } from '../models/events/event'; +import { Assertion } from '../models/events/assertion'; +import { Logger } from '../loggers/logger'; +import { TestModel } from '../models/outputs/test-model'; +import { EventCodeGenerator } from '../code-generators/event-code-generator'; export class EventExecutor { - private arguments: { name: string, value: any }[] = []; - private readonly thisArg: any; - private readonly event: Event; - private readonly eventName: string; + private arguments: { name: string; value: any }[] = []; + private readonly thisArg: any; + private readonly event: Event; + private readonly eventName: string; + private readonly componentName?: string; - public constructor(thisArg: any, eventName: string, componentName?: string) { - this.thisArg = thisArg; - this.eventName = eventName; - this.event = this.initializeEvent(this.thisArg[eventName]); - if (componentName) { - this.addArgument(componentName, thisArg); - } + public constructor(thisArg: any, eventName: string, componentName?: string) { + this.componentName = componentName; + this.thisArg = thisArg; + this.eventName = eventName; + this.event = this.initializeEvent(this.thisArg[eventName]); + this.componentName = componentName; + if (componentName) { + this.addArgument(componentName, thisArg); } + } - public addArgument(name: string, value: any): void { - this.arguments.push({name: name, value: value}); - } + public isDebugMode(): boolean { + return !!this.event.debug; + } - public execute(): TestModel[] { - Logger.debug(`Executing '${this.eventName}' hook`); - Logger.trace(`'${this.eventName}': ${JSON.stringify(this.event)}`); - return new EventCodeGenerator(this.thisArg, this.eventName).run(this.arguments); - } + public addArgument(name: string, value: any): void { + this.arguments.push({ name: name, value: value }); + } - private initializeEvent(event: Event): Event { - let result: Event = { - script: '', - store: {}, - assertions: [] - }; - if (event) { - result = { - script: event.script || '', - store: event.store || {}, - assertions: this.baptizeAssertions(event.assertions || []) - }; - } - return result; - } + public execute(): TestModel[] { + Logger.debug(`Executing ${this.componentName ?? 'component'}'s '${this.eventName}' hook`); + Logger.trace(`'${this.eventName}': ${JSON.stringify(this.event)}`); + this.arguments.unshift({ + name: 'argumentNames', + value: this.arguments.map(item => item.name) + }); + return new EventCodeGenerator(this.thisArg, this.eventName).run(this.arguments); + } - private baptizeAssertions(assertions: Assertion[]): Assertion[] { - return assertions.map((assertion, index) => { - if (!assertion.name) { - assertion.name = `Assertion #${index}`; - } - return assertion; - }); + private initializeEvent(event: Event): Event { + let result: Event = { + debug: false, + script: '', + store: {}, + assertions: [] + }; + if (event) { + result = { + debug: !!event.debug, + script: event.script || '', + store: event.store || {}, + assertions: this.baptizeAssertions(event.assertions || []) + }; } + return result; + } + + private baptizeAssertions(assertions: Assertion[]): Assertion[] { + return assertions.map((assertion, index) => { + if (!assertion.name) { + assertion.name = `Assertion #${index}`; + } + return assertion; + }); + } } diff --git a/src/handlers/handler-listener.test.ts b/src/handlers/handler-listener.test.ts index 70ab5842..380f66d4 100644 --- a/src/handlers/handler-listener.test.ts +++ b/src/handlers/handler-listener.test.ts @@ -1,6 +1,9 @@ -import {HandlerListener} from "./handler-listener"; -import {HandlerListener} from "./handler-listener"; -import {Logger} from "../loggers/logger"; +import { HandlerListener } from './handler-listener'; +import { Logger } from '../loggers/logger'; + +// global.setTimeout.mockImplementation(cb => cb()); +jest.useFakeTimers(); +jest.spyOn(global, 'setTimeout'); let onErrorMock = jest.fn(); let listenMock = jest.fn(); @@ -10,203 +13,195 @@ const warningMock = jest.fn(); const traceMock = jest.fn(); const debugMock = jest.fn(); const errorMock = jest.fn(); -jest.mock("../loggers/logger"); +jest.mock('../loggers/logger'); +// @ts-expect-error Logger.warning.mockImplementation(warningMock); +// @ts-expect-error Logger.trace.mockImplementation(traceMock); +// @ts-expect-error Logger.debug.mockImplementation(debugMock); +// @ts-expect-error Logger.error.mockImplementation(errorMock); -// global.setTimeout.mockImplementation(cb => cb()); -jest.useFakeTimers(); - const createServerMock = () => { - return { - on: onErrorMock, - listen: listenMock, - close: closeMock, - address: jest.fn(), - }; + return { + on: onErrorMock, + listen: listenMock, + close: closeMock, + address: jest.fn() + }; }; describe('HandleListener', () => { - - beforeEach(() => { - onErrorMock = jest.fn(); - listenMock.mockClear(); - closeMock.mockClear(); - errorMock.mockClear(); - warningMock.mockClear(); - setTimeout.mockClear(); + beforeEach(() => { + onErrorMock = jest.fn(); + listenMock.mockClear(); + closeMock.mockClear(); + errorMock.mockClear(); + warningMock.mockClear(); + // @ts-expect-error + setTimeout.mockClear(); + }); + + it('Happy path', done => { + const handler = 987; + + listenMock = jest.fn((argHandler, cb) => { + expect(argHandler).toBe(handler); + return cb(); }); - it('Happy path', done => { - const handler = 987; - - listenMock = jest.fn((argHandler, cb) => { - expect(argHandler).toBe(handler); - return cb() - }); - - const listener = new HandlerListener(createServerMock()); - listener.listen(handler) - .then(() => { - expect(listenMock).toHaveBeenCalledTimes(1); - expect(listenMock).toHaveBeenCalledWith(handler, expect.any(Function)); - expect(onErrorMock).toHaveBeenCalledTimes(1); - expect(setTimeout).toHaveBeenCalledTimes(0); - expect(listener.getHandler()).toBe(handler); - done(); - }); + const listener = new HandlerListener(createServerMock()); + listener.listen(handler).then(() => { + expect(listenMock).toHaveBeenCalledTimes(1); + expect(listenMock).toHaveBeenCalledWith(handler, expect.any(Function)); + expect(onErrorMock).toHaveBeenCalledTimes(1); + expect(setTimeout).toHaveBeenCalledTimes(0); + expect(listener.getHandler()).toBe(handler); + done(); }); + }); - it('Should bind to "error" event', done => { - const handler = 987; - - onErrorMock = jest.fn((eventName, cb) => { - expect(eventName).toBe('error'); - return cb('error'); - }); - - const listener = new HandlerListener(createServerMock()); - listener.listen(handler) - .catch((err) => { - expect(err).toBe('Error listening to handler (987) "error"'); - expect(listenMock).toHaveBeenCalledTimes(1); - expect(onErrorMock).toHaveBeenCalledTimes(1); - expect(setTimeout).toHaveBeenCalledTimes(0); - expect(warningMock).toHaveBeenCalledTimes(1); - done(); - }); + it('Should bind to "error" event', done => { + const handler = 987; + onErrorMock = jest.fn((eventName, cb) => { + expect(eventName).toBe('error'); + return cb('error'); }); - it('Should catch listen exception', done => { - const handler = 987; - const exception = 'exception'; - - listenMock = jest.fn((argHandler, cb) => { - throw exception; - }); - - const listener = new HandlerListener(createServerMock()); - listener.listen(handler) - .catch((err) => { - expect(err).toBe(`Error listening to handler (${handler}) "${exception}"`); - expect(listenMock).toHaveBeenCalledTimes(1); - expect(onErrorMock).toHaveBeenCalledTimes(1); - expect(setTimeout).toHaveBeenCalledTimes(0); - expect(warningMock).toHaveBeenCalledTimes(1); - done(); - }); - + const listener = new HandlerListener(createServerMock()); + listener.listen(handler).catch(err => { + expect(err).toBe('Error listening to handler (987) "error"'); + expect(listenMock).toHaveBeenCalledTimes(1); + expect(onErrorMock).toHaveBeenCalledTimes(1); + expect(setTimeout).toHaveBeenCalledTimes(0); + expect(warningMock).toHaveBeenCalledTimes(1); + done(); }); + }); - it('Retry just once when it\'s not EADDRINUSE', done => { - const handler = 987; - jest.runAllTimers(); - - listenMock.mockImplementationOnce((argHandler, cb) => cb({ - code: 'It\'s not EADDRINUSE' - })); - - const listener = new HandlerListener(createServerMock()); - - listener.listen(handler) - .catch((err) => { - expect(err).toBeDefined(); - expect(listenMock).toHaveBeenCalledTimes(1); - expect(setTimeout).toHaveBeenCalledTimes(0); - expect(errorMock).toHaveBeenCalledTimes(1); - done(); - }); + it('Should catch listen exception', done => { + const handler = 987; + const exception = 'exception'; + listenMock = jest.fn((argHandler, cb) => { + throw exception; }); - it('Retry after retryInterval if error is EADDRINUSE', done => { - const handler = 987; - const numAttempts = 2; - const retryInterval = 2; - - listenMock.mockImplementationOnce((argHandler, cb) => { - return cb({ - code: HandlerListener.ADDRESS_IN_USE - }) - }); - listenMock.mockImplementationOnce((argHandler, cb) => { - return cb() - }); - - const listener = new HandlerListener(createServerMock(), numAttempts, retryInterval); - - listener.listen(handler) - .then(() => { - expect(listenMock).toHaveBeenCalledTimes(numAttempts); - expect(closeMock).toHaveBeenCalledTimes(numAttempts - 1); - expect(setTimeout).toHaveBeenCalledTimes(1); - expect(setTimeout).toHaveBeenCalledWith(expect.any(Function), retryInterval); - done(); - }); - - // Fast-forward until all timers have been executed - jest.runAllTimers(); - - + const listener = new HandlerListener(createServerMock()); + listener.listen(handler).catch(err => { + expect(err).toBe(`Error listening to handler (${handler}) "${exception}"`); + expect(listenMock).toHaveBeenCalledTimes(1); + expect(onErrorMock).toHaveBeenCalledTimes(1); + expect(setTimeout).toHaveBeenCalledTimes(0); + expect(warningMock).toHaveBeenCalledTimes(1); + done(); }); + }); + + it("Retry just once when it's not EADDRINUSE", done => { + const handler = 987; + jest.runAllTimers(); + + listenMock.mockImplementationOnce((argHandler, cb) => + cb({ + code: "It's not EADDRINUSE" + }) + ); + + const listener = new HandlerListener(createServerMock()); + + listener.listen(handler).catch(err => { + expect(err).toBeDefined(); + expect(listenMock).toHaveBeenCalledTimes(1); + expect(setTimeout).toHaveBeenCalledTimes(0); + expect(errorMock).toHaveBeenCalledTimes(1); + done(); + }); + }); - it('Try max attempts num if error is EADDRINUSE', done => { - const handler = 987; - const numAttempts = 15; - - for (let i = 0; i < numAttempts - 1; ++i) { - listenMock.mockImplementationOnce((argHandler, cb) => cb({ - code: 'EADDRINUSE' - })); - } - listenMock.mockImplementationOnce((argHandler, cb) => { - return cb() - }); - - const listener = new HandlerListener(createServerMock(), numAttempts); - - listener.listen(handler) - .then(() => { - expect(listenMock).toHaveBeenCalledTimes(numAttempts); - expect(closeMock).toHaveBeenCalledTimes(numAttempts - 1); - expect(setTimeout).toHaveBeenCalledTimes(numAttempts - 1); - expect(setTimeout).toHaveBeenCalledWith(expect.any(Function), expect.any(Number)); - done(); - }); - - // Fast-forward until all timers have been executed - jest.runAllTimers(); + it('Retry after retryInterval if error is EADDRINUSE', done => { + const handler = 987; + const numAttempts = 2; + const retryInterval = 2; + listenMock.mockImplementationOnce((argHandler, cb) => { + return cb({ + code: HandlerListener.ADDRESS_IN_USE + }); + }); + listenMock.mockImplementationOnce((argHandler, cb) => { + return cb(); }); - it('Should fail it try max attempts is over when error is EADDRINUSE', done => { - const handler = 'virgs'; - const numAttempts = 15; + const listener = new HandlerListener(createServerMock(), numAttempts, retryInterval); - for (let i = 0; i < numAttempts; ++i) { - listenMock.mockImplementationOnce((argHandler, cb) => cb({ - code: HandlerListener.ADDRESS_IN_USE - })); - } + listener.listen(handler).then(() => { + expect(listenMock).toHaveBeenCalledTimes(numAttempts); + expect(closeMock).toHaveBeenCalledTimes(numAttempts - 1); + expect(setTimeout).toHaveBeenCalledTimes(1); + expect(setTimeout).toHaveBeenCalledWith(expect.any(Function), retryInterval); + done(); + }); - const listener = new HandlerListener(createServerMock(), numAttempts); + // Fast-forward until all timers have been executed + jest.runAllTimers(); + }); + + it('Try max attempts num if error is EADDRINUSE', done => { + const handler = 987; + const numAttempts = 15; + + for (let i = 0; i < numAttempts - 1; ++i) { + listenMock.mockImplementationOnce((argHandler, cb) => + cb({ + code: 'EADDRINUSE' + }) + ); + } + listenMock.mockImplementationOnce((argHandler, cb) => { + return cb(); + }); - listener.listen(handler) - .catch((err) => { - expect(err).toBe(`Could not bind to handler ${handler}`); - expect(listenMock).toHaveBeenCalledTimes(numAttempts); - expect(closeMock).toHaveBeenCalledTimes(numAttempts); - expect(setTimeout).toHaveBeenCalledTimes(numAttempts); - expect(setTimeout).toHaveBeenCalledWith(expect.any(Function), 300); - done(); - }); + const listener = new HandlerListener(createServerMock(), numAttempts); - // Fast-forward until all timers have been executed - jest.runAllTimers(); + listener.listen(handler).then(() => { + expect(listenMock).toHaveBeenCalledTimes(numAttempts); + expect(closeMock).toHaveBeenCalledTimes(numAttempts - 1); + expect(setTimeout).toHaveBeenCalledTimes(numAttempts - 1); + expect(setTimeout).toHaveBeenCalledWith(expect.any(Function), expect.any(Number)); + done(); + }); + // Fast-forward until all timers have been executed + jest.runAllTimers(); + }); + + it('Should fail it try max attempts is over when error is EADDRINUSE', done => { + const handler = 'virgs'; + const numAttempts = 15; + + for (let i = 0; i < numAttempts; ++i) { + listenMock.mockImplementationOnce((argHandler, cb) => + cb({ + code: HandlerListener.ADDRESS_IN_USE + }) + ); + } + + const listener = new HandlerListener(createServerMock(), numAttempts); + + listener.listen(handler).catch(err => { + expect(err).toBe(`Could not bind to handler ${handler}`); + expect(listenMock).toHaveBeenCalledTimes(numAttempts); + expect(closeMock).toHaveBeenCalledTimes(numAttempts); + expect(setTimeout).toHaveBeenCalledTimes(numAttempts); + expect(setTimeout).toHaveBeenCalledWith(expect.any(Function), 300); + done(); }); + // Fast-forward until all timers have been executed + jest.runAllTimers(); + }); }); diff --git a/src/handlers/handler-listener.ts b/src/handlers/handler-listener.ts index 3ede6e6e..273d487a 100644 --- a/src/handlers/handler-listener.ts +++ b/src/handlers/handler-listener.ts @@ -1,76 +1,78 @@ -import {Logger} from '../loggers/logger'; +import { Logger } from '../loggers/logger'; export class HandlerListener { - public static ADDRESS_IN_USE = 'EADDRINUSE'; + public static ADDRESS_IN_USE = 'EADDRINUSE'; - private server: any; - private remainingAttempts: number; - private retryTimeout: number; - private handler: number | string = ''; + private server: any; + private remainingAttempts: number; + private retryTimeout: number; + private handler: number | string = ''; - public constructor(server: any, remainingAttempts: number = 3, retryTimeout: number = 300) { - this.server = server; - this.remainingAttempts = remainingAttempts; - this.retryTimeout = retryTimeout; - } + public constructor(server: any, remainingAttempts: number = 3, retryTimeout: number = 300) { + this.server = server; + this.remainingAttempts = remainingAttempts; + this.retryTimeout = retryTimeout; + } - public listen(handler: number | string): Promise { - return new Promise((resolve, reject) => { - Logger.trace(`Binding server to ${handler}`); - this.server.on('error', (err: any) => { - Logger.warning(`Server emitted error: ${err}`); - this.handleError(err, handler, resolve, reject); - }); - try { - this.tryToListen(handler, resolve, reject); - } catch (err) { - Logger.warning(`Listen error caught: ${err}`); - this.handleError(err, handler, resolve, reject); - } - }); - } + public listen(handler: number | string): Promise { + return new Promise((resolve, reject) => { + Logger.trace(`Binding server to ${handler}`); + this.server.on('error', (err: any) => { + Logger.warning(`Server emitted error: ${err}`); + this.handleError(err, handler, resolve, reject); + }); + try { + this.tryToListen(handler, resolve, reject); + } catch (err) { + Logger.warning(`Listen error caught: ${err}`); + this.handleError(err, handler, resolve, reject); + } + }); + } - private tryToListen(handler: number | string, resolve: any, reject: any) { - if (this.remainingAttempts > 0) { - this.server.listen(handler, (err: any) => { - if (err) { - this.handleError(err, handler, resolve, reject); - } else { - const address = this.server.address(); - if (!handler && address) { - this.handler = address.port; - Logger.info(`No specified handler. Server is bound to (${this.handler})`); - } else { - this.handler = handler; - Logger.debug(`Server is bound to (${this.handler})`); - } - resolve(); - } - }); + private tryToListen(handler: number | string, resolve: any, reject: any) { + if (this.remainingAttempts > 0) { + this.server.listen(handler, (err: any) => { + if (err) { + this.handleError(err, handler, resolve, reject); } else { - reject(`Could not bind to handler ${handler}`); + const address = this.server.address(); + if (!handler && address) { + this.handler = address.port; + Logger.info(`No specified handler. Server is bound to (${this.handler})`); + } else { + this.handler = handler; + Logger.debug(`Server is bound to (${this.handler})`); + } + resolve(); } + }); + } else { + reject(`Could not bind to handler ${handler}`); } + } - private handleError(err: any, handler: number | string, resolve: any, reject: any) { - if (err.code === HandlerListener.ADDRESS_IN_USE) { - --this.remainingAttempts; - Logger.warning(`Handler ${handler} is busy.` + - ` Waiting for ${this.retryTimeout}ms before trying again for ${this.remainingAttempts} more times...`); - setTimeout(() => { - Logger.debug(`Closing server`); - this.server.close(); - this.retryTimeout *= 2; - this.tryToListen(handler, resolve, reject); - }, this.retryTimeout); - } else { - const message = `Error listening to handler (${handler}) ${JSON.stringify(err)}`; - Logger.error(message); - reject(message); - } + private handleError(err: any, handler: number | string, resolve: any, reject: any) { + if (err.code === HandlerListener.ADDRESS_IN_USE) { + --this.remainingAttempts; + Logger.warning( + `Handler ${handler} is busy.` + + ` Waiting for ${this.retryTimeout}ms before trying again for ${this.remainingAttempts} more times...` + ); + setTimeout(() => { + Logger.debug(`Closing server`); + this.server.close(); + this.retryTimeout *= 2; + this.tryToListen(handler, resolve, reject); + }, this.retryTimeout); + } else { + const message = `Error listening to handler (${handler}) ${JSON.stringify(err)}`; + Logger.error(message); + reject(message); } + } - public getHandler(): string | number { - return this.handler; - } + public getHandler(): string | number { + return this.handler; + } } diff --git a/src/handlers/stream-input-handler.test.ts b/src/handlers/stream-input-handler.test.ts index 87a673a7..332a5e87 100644 --- a/src/handlers/stream-input-handler.test.ts +++ b/src/handlers/stream-input-handler.test.ts @@ -1,132 +1,132 @@ -import {StreamInputHandler} from "./stream-input-handler"; -import {HandlerListener} from './handler-listener'; +import { StreamInputHandler } from './stream-input-handler'; +import { HandlerListener } from './handler-listener'; import * as net from 'net'; jest.mock('./handler-listener'); jest.mock('net'); describe('StreamInputHandler', () => { - it('Should create server and listener', () => { - const server = 'server'; - HandlerListener.mockImplementationOnce(); - net.createServer.mockImplementationOnce(() => server); - - new StreamInputHandler(); - - expect(HandlerListener).toHaveBeenCalledWith(server); - expect(net.createServer).toHaveBeenCalled(); + it('Should create server and listener', () => { + const server = 'server'; + // @ts-expect-error + HandlerListener.mockImplementationOnce(); + // @ts-expect-error + net.createServer.mockImplementationOnce(() => server); + + // @ts-expect-error + new StreamInputHandler(); + + expect(HandlerListener).toHaveBeenCalledWith(server); + expect(net.createServer).toHaveBeenCalled(); + }); + + it('Should call listen', () => { + const handlerName = 'handlerName'; + const listen = jest.fn(() => new Promise(() => {})); + // @ts-expect-error + HandlerListener.mockImplementationOnce(() => { + return { + listen: listen, + getHandler: handlerName + }; }); - it('Should call listen', () => { - const handlerName = 'handlerName'; - const listen = jest.fn(() => new Promise(() => {})); - HandlerListener.mockImplementationOnce(() => { - return { - listen: listen, - getHandler: handlerName - } - }); - - new StreamInputHandler(handlerName).subscribe(() => {}); - - expect(listen).toHaveBeenCalledWith(handlerName); - }); + new StreamInputHandler(handlerName).mount(() => {}); - it('Should update handler', done => { - net.createServer.mockImplementationOnce(() => { - return { - on: () => {} - } - }); - HandlerListener.mockImplementationOnce(() => { - return { - listen: () => Promise.resolve(), - getHandler: () => 54321 - } - }); - - const streamInputHandler = new StreamInputHandler('handlerName'); - streamInputHandler.subscribe(() => {}).then(() => { - - expect(streamInputHandler.getHandler()).toBe(54321); - done(); - }); + expect(listen).toHaveBeenCalledWith(handlerName); + }); + it('Should update handler', done => { + // @ts-expect-error + net.createServer.mockImplementationOnce(() => { + return { + on: () => {} + }; }); - - it('Should unsubscribe', () => { - const closeMock = jest.fn(); - net.createServer.mockImplementationOnce(() => { - return { - close: closeMock - } - }); - - new StreamInputHandler('handlerName').unsubscribe(); - - expect(closeMock).toBeCalled(); - + // @ts-expect-error + HandlerListener.mockImplementationOnce(() => { + return { + listen: () => Promise.resolve(), + getHandler: () => 54321 + }; }); - it('Should close', () => { - const endMock = jest.fn(); - const stream = { - end: endMock - }; - - new StreamInputHandler('handlerName').close(stream); - - expect(endMock).toBeCalled(); - + const streamInputHandler = new StreamInputHandler('handlerName'); + streamInputHandler + .mount(() => {}) + .then(() => { + expect(streamInputHandler.getHandler()).toBe(54321); + done(); + }); + }); + + it('Should close', () => { + const closeMock = jest.fn(); + // @ts-expect-error + net.createServer.mockImplementationOnce(() => { + return { + close: closeMock + }; }); - it('Should sendResponse object', done => { - const writeMock = jest.fn((value, cb) => cb()); - const stream = { - write: writeMock - }; + new StreamInputHandler('handlerName').close(); - const message = { - cycle: { - 'object': 2 - } - }; - new StreamInputHandler('handlerName').sendResponse(stream, message).then(() => { + expect(closeMock).toBeCalled(); + }); - const mockCalls = writeMock.mock.calls; - const messageSent = mockCalls[0][0]; + it('Should close', () => { + const endMock = jest.fn(); + const stream = { + end: endMock + }; - expect(JSON.parse(messageSent)).toEqual(message); - done(); - }); - - }); + new StreamInputHandler('handlerName').end(stream); - it('Should sendResponse string', done => { - const writeMock = jest.fn(); - const stream = { - write: writeMock - }; + expect(endMock).toBeCalled(); + }); - const message = 'message'; - new StreamInputHandler('handlerName').sendResponse(stream, message); + it('Should respond object', done => { + const writeMock = jest.fn((value, cb) => cb()); + const stream = { + write: writeMock + }; - expect(writeMock).toHaveBeenCalledWith('message', expect.any(Function)); - done(); + const message = { + cycle: { + object: 2 + } + }; + new StreamInputHandler('handlerName').respond(stream, message).then(() => { + const mockCalls = writeMock.mock.calls; + const messageSent = mockCalls[0][0]; + expect(JSON.parse(messageSent)).toEqual(message); + done(); }); - - it('Should handler sendResponse error', done => { - const stream = { - write: () => {throw 'error'} - }; - - new StreamInputHandler('handlerName') - .sendResponse(stream, {}) - .catch((err) => { - expect(err).toBe("Error sending input handler: error"); - done(); - }); - + }); + + it('Should respond string', done => { + const writeMock = jest.fn(); + const stream = { + write: writeMock + }; + + const message = 'message'; + new StreamInputHandler('handlerName').respond(stream, message); + + expect(writeMock).toHaveBeenCalledWith('message', expect.any(Function)); + done(); + }); + + it('Should handler respond error', done => { + const stream = { + write: () => { + throw 'error'; + } + }; + + new StreamInputHandler('handlerName').respond(stream, {}).catch(err => { + expect(err).toBe('Error sending input handler: error'); + done(); }); - + }); }); diff --git a/src/handlers/stream-input-handler.ts b/src/handlers/stream-input-handler.ts index f4b21c3f..14ebc57b 100644 --- a/src/handlers/stream-input-handler.ts +++ b/src/handlers/stream-input-handler.ts @@ -1,70 +1,71 @@ -import {HandlerListener} from './handler-listener'; +import { HandlerListener } from './handler-listener'; import * as net from 'net'; export class StreamInputHandler { - private readonly handlerListener: HandlerListener; - private readonly server: net.Server; - private handler: string | number; + private readonly handlerListener: HandlerListener; + private readonly server: net.Server; + private handler: string | number; - public constructor(handler: string | number) { - this.server = net.createServer(); - this.handler = handler; - this.handlerListener = new HandlerListener(this.server); - } + public constructor(handler: string | number) { + this.server = net.createServer(); + this.handler = handler; + this.handlerListener = new HandlerListener(this.server); + } - public async subscribe(onMessageReceived: (requisition: any) => void): Promise { - return this.handlerListener.listen(this.handler) - .then(() => { - this.handler = this.handlerListener.getHandler(); - this.server.on('connection', (stream: any) => { - stream.on('data', (msg: any) => onMessageReceived({ - message: this.stringifyPayloadReceived(msg.payload || msg), - stream: stream - })); - }); - }); - } + public async mount(onMessageReceived: (task: any) => void): Promise { + return this.handlerListener.listen(this.handler).then(() => { + this.handler = this.handlerListener.getHandler(); + this.server.on('connection', (stream: any) => { + stream.on('data', (msg: any) => + onMessageReceived({ + message: this.stringifyPayloadReceived(msg.payload || msg), + stream: stream + }) + ); + }); + }); + } - public getHandler(): string | number { - return this.handler; - } + public getHandler(): string | number { + return this.handler; + } - public async unsubscribe(): Promise { - if (this.server) { - this.server.close(); - // @ts-ignore - delete this.server; - } + public async close(): Promise { + if (this.server) { + this.server.close(); + // @ts-ignore + delete this.server; } + } - public sendResponse(stream: any, message: any): Promise { - return new Promise((resolve, reject) => { - const strMsg = this.stringifyPayloadToSend(message); - try { - stream.write(strMsg, () => resolve()); - } catch (err) { - reject(`Error sending input handler: ${err}`); - } - }); - } + public respond(stream: any, message: any): Promise { + return new Promise((resolve, reject) => { + const strMsg = this.stringifyPayloadToSend(message); + try { + stream.write(strMsg, () => resolve()); + } catch (err) { + reject(`Error sending input handler: ${err}`); + } + }); + } - public close(stream: any) { - stream.end(); - stream = null; - } + public end(stream: any) { + stream.end(); + stream = null; + } - private stringifyPayloadReceived(message: string | Buffer): string { - const messageType = typeof(message); - if (messageType == 'string') { - return message as string; - } - return Buffer.from(message as Buffer).toString(); + private stringifyPayloadReceived(message: string | Buffer): string { + const messageType = typeof message; + if (messageType == 'string') { + return message as string; } + return Buffer.from(message as Buffer).toString(); + } - private stringifyPayloadToSend(payload: any): string | Buffer { - if (typeof(payload) == 'string' || Buffer.isBuffer(payload)) { - return payload; - } - return JSON.stringify(payload || {}); + private stringifyPayloadToSend(payload: any): string | Buffer { + if (typeof payload == 'string' || Buffer.isBuffer(payload)) { + return payload; } + return JSON.stringify(payload || {}); + } } diff --git a/src/http-authentications/http-authentication-factory.test.ts b/src/http-authentications/http-authentication-factory.test.ts index ceb58385..82b009ea 100644 --- a/src/http-authentications/http-authentication-factory.test.ts +++ b/src/http-authentications/http-authentication-factory.test.ts @@ -1,25 +1,25 @@ -import {HttpAuthenticationFactory} from './http-authentication-factory'; -import {HttpNoAuthentication} from './http-no-authentication'; -import {HttpBasicAuthentication} from './http-basic-authentication'; -import {HttpBearerAuthentication} from './http-bearer-authentication'; -import {HttpDigestAuthentication} from './http-digest-authentication'; +import { HttpAuthenticationFactory } from './http-authentication-factory'; +import { HttpNoAuthentication } from './http-no-authentication'; +import { HttpBasicAuthentication } from './http-basic-authentication'; +import { HttpBearerAuthentication } from './http-bearer-authentication'; +import { HttpDigestAuthentication } from './http-digest-authentication'; describe('HttpAuthenticationFactory', () => { + it('should return default', () => { + expect(new HttpAuthenticationFactory().create({})).toBeInstanceOf(HttpNoAuthentication); + }); - it('should return default', () => { - expect(new HttpAuthenticationFactory().create({})).toBeInstanceOf(HttpNoAuthentication); - }); + it('should return basic', () => { + expect(new HttpAuthenticationFactory().create({ basic: true })).toBeInstanceOf(HttpBasicAuthentication); + }); - it('should return basic', () => { - expect(new HttpAuthenticationFactory().create({basic: true})).toBeInstanceOf(HttpBasicAuthentication); - }); - - it('should return bearer', () => { - expect(new HttpAuthenticationFactory().create({bearer: {token: true}})).toBeInstanceOf(HttpBearerAuthentication); - }); - - it('should return digest', () => { - expect(new HttpAuthenticationFactory().create({digest: true})).toBeInstanceOf(HttpDigestAuthentication); - }); + it('should return bearer', () => { + expect(new HttpAuthenticationFactory().create({ bearer: { token: true } })).toBeInstanceOf( + HttpBearerAuthentication + ); + }); + it('should return digest', () => { + expect(new HttpAuthenticationFactory().create({ digest: true })).toBeInstanceOf(HttpDigestAuthentication); + }); }); diff --git a/src/http-authentications/http-authentication-factory.ts b/src/http-authentications/http-authentication-factory.ts index abdf36a7..1a265164 100644 --- a/src/http-authentications/http-authentication-factory.ts +++ b/src/http-authentications/http-authentication-factory.ts @@ -1,19 +1,18 @@ -import {HttpAuthentication} from './http-authentication'; -import {HttpBasicAuthentication} from './http-basic-authentication'; -import {HttpBearerAuthentication} from './http-bearer-authentication'; -import {HttpDigestAuthentication} from './http-digest-authentication'; -import {HttpNoAuthentication} from './http-no-authentication'; +import { HttpAuthentication } from './http-authentication'; +import { HttpBasicAuthentication } from './http-basic-authentication'; +import { HttpBearerAuthentication } from './http-bearer-authentication'; +import { HttpDigestAuthentication } from './http-digest-authentication'; +import { HttpNoAuthentication } from './http-no-authentication'; export class HttpAuthenticationFactory { - public create(component: any): HttpAuthentication { - if (component.basic) { - return new HttpBasicAuthentication(component); - } else if (component.bearer && component.bearer.token) { - return new HttpBearerAuthentication(component); - } else if (component.digest) { - return new HttpDigestAuthentication(component); - } - return new HttpNoAuthentication(component); - + public create(component: any): HttpAuthentication { + if (component.basic) { + return new HttpBasicAuthentication(component); + } else if (component.bearer && component.bearer.token) { + return new HttpBearerAuthentication(component); + } else if (component.digest) { + return new HttpDigestAuthentication(component); } + return new HttpNoAuthentication(component); + } } diff --git a/src/http-authentications/http-authentication.ts b/src/http-authentications/http-authentication.ts index a7a4c938..0627075c 100644 --- a/src/http-authentications/http-authentication.ts +++ b/src/http-authentications/http-authentication.ts @@ -1,6 +1,6 @@ -import {TestModel} from '../models/outputs/test-model'; +import { TestModel } from '../models/outputs/test-model'; export interface HttpAuthentication { - generate(): any; - verify(auth: any): TestModel[]; + generate(): any; + verify(auth: any): TestModel[]; } diff --git a/src/http-authentications/http-basic-authentication.test.ts b/src/http-authentications/http-basic-authentication.test.ts index db355c33..9d2e44a4 100644 --- a/src/http-authentications/http-basic-authentication.test.ts +++ b/src/http-authentications/http-basic-authentication.test.ts @@ -1,111 +1,111 @@ -import {HttpBasicAuthentication} from './http-basic-authentication'; +import { HttpBasicAuthentication } from './http-basic-authentication'; describe('HttpBasicAuthentication', () => { - - it('tests number', () => { - const authentication = { - basic: { - user: 'user', - password: 'password' - } - }; - const authorization: HttpBasicAuthentication = new HttpBasicAuthentication(authentication); - const verify = authorization.verify('Basic dXNlcjpwYXNzd29yZA'); - - expect(verify.length).toBe(4); - }); - - it('user:password', () => { - const authentication = { - basic: { - user: 'user', - password: 'password' - } - }; - const basic: HttpBasicAuthentication = new HttpBasicAuthentication(authentication); - - const value: any = basic.generate(); - - expect(value.authorization).toBe('Basic dXNlcjpwYXNzd29yZA=='); - }); - - it('Verify user:password', () => { - const authentication = { - basic: { - user: 'user', - password: 'password' - } - }; - const authorization: HttpBasicAuthentication = new HttpBasicAuthentication(authentication); - - const verify = authorization.verify('Basic dXNlcjpwYXNzd29yZA'); - - expect(verify.every((test) => test.valid)).toBeTruthy(); - }); - - it('No basic prefix', () => { - const authentication = { - basic: { - user: 'user', - password: 'password' - } - }; - const authorization: HttpBasicAuthentication = new HttpBasicAuthentication(authentication); - - const verify = authorization.verify('WrongPrefix dXNlcjpwYXNzd29yZA'); - - expect(verify.filter((test) => !test.valid)[0].name).toBe('"Basic" authentication prefix'); - }); - - it('alladin:OpenSesame', () => { - const authentication = { - basic: { - user: 'Aladdin', - password: 'OpenSesame' - } - }; - const basic: HttpBasicAuthentication = new HttpBasicAuthentication(authentication); - - const value: any = basic.generate(); - - expect(value.authorization).toBe('Basic QWxhZGRpbjpPcGVuU2VzYW1l'); - }); - - it('Verify alladin:OpenSesame', () => { - const authentication = { - basic: { - user: 'Aladdin', - password: 'OpenSesame' - } - }; - const authorization: HttpBasicAuthentication = new HttpBasicAuthentication(authentication); - - const verify = authorization.verify('Basic QWxhZGRpbjpPcGVuU2VzYW1l'); - - expect(verify.every((test) => test.valid)).toBeTruthy(); + it('tests number', () => { + const authentication = { + basic: { + user: 'user', + password: 'password' + } + }; + const authorization: HttpBasicAuthentication = new HttpBasicAuthentication(authentication); + const verify = authorization.verify('Basic dXNlcjpwYXNzd29yZA'); + + expect(verify.length).toBe(4); + }); + + it('user:password', () => { + const authentication = { + basic: { + user: 'user', + password: 'password' + } + }; + const basic: HttpBasicAuthentication = new HttpBasicAuthentication(authentication); + + const value: any = basic.generate(); + + expect(value.authorization).toBe('Basic dXNlcjpwYXNzd29yZA=='); + }); + + it('Verify user:password', () => { + const authentication = { + basic: { + user: 'user', + password: 'password' + } + }; + const authorization: HttpBasicAuthentication = new HttpBasicAuthentication(authentication); + + const verify = authorization.verify('Basic dXNlcjpwYXNzd29yZA'); + + expect(verify.every(test => test.valid)).toBeTruthy(); + }); + + it('No basic prefix', () => { + const authentication = { + basic: { + user: 'user', + password: 'password' + } + }; + const authorization: HttpBasicAuthentication = new HttpBasicAuthentication(authentication); + + const verify = authorization.verify('WrongPrefix dXNlcjpwYXNzd29yZA'); + + expect(verify.filter(test => !test.valid)[0].name).toBe('"Basic" authentication prefix'); + }); + + it('alladin:OpenSesame', () => { + const authentication = { + basic: { + user: 'Aladdin', + password: 'OpenSesame' + } + }; + const basic: HttpBasicAuthentication = new HttpBasicAuthentication(authentication); + + const value: any = basic.generate(); + + expect(value.authorization).toBe('Basic QWxhZGRpbjpPcGVuU2VzYW1l'); + }); + + it('Verify alladin:OpenSesame', () => { + const authentication = { + basic: { + user: 'Aladdin', + password: 'OpenSesame' + } + }; + const authorization: HttpBasicAuthentication = new HttpBasicAuthentication(authentication); + + const verify = authorization.verify('Basic QWxhZGRpbjpPcGVuU2VzYW1l'); + + expect(verify.every(test => test.valid)).toBeTruthy(); + }); + + it('Empty authentication is falsy', () => { + const authentication = { + basic: { + user: 'Aladdin', + password: 'OpenSesame' + } + }; + const authorization: HttpBasicAuthentication = new HttpBasicAuthentication(authentication); + + // @ts-ignore + const verify = authorization.verify(); + + expect(verify.some(test => test.valid)).toBeFalsy(); + }); + + it('Empty auth credentials', () => { + const authorization: HttpBasicAuthentication = new HttpBasicAuthentication({ + basic: '' }); - it('Empty authentication is falsy', () => { - const authentication = { - basic: { - user: 'Aladdin', - password: 'OpenSesame' - } - }; - const authorization: HttpBasicAuthentication = new HttpBasicAuthentication(authentication); - - // @ts-ignore - const verify = authorization.verify(); - - expect(verify.some(test => test.valid)).toBeFalsy(); - }); - - it('Empty auth credentials', () => { - const authorization: HttpBasicAuthentication = new HttpBasicAuthentication({basic: ''}); - - const verify = authorization.verify('Basic QWxhZGRpbjpPcGVuU2VzYW1l'); - - expect(verify.every((test) => test.valid)).toBeFalsy(); - }); + const verify = authorization.verify('Basic QWxhZGRpbjpPcGVuU2VzYW1l'); + expect(verify.every(test => test.valid)).toBeFalsy(); + }); }); diff --git a/src/http-authentications/http-basic-authentication.ts b/src/http-authentications/http-basic-authentication.ts index 81ba78be..fde80a37 100644 --- a/src/http-authentications/http-basic-authentication.ts +++ b/src/http-authentications/http-basic-authentication.ts @@ -1,95 +1,96 @@ -import {HttpAuthentication} from './http-authentication'; -import {TestModel} from '../models/outputs/test-model'; -import {Logger} from '../loggers/logger'; +import { HttpAuthentication } from './http-authentication'; +import { TestModel } from '../models/outputs/test-model'; +import { Logger } from '../loggers/logger'; export class HttpBasicAuthentication implements HttpAuthentication { + private readonly user: any; + private readonly password: any; + private tests: TestModel[] = []; - private readonly user: any; - private readonly password: any; - private tests: TestModel[] = []; + public constructor(authentication: any) { + this.user = authentication.basic.user || ''; + this.password = authentication.basic.password; + } - public constructor(authentication: any) { - this.user = authentication.basic.user || ''; - this.password = authentication.basic.password; - } - - public generate(): any { - return {'authorization': 'Basic ' + Buffer.from(`${this.user}:${this.password}`, 'ascii').toString('base64')}; - } + public generate(): any { + return { + authorization: 'Basic ' + Buffer.from(`${this.user}:${this.password}`, 'ascii').toString('base64') + }; + } - public verify(authorization: string): TestModel[] { - try { - const splittedAuthorization = authorization.split(' '); - const prefix = splittedAuthorization[0]; - const codedUserColonPass = splittedAuthorization[1]; + public verify(authorization: string): TestModel[] { + try { + const splittedAuthorization = authorization.split(' '); + const prefix = splittedAuthorization[0]; + const codedUserColonPass = splittedAuthorization[1]; - const decodedUserColonPass = Buffer.from(codedUserColonPass, 'base64').toString(); - const credentials = decodedUserColonPass.split(':'); - const plainUser = credentials[0]; - const plainPass = credentials[1]; - this.tests = []; - this.tests.push(this.authenticatePrefix(prefix)); - this.tests.push(this.authenticateUser(plainUser)); - this.tests.push(this.authenticatePassword(plainPass)); - } catch (err) { - Logger.error(`Error trying to authenticate: ${err}`); - } - this.tests.push(this.basicAuthentication()); - - return this.tests; + const decodedUserColonPass = Buffer.from(codedUserColonPass, 'base64').toString(); + const credentials = decodedUserColonPass.split(':'); + const plainUser = credentials[0]; + const plainPass = credentials[1]; + this.tests = []; + this.tests.push(this.authenticatePrefix(prefix)); + this.tests.push(this.authenticateUser(plainUser)); + this.tests.push(this.authenticatePassword(plainPass)); + } catch (err) { + Logger.error(`Error trying to authenticate: ${err}`); } + this.tests.push(this.basicAuthentication()); + + return this.tests; + } - private basicAuthentication() { - let test = { - name: '"Basic" authentication', - valid: false, - description: 'Fail to authenticate \'Basic\' authentication' - }; - if (this.tests.length > 0) { - if (this.tests.every(test => test.valid)) { - test.valid = true; - test.description = `Basic authentication is valid`; - } - } - return test; + private basicAuthentication() { + let test = { + name: '"Basic" authentication', + valid: false, + description: "Fail to authenticate 'Basic' authentication" + }; + if (this.tests.length > 0) { + if (this.tests.every(test => test.valid)) { + test.valid = true; + test.description = `Basic authentication is valid`; + } } + return test; + } - private authenticatePrefix(prefix: string) { - let test = { - name: '"Basic" authentication prefix', - valid: false, - description: `Prefix "Basic" was not found in Basic authentication. Got ${prefix} instead` - }; - if (prefix == 'Basic') { - test.valid = true; - test.description = `Prefix "Basic" was found`; - } - return test; + private authenticatePrefix(prefix: string) { + let test = { + name: '"Basic" authentication prefix', + valid: false, + description: `Prefix "Basic" was not found in Basic authentication. Got ${prefix} instead` + }; + if (prefix == 'Basic') { + test.valid = true; + test.description = `Prefix "Basic" was found`; } + return test; + } - private authenticateUser(user: string) { - let test = { - name: '"Basic" authentication user', - valid: false, - description: `User was not found. Got ${user} instead` - }; - if (user == this.user) { - test.valid = true; - test.description = `User found`; - } - return test; + private authenticateUser(user: string) { + let test = { + name: '"Basic" authentication user', + valid: false, + description: `User was not found. Got ${user} instead` + }; + if (user == this.user) { + test.valid = true; + test.description = `User found`; } + return test; + } - private authenticatePassword(pass: string) { - let test = { - name: '"Basic" authentication password', - valid: false, - description: `Password does not match. Got ${pass} instead` - }; - if (pass == this.password) { - test.valid = true; - test.description = `Password match`; - } - return test; + private authenticatePassword(pass: string) { + let test = { + name: '"Basic" authentication password', + valid: false, + description: `Password does not match. Got ${pass} instead` + }; + if (pass == this.password) { + test.valid = true; + test.description = `Password match`; } + return test; + } } diff --git a/src/http-authentications/http-bearer-authentication.test.ts b/src/http-authentications/http-bearer-authentication.test.ts index a6661398..7b40d17e 100644 --- a/src/http-authentications/http-bearer-authentication.test.ts +++ b/src/http-authentications/http-bearer-authentication.test.ts @@ -1,61 +1,60 @@ -import {HttpBearerAuthentication} from './http-bearer-authentication'; +import { HttpBearerAuthentication } from './http-bearer-authentication'; describe('HttpBearerAuthentication', () => { + const BEARER_TOKEN = 'Bearer dXNlcjpwYXNzd29yZA'; - const BEARER_TOKEN = 'Bearer dXNlcjpwYXNzd29yZA'; + const DEFAULT_AUTH = { + bearer: { + token: 'dXNlcjpwYXNzd29yZA' + } + }; - const DEFAULT_AUTH = { - bearer: { - token: 'dXNlcjpwYXNzd29yZA' - } - }; + it('tests number', () => { + const authorization: HttpBearerAuthentication = new HttpBearerAuthentication(DEFAULT_AUTH); + const verify = authorization.verify(BEARER_TOKEN); - it('tests number', () => { - const authorization: HttpBearerAuthentication = new HttpBearerAuthentication(DEFAULT_AUTH); - const verify = authorization.verify(BEARER_TOKEN); + expect(verify.length).toBe(3); + }); - expect(verify.length).toBe(3); - }); + it('Verify token', () => { + const authorization: HttpBearerAuthentication = new HttpBearerAuthentication(DEFAULT_AUTH); - it('Verify token', () => { - const authorization: HttpBearerAuthentication = new HttpBearerAuthentication(DEFAULT_AUTH); + const verify = authorization.verify(BEARER_TOKEN); - const verify = authorization.verify(BEARER_TOKEN); + expect(verify.every(test => test.valid)).toBeTruthy(); + }); - expect(verify.every((test) => test.valid)).toBeTruthy(); - }); + it('Verify token unmatch', () => { + const authorization: HttpBearerAuthentication = new HttpBearerAuthentication(DEFAULT_AUTH); - it('Verify token unmatch', () => { - const authorization: HttpBearerAuthentication = new HttpBearerAuthentication(DEFAULT_AUTH); + const verify = authorization.verify('Bearer tokenUnmatch'); + const bearerTokenTest = verify.filter(test => test.name == '"Bearer" authentication token')[0]; - const verify = authorization.verify('Bearer tokenUnmatch'); - const bearerTokenTest = verify.filter((test) => test.name == '"Bearer" authentication token')[0]; + expect(bearerTokenTest.valid).toBeFalsy(); + }); - expect(bearerTokenTest.valid).toBeFalsy(); - }); + it('No Bearer prefix', () => { + const authorization: HttpBearerAuthentication = new HttpBearerAuthentication(DEFAULT_AUTH); - it('No Bearer prefix', () => { - const authorization: HttpBearerAuthentication = new HttpBearerAuthentication(DEFAULT_AUTH); + const verify = authorization.verify('WrongPrefix dXNlcjpwYXNzd29yZA'); - const verify = authorization.verify('WrongPrefix dXNlcjpwYXNzd29yZA'); + expect(verify.filter(test => !test.valid)[0].name).toBe('"Bearer" authentication prefix'); + }); - expect(verify.filter((test) => !test.valid)[0].name).toBe('"Bearer" authentication prefix'); - }); + it('Generate authorization based on token', () => { + const bearer: HttpBearerAuthentication = new HttpBearerAuthentication(DEFAULT_AUTH); - it('Generate authorization based on token', () => { - const bearer: HttpBearerAuthentication = new HttpBearerAuthentication(DEFAULT_AUTH); + const value: any = bearer.generate(); - const value: any = bearer.generate(); + expect(value.authorization).toBe(BEARER_TOKEN); + }); - expect(value.authorization).toBe(BEARER_TOKEN); - }); + it('Empty authentication is falsy', () => { + const authorization: HttpBearerAuthentication = new HttpBearerAuthentication(DEFAULT_AUTH); - it('Empty authentication is falsy', () => { - const authorization: HttpBearerAuthentication = new HttpBearerAuthentication(DEFAULT_AUTH); + // @ts-ignore + const verify = authorization.verify(); - // @ts-ignore - const verify = authorization.verify(); - - expect(verify.some(test => test.valid)).toBeFalsy(); - }); + expect(verify.some(test => test.valid)).toBeFalsy(); + }); }); diff --git a/src/http-authentications/http-bearer-authentication.ts b/src/http-authentications/http-bearer-authentication.ts index 1ad1c6b2..27585ac6 100644 --- a/src/http-authentications/http-bearer-authentication.ts +++ b/src/http-authentications/http-bearer-authentication.ts @@ -1,71 +1,70 @@ -import {HttpAuthentication} from './http-authentication'; -import {TestModel} from '../models/outputs/test-model'; -import {Logger} from '../loggers/logger'; +import { HttpAuthentication } from './http-authentication'; +import { TestModel } from '../models/outputs/test-model'; +import { Logger } from '../loggers/logger'; export class HttpBearerAuthentication implements HttpAuthentication { + private readonly token: any; - private readonly token: any; + public constructor(authentication: any) { + this.token = authentication.bearer.token; + } - public constructor(authentication: any) { - this.token = authentication.bearer.token; - } + public generate(): any { + return { authorization: 'Bearer ' + this.token }; + } - public generate(): any { - return {'authorization': 'Bearer ' + this.token}; + public verify(authorization: string): TestModel[] { + const tests = []; + try { + const token = authorization.split(' ')[1]; + tests.push(this.authenticatePrefix(authorization.split(' ')[0])); + tests.push(this.authenticateToken(token)); + } catch (err) { + Logger.error(`Error trying to authenticate: ${err}`); } + tests.push(this.bearerAuthentication(tests)); - public verify(authorization: string): TestModel[] { - const tests = []; - try { - const token = authorization.split(' ')[1]; - tests.push(this.authenticatePrefix(authorization.split(' ')[0])); - tests.push(this.authenticateToken(token)); - } catch (err) { - Logger.error(`Error trying to authenticate: ${err}`); - } - tests.push(this.bearerAuthentication(tests)); - - return tests; - } + return tests; + } - private bearerAuthentication(tests: TestModel[]) { - let test = { - name: '"Bearer" authentication', - valid: false, - description: 'Fail to authenticate \'Bearer\' authentication' - }; - if (tests.length > 0) { - if (tests.every(test => test.valid)) { - test.valid = true; - test.description = `Bearer authentication is valid`; - } - } - return test; + private bearerAuthentication(tests: TestModel[]) { + let test = { + name: '"Bearer" authentication', + valid: false, + description: "Fail to authenticate 'Bearer' authentication" + }; + if (tests.length > 0) { + if (tests.every(test => test.valid)) { + test.valid = true; + test.description = `Bearer authentication is valid`; + } } + return test; + } - private authenticatePrefix(prefix: string) { - let test = { - name: '"Bearer" authentication prefix', - valid: false, - description: `Prefix "Bearer" was not found in Bearer authentication. Got ${prefix} instead` - }; - if (prefix == 'Bearer') { - test.valid = true; - test.description = `Prefix "Bearer" was found.`; - } - return test; + private authenticatePrefix(prefix: string) { + let test = { + name: '"Bearer" authentication prefix', + valid: false, + description: `Prefix "Bearer" was not found in Bearer authentication. Got ${prefix} instead` + }; + if (prefix == 'Bearer') { + test.valid = true; + test.description = `Prefix "Bearer" was found.`; } + return test; + } - private authenticateToken(token: string) { - let test = { - name: '"Bearer" authentication token', - valid: false, - description: `Token does not match. Got ${token} instead` - }; - if (token == this.token) { - test.valid = true; - test.description = `Token match`; - } - return test; + private authenticateToken(token: string) { + let test = { + name: '"Bearer" authentication token', + valid: false, + description: `Token does not match. Got ${token} instead` + }; + if (token == this.token) { + test.valid = true; + test.description = `Token match`; } + return test; + } } diff --git a/src/http-authentications/http-digest-authentication.test.ts b/src/http-authentications/http-digest-authentication.test.ts index 2ba15fb9..0742bf2e 100644 --- a/src/http-authentications/http-digest-authentication.test.ts +++ b/src/http-authentications/http-digest-authentication.test.ts @@ -1,270 +1,280 @@ -import {HttpDigestAuthentication} from './http-digest-authentication'; +import { HttpDigestAuthentication } from './http-digest-authentication'; describe('HttpDigestAuthentication', () => { - it('generate MD5-sess', () => { - const authentication = { - digest: { - username: 'guest', - password: 'guest', - realm: 'nqrRealm', - - method: 'GET', - algorithm: HttpDigestAuthentication.MD5_SESS, - uri: '/auth/02-2617.php', - - nonce: '58bac26865505', - nonceCount: '00000001', - clientNonce: '72ae56dde9406045', - opaque: 'opaque', - qop: 'auth' - } - }; - const digest: HttpDigestAuthentication = new HttpDigestAuthentication(authentication); - - const value: any = digest.generate(); - - const expected = { - authorization: 'Digest username="guest",' + - ' realm="nqrRealm",' + - ' nonce="58bac26865505",' + - ' uri="/auth/02-2617.php",' + - ' algorithm="MD5-sess",' + - ' response="9e254ee246ca9f39a82048580b2e7e53",' + - ' opaque="opaque"' - }; - - expect(value).toEqual(expected); - }); - - it('first hash', () => { - const authentication = { - digest: { - username: 'guest', - password: 'guest', - realm: 'nqrRealm' - } - }; - const digest: HttpDigestAuthentication = new HttpDigestAuthentication(authentication); - - const value: any = digest.firstHash(); - - expect(value).toBe('a429de301f1bd4aa42fbf8d52e5d6b28'); - }); - - it('second hash', () => { - const authentication = { - digest: { - method: 'GET', - uri: '/auth/02-2617.php' - } - }; - const digest: HttpDigestAuthentication = new HttpDigestAuthentication(authentication); - - const value: any = digest.secondHash(); - - expect(value).toBe('b6a6df472ee01a9dbccba5f5e6271ca8'); - }); - - it('response MD5', () => { - const authentication = { - digest: { - username: 'guest', - password: 'guest', - realm: 'nqrRealm', - - method: 'GET', - uri: '/auth/02-2617.php', - - opaque: 'opaque', - nonce: '58bac26865505', - nonceCount: '00000001', - clientNonce: '72ae56dde9406045', - qop: 'auth' - } - }; - const digest: HttpDigestAuthentication = new HttpDigestAuthentication(authentication); - - const value: any = digest.generate(); - - const expected = { - authorization: 'Digest username="guest",' + - ' realm="nqrRealm",' + - ' nonce="58bac26865505",' + - ' uri="/auth/02-2617.php",' + - ' algorithm="MD5",' + - ' response="a42e17afd5200c6f2dab4e278cfe39bf",' + - ' opaque="opaque"' - }; - - expect(value).toEqual(expected); - }); - - it('verify', () => { - const authentication = { - digest: { - username: 'guest', - password: 'guest', - realm: 'nqrRealm', - - method: 'GET', - uri: '/auth/02-2617.php', - - opaque: 'opaque', - nonce: '58bac26865505', - nonceCount: '00000001', - clientNonce: '72ae56dde9406045', - qop: 'auth' - } - }; - const digest: HttpDigestAuthentication = new HttpDigestAuthentication(authentication); - - const authorization = 'Digest username="guest",' + - ' realm="nqrRealm",' + - ' nonce="58bac26865505",' + - ' uri="/auth/02-2617.php",' + - ' algorithm="MD5",' + - ' response="a42e17afd5200c6f2dab4e278cfe39bf",' + - ' opaque="opaque"'; - - const tests: any = digest.verify(authorization); - - expect(tests.every(test => test.valid)).toBeTruthy(); - }); - - it('prefix was not found', () => { - const authentication = { - digest: {} - }; - const digest: HttpDigestAuthentication = new HttpDigestAuthentication(authentication); - - const authorization = 'NotDigest username="guest"'; - - const tests: any = digest.verify(authorization); - - expect(tests.some(test => !test.valid)).toBeTruthy(); - expect(tests.find(test => test.name == '"Digest" authentication prefix').valid).toBeFalsy(); - }); - - it('response is not ok', () => { - const authentication = { - digest: { - username: 'guest', - password: 'guest', - realm: 'nqrRealm', - - method: 'GET', - uri: '/auth/02-2617.php', - - nonce: '58bac26865505', - nonceCount: '00000001', - opaque: 'opaque', - clientNonce: '72ae56dde9406045', - qop: 'auth' - } - }; - const digest: HttpDigestAuthentication = new HttpDigestAuthentication(authentication); - - const authorization = 'Digest username="guest",' + - ' realm="nqrRealm",' + - ' nonce="58bac26865505",' + - ' uri="/auth/02-2617.php",' + - ' algorithm="MD5",' + - ' response="obviouslyWrong",' + - ' opaque="opaque"'; - - const tests: any = digest.verify(authorization); - - expect(tests.some(test => !test.valid)).toBeTruthy(); - expect(tests.find(test => test.name == '"Response" authentication value').valid).toBeFalsy(); - }); - - it('some value (username) does not match', () => { - const authentication = { - digest: { - username: 'WRONG', - password: 'guest', - realm: 'nqrRealm', - - method: 'GET', - uri: '/auth/02-2617.php', - - opaque: 'opaque', - nonce: '58bac26865505', - nonceCount: '00000001', - clientNonce: '72ae56dde9406045', - qop: 'auth' - } - }; - const digest: HttpDigestAuthentication = new HttpDigestAuthentication(authentication); - - const authorization = 'Digest username="guest",' + - ' realm="nqrRealm",' + - ' nonce="58bac26865505",' + - ' uri="/auth/02-2617.php",' + - ' algorithm="MD5",' + - ' response="obviouslyWrong",' + - ' opaque="opaque"'; - - const tests: any = digest.verify(authorization); - - expect(tests.some(test => !test.valid)).toBeTruthy(); - expect(tests.find(test => test.name == `"username" value does not match`).valid).toBeFalsy(); - }); - - it('essential fields are missing as constructor attribute', () => { - const authentication = { - digest: { - password: 'guest', - realm: 'nqrRealm', - opaque: 'opaque' - } - }; - const digest: HttpDigestAuthentication = new HttpDigestAuthentication(authentication); - - const authorization = 'Digest username="guest",' + - ' realm="nqrRealm",' + - ' nonce="58bac26865505",' + - ' uri="/auth/02-2617.php",' + - ' algorithm="MD5",' + - ' response="obviouslyWrong",' + - ' opaque="opaque"'; - - const tests: any = digest.verify(authorization); - - expect(tests.some(test => !test.valid)).toBeTruthy(); - expect(tests.find(test => test.name == `"username" was not found as attribute`).valid).toBeFalsy(); - }); - - it('essential fields are missing as authorization string', () => { - const authentication = { - digest: { - username: 'WRONG', - password: 'guest', - realm: 'nqrRealm', - - method: 'GET', - uri: '/auth/02-2617.php', - - opaque: 'opaque', - nonce: '58bac26865505', - nonceCount: '00000001', - clientNonce: '72ae56dde9406045', - qop: 'auth' - } - }; - const digest: HttpDigestAuthentication = new HttpDigestAuthentication(authentication); - - const authorization = 'Digest username="guest",' + - ' realm="nqrRealm",' + - ' nonce="58bac26865505",' + - ' uri="/auth/02-2617.php",' + - ' response="obviouslyWrong",' + - ' opaque="opaque"'; - - const tests: any = digest.verify(authorization); - - expect(tests.some(test => !test.valid)).toBeTruthy(); - expect(tests.find(test => test.name == `Every field was found`).valid).toBeFalsy(); - }); - + it('generate MD5-sess', () => { + const authentication = { + digest: { + username: 'guest', + password: 'guest', + realm: 'nqrRealm', + + method: 'GET', + algorithm: HttpDigestAuthentication.MD5_SESS, + uri: '/auth/02-2617.php', + + nonce: '58bac26865505', + nonceCount: '00000001', + clientNonce: '72ae56dde9406045', + opaque: 'opaque', + qop: 'auth' + } + }; + const digest: HttpDigestAuthentication = new HttpDigestAuthentication(authentication); + + const value: any = digest.generate(); + + const expected = { + authorization: + 'Digest username="guest",' + + ' realm="nqrRealm",' + + ' nonce="58bac26865505",' + + ' uri="/auth/02-2617.php",' + + ' algorithm="MD5-sess",' + + ' response="9e254ee246ca9f39a82048580b2e7e53",' + + ' opaque="opaque"' + }; + + expect(value).toEqual(expected); + }); + + it('first hash', () => { + const authentication = { + digest: { + username: 'guest', + password: 'guest', + realm: 'nqrRealm' + } + }; + const digest: HttpDigestAuthentication = new HttpDigestAuthentication(authentication); + + // @ts-expect-error + const value: any = digest.firstHash(); + + expect(value).toBe('a429de301f1bd4aa42fbf8d52e5d6b28'); + }); + + it('second hash', () => { + const authentication = { + digest: { + method: 'GET', + uri: '/auth/02-2617.php' + } + }; + const digest: HttpDigestAuthentication = new HttpDigestAuthentication(authentication); + + // @ts-expect-error + const value: any = digest.secondHash(); + + expect(value).toBe('b6a6df472ee01a9dbccba5f5e6271ca8'); + }); + + it('response MD5', () => { + const authentication = { + digest: { + username: 'guest', + password: 'guest', + realm: 'nqrRealm', + + method: 'GET', + uri: '/auth/02-2617.php', + + opaque: 'opaque', + nonce: '58bac26865505', + nonceCount: '00000001', + clientNonce: '72ae56dde9406045', + qop: 'auth' + } + }; + const digest: HttpDigestAuthentication = new HttpDigestAuthentication(authentication); + + const value: any = digest.generate(); + + const expected = { + authorization: + 'Digest username="guest",' + + ' realm="nqrRealm",' + + ' nonce="58bac26865505",' + + ' uri="/auth/02-2617.php",' + + ' algorithm="MD5",' + + ' response="a42e17afd5200c6f2dab4e278cfe39bf",' + + ' opaque="opaque"' + }; + + expect(value).toEqual(expected); + }); + + it('verify', () => { + const authentication = { + digest: { + username: 'guest', + password: 'guest', + realm: 'nqrRealm', + + method: 'GET', + uri: '/auth/02-2617.php', + + opaque: 'opaque', + nonce: '58bac26865505', + nonceCount: '00000001', + clientNonce: '72ae56dde9406045', + qop: 'auth' + } + }; + const digest: HttpDigestAuthentication = new HttpDigestAuthentication(authentication); + + const authorization = + 'Digest username="guest",' + + ' realm="nqrRealm",' + + ' nonce="58bac26865505",' + + ' uri="/auth/02-2617.php",' + + ' algorithm="MD5",' + + ' response="a42e17afd5200c6f2dab4e278cfe39bf",' + + ' opaque="opaque"'; + + const tests: any = digest.verify(authorization); + + expect(tests.every((test: { valid: any }) => test.valid)).toBeTruthy(); + }); + + it('prefix was not found', () => { + const authentication = { + digest: {} + }; + const digest: HttpDigestAuthentication = new HttpDigestAuthentication(authentication); + + const authorization = 'NotDigest username="guest"'; + + const tests: any = digest.verify(authorization); + + expect(tests.some((test: { valid: any }) => !test.valid)).toBeTruthy(); + expect(tests.find((test: { name: string }) => test.name == '"Digest" authentication prefix').valid).toBeFalsy(); + }); + + it('response is not ok', () => { + const authentication = { + digest: { + username: 'guest', + password: 'guest', + realm: 'nqrRealm', + + method: 'GET', + uri: '/auth/02-2617.php', + + nonce: '58bac26865505', + nonceCount: '00000001', + opaque: 'opaque', + clientNonce: '72ae56dde9406045', + qop: 'auth' + } + }; + const digest: HttpDigestAuthentication = new HttpDigestAuthentication(authentication); + + const authorization = + 'Digest username="guest",' + + ' realm="nqrRealm",' + + ' nonce="58bac26865505",' + + ' uri="/auth/02-2617.php",' + + ' algorithm="MD5",' + + ' response="obviouslyWrong",' + + ' opaque="opaque"'; + + const tests: any = digest.verify(authorization); + + expect(tests.some((test: { valid: any }) => !test.valid)).toBeTruthy(); + expect(tests.find((test: { name: string }) => test.name == '"Response" authentication value').valid).toBeFalsy(); + }); + + it('some value (username) does not match', () => { + const authentication = { + digest: { + username: 'WRONG', + password: 'guest', + realm: 'nqrRealm', + + method: 'GET', + uri: '/auth/02-2617.php', + + opaque: 'opaque', + nonce: '58bac26865505', + nonceCount: '00000001', + clientNonce: '72ae56dde9406045', + qop: 'auth' + } + }; + const digest: HttpDigestAuthentication = new HttpDigestAuthentication(authentication); + + const authorization = + 'Digest username="guest",' + + ' realm="nqrRealm",' + + ' nonce="58bac26865505",' + + ' uri="/auth/02-2617.php",' + + ' algorithm="MD5",' + + ' response="obviouslyWrong",' + + ' opaque="opaque"'; + + const tests: any = digest.verify(authorization); + + expect(tests.some((test: { valid: any }) => !test.valid)).toBeTruthy(); + expect(tests.find((test: { name: string }) => test.name == `"username" value does not match`).valid).toBeFalsy(); + }); + + it('essential fields are missing as constructor attribute', () => { + const authentication = { + digest: { + password: 'guest', + realm: 'nqrRealm', + opaque: 'opaque' + } + }; + const digest: HttpDigestAuthentication = new HttpDigestAuthentication(authentication); + + const authorization = + 'Digest username="guest",' + + ' realm="nqrRealm",' + + ' nonce="58bac26865505",' + + ' uri="/auth/02-2617.php",' + + ' algorithm="MD5",' + + ' response="obviouslyWrong",' + + ' opaque="opaque"'; + + const tests: any = digest.verify(authorization); + + expect(tests.some((test: { valid: any }) => !test.valid)).toBeTruthy(); + expect( + tests.find((test: { name: string }) => test.name == `"username" was not found as attribute`).valid + ).toBeFalsy(); + }); + + it('essential fields are missing as authorization string', () => { + const authentication = { + digest: { + username: 'WRONG', + password: 'guest', + realm: 'nqrRealm', + + method: 'GET', + uri: '/auth/02-2617.php', + + opaque: 'opaque', + nonce: '58bac26865505', + nonceCount: '00000001', + clientNonce: '72ae56dde9406045', + qop: 'auth' + } + }; + const digest: HttpDigestAuthentication = new HttpDigestAuthentication(authentication); + + const authorization = + 'Digest username="guest",' + + ' realm="nqrRealm",' + + ' nonce="58bac26865505",' + + ' uri="/auth/02-2617.php",' + + ' response="obviouslyWrong",' + + ' opaque="opaque"'; + + const tests: any = digest.verify(authorization); + + expect(tests.some((test: { valid: any }) => !test.valid)).toBeTruthy(); + expect(tests.find((test: { name: string }) => test.name == `Every field was found`).valid).toBeFalsy(); + }); }); diff --git a/src/http-authentications/http-digest-authentication.ts b/src/http-authentications/http-digest-authentication.ts index 117d3cf1..edc7cec3 100644 --- a/src/http-authentications/http-digest-authentication.ts +++ b/src/http-authentications/http-digest-authentication.ts @@ -1,184 +1,182 @@ -import {HttpAuthentication} from './http-authentication'; -import {TestModel} from '../models/outputs/test-model'; -import {createHash} from 'crypto'; +import { HttpAuthentication } from './http-authentication'; +import { TestModel } from '../models/outputs/test-model'; +import { createHash } from 'crypto'; export class HttpDigestAuthentication implements HttpAuthentication { - public static MD5_SESS = 'MD5-sess'; - - private readonly qop: string; - private readonly algorithm: string; - private readonly nonce: string; - private readonly nonceCount: string; - private readonly clientNonce: string; - private readonly method: string; - private readonly uri: any; - private readonly username: string; - private readonly realm: string; - private readonly password: string; - private readonly opaque: string; - - public constructor(authentication: any) { - const digest = authentication.digest; - this.qop = digest.qop; - this.algorithm = digest.algorithm; - this.nonce = digest.nonce; - this.nonceCount = digest.nonceCount; - this.clientNonce = digest.clientNonce; - this.method = digest.method; - this.uri = digest.uri; - this.username = digest.username; - this.realm = digest.realm; - this.password = digest.password; - this.opaque = digest.opaque; - - if (HttpDigestAuthentication.MD5_SESS !== this.algorithm) { - this.algorithm = 'MD5'; - } + public static MD5_SESS = 'MD5-sess'; + + private readonly qop: string; + private readonly algorithm: string; + private readonly nonce: string; + private readonly nonceCount: string; + private readonly clientNonce: string; + private readonly method: string; + private readonly uri: any; + private readonly username: string; + private readonly realm: string; + private readonly password: string; + private readonly opaque: string; + + public constructor(authentication: any) { + const digest = authentication.digest; + this.qop = digest.qop; + this.algorithm = digest.algorithm; + this.nonce = digest.nonce; + this.nonceCount = digest.nonceCount; + this.clientNonce = digest.clientNonce; + this.method = digest.method; + this.uri = digest.uri; + this.username = digest.username; + this.realm = digest.realm; + this.password = digest.password; + this.opaque = digest.opaque; + + if (HttpDigestAuthentication.MD5_SESS !== this.algorithm) { + this.algorithm = 'MD5'; } - - public generate(): any { - const response = this.generateResponse(); - return {'authorization': this.createDigestValue(response)}; + } + + public generate(): any { + const response = this.generateResponse(); + return { authorization: this.createDigestValue(response) }; + } + + public verify(authorization: string): TestModel[] { + const tests: TestModel[] = []; + const parts = authorization.split(' '); + tests.push(this.checkDigestPrefix(parts[0])); + const params = parts.slice(1).join(' '); + const tokens = params.split(/,(?=(?:[^"]|"[^"]*")*$)/) || []; + const essentialFields = this.buildEssentialFields(); + tokens.forEach(token => tests.push(this.analyzeToken(token, essentialFields))); + tests.push(this.checkEssentialFieldsMissingInAuthorization(essentialFields)); + return tests; + } + + private buildEssentialFields() { + const essentials: any = Object.assign({}, this); + delete essentials.response; + delete essentials.password; + delete essentials.qop; + delete essentials.nonceCount; + delete essentials.clientNonce; + delete essentials.method; + return essentials; + } + + private generateResponse() { + const hash1 = this.firstHash(); + const hash2 = this.secondHash(); + + if (this.qop) { + return this.md5(`${hash1}:${this.nonce}:${this.nonceCount}:${this.clientNonce}:${this.qop}:${hash2}`); + } else { + return this.md5(`${hash1}:${this.nonce}:${hash2}`); } + } - public verify(authorization: string): TestModel[] { - const tests: TestModel[] = []; - const parts = authorization.split(' '); - tests.push(this.checkDigestPrefix(parts[0])); - const params = parts.slice(1).join(' '); - const tokens = params.split(/,(?=(?:[^"]|"[^"]*")*$)/) || []; - const essentialFields = this.buildEssentialFields(); - tokens.forEach((token) => tests.push(this.analyzeToken(token, essentialFields))); - tests.push(this.checkEssentialFieldsMissingInAuthorization(essentialFields)); - return tests; - } + private secondHash() { + return this.md5(`${this.method}:${this.uri}`); + } - private buildEssentialFields() { - const essentials: any = Object.assign({}, this); - delete essentials.response; - delete essentials.password; - delete essentials.qop; - delete essentials.nonceCount; - delete essentials.clientNonce; - delete essentials.method; - return essentials; + private firstHash() { + // Hash1=MD5(username:realm:password) + let hash1 = this.md5(`${this.username}:${this.realm}:${this.password}`); + if (this.algorithm === HttpDigestAuthentication.MD5_SESS) { + hash1 = this.md5(`${hash1}:${this.nonce}:${this.clientNonce}`); } - - private generateResponse() { - const hash1 = this.firstHash(); - const hash2 = this.secondHash(); - - if (this.qop) { - return this.md5(`${hash1}:${this.nonce}:${this.nonceCount}:${this.clientNonce}:${this.qop}:${hash2}`); - } else { - return this.md5(`${hash1}:${this.nonce}:${hash2}`); - } + return hash1; + } + + private md5(value: string): string { + return createHash('MD5').update(value).digest('hex'); + } + + private createDigestValue(response: string) { + let value = `Digest`; + value += ` username="${this.username}"`; + value += `, realm="${this.realm}"`; + value += `, nonce="${this.nonce}"`; + value += `, uri="${this.uri}"`; + value += `, algorithm="${this.algorithm}"`; + value += `, response="${response}"`; + value += `, opaque="${this.opaque}"`; + return value; + } + + private analyzeToken(token: string, essentialFields: any): TestModel { + let value = token.split('='); + const key = value[0].trimRight().trimLeft(); + const self = essentialFields[key]; + delete essentialFields[key]; + if (key == 'response') { + return this.checkResponseValue(value[1]); } - - private secondHash() { - return this.md5(`${this.method}:${this.uri}`); + if (!self) { + return this.attributeNotFoundTest(key); } + const thisValue = `"${self}"`; - private firstHash() { - // Hash1=MD5(username:realm:password) - let hash1 = this.md5(`${this.username}:${this.realm}:${this.password}`); - if (this.algorithm === HttpDigestAuthentication.MD5_SESS) { - hash1 = this.md5(`${hash1}:${this.nonce}:${this.clientNonce}`); - } - return hash1; - } + let test = { + name: `"${key}" value does not match`, + valid: false, + description: `Expected '${key}'. Got '${value[1]}' instead` + }; - private md5(value: string): string { - return createHash('MD5') - .update(value) - .digest('hex'); + if (thisValue == value[1]) { + test.valid = true; + test.description = `Calculated "${key}" value matches`; } - - private createDigestValue(response: string) { - let value = `Digest`; - value += ` username="${this.username}"`; - value += `, realm="${this.realm}"`; - value += `, nonce="${this.nonce}"`; - value += `, uri="${this.uri}"`; - value += `, algorithm="${this.algorithm}"`; - value += `, response="${response}"`; - value += `, opaque="${this.opaque}"`; - return value; + return test; + } + + private attributeNotFoundTest(key: string) { + return { + name: `"${key}" was not found as attribute`, + valid: false, + description: `Expected to find '${key}' but got nothing` + }; + } + + private checkResponseValue(response?: string): TestModel { + const generateResponse = `"${this.generateResponse()}"`; + let test = { + name: '"Response" authentication value', + valid: false, + description: `Calculated "response" value does not match` + }; + + if (response && response == generateResponse) { + test.valid = true; + test.description = `Calculated "response" value matches`; } - - private analyzeToken(token: string, essentialFields: any): TestModel { - let value = token.split('='); - const key = value[0].trimRight().trimLeft(); - const self = (essentialFields)[key]; - delete essentialFields[key]; - if (key == 'response') { - return this.checkResponseValue(value[1]); - } - if (!self) { - return this.attributeNotFoundTest(key); - } - const thisValue = `"${self}"`; - - let test = { - name: `"${key}" value does not match`, - valid: false, - description: `Expected '${key}'. Got '${value[1]}' instead` - }; - - if (thisValue == value[1]) { - test.valid = true; - test.description = `Calculated "${key}" value matches`; - } - return test; + return test; + } + + private checkDigestPrefix(prefix: string): TestModel { + let test = { + name: '"Digest" authentication prefix', + valid: false, + description: `Prefix "Digest" was not found in Basic authentication. Got ${prefix} instead` + }; + if (prefix == 'Digest') { + test.valid = true; + test.description = `Prefix "Digest" was found`; } - - private attributeNotFoundTest(key: string) { - return { - name: `"${key}" was not found as attribute`, - valid: false, - description: `Expected to find '${key}' but got nothing` - }; - } - - private checkResponseValue(response?: string): TestModel { - const generateResponse = `"${this.generateResponse()}"`; - let test = { - name: '"Response" authentication value', - valid: false, - description: `Calculated "response" value does not match` - }; - - if (response && response == generateResponse) { - test.valid = true; - test.description = `Calculated "response" value matches`; - } - return test; - } - - private checkDigestPrefix(prefix: string): TestModel { - let test = { - name: '"Digest" authentication prefix', - valid: false, - description: `Prefix "Digest" was not found in Basic authentication. Got ${prefix} instead` - }; - if (prefix == 'Digest') { - test.valid = true; - test.description = `Prefix "Digest" was found`; - } - return test; - } - - private checkEssentialFieldsMissingInAuthorization(selfCopy: any) { - const notUsedKeys = Object.keys(selfCopy) || []; - let test = { - name: 'Every field was found', - valid: false, - description: `Keys '${notUsedKeys.join('; ')}' were not found in authorization` - }; - if (notUsedKeys.length == 0) { - test.valid = true; - test.description = `Every field was found`; - } - return test; + return test; + } + + private checkEssentialFieldsMissingInAuthorization(selfCopy: any) { + const notUsedKeys = Object.keys(selfCopy) || []; + let test = { + name: 'Every field was found', + valid: false, + description: `Keys '${notUsedKeys.join('; ')}' were not found in authorization` + }; + if (notUsedKeys.length == 0) { + test.valid = true; + test.description = `Every field was found`; } + return test; + } } diff --git a/src/http-authentications/http-no-authentication.test.ts b/src/http-authentications/http-no-authentication.test.ts index 88e910fe..d248e20d 100644 --- a/src/http-authentications/http-no-authentication.test.ts +++ b/src/http-authentications/http-no-authentication.test.ts @@ -1,21 +1,19 @@ -import {HttpNoAuthentication} from './http-no-authentication'; +import { HttpNoAuthentication } from './http-no-authentication'; describe('HttpNoAuthentication', () => { + it('generate', () => { + const no: HttpNoAuthentication = new HttpNoAuthentication({}); - it('generate', () => { - const no: HttpNoAuthentication = new HttpNoAuthentication({}); + const value: any = no.generate(); - const value: any = no.generate(); + expect(value).toBeNull(); + }); - expect(value).toBeNull(); - }); + it('verify', () => { + const no: HttpNoAuthentication = new HttpNoAuthentication({}); - it('verify', () => { - const no: HttpNoAuthentication = new HttpNoAuthentication({}); - - const value = no.verify(no.generate()); - - expect(value.every(test => !test.valid)).toBeTruthy(); - }); + const value = no.verify(no.generate()); + expect(value.every(test => !test.valid)).toBeTruthy(); + }); }); diff --git a/src/http-authentications/http-no-authentication.ts b/src/http-authentications/http-no-authentication.ts index e94d9d91..62c54d57 100644 --- a/src/http-authentications/http-no-authentication.ts +++ b/src/http-authentications/http-no-authentication.ts @@ -1,23 +1,24 @@ -import {HttpAuthentication} from './http-authentication'; -import {TestModel} from '../models/outputs/test-model'; +import { HttpAuthentication } from './http-authentication'; +import { TestModel } from '../models/outputs/test-model'; export class HttpNoAuthentication implements HttpAuthentication { - private readonly authentication: any; + private readonly authentication: any; - public constructor(authentication: any) { - this.authentication = authentication; - } + public constructor(authentication: any) { + this.authentication = authentication; + } - public generate(): any { - return null; - } - - public verify(requisition: string): TestModel[] { - return [{ - name: 'Http authentication', - description: `No supported http authentication method was found from: ${this.authentication}`, - valid: false - }]; - } + public generate(): any { + return null; + } + public verify(task: string): TestModel[] { + return [ + { + name: 'Http authentication', + description: `No supported http authentication method was found from: ${this.authentication}`, + valid: false + } + ]; + } } diff --git a/src/index.ts b/src/index.ts index 34259a7e..f9167ae8 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,17 +1,13 @@ #!/usr/bin/env node -import {EnqueuerStarter} from './enqueuer-starter'; -import {EnqueuerAsNodeChildRunner} from './enqueuer-as-node-child-runner'; +import { EnqueuerStarter } from './enqueuer-starter'; +import { EnqueuerAsNodeChildRunner } from './enqueuer-as-node-child-runner'; // It's the executable if (require.main === module) { - // It's the a child process - if (process.send) { - new EnqueuerAsNodeChildRunner() - .execute() - .then((statusCode: number) => process.exit(statusCode)); - } else { - new EnqueuerStarter() - .start() - .then((statusCode: number) => process.exit(statusCode)); - } + // It's the a child process + if (process.send) { + new EnqueuerAsNodeChildRunner().execute().then((statusCode: number) => process.exit(statusCode)); + } else { + new EnqueuerStarter().start().then((statusCode: number) => process.exit(statusCode)); + } } diff --git a/src/loggers/log-level.test.ts b/src/loggers/log-level.test.ts index 1b7eca84..a13d01d0 100644 --- a/src/loggers/log-level.test.ts +++ b/src/loggers/log-level.test.ts @@ -1,33 +1,33 @@ -import {LogLevel} from './log-level'; +import { LogLevel } from './log-level'; describe('LogLevel', () => { - it('should have correct priorities', () => { - expect(LogLevel.TRACE.hasPriorityLessThanOrEqualTo(LogLevel.DEBUG)).toBeTruthy(); - expect(LogLevel.DEBUG.hasPriorityLessThanOrEqualTo(LogLevel.INFO)).toBeTruthy(); - expect(LogLevel.INFO.hasPriorityLessThanOrEqualTo(LogLevel.WARN)).toBeTruthy(); - expect(LogLevel.WARN.hasPriorityLessThanOrEqualTo(LogLevel.ERROR)).toBeTruthy(); - expect(LogLevel.ERROR.hasPriorityLessThanOrEqualTo(LogLevel.FATAL)).toBeTruthy(); - }); + it('should have correct priorities', () => { + expect(LogLevel.TRACE.hasPriorityLessThanOrEqualTo(LogLevel.DEBUG)).toBeTruthy(); + expect(LogLevel.DEBUG.hasPriorityLessThanOrEqualTo(LogLevel.INFO)).toBeTruthy(); + expect(LogLevel.INFO.hasPriorityLessThanOrEqualTo(LogLevel.WARN)).toBeTruthy(); + expect(LogLevel.WARN.hasPriorityLessThanOrEqualTo(LogLevel.ERROR)).toBeTruthy(); + expect(LogLevel.ERROR.hasPriorityLessThanOrEqualTo(LogLevel.FATAL)).toBeTruthy(); + }); - it('should match var name', () => { - expect(LogLevel.TRACE.toString()).toBe('TRACE'); - expect(LogLevel.DEBUG.toString()).toBe('DEBUG'); - expect(LogLevel.INFO.toString()).toBe('INFO'); - expect(LogLevel.WARN.toString()).toBe('WARN'); - expect(LogLevel.ERROR.toString()).toBe('ERROR'); - expect(LogLevel.FATAL.toString()).toBe('FATAL'); - }); + it('should match var name', () => { + expect(LogLevel.TRACE.toString()).toBe('TRACE'); + expect(LogLevel.DEBUG.toString()).toBe('DEBUG'); + expect(LogLevel.INFO.toString()).toBe('INFO'); + expect(LogLevel.WARN.toString()).toBe('WARN'); + expect(LogLevel.ERROR.toString()).toBe('ERROR'); + expect(LogLevel.FATAL.toString()).toBe('FATAL'); + }); - it('should build from string', () => { - expect(LogLevel.buildFromString('TRACE').getPriority()).toBe(LogLevel.TRACE.getPriority()); - expect(LogLevel.buildFromString('DEBUG').getPriority()).toBe(LogLevel.DEBUG.getPriority()); - expect(LogLevel.buildFromString('INFO').getPriority()).toBe(LogLevel.INFO.getPriority()); - expect(LogLevel.buildFromString('WARN').getPriority()).toBe(LogLevel.WARN.getPriority()); - expect(LogLevel.buildFromString('ERROR').getPriority()).toBe(LogLevel.ERROR.getPriority()); - expect(LogLevel.buildFromString('FATAL').getPriority()).toBe(LogLevel.FATAL.getPriority()); - }); + it('should build from string', () => { + expect(LogLevel.buildFromString('TRACE').getPriority()).toBe(LogLevel.TRACE.getPriority()); + expect(LogLevel.buildFromString('DEBUG').getPriority()).toBe(LogLevel.DEBUG.getPriority()); + expect(LogLevel.buildFromString('INFO').getPriority()).toBe(LogLevel.INFO.getPriority()); + expect(LogLevel.buildFromString('WARN').getPriority()).toBe(LogLevel.WARN.getPriority()); + expect(LogLevel.buildFromString('ERROR').getPriority()).toBe(LogLevel.ERROR.getPriority()); + expect(LogLevel.buildFromString('FATAL').getPriority()).toBe(LogLevel.FATAL.getPriority()); + }); - it('should handle non existent colorize library', () => { - expect(() => LogLevel.TRACE.getColorFunction()('message')).not.toThrow(); - }); + it('should handle non existent colorize library', () => { + expect(() => LogLevel.TRACE.getColorFunction()('message')).not.toThrow(); + }); }); diff --git a/src/loggers/log-level.ts b/src/loggers/log-level.ts index 3553d8eb..efd87462 100644 --- a/src/loggers/log-level.ts +++ b/src/loggers/log-level.ts @@ -1,68 +1,66 @@ -import colorizer from '../outputs/colorizer'; +import chalk from 'chalk'; export class LogLevel { - private static priorityCounter: number = 0; + private static priorityCounter: number = 0; - public static readonly TRACE: LogLevel = new LogLevel(colorizer.cyan); - public static readonly DEBUG: LogLevel = new LogLevel(colorizer.blue); - public static readonly INFO: LogLevel = new LogLevel(colorizer.green); - public static readonly WARN: LogLevel = new LogLevel(colorizer.yellow); - public static readonly ERROR: LogLevel = new LogLevel(colorizer.magenta); - public static readonly FATAL: LogLevel = new LogLevel(colorizer.red); + public static readonly TRACE: LogLevel = new LogLevel(chalk.cyan); + public static readonly DEBUG: LogLevel = new LogLevel(chalk.blue); + public static readonly INFO: LogLevel = new LogLevel(chalk.green); + public static readonly WARN: LogLevel = new LogLevel(chalk.yellow); + public static readonly ERROR: LogLevel = new LogLevel(chalk.magenta); + public static readonly FATAL: LogLevel = new LogLevel(chalk.red); - private readonly priority: number; - private readonly colorFunction: Function; + private readonly priority: number; + private readonly colorFunction: Function; - constructor(colorFunction: Function) { - this.priority = ++LogLevel.priorityCounter; - this.colorFunction = colorFunction; - } - - public static buildFromString(level: string): LogLevel { - switch (level.toUpperCase()) { - case 'TRACE': - return LogLevel.TRACE; - case 'DEBUG': - return LogLevel.DEBUG; - case 'INFO': - return LogLevel.INFO; - case 'WARN': - return LogLevel.WARN; - case 'ERROR': - return LogLevel.ERROR; - case 'FATAL': - return LogLevel.FATAL; - default: - return LogLevel.WARN; - } - } + constructor(colorFunction: Function) { + this.priority = ++LogLevel.priorityCounter; + this.colorFunction = colorFunction; + } - public getPriority(): number { - return this.priority; + public static buildFromString(level: string): LogLevel { + switch (level.toUpperCase()) { + case 'TRACE': + return LogLevel.TRACE; + case 'DEBUG': + return LogLevel.DEBUG; + case 'INFO': + return LogLevel.INFO; + case 'WARN': + return LogLevel.WARN; + case 'ERROR': + return LogLevel.ERROR; + case 'FATAL': + return LogLevel.FATAL; + default: + return LogLevel.WARN; } + } - public toString(): string { - if (this.getPriority() === LogLevel.TRACE.getPriority()) { - return 'TRACE'; - } else if (this.getPriority() === LogLevel.DEBUG.getPriority()) { - return 'DEBUG'; - } else if (this.getPriority() === LogLevel.INFO.getPriority()) { - return 'INFO'; - } else if (this.getPriority() === LogLevel.ERROR.getPriority()) { - return 'ERROR'; - } else if (this.getPriority() === LogLevel.FATAL.getPriority()) { - return 'FATAL'; - } //else if (this.getPriority() === LogLevel.WARN.getPriority()) { - return 'WARN'; + public getPriority(): number { + return this.priority; + } - } + public toString(): string { + if (this.getPriority() === LogLevel.TRACE.getPriority()) { + return 'TRACE'; + } else if (this.getPriority() === LogLevel.DEBUG.getPriority()) { + return 'DEBUG'; + } else if (this.getPriority() === LogLevel.INFO.getPriority()) { + return 'INFO'; + } else if (this.getPriority() === LogLevel.ERROR.getPriority()) { + return 'ERROR'; + } else if (this.getPriority() === LogLevel.FATAL.getPriority()) { + return 'FATAL'; + } //else if (this.getPriority() === LogLevel.WARN.getPriority()) { + return 'WARN'; + } - public getColorFunction(): Function { - return this.colorFunction; - } - - public hasPriorityLessThanOrEqualTo(other: LogLevel): boolean { - return this.priority <= other.priority; - } + public getColorFunction(): Function { + return this.colorFunction; + } + public hasPriorityLessThanOrEqualTo(other: LogLevel): boolean { + return this.priority <= other.priority; + } } diff --git a/src/loggers/logger.test.ts b/src/loggers/logger.test.ts index 15bf7ff1..ea6b7a96 100644 --- a/src/loggers/logger.test.ts +++ b/src/loggers/logger.test.ts @@ -1,73 +1,70 @@ -import {Logger} from './logger'; -import {DateController} from '../timers/date-controller'; -import {LogLevel} from './log-level'; +import { Logger } from './logger'; +import { DateController } from '../timers/date-controller'; +import { LogLevel } from './log-level'; jest.mock('../timers/date-controller'); // @ts-ignore DateController.mockImplementation(() => { - return { - toString: () => 'date', - }; + return { + toString: () => 'date' + }; }); const consoleLogMock = jest.fn(() => ``); console.log = consoleLogMock; describe('Logger', () => { - beforeEach(() => { - // @ts-ignore - delete Logger.logLevel; - consoleLogMock.mockClear(); - Logger.setLoggerLevel(LogLevel.WARN); - }); - - it('should format properly', () => { - Logger.fatal('message'); - // @ts-ignore - const message: string = consoleLogMock.mock.calls[0][0]; - - expect(consoleLogMock).toHaveBeenCalled(); - expect(message).toContain('[date] [FATAL] - message'); - - }); - - it('should not print less priority messages', () => { - Logger.trace('message'); - - expect(consoleLogMock).not.toHaveBeenCalled(); - - }); - - it('should change priority', () => { - Logger.trace('message'); - - expect(consoleLogMock).not.toHaveBeenCalled(); - - Logger.setLoggerLevel(LogLevel.TRACE); - Logger.trace('message'); - - expect(consoleLogMock).toHaveBeenCalled(); - - }); - - it('should PRINT category', () => { - Logger.setLoggerLevel(LogLevel.TRACE); - Logger.trace('message'); - Logger.debug('message'); - Logger.info('message'); - Logger.warning('message'); - Logger.error('message'); - Logger.fatal('message'); - - // @ts-ignore - const calls: string[] = consoleLogMock.mock.calls; - - expect(calls[0][0]).toContain('TRACE'); - expect(calls[1][0]).toContain('DEBUG'); - expect(calls[2][0]).toContain('INFO'); - expect(calls[3][0]).toContain('WARN'); - expect(calls[4][0]).toContain('ERROR'); - expect(calls[5][0]).toContain('FATAL'); - }); + beforeEach(() => { + // @ts-ignore + delete Logger.logLevel; + consoleLogMock.mockClear(); + Logger.setLoggerLevel(LogLevel.WARN); + }); + + it('should format properly', () => { + Logger.fatal('message'); + // @ts-ignore + const message: string = consoleLogMock.mock.calls[0][0]; + + expect(consoleLogMock).toHaveBeenCalled(); + expect(message).toContain('[date] [FATAL] - message'); + }); + + it('should not print less priority messages', () => { + Logger.trace('message'); + + expect(consoleLogMock).not.toHaveBeenCalled(); + }); + + it('should change priority', () => { + Logger.trace('message'); + + expect(consoleLogMock).not.toHaveBeenCalled(); + + Logger.setLoggerLevel(LogLevel.TRACE); + Logger.trace('message'); + + expect(consoleLogMock).toHaveBeenCalled(); + }); + + it('should PRINT category', () => { + Logger.setLoggerLevel(LogLevel.TRACE); + Logger.trace('message'); + Logger.debug('message'); + Logger.info('message'); + Logger.warning('message'); + Logger.error('message'); + Logger.fatal('message'); + + // @ts-ignore + const calls: string[] = consoleLogMock.mock.calls; + + expect(calls[0][0]).toContain('TRACE'); + expect(calls[1][0]).toContain('DEBUG'); + expect(calls[2][0]).toContain('INFO'); + expect(calls[3][0]).toContain('WARN'); + expect(calls[4][0]).toContain('ERROR'); + expect(calls[5][0]).toContain('FATAL'); + }); }); diff --git a/src/loggers/logger.ts b/src/loggers/logger.ts index 80d3089d..be320e71 100644 --- a/src/loggers/logger.ts +++ b/src/loggers/logger.ts @@ -1,53 +1,51 @@ -import {LogLevel} from './log-level'; -import {DateController} from '../timers/date-controller'; +import { LogLevel } from './log-level'; +import { DateController } from '../timers/date-controller'; export class Logger { + private static logLevel?: LogLevel; - private static logLevel?: LogLevel; + public static setLoggerLevel(level: LogLevel): void { + Logger.logLevel = level; + } - public static setLoggerLevel(level: LogLevel): void { - Logger.logLevel = level; - } + public static trace(message: string) { + Logger.logIfNecessary(message, LogLevel.TRACE); + } - public static trace(message: string) { - Logger.logIfNecessary(message, LogLevel.TRACE); - } + public static debug(message: string) { + Logger.logIfNecessary(message, LogLevel.DEBUG); + } - public static debug(message: string) { - Logger.logIfNecessary(message, LogLevel.DEBUG); - } + public static info(message: string) { + Logger.logIfNecessary(message, LogLevel.INFO); + } - public static info(message: string) { - Logger.logIfNecessary(message, LogLevel.INFO); - } + public static warning(message: string) { + Logger.logIfNecessary(message, LogLevel.WARN); + } - public static warning(message: string) { - Logger.logIfNecessary(message, LogLevel.WARN); - } + public static error(message: string) { + Logger.logIfNecessary(message, LogLevel.ERROR); + } - public static error(message: string) { - Logger.logIfNecessary(message, LogLevel.ERROR); - } - - public static fatal(message: string) { - Logger.logIfNecessary(message, LogLevel.FATAL); - } + public static fatal(message: string) { + Logger.logIfNecessary(message, LogLevel.FATAL); + } - private static getLogger(): LogLevel { - if (!Logger.logLevel) { - Logger.logLevel = LogLevel.WARN; - } - return Logger.logLevel; + private static getLogger(): LogLevel { + if (!Logger.logLevel) { + Logger.logLevel = LogLevel.WARN; } + return Logger.logLevel; + } - private static logIfNecessary(message: string, level: LogLevel) { - const logger = Logger.getLogger(); - if (logger.hasPriorityLessThanOrEqualTo(level)) { - const date = new DateController().toString(); - const category = level.toString(); - const pattern = `[${date}] [${category}] - ${message}`; - console.log(level.getColorFunction()(pattern)); - } + private static logIfNecessary(message: string, level: LogLevel) { + const logger = Logger.getLogger(); + if (logger.hasPriorityLessThanOrEqualTo(level)) { + const date = new DateController().toString(); + const category = level.toString(); + const pattern = `[${date}] [${category}] - ${message}`; + console.log(level.getColorFunction()(pattern)); } - + } } diff --git a/src/models-defaults/outputs/requisition-default-reports.test.ts b/src/models-defaults/outputs/requisition-default-reports.test.ts deleted file mode 100644 index 455994ab..00000000 --- a/src/models-defaults/outputs/requisition-default-reports.test.ts +++ /dev/null @@ -1,123 +0,0 @@ -import {RequisitionDefaultReports} from './requisition-default-reports'; - -describe('RequisitionDefaultReports', () => { - it('default', () => { - const report = RequisitionDefaultReports.createDefaultReport({name: 'g', id: 'id', level: 13, iteration: 1, totalIterations: 5}); - expect(report.time!.startTime).toBeDefined(); - expect(report.time!.endTime).toBeDefined(); - expect(report.time!.totalTime).toBeLessThan(1000); - delete report.time; - expect(report).toEqual({ - 'hooks': { - 'onFinish': {arguments: {}, 'tests': [], 'valid': true}, - 'onInit': {arguments: {}, 'tests': [], 'valid': true} - }, - 'id': 'id', - iteration: 1, - totalIterations: 5, - 'ignored': undefined, - 'level': 13, - 'name': 'g', - 'publishers': [], - 'requisitions': [], - 'subscriptions': [], - 'valid': true - }); - }); - - it('createIteratorReport', () => { - const report = RequisitionDefaultReports.createIteratorReport({name: 'g'}); - expect(report.id).toBeUndefined(); - expect(report.time!.startTime).toBeDefined(); - expect(report.time!.endTime).toBeDefined(); - expect(report.time!.totalTime).toBeLessThan(1000); - delete report.time; - expect(report).toEqual({ - 'hooks': { - 'onFinish': {arguments: {}, 'tests': [], 'valid': true}, - 'onInit': {arguments: {}, 'tests': [], 'valid': true} - }, - 'id': undefined, - 'ignored': undefined, - 'level': undefined, - 'name': 'g', - 'publishers': [], - 'requisitions': [], - 'subscriptions': [], - 'valid': true - }); - }); - - it('createRunningError', () => { - const report = RequisitionDefaultReports.createRunningError({name: 'lopidio'}, 'err'); - expect(report.time!.startTime).toBeDefined(); - expect(report.time!.endTime).toBeDefined(); - expect(report.time!.totalTime).toBeLessThan(1000); - delete report.time; - expect(report).toEqual({ - 'hooks': { - 'onFinish': { - arguments: {}, - 'tests': [{'description': 'err', 'name': 'Requisition ran', 'valid': false}], - 'valid': false - }, 'onInit': {arguments: {}, 'tests': [], 'valid': true} - }, - 'id': undefined, - 'ignored': undefined, - 'level': undefined, - 'name': 'lopidio', - 'publishers': [], - 'requisitions': [], - 'subscriptions': [], - 'valid': false - }); - }); - - it('createSkippedReport', () => { - const report = RequisitionDefaultReports.createSkippedReport({name: 'virgs'}); - expect(report.time!.startTime).toBeDefined(); - expect(report.time!.endTime).toBeDefined(); - expect(report.time!.totalTime).toBe(0); - delete report.time; - expect(report).toEqual({ - 'hooks': { - 'onFinish': { - arguments: {}, - 'tests': [{'description': 'There is no iterations set to this requisition', 'name': 'Requisition skipped', 'valid': true}], - 'valid': true - }, 'onInit': {arguments: {}, 'tests': [], 'valid': true} - }, - 'id': undefined, - 'ignored': undefined, - 'level': undefined, - 'name': 'virgs', - 'publishers': [], - 'requisitions': [], - 'subscriptions': [], - 'valid': true - }); - }); - - it('createIgnoredReport', () => { - const report = RequisitionDefaultReports.createIgnoredReport({name: 'virgs', level: 4}); - expect(report.time!.startTime).toBeDefined(); - expect(report.time!.endTime).toBeDefined(); - expect(report.time!.totalTime).toBe(0); - delete report.time; - expect(report).toEqual({ - 'hooks': { - 'onFinish': {arguments: {}, 'tests': [], 'valid': true}, - 'onInit': {arguments: {}, 'tests': [], 'valid': true} - }, - 'id': undefined, - 'ignored': true, - 'level': 4, - 'name': 'virgs', - 'publishers': [], - 'requisitions': [], - 'subscriptions': [], - 'valid': true - }); - }); - -}); diff --git a/src/models-defaults/outputs/requisition-default-reports.ts b/src/models-defaults/outputs/requisition-default-reports.ts deleted file mode 100644 index 5be8386c..00000000 --- a/src/models-defaults/outputs/requisition-default-reports.ts +++ /dev/null @@ -1,74 +0,0 @@ -import * as output from '../../models/outputs/requisition-model'; -import {DateController} from '../../timers/date-controller'; -import {TestModel} from '../../models/outputs/test-model'; -import {DefaultHookEvents} from '../../models/events/event'; - -export class RequisitionDefaultReports { - - public static createDefaultReport(base: { - name: string, - id: string, - ignored?: boolean, - level?: number, - iteration?: number, - totalIterations?: number - }, - onFinishTests: TestModel[] = []): output.RequisitionModel { - const valid = onFinishTests.every((test) => test.valid); - return { - valid: valid, - name: base.name, - id: base.id, - ignored: base.ignored, - level: base.level, - subscriptions: [], - publishers: [], - iteration: base.iteration, - totalIterations: base.totalIterations, - hooks: { - [DefaultHookEvents.ON_INIT]: { - arguments: {}, - valid: true, - tests: [], - }, - [DefaultHookEvents.ON_FINISH]: { - arguments: {}, - valid: valid, - tests: onFinishTests, - }, - }, - time: { - startTime: new DateController().toString(), - endTime: new DateController().toString(), - totalTime: 0 - }, - requisitions: [] - }; - } - - public static createRunningError(base: { name: string, id: string }, err: any): output.RequisitionModel { - return RequisitionDefaultReports.createDefaultReport(base, [{ - valid: false, - name: 'Requisition ran', - description: err - }]); - } - - public static createSkippedReport(base: { name: string, id: string }): output.RequisitionModel { - return RequisitionDefaultReports.createDefaultReport(base, [{ - valid: true, - name: 'Requisition skipped', - description: 'There is no iterations set to this requisition' - }]); - } - - public static createIgnoredReport(base: { name: string, id: string, ignored?: true }): output.RequisitionModel { - base.ignored = true; - return RequisitionDefaultReports.createDefaultReport(base); - } - - public static createIteratorReport(base: { name: string, id: string }): output.RequisitionModel { - return RequisitionDefaultReports.createDefaultReport(base); - } - -} diff --git a/src/models-defaults/outputs/task-default-reports.test.ts b/src/models-defaults/outputs/task-default-reports.test.ts new file mode 100644 index 00000000..8cdfd177 --- /dev/null +++ b/src/models-defaults/outputs/task-default-reports.test.ts @@ -0,0 +1,155 @@ +import { TaskDefaultReports } from './task-default-reports'; + +describe('TaskDefaultReports', () => { + it('default', () => { + const report = TaskDefaultReports.createDefaultReport({ + name: 'g', + id: 'id', + level: 13, + iteration: 1, + totalIterations: 5 + }); + expect(report.time!.startTime).toBeDefined(); + expect(report.time!.endTime).toBeDefined(); + expect(report.time!.totalTime).toBeLessThan(1000); + expect(report).toEqual({ + hooks: { + onFinish: { arguments: {}, tests: [], valid: true }, + onInit: { arguments: {}, tests: [], valid: true } + }, + time: { + endTime: expect.any(String), + startTime: expect.any(String), + totalTime: expect.any(Number) + }, + id: 'id', + iteration: 1, + totalIterations: 5, + ignored: undefined, + level: 13, + name: 'g', + actuators: [], + tasks: [], + sensors: [], + valid: true + }); + }); + + it('createIteratorReport', () => { + // @ts-expect-error + const report = TaskDefaultReports.createIteratorReport({ + name: 'g' + }); + expect(report.id).toBeUndefined(); + expect(report).toEqual({ + hooks: { + onFinish: { arguments: {}, tests: [], valid: true }, + onInit: { arguments: {}, tests: [], valid: true } + }, + time: { + endTime: expect.any(String), + startTime: expect.any(String), + totalTime: expect.any(Number) + }, + id: undefined, + ignored: undefined, + level: undefined, + name: 'g', + actuators: [], + tasks: [], + sensors: [], + valid: true + }); + }); + + it('createRunningError', () => { + // @ts-expect-error + const report = TaskDefaultReports.createRunningError({ name: 'lopidio' }, 'err'); + expect(report).toEqual({ + hooks: { + onFinish: { + arguments: {}, + tests: [{ description: 'err', name: 'Task ran', valid: false }], + valid: false + }, + onInit: { arguments: {}, tests: [], valid: true } + }, + time: { + endTime: expect.any(String), + startTime: expect.any(String), + totalTime: expect.any(Number) + }, + id: undefined, + ignored: undefined, + level: undefined, + name: 'lopidio', + actuators: [], + tasks: [], + sensors: [], + valid: false + }); + }); + + it('createSkippedReport', () => { + // @ts-expect-error + const report = TaskDefaultReports.createSkippedReport({ + name: 'virgs' + }); + expect(report).toEqual({ + hooks: { + onFinish: { + arguments: {}, + tests: [ + { + description: 'There is no iterations set to this task', + name: 'Task skipped', + valid: true + } + ], + valid: true + }, + onInit: { arguments: {}, tests: [], valid: true } + }, + time: { + endTime: expect.any(String), + startTime: expect.any(String), + totalTime: expect.any(Number) + }, + id: undefined, + ignored: undefined, + level: undefined, + name: 'virgs', + actuators: [], + tasks: [], + sensors: [], + valid: true + }); + }); + + it('createIgnoredReport', () => { + // @ts-expect-error + const report = TaskDefaultReports.createIgnoredReport({ + name: 'virgs' + }); + expect(report).toEqual({ + hooks: { + onFinish: { arguments: {}, tests: [], valid: true }, + onInit: { arguments: {}, tests: [], valid: true } + }, + time: { + endTime: expect.any(String), + startTime: expect.any(String), + totalTime: expect.any(Number) + }, + iteration: undefined, + totalIterations: undefined, + id: undefined, + ignored: true, + name: 'virgs', + actuators: [], + tasks: [], + sensors: [], + valid: true + }); + }); +}); diff --git a/src/models-defaults/outputs/task-default-reports.ts b/src/models-defaults/outputs/task-default-reports.ts new file mode 100644 index 00000000..a1b0c78a --- /dev/null +++ b/src/models-defaults/outputs/task-default-reports.ts @@ -0,0 +1,78 @@ +import * as output from '../../models/outputs/task-model'; +import { DateController } from '../../timers/date-controller'; +import { TestModel } from '../../models/outputs/test-model'; +import { DefaultHookEvents } from '../../models/events/event'; + +export class TaskDefaultReports { + public static createDefaultReport( + base: { + name: string; + id: string; + ignored?: boolean; + level?: number; + iteration?: number; + totalIterations?: number; + }, + onFinishTests: TestModel[] = [] + ): output.TaskModel { + const valid = onFinishTests.every(test => test.valid); + return { + valid: valid, + name: base.name, + id: base.id, + ignored: base.ignored, + level: base.level, + sensors: [], + actuators: [], + iteration: base.iteration, + totalIterations: base.totalIterations, + hooks: { + [DefaultHookEvents.ON_INIT]: { + arguments: {}, + valid: true, + tests: [] + }, + [DefaultHookEvents.ON_FINISH]: { + arguments: {}, + valid: valid, + tests: onFinishTests + } + }, + time: { + startTime: new DateController().toString(), + endTime: new DateController().toString(), + totalTime: 0 + }, + tasks: [] + }; + } + + public static createRunningError(base: { name: string; id: string }, err: any): output.TaskModel { + return TaskDefaultReports.createDefaultReport(base, [ + { + valid: false, + name: 'Task ran', + description: err + } + ]); + } + + public static createSkippedReport(base: { name: string; id: string }): output.TaskModel { + return TaskDefaultReports.createDefaultReport(base, [ + { + valid: true, + name: 'Task skipped', + description: 'There is no iterations set to this task' + } + ]); + } + + public static createIgnoredReport(base: { name: string; id: string; ignored?: true }): output.TaskModel { + base.ignored = true; + return TaskDefaultReports.createDefaultReport(base); + } + + public static createIteratorReport(base: { name: string; id: string }): output.TaskModel { + return TaskDefaultReports.createDefaultReport(base); + } +} diff --git a/src/models/events/assertion.ts b/src/models/events/assertion.ts index 9cfb2ddc..adfdfa12 100644 --- a/src/models/events/assertion.ts +++ b/src/models/events/assertion.ts @@ -1,5 +1,5 @@ export interface Assertion { - name: string; - ignore?: boolean; - [propName: string]: any; + name?: string; + ignore?: boolean; + [propName: string]: any; } diff --git a/src/models/events/event.ts b/src/models/events/event.ts index 46ba8958..9c30c26a 100644 --- a/src/models/events/event.ts +++ b/src/models/events/event.ts @@ -1,12 +1,13 @@ -import {Assertion} from './assertion'; +import { Assertion } from './assertion'; export enum DefaultHookEvents { - ON_INIT = 'onInit', - ON_FINISH = 'onFinish', + ON_INIT = 'onInit', + ON_FINISH = 'onFinish' } export interface Event { - script?: string; - assertions?: Assertion[]; - store?: { [propName: string]: any; }; + debug?: boolean; + script?: string; + assertions?: Assertion[]; + store?: { [propName: string]: any }; } diff --git a/src/models/events/finishable.ts b/src/models/events/finishable.ts index 01be1aef..714db497 100644 --- a/src/models/events/finishable.ts +++ b/src/models/events/finishable.ts @@ -1,5 +1,5 @@ -import {Event} from './event'; +import { Event } from './event'; export interface Finishable { - onFinish?: Event; -} \ No newline at end of file + onFinish?: Event; +} diff --git a/src/models/events/initializable.ts b/src/models/events/initializable.ts index 448f9848..dafdc171 100644 --- a/src/models/events/initializable.ts +++ b/src/models/events/initializable.ts @@ -1,5 +1,5 @@ -import {Event} from './event'; +import { Event } from './event'; export interface Initializable { - onInit?: Event; -} \ No newline at end of file + onInit?: Event; +} diff --git a/src/models/events/message-receiver.ts b/src/models/events/message-receiver.ts index b5469218..c6ce7ca1 100644 --- a/src/models/events/message-receiver.ts +++ b/src/models/events/message-receiver.ts @@ -1,6 +1,6 @@ -import {Event} from './event'; +import { Event } from './event'; export interface MessageReceiver { - messageReceived?: any; - onMessageReceived?: Event; -} \ No newline at end of file + messageReceived?: any; + onMessageReceived?: Event; +} diff --git a/src/models/inputs/actuator-model.ts b/src/models/inputs/actuator-model.ts new file mode 100644 index 00000000..6612f645 --- /dev/null +++ b/src/models/inputs/actuator-model.ts @@ -0,0 +1,10 @@ +import { Finishable } from '../events/finishable'; +import { Initializable } from '../events/initializable'; +import { MessageReceiver } from '../events/message-receiver'; + +export interface ActuatorModel extends Finishable, Initializable, MessageReceiver { + type: string; + name: string; + + [propName: string]: any; +} diff --git a/src/models/inputs/publisher-model.ts b/src/models/inputs/publisher-model.ts deleted file mode 100644 index bb9228d5..00000000 --- a/src/models/inputs/publisher-model.ts +++ /dev/null @@ -1,10 +0,0 @@ -import {Finishable} from '../events/finishable'; -import {Initializable} from '../events/initializable'; -import {MessageReceiver} from '../events/message-receiver'; - -export interface PublisherModel extends Finishable, Initializable, MessageReceiver { - type: string; - name: string; - - [propName: string]: any; -} \ No newline at end of file diff --git a/src/models/inputs/requisition-model.ts b/src/models/inputs/requisition-model.ts deleted file mode 100644 index 0d5cfdb6..00000000 --- a/src/models/inputs/requisition-model.ts +++ /dev/null @@ -1,21 +0,0 @@ -import {SubscriptionModel} from './subscription-model'; -import {Finishable} from '../events/finishable'; -import {Initializable} from '../events/initializable'; -import {PublisherModel} from './publisher-model'; - -export interface RequisitionModel extends Finishable, Initializable { - timeout: number; - id: string; - name: string; - level: number; - subscriptions: SubscriptionModel[]; - publishers: PublisherModel[]; - parent?: RequisitionModel; - delay: number; - iterations: number; - ignore?: boolean; - import?: RequisitionModel; - requisitions: RequisitionModel[]; - - [propName: string]: any; -} diff --git a/src/models/inputs/sensor-model.ts b/src/models/inputs/sensor-model.ts new file mode 100644 index 00000000..608b423d --- /dev/null +++ b/src/models/inputs/sensor-model.ts @@ -0,0 +1,13 @@ +import { Finishable } from '../events/finishable'; +import { Initializable } from '../events/initializable'; +import { MessageReceiver } from '../events/message-receiver'; + +export interface SensorModel extends Finishable, Initializable, MessageReceiver { + type: string; + name: string; + response?: any; + avoid?: boolean; + timeout?: number; + + [propName: string]: any; +} diff --git a/src/models/inputs/subscription-model.ts b/src/models/inputs/subscription-model.ts deleted file mode 100644 index ac598b1d..00000000 --- a/src/models/inputs/subscription-model.ts +++ /dev/null @@ -1,13 +0,0 @@ -import {Finishable} from '../events/finishable'; -import {Initializable} from '../events/initializable'; -import {MessageReceiver} from '../events/message-receiver'; - -export interface SubscriptionModel extends Finishable, Initializable, MessageReceiver { - type: string; - name: string; - response?: any; - avoid?: boolean; - timeout?: number; - - [propName: string]: any; -} \ No newline at end of file diff --git a/src/models/inputs/task-model.ts b/src/models/inputs/task-model.ts new file mode 100644 index 00000000..f1187406 --- /dev/null +++ b/src/models/inputs/task-model.ts @@ -0,0 +1,21 @@ +import { SensorModel } from './sensor-model'; +import { Finishable } from '../events/finishable'; +import { Initializable } from '../events/initializable'; +import { ActuatorModel } from './actuator-model'; + +export interface TaskModel extends Finishable, Initializable { + timeout: number; + id: string; + name: string; + level: number; + sensors: SensorModel[]; + actuators: ActuatorModel[]; + parent?: TaskModel; + delay: number; + iterations: number; + ignore?: boolean; + import?: TaskModel; + tasks: TaskModel[]; + + [propName: string]: any; +} diff --git a/src/models/outputs/actuator-model.ts b/src/models/outputs/actuator-model.ts new file mode 100644 index 00000000..e1103765 --- /dev/null +++ b/src/models/outputs/actuator-model.ts @@ -0,0 +1,8 @@ +import { ReportModel } from './report-model'; + +export interface ActuatorModel extends ReportModel { + id: string; + type: string; + messageReceived?: any; + messageSentInstant?: Date | string; +} diff --git a/src/models/outputs/hook-model.ts b/src/models/outputs/hook-model.ts index 0a56b9cb..6c4bc0e2 100644 --- a/src/models/outputs/hook-model.ts +++ b/src/models/outputs/hook-model.ts @@ -1,7 +1,7 @@ -import {TestModel} from './test-model'; +import { TestModel } from './test-model'; export interface HookModel { - arguments?: { [name: string]: any }; - tests: TestModel[]; - valid: boolean; + arguments?: { [name: string]: any }; + tests: TestModel[]; + valid: boolean; } diff --git a/src/models/outputs/publisher-model.ts b/src/models/outputs/publisher-model.ts deleted file mode 100644 index f4181350..00000000 --- a/src/models/outputs/publisher-model.ts +++ /dev/null @@ -1,8 +0,0 @@ -import {ReportModel} from './report-model'; - -export interface PublisherModel extends ReportModel { - id: string; - type: string; - messageReceived?: any; - publishTime?: Date | string; -} diff --git a/src/models/outputs/report-model.ts b/src/models/outputs/report-model.ts index 9188ebaf..ac085eb5 100644 --- a/src/models/outputs/report-model.ts +++ b/src/models/outputs/report-model.ts @@ -1,13 +1,13 @@ -import {HookModel} from './hook-model'; +import { HookModel } from './hook-model'; export interface ReportModel { - name: string; - valid: boolean; - ignored?: boolean; + name: string; + valid: boolean; + ignored?: boolean; - hooks?: { - [name: string]: HookModel - }; + hooks?: { + [name: string]: HookModel; + }; - [propName: string]: any; + [propName: string]: any; } diff --git a/src/models/outputs/requisition-model.ts b/src/models/outputs/requisition-model.ts deleted file mode 100644 index 8af1c147..00000000 --- a/src/models/outputs/requisition-model.ts +++ /dev/null @@ -1,15 +0,0 @@ -import {TimeModel} from './time-model'; -import {SubscriptionModel} from './subscription-model'; -import {PublisherModel} from './publisher-model'; -import {ReportModel} from './report-model'; - -export interface RequisitionModel extends ReportModel { - id: string; - time: TimeModel; - publishers: PublisherModel[]; - subscriptions: SubscriptionModel[]; - requisitions: RequisitionModel[]; - level?: number; - iteration?: number; - totalIterations?: number; -} diff --git a/src/models/outputs/sensor-model.ts b/src/models/outputs/sensor-model.ts new file mode 100644 index 00000000..591baa7f --- /dev/null +++ b/src/models/outputs/sensor-model.ts @@ -0,0 +1,9 @@ +import { ReportModel } from './report-model'; + +export interface SensorModel extends ReportModel { + id: string; + type: string; + messageReceived?: any; + sensorTime?: Date | string; + messageReceivedTime?: Date | string; +} diff --git a/src/models/outputs/subscription-model.ts b/src/models/outputs/subscription-model.ts deleted file mode 100644 index 33fba345..00000000 --- a/src/models/outputs/subscription-model.ts +++ /dev/null @@ -1,9 +0,0 @@ -import {ReportModel} from './report-model'; - -export interface SubscriptionModel extends ReportModel { - id: string; - type: string; - messageReceived?: any; - subscriptionTime?: Date | string; - messageReceivedTime?: Date | string; -} diff --git a/src/models/outputs/task-model.ts b/src/models/outputs/task-model.ts new file mode 100644 index 00000000..b2f79e9b --- /dev/null +++ b/src/models/outputs/task-model.ts @@ -0,0 +1,15 @@ +import { TimeModel } from './time-model'; +import { SensorModel } from './sensor-model'; +import { ActuatorModel } from './actuator-model'; +import { ReportModel } from './report-model'; + +export interface TaskModel extends ReportModel { + id: string; + time: TimeModel; + actuators: ActuatorModel[]; + sensors: SensorModel[]; + tasks: TaskModel[]; + level?: number; + iteration?: number; + totalIterations?: number; +} diff --git a/src/models/outputs/test-model.test.ts b/src/models/outputs/test-model.test.ts index 40118e37..2f5e5db3 100644 --- a/src/models/outputs/test-model.test.ts +++ b/src/models/outputs/test-model.test.ts @@ -1,101 +1,99 @@ -import {TestModel, testModelIsFailing, testModelIsNotFailing, testModelIsPassing} from './test-model'; +import { TestModel, testModelIsFailing, testModelIsNotFailing, testModelIsPassing } from './test-model'; describe('TestModel', () => { - it('Valid is passing', () => { - const test: TestModel = { - description: '', - name: '', - valid: true - }; - expect(testModelIsPassing(test)).toBeTruthy(); - }); + it('Valid is passing', () => { + const test: TestModel = { + description: '', + name: '', + valid: true + }; + expect(testModelIsPassing(test)).toBeTruthy(); + }); - it('Ignored is not passing', () => { - const test: TestModel = { - description: '', - name: '', - valid: false, - ignored: true - }; - expect(testModelIsPassing(test)).toBeFalsy(); - }); + it('Ignored is not passing', () => { + const test: TestModel = { + description: '', + name: '', + valid: false, + ignored: true + }; + expect(testModelIsPassing(test)).toBeFalsy(); + }); - it('Valid and ignored is not passing', () => { - const test: TestModel = { - description: '', - name: '', - valid: true, - ignored: true - }; - expect(testModelIsPassing(test)).toBeFalsy(); - }); + it('Valid and ignored is not passing', () => { + const test: TestModel = { + description: '', + name: '', + valid: true, + ignored: true + }; + expect(testModelIsPassing(test)).toBeFalsy(); + }); - it('Not valid and not ignored and not valid is not passing', () => { - const test: TestModel = { - description: '', - name: '', - valid: false, - ignored: false - }; - expect(testModelIsPassing(test)).toBeFalsy(); - }); + it('Not valid and not ignored and not valid is not passing', () => { + const test: TestModel = { + description: '', + name: '', + valid: false, + ignored: false + }; + expect(testModelIsPassing(test)).toBeFalsy(); + }); + it('Valid is not failing', () => { + const test: TestModel = { + description: '', + name: '', + valid: true + }; + expect(testModelIsNotFailing(test)).toBeTruthy(); + }); + it('Ignored is not failing', () => { + const test: TestModel = { + description: '', + name: '', + valid: false, + ignored: true + }; + expect(testModelIsNotFailing(test)).toBeTruthy(); + }); - it('Valid is not failing', () => { - const test: TestModel = { - description: '', - name: '', - valid: true - }; - expect(testModelIsNotFailing(test)).toBeTruthy(); - }); + it('Valid and ignored is not passing', () => { + const test: TestModel = { + description: '', + name: '', + valid: false + }; + expect(testModelIsNotFailing(test)).toBeFalsy(); + }); - it('Ignored is not failing', () => { - const test: TestModel = { - description: '', - name: '', - valid: false, - ignored: true - }; - expect(testModelIsNotFailing(test)).toBeTruthy(); - }); + it('Not valid and not ignored is not passing is not failing', () => { + const test: TestModel = { + description: '', + name: '', + valid: false, + ignored: false + }; + expect(testModelIsNotFailing(test)).toBeFalsy(); + }); - it('Valid and ignored is not passing', () => { - const test: TestModel = { - description: '', - name: '', - valid: false, - }; - expect(testModelIsNotFailing(test)).toBeFalsy(); - }); + it('Not valid and ignored is non failing', () => { + const test: TestModel = { + description: '', + name: '', + valid: false, + ignored: true + }; + expect(testModelIsFailing(test)).toBeFalsy(); + }); - it('Not valid and not ignored is not passing is not failing', () => { - const test: TestModel = { - description: '', - name: '', - valid: false, - ignored: false - }; - expect(testModelIsNotFailing(test)).toBeFalsy(); - }); - - it('Not valid and ignored is non failing', () => { - const test: TestModel = { - description: '', - name: '', - valid: false, - ignored: true - }; - expect(testModelIsFailing(test)).toBeFalsy(); - }); - - it('Not valid and not ignored is non failing', () => { - const test: TestModel = { - description: '', - name: '', - valid: false, - }; - expect(testModelIsFailing(test)).toBeTruthy(); - }); + it('Not valid and not ignored is non failing', () => { + const test: TestModel = { + description: '', + name: '', + valid: false + }; + expect(testModelIsFailing(test)).toBeTruthy(); + }); }); diff --git a/src/models/outputs/test-model.ts b/src/models/outputs/test-model.ts index e3a91462..3b1304c9 100644 --- a/src/models/outputs/test-model.ts +++ b/src/models/outputs/test-model.ts @@ -1,27 +1,28 @@ export interface TestModel { - ignored?: boolean; - description: string; - valid: boolean; - name: string; + implicit?: boolean; + ignored?: boolean; + description: string; + valid: boolean; + name?: string; } -export function testModelIsPassing(test: { valid: boolean, ignored?: boolean }): boolean { - if (test.ignored === true) { - return false; - } - return test.valid === true; +export function testModelIsPassing(test: { valid: boolean; ignored?: boolean }): boolean { + if (test.ignored === true) { + return false; + } + return test.valid === true; } -export function testModelIsFailing(test: { valid: boolean, ignored?: boolean }): boolean { - if (test.ignored === true) { - return false; - } - return test.valid === false; +export function testModelIsFailing(test: { valid: boolean; ignored?: boolean }): boolean { + if (test.ignored === true) { + return false; + } + return test.valid === false; } -export function testModelIsNotFailing(test: { valid: boolean, ignored?: boolean }): boolean { - if (test.ignored === true) { - return true; - } - return test.valid !== false; +export function testModelIsNotFailing(test: { valid: boolean; ignored?: boolean }): boolean { + if (test.ignored === true) { + return true; + } + return test.valid !== false; } diff --git a/src/models/outputs/time-model.ts b/src/models/outputs/time-model.ts index 9c2d4e7e..ceeb943e 100644 --- a/src/models/outputs/time-model.ts +++ b/src/models/outputs/time-model.ts @@ -1,6 +1,6 @@ export interface TimeModel { - totalTime: number; - startTime: Date | string; - endTime: Date | string; - timeout?: number; -} \ No newline at end of file + totalTime: number; + startTime: Date | string; + endTime: Date | string; + timeout?: number; +} diff --git a/src/notifications/notification-emitter.test.ts b/src/notifications/notification-emitter.test.ts index 9a131e68..2195f563 100644 --- a/src/notifications/notification-emitter.test.ts +++ b/src/notifications/notification-emitter.test.ts @@ -1,15 +1,15 @@ -import {NotificationEmitter} from './notification-emitter'; -import {Notifications} from './notifications'; +import { NotificationEmitter } from './notification-emitter'; +import { Notifications } from './notifications'; describe('NotificationEmitter', () => { - it('Should emit notification', done => { - const emittedNotification = {booleanValue: true, stringValue: 'string'}; + it('Should emit notification', done => { + const emittedNotification = { booleanValue: true, stringValue: 'string' }; - NotificationEmitter.on(Notifications.REQUISITION_FINISHED, (receivedNotification: any) => { - expect(receivedNotification).toEqual(emittedNotification); - done(); - }); - - NotificationEmitter.emit(Notifications.REQUISITION_FINISHED, emittedNotification); + NotificationEmitter.on(Notifications.REQUISITION_FINISHED, (receivedNotification: any) => { + expect(receivedNotification).toEqual(emittedNotification); + done(); }); + + NotificationEmitter.emit(Notifications.REQUISITION_FINISHED, emittedNotification); + }); }); diff --git a/src/notifications/notification-emitter.ts b/src/notifications/notification-emitter.ts index 8c439d25..52c21669 100644 --- a/src/notifications/notification-emitter.ts +++ b/src/notifications/notification-emitter.ts @@ -1,25 +1,22 @@ -import {EventEmitter} from 'events'; -import {Logger} from '../loggers/logger'; -import {Notifications} from './notifications'; +import { EventEmitter } from 'events'; +import { Logger } from '../loggers/logger'; +import { Notifications } from './notifications'; export class NotificationEmitter { - private static readonly notificationEmitter = new NotificationEmitter(); + private static readonly notificationEmitter = new NotificationEmitter(); - private readonly eventEmitter = new EventEmitter(); + private readonly eventEmitter = new EventEmitter(); - private constructor() { + private constructor() {} - } - - public static on(event: Notifications, listener: (...args: any[]) => void): NotificationEmitter { - NotificationEmitter.notificationEmitter.eventEmitter.on(Notifications[event], listener); - return NotificationEmitter.notificationEmitter; - } - - public static emit(event: Notifications, ...args: any[]): NotificationEmitter { - Logger.trace(`Notification '${Notifications[event]}' emitted`); - NotificationEmitter.notificationEmitter.eventEmitter.emit(Notifications[event], ...args); - return NotificationEmitter.notificationEmitter; - } + public static on(event: Notifications, listener: (...args: any[]) => void): NotificationEmitter { + NotificationEmitter.notificationEmitter.eventEmitter.on(Notifications[event], listener); + return NotificationEmitter.notificationEmitter; + } + public static emit(event: Notifications, ...args: any[]): NotificationEmitter { + Logger.trace(`Notification '${Notifications[event]}' emitted`); + NotificationEmitter.notificationEmitter.eventEmitter.emit(Notifications[event], ...args); + return NotificationEmitter.notificationEmitter; + } } diff --git a/src/notifications/notifications.ts b/src/notifications/notifications.ts index d0eabb04..dec8a795 100644 --- a/src/notifications/notifications.ts +++ b/src/notifications/notifications.ts @@ -1,10 +1,10 @@ export enum Notifications { - REQUISITION_FINISHED, - PUBLISHER_FINISHED, - SUBSCRIPTION_FINISHED, - HOOK_FINISHED, + REQUISITION_FINISHED, + PUBLISHER_FINISHED, + SUBSCRIPTION_FINISHED, + HOOK_FINISHED, - REQUISITION_STARTED, - PUBLISHER_STARTED, - SUBSCRIPTION_STARTED, + REQUISITION_STARTED, + PUBLISHER_STARTED, + SUBSCRIPTION_STARTED } diff --git a/src/object-parser/csv-object-parser.test.ts b/src/object-parser/csv-object-parser.test.ts index e9011a89..3fd5c7ad 100644 --- a/src/object-parser/csv-object-parser.test.ts +++ b/src/object-parser/csv-object-parser.test.ts @@ -1,85 +1,93 @@ -import {CsvObjectParser, entryPoint} from './csv-object-parser'; -import {MainInstance} from '../plugins/main-instance'; +import { CsvObjectParser, entryPoint } from './csv-object-parser'; +import { MainInstance } from '../plugins/main-instance'; describe('CsvObjectParser', () => { + test('should stringify undefined objects', () => { + const stringified = new CsvObjectParser().stringify(undefined); - test('should stringify undefined objects', () => { - const stringified = new CsvObjectParser().stringify(undefined); + expect(stringified).toBe('{}'); + }); - expect(stringified).toBe('{}'); - }); - - test('should parse empty objects', () => { - const stringified = new CsvObjectParser().parse({}, {}); + test('should parse empty objects', () => { + // @ts-expect-error + const stringified = new CsvObjectParser().parse({}, {}); - expect(stringified).toEqual([]); - }); + expect(stringified).toEqual([]); + }); - test('should parse empty lines', () => { - const stringified = new CsvObjectParser().parse('\n', {}); - - expect(stringified).toEqual([]); - }); + test('should parse empty lines', () => { + const stringified = new CsvObjectParser().parse('\n', {}); - test('should parse with ; and header as default', () => { - const value = 'title1;title2;title3\n' + - 'a1;a2;a3\n' + - 'b1;b2;b3\r\n'; - const expected = [ - {'title1': 'a1', 'title2': 'a2', 'title3': 'a3'}, - {'title1': 'b1', 'title2': 'b2', 'title3': 'b3'}]; + expect(stringified).toEqual([]); + }); - const parsed = new CsvObjectParser().parse(value); + test('should parse with ; and header as default', () => { + const value = 'title1;title2;title3\n' + 'a1;a2;a3\n' + 'b1;b2;b3\r\n'; + const expected = [ + { title1: 'a1', title2: 'a2', title3: 'a3' }, + { title1: 'b1', title2: 'b2', title3: 'b3' } + ]; - expect(parsed).toEqual(expected); - }); + const parsed = new CsvObjectParser().parse(value); - test('should parse tsv', () => { - const value = 'a1\ta2\ta3\r\n' + - 'b1\tb2\tb3'; - const expected = [ - ['a1', 'a2', 'a3'], - ['b1', 'b2', 'b3']]; + expect(parsed).toEqual(expected); + }); - const parsed = new CsvObjectParser().parse(value, {delimiter: '\t', header: false}); + test('should parse tsv', () => { + const value = 'a1\ta2\ta3\r\n' + 'b1\tb2\tb3'; + const expected = [ + ['a1', 'a2', 'a3'], + ['b1', 'b2', 'b3'] + ]; - expect(parsed).toEqual(expected); + const parsed = new CsvObjectParser().parse(value, { + delimiter: '\t', + header: false }); - test('should stringify with header (false) and delimiter (\t) value and numbers', () => { - const value = [ - [0, 1, false], - ['b1', 'b2', 'b3']]; + expect(parsed).toEqual(expected); + }); - const expected = '0\t1\tfalse\r\n\ -b1\tb2\tb3'; + test('should stringify with header (false) and delimiter (\t) value and numbers', () => { + const value = [ + [0, 1, false], + ['b1', 'b2', 'b3'] + ]; - const parsed = new CsvObjectParser().stringify(value, {delimiter: '\t', header: false}); + const expected = + '0\t1\tfalse\r\n\ +b1\tb2\tb3'; - expect(parsed).toEqual(expected); + const parsed = new CsvObjectParser().stringify(value, { + delimiter: '\t', + header: false }); - test('should stringify cycle reference', () => { - const value: any = [['00']]; - value[0][0] = value; + expect(parsed).toEqual(expected); + }); - const stringified = new CsvObjectParser().stringify(value, {header: false}); - - expect(stringified).toBe('[CYCLIC REFERENCE]'); - }); + test('should stringify cycle reference', () => { + const value: any = [['00']]; + value[0][0] = value; - it('Should export an entry point', done => { - const mainInstance: MainInstance = { - // @ts-ignore - objectParserManager: { - addObjectParser: (createFunction: any, ...tags: any) => { - expect(createFunction()).toBeInstanceOf(CsvObjectParser); - expect(tags).toEqual(['csv']); - done(); - } - } - }; - entryPoint(mainInstance); + const stringified = new CsvObjectParser().stringify(value, { + header: false }); + expect(stringified).toBe('[CYCLIC REFERENCE]'); + }); + + it('Should export an entry point', done => { + const mainInstance: MainInstance = { + // @ts-ignore + objectParserManager: { + addObjectParser: (createFunction: any, ...tags: any) => { + expect(createFunction()).toBeInstanceOf(CsvObjectParser); + expect(tags).toEqual(['csv']); + done(); + } + } + }; + entryPoint(mainInstance); + }); }); diff --git a/src/object-parser/csv-object-parser.ts b/src/object-parser/csv-object-parser.ts index f42c5d8d..12b0bba6 100644 --- a/src/object-parser/csv-object-parser.ts +++ b/src/object-parser/csv-object-parser.ts @@ -1,81 +1,74 @@ -import {ObjectParser} from './object-parser'; -import {ObjectDecycler} from './object-decycler'; -import {MainInstance} from '../plugins/main-instance'; +import { ObjectParser } from './object-parser'; +import { ObjectDecycler } from './object-decycler'; +import { MainInstance } from '../plugins/main-instance'; export class CsvObjectParser implements ObjectParser { - - public parse(text: string, query: any = {}): object { - const {header, delimiter} = this.parseQuery(query); - const lineSeparator = /\r?\n/; - if (text.split) { - const lines = text.split(lineSeparator); - if (!header) { - return lines - .filter(line => line.length > 0) - .map((line: string) => line.split(delimiter)); - } else if (lines[0]) { - return this.parseWithHeader(lines, delimiter); - } - } - return []; + public parse(text: string, query: any = {}): object { + const { header, delimiter } = this.parseQuery(query); + const lineSeparator = /\r?\n/; + if (text.split) { + const lines = text.split(lineSeparator); + if (!header) { + return lines.filter(line => line.length > 0).map((line: string) => line.split(delimiter)); + } else if (lines[0]) { + return this.parseWithHeader(lines, delimiter); + } } + return []; + } - public stringify(value: any, query: any = {}): string { - const {header, delimiter} = this.parseQuery(query); - if (!value) { - return '{}'; - } - const decycler = new ObjectDecycler('[CYCLIC REFERENCE]'); - - if (header) { - return this.stringifyWithHeader(value, decycler, delimiter); - } else { - return value - .map((row: string[]) => row - .map((value: any) => decycler.decycle(value)) - .join(delimiter)) - .join('\r\n'); - } + public stringify(value: any, query: any = {}): string { + const { header, delimiter } = this.parseQuery(query); + if (!value) { + return '{}'; } + const decycler = new ObjectDecycler('[CYCLIC REFERENCE]'); - private parseQuery(query: any) { - return Object.assign({}, - { - header: true, - delimiter: ';' - }, query); + if (header) { + return this.stringifyWithHeader(value, decycler, delimiter); + } else { + return value + .map((row: string[]) => row.map((value: any) => decycler.decycle(value)).join(delimiter)) + .join('\r\n'); } + } - private stringifyWithHeader(value: any, decycle: ObjectDecycler, delimiter: string) { - const title = Object.keys(value[0]); + private parseQuery(query: any) { + return Object.assign( + {}, + { + header: true, + delimiter: ';' + }, + query + ); + } - const csv = value - .map((row: any) => title - .map(fieldName => decycle.decycle(row[fieldName])) - .join(delimiter)); + private stringifyWithHeader(value: any, decycle: ObjectDecycler, delimiter: string) { + const title = Object.keys(value[0]); - csv.unshift(title.join(delimiter)); - return csv.join('\r\n'); - } + const csv = value.map((row: any) => title.map(fieldName => decycle.decycle(row[fieldName])).join(delimiter)); - private parseWithHeader(lines: string[], delimiter: string): object { - let result: any = []; - const headers = lines[0].split(delimiter); - lines - .filter((line, index) => line.length > 0 && index > 0) - .forEach((currentLine: string) => { - let parsedLine: any = {}; - currentLine.split(delimiter) - .forEach((value, valuesIndex) => { - parsedLine[headers[valuesIndex]] = value; - }); - result.push(parsedLine); - }); - return result; - } + csv.unshift(title.join(delimiter)); + return csv.join('\r\n'); + } + private parseWithHeader(lines: string[], delimiter: string): object { + let result: any = []; + const headers = lines[0].split(delimiter); + lines + .filter((line, index) => line.length > 0 && index > 0) + .forEach((currentLine: string) => { + let parsedLine: any = {}; + currentLine.split(delimiter).forEach((value, valuesIndex) => { + parsedLine[headers[valuesIndex]] = value; + }); + result.push(parsedLine); + }); + return result; + } } export function entryPoint(mainInstance: MainInstance): void { - mainInstance.objectParserManager.addObjectParser(() => new CsvObjectParser(), 'csv'); + mainInstance.objectParserManager.addObjectParser(() => new CsvObjectParser(), 'csv'); } diff --git a/src/object-parser/file-object-parser.test.ts b/src/object-parser/file-object-parser.test.ts index 62c6af8c..f589b17c 100644 --- a/src/object-parser/file-object-parser.test.ts +++ b/src/object-parser/file-object-parser.test.ts @@ -1,28 +1,26 @@ -import {MainInstance} from '../plugins/main-instance'; -import {FileObjectParserTest, entryPoint} from './file-object-parser'; +import { MainInstance } from '../plugins/main-instance'; +import { FileObjectParser, entryPoint } from './file-object-parser'; describe('FileObjectParserTest', () => { + test('should parse', () => { + const code = 'anyStuff'; - test('should parse', () => { - const code = 'anyStuff'; + const parsed: any = new FileObjectParser().parse(code); - const parsed: any = new FileObjectParserTest().parse(code); - - expect(code).toBe(code); - }); - - it('Should export an entry point', done => { - const mainInstance: MainInstance = { - // @ts-ignore - objectParserManager: { - addObjectParser: (createFunction: any, ...tags: any) => { - expect(createFunction()).toBeInstanceOf(FileObjectParserTest); - expect(tags.sort()).toEqual(['file'].sort()); - done(); - } - } - }; - entryPoint(mainInstance); - }); + expect(code).toBe(code); + }); + it('Should export an entry point', done => { + const mainInstance: MainInstance = { + // @ts-ignore + objectParserManager: { + addObjectParser: (createFunction: any, ...tags: any) => { + expect(createFunction()).toBeInstanceOf(FileObjectParser); + expect(tags.sort()).toEqual(['file'].sort()); + done(); + } + } + }; + entryPoint(mainInstance); + }); }); diff --git a/src/object-parser/file-object-parser.ts b/src/object-parser/file-object-parser.ts index a3039438..0df25630 100644 --- a/src/object-parser/file-object-parser.ts +++ b/src/object-parser/file-object-parser.ts @@ -1,12 +1,12 @@ -import {ObjectParser} from './object-parser'; -import {MainInstance} from '../plugins/main-instance'; +import { ObjectParser } from './object-parser'; +import { MainInstance } from '../plugins/main-instance'; -export class FileObjectParserTest implements ObjectParser { - public parse(value: string): object { - return value as unknown as object; - } +export class FileObjectParser implements ObjectParser { + public parse(value: string): object { + return value as unknown as object; + } } export function entryPoint(mainInstance: MainInstance): void { - mainInstance.objectParserManager.addObjectParser(() => new FileObjectParserTest(), 'file'); + mainInstance.objectParserManager.addObjectParser(() => new FileObjectParser(), 'file'); } diff --git a/src/object-parser/json-object-parser.test.ts b/src/object-parser/json-object-parser.test.ts index f8a63b93..feeba1a1 100644 --- a/src/object-parser/json-object-parser.test.ts +++ b/src/object-parser/json-object-parser.test.ts @@ -1,78 +1,70 @@ -import {entryPoint, JsonObjectParser} from './json-object-parser'; -import {MainInstance} from '../plugins/main-instance'; +import { entryPoint, JsonObjectParser } from './json-object-parser'; +import { MainInstance } from '../plugins/main-instance'; describe('JsonObjectParser', () => { + test('should keep string numbers as string', () => { + const value = '{\n' + ' "firstLevel": {\n' + ' "secondLevel": "123.00"\n' + ' }\n' + '}'; - test('should keep string numbers as string', () => { - const value = '{\n' + - ' "firstLevel": {\n' + - ' "secondLevel": "123.00"\n' + - ' }\n' + - '}'; + const parsed: any = new JsonObjectParser().parse(value); - const parsed: any = new JsonObjectParser().parse(value); + expect(typeof parsed.firstLevel.secondLevel).toEqual('string'); + expect(parsed.firstLevel.secondLevel).toEqual('123.00'); + }); - expect(typeof parsed.firstLevel.secondLevel).toEqual('string'); - expect(parsed.firstLevel.secondLevel).toEqual('123.00'); - }); - - test('should stringify with space', () => { - const value = {firstLevel: {secondLevel: 'value'}}; - const space = 4; + test('should stringify with space', () => { + const value = { firstLevel: { secondLevel: 'value' } }; + const space = 4; - const stringified = new JsonObjectParser().stringify(value, {space: space}); - - expect(stringified).toBe(JSON.stringify(value, null, space)); + const stringified = new JsonObjectParser().stringify(value, { + space: space }); - test('should stringify with default space', () => { - const value = {firstLevel: {secondLevel: 'value'}}; - const defaultSpace = 2; + expect(stringified).toBe(JSON.stringify(value, null, space)); + }); - const stringified = new JsonObjectParser().stringify(value); + test('should stringify with default space', () => { + const value = { firstLevel: { secondLevel: 'value' } }; + const defaultSpace = 2; - expect(stringified).toBe(JSON.stringify(value, null, defaultSpace)); - }); + const stringified = new JsonObjectParser().stringify(value); - test('should stringify undefined objects', () => { - // @ts-ignore - const stringified = new JsonObjectParser().stringify(undefined); + expect(stringified).toBe(JSON.stringify(value, null, defaultSpace)); + }); - expect(stringified).toBe('{}'); - }); + test('should stringify undefined objects', () => { + // @ts-ignore + const stringified = new JsonObjectParser().stringify(undefined); - test('should stringify cycle reference', () => { - let value: any = {firstLevel: {secondLevel: {}}}; - value.firstLevel.secondLevel.thirdLevel = value; - const expected = '{\n' + - ' "firstLevel": {\n' + - ' "secondLevel": {}\n' + - ' }\n' + - '}'; + expect(stringified).toBe('{}'); + }); - const stringified = new JsonObjectParser().stringify(value); + test('should stringify cycle reference', () => { + let value: any = { firstLevel: { secondLevel: {} } }; + value.firstLevel.secondLevel.thirdLevel = value; + const expected = '{\n' + ' "firstLevel": {\n' + ' "secondLevel": {}\n' + ' }\n' + '}'; - expect(stringified).toBe(expected); - }); + const stringified = new JsonObjectParser().stringify(value); - test('should throw parse error', () => { - const notJson = 'foo bar\nfoo: bar'; + expect(stringified).toBe(expected); + }); - expect(() => new JsonObjectParser().parse(notJson)).toThrow(); - }); + test('should throw parse error', () => { + const notJson = 'foo bar\nfoo: bar'; - it('Should export an entry point', done => { - const mainInstance: MainInstance = { - // @ts-ignore - objectParserManager: { - addObjectParser: (createFunction: any, ...tags: any) => { - expect(createFunction()).toBeInstanceOf(JsonObjectParser); - expect(tags).toEqual(['json']); - done(); - } - } - }; - entryPoint(mainInstance); - }); + expect(() => new JsonObjectParser().parse(notJson)).toThrow(); + }); + it('Should export an entry point', done => { + const mainInstance: MainInstance = { + // @ts-ignore + objectParserManager: { + addObjectParser: (createFunction: any, ...tags: any) => { + expect(createFunction()).toBeInstanceOf(JsonObjectParser); + expect(tags).toEqual(['json']); + done(); + } + } + }; + entryPoint(mainInstance); + }); }); diff --git a/src/object-parser/json-object-parser.ts b/src/object-parser/json-object-parser.ts index 1b3b7a8c..fb2fe38e 100644 --- a/src/object-parser/json-object-parser.ts +++ b/src/object-parser/json-object-parser.ts @@ -1,26 +1,27 @@ -import {ObjectParser} from './object-parser'; -import {ObjectDecycler} from './object-decycler'; -import {MainInstance} from '../plugins/main-instance'; +import { ObjectParser } from './object-parser'; +import { ObjectDecycler } from './object-decycler'; +import { MainInstance } from '../plugins/main-instance'; export class JsonObjectParser implements ObjectParser { + public parse(value: string): object { + return JSON.parse(value); + } - public parse(value: string): object { - return JSON.parse(value); - } + public stringify(value: object, query: any = {}): string { + return JSON.stringify(new ObjectDecycler().decycle(value || {}), null, this.parseQuery(query).space); + } - public stringify(value: object, query: any = {}): string { - return JSON.stringify(new ObjectDecycler().decycle(value || {}), null, this.parseQuery(query).space); - } - - private parseQuery(query: any): any { - return Object.assign({}, - { - space: 2 - }, - query); - } + private parseQuery(query: any): any { + return Object.assign( + {}, + { + space: 2 + }, + query + ); + } } export function entryPoint(mainInstance: MainInstance): void { - mainInstance.objectParserManager.addObjectParser(() => new JsonObjectParser(), 'json'); + mainInstance.objectParserManager.addObjectParser(() => new JsonObjectParser(), 'json'); } diff --git a/src/object-parser/object-decycler.test.ts b/src/object-parser/object-decycler.test.ts index 1d140ba6..e0102584 100644 --- a/src/object-parser/object-decycler.test.ts +++ b/src/object-parser/object-decycler.test.ts @@ -1,25 +1,24 @@ -import {ObjectDecycler} from './object-decycler'; +import { ObjectDecycler } from './object-decycler'; describe('ObjectDecycler', () => { - it('should stringify cycle reference', () => { - let value: any = {firstLevel: {secondLevel: {}}}; - value.firstLevel.secondLevel.thirdLevel = value; - const expected = {firstLevel: {secondLevel: {}}}; + it('should stringify cycle reference', () => { + let value: any = { firstLevel: { secondLevel: {} } }; + value.firstLevel.secondLevel.thirdLevel = value; + const expected = { firstLevel: { secondLevel: {} } }; - const stringified = new ObjectDecycler().decycle(value); + const stringified = new ObjectDecycler().decycle(value); - expect(stringified).toEqual(expected); - }); + expect(stringified).toEqual(expected); + }); - it('should replace cycle reference', () => { - let value: any = {firstLevel: {secondLevel: {}}}; - value.firstLevel.secondLevel.thirdLevel = value; - const replacer = '[CYCLE REFERENCE]'; - const expected = {firstLevel: {secondLevel: {thirdLevel: replacer}}}; + it('should replace cycle reference', () => { + let value: any = { firstLevel: { secondLevel: {} } }; + value.firstLevel.secondLevel.thirdLevel = value; + const replacer = '[CYCLE REFERENCE]'; + const expected = { firstLevel: { secondLevel: { thirdLevel: replacer } } }; - const stringified = new ObjectDecycler(replacer).decycle(value); - - expect(stringified).toEqual(expected); - }); + const stringified = new ObjectDecycler(replacer).decycle(value); + expect(stringified).toEqual(expected); + }); }); diff --git a/src/object-parser/object-decycler.ts b/src/object-parser/object-decycler.ts index 5e3b2336..0c915082 100644 --- a/src/object-parser/object-decycler.ts +++ b/src/object-parser/object-decycler.ts @@ -1,40 +1,39 @@ export class ObjectDecycler { - - private readonly circularReplacer?: string; - private cache = new Map(); - - public constructor(circularReplacer?: string) { - this.circularReplacer = circularReplacer; - } - - public decycle(value: object): object { - const stringified = JSON.stringify(value, (key, value) => this.jsonStringifyReplacer(key, value)); - return JSON.parse(stringified); - } - - private isObject(value: any) { - return typeof(value) === 'object'; - } - - private isNotNull(value: any) { - return value !== null; - } - - private register(value: any) { - this.cache.set(value, true); - } - - private isRegistered(value: any) { - return this.cache.has(value); - } - - private jsonStringifyReplacer(key: string, value: any) { - if (this.isObject(value) && this.isNotNull(value)) { - if (this.isRegistered(value)) { - return this.circularReplacer; - } - this.register(value); - } - return value; + private readonly circularReplacer?: string; + private cache = new Map(); + + public constructor(circularReplacer?: string) { + this.circularReplacer = circularReplacer; + } + + public decycle(value: object): object { + const stringified = JSON.stringify(value, (key, value) => this.jsonStringifyReplacer(key, value)); + return JSON.parse(stringified); + } + + private isObject(value: any) { + return typeof value === 'object'; + } + + private isNotNull(value: any) { + return value !== null; + } + + private register(value: any) { + this.cache.set(value, true); + } + + private isRegistered(value: any) { + return this.cache.has(value); + } + + private jsonStringifyReplacer(key: string, value: any) { + if (this.isObject(value) && this.isNotNull(value)) { + if (this.isRegistered(value)) { + return this.circularReplacer; + } + this.register(value); } + return value; + } } diff --git a/src/object-parser/object-parser.ts b/src/object-parser/object-parser.ts index cf97b895..520852e5 100644 --- a/src/object-parser/object-parser.ts +++ b/src/object-parser/object-parser.ts @@ -1,3 +1,3 @@ export interface ObjectParser { - parse(value: string, query?: any): object; + parse(value: string, query?: any): object; } diff --git a/src/object-parser/yml-object-parser.test.ts b/src/object-parser/yml-object-parser.test.ts index b4b666dd..efbf409d 100644 --- a/src/object-parser/yml-object-parser.test.ts +++ b/src/object-parser/yml-object-parser.test.ts @@ -1,72 +1,72 @@ -import {YmlObjectParser, entryPoint} from './yml-object-parser'; +import { YmlObjectParser, entryPoint } from './yml-object-parser'; import * as yaml from 'yamljs'; -import {ObjectDecycler} from './object-decycler'; -import {MainInstance} from '../plugins/main-instance'; +import { ObjectDecycler } from './object-decycler'; +import { MainInstance } from '../plugins/main-instance'; describe('YmlObjectParser', () => { + test('should stringify with param', () => { + const value = { firstLevel: { secondLevel: 'value' } }; - test('should stringify with param', () => { - const value = {firstLevel: {secondLevel: 'value'}}; - - const stringified = new YmlObjectParser().stringify(value, {space: 4, inline: 1}); - - expect(stringified).toBe(yaml.stringify(value, 1, 4)); + const stringified = new YmlObjectParser().stringify(value, { + space: 4, + inline: 1 }); - test('should stringify default', () => { - const value = {firstLevel: {secondLevel: 'value'}}; - const defaultInline = 100; - const defaultSpace = 2; + expect(stringified).toBe(yaml.stringify(value, 1, 4)); + }); - const stringified = new YmlObjectParser().stringify(value); + test('should stringify default', () => { + const value = { firstLevel: { secondLevel: 'value' } }; + const defaultInline = 100; + const defaultSpace = 2; - expect(stringified).toBe(yaml.stringify(value, defaultInline, defaultSpace)); - }); + const stringified = new YmlObjectParser().stringify(value); - test('should keep string numbers as string', () => { - const value = 'firstLevel:\n' + - " secondLevel: '123.00'\n"; + expect(stringified).toBe(yaml.stringify(value, defaultInline, defaultSpace)); + }); - const parsed: any = new YmlObjectParser().parse(value); + test('should keep string numbers as string', () => { + const value = 'firstLevel:\n' + " secondLevel: '123.00'\n"; - expect(typeof parsed.firstLevel.secondLevel).toEqual('string'); - expect(parsed.firstLevel.secondLevel).toEqual('123.00'); - }); + const parsed: any = new YmlObjectParser().parse(value); - test('should stringify cycle reference', () => { - let value: any = {firstLevel: {secondLevel: {}}}; - value.firstLevel.secondLevel.thirdLevel = value; + expect(typeof parsed.firstLevel.secondLevel).toEqual('string'); + expect(parsed.firstLevel.secondLevel).toEqual('123.00'); + }); - const stringified = new YmlObjectParser().stringify(value); + test('should stringify cycle reference', () => { + let value: any = { firstLevel: { secondLevel: {} } }; + value.firstLevel.secondLevel.thirdLevel = value; - expect(stringified).toBe(yaml.stringify(new ObjectDecycler().decycle(value), 100, 2)); - }); + const stringified = new YmlObjectParser().stringify(value); - test('should stringify undefined objects', () => { - // @ts-ignore - const stringified = new YmlObjectParser().stringify(undefined); + expect(stringified).toBe(yaml.stringify(new ObjectDecycler().decycle(value), 100, 2)); + }); - expect(stringified).toBe('{}'); - }); + test('should stringify undefined objects', () => { + // @ts-ignore + const stringified = new YmlObjectParser().stringify(undefined); - test('should throw parse error', () => { - const notYml = 'foo bar\nfoo: bar'; + expect(stringified).toBe('{}'); + }); - expect(() => new YmlObjectParser().parse(notYml)).toThrow(); - }); + test('should throw parse error', () => { + const notYml = 'foo bar\nfoo: bar'; - it('Should export an entry point', done => { - const mainInstance: MainInstance = { - // @ts-ignore - objectParserManager: { - addObjectParser: (createFunction: any, ...tags: any) => { - expect(createFunction()).toBeInstanceOf(YmlObjectParser); - expect(tags.sort()).toEqual(['yml', 'yaml'].sort()); - done(); - } - } - }; - entryPoint(mainInstance); - }); + expect(() => new YmlObjectParser().parse(notYml)).toThrow(); + }); + it('Should export an entry point', done => { + const mainInstance: MainInstance = { + // @ts-ignore + objectParserManager: { + addObjectParser: (createFunction: any, ...tags: any) => { + expect(createFunction()).toBeInstanceOf(YmlObjectParser); + expect(tags.sort()).toEqual(['yml', 'yaml'].sort()); + done(); + } + } + }; + entryPoint(mainInstance); + }); }); diff --git a/src/object-parser/yml-object-parser.ts b/src/object-parser/yml-object-parser.ts index 9bef552b..c61c3340 100644 --- a/src/object-parser/yml-object-parser.ts +++ b/src/object-parser/yml-object-parser.ts @@ -1,29 +1,30 @@ -import {ObjectParser} from './object-parser'; +import { ObjectParser } from './object-parser'; import * as yaml from 'yamljs'; -import {ObjectDecycler} from './object-decycler'; -import {MainInstance} from '../plugins/main-instance'; +import { ObjectDecycler } from './object-decycler'; +import { MainInstance } from '../plugins/main-instance'; export class YmlObjectParser implements ObjectParser { - public parse(value: string): object { - return yaml.parse(value); - } + public parse(value: string): object { + return yaml.parse(value); + } - public stringify(value: object, params: any = {}): string { - const parsedParams = this.parseParams(params); - return yaml.stringify(new ObjectDecycler().decycle(value || {}), parsedParams.inline, parsedParams.space); - } - - private parseParams(params: any): any { - return Object.assign({}, - { - inline: 100, - space: 2 - }, - params); - } + public stringify(value: object, params: any = {}): string { + const config = this.createStringifyConfiguration(params); + return yaml.stringify(new ObjectDecycler().decycle(value || {}), config.inline, config.space); + } + private createStringifyConfiguration(params: any): any { + return Object.assign( + {}, + { + inline: 100, + space: 2 + }, + params + ); + } } export function entryPoint(mainInstance: MainInstance): void { - mainInstance.objectParserManager.addObjectParser(() => new YmlObjectParser(), 'yml', 'yaml'); + mainInstance.objectParserManager.addObjectParser(() => new YmlObjectParser(), 'yml', 'yaml'); } diff --git a/src/outputs/colorizer.ts b/src/outputs/colorizer.ts deleted file mode 100644 index b7595a24..00000000 --- a/src/outputs/colorizer.ts +++ /dev/null @@ -1,41 +0,0 @@ -let colorizer: { - black: { - bgHex: (s: string) => Function; - bgYellow: (s: string) => string; - bgRed: (s: string) => string; - bgGreen: (s: string) => string; - }; - magenta: (s: string) => string; - blue: (s: string) => string; - cyan: (s: string) => string; - green: (s: string) => string; - red: (s: string) => string; - yellow: (s: string) => string; - hex: (s: string) => string; - reset: (s: string) => string; - gray: (s: string) => string; -}; - -try { - colorizer = require('chalk'); -} catch (e) { - colorizer = { - black: { - bgHex: () => (message: string) => message, - bgYellow: (message: string) => message, - bgRed: (message: string) => message, - bgGreen: (message: string) => message, - }, - magenta: (message: string) => message, - blue: (message: string) => message, - cyan: (message: string) => message, - green: (message: string) => message, - red: (message: string) => message, - yellow: (message: string) => message, - hex: (message: string) => message, - reset: (message: string) => message, - gray: (message: string) => message, - - }; -} -export default colorizer; diff --git a/src/outputs/formatters/console-formatter.test.ts b/src/outputs/formatters/console-formatter.test.ts index f2fca96a..80df2b00 100644 --- a/src/outputs/formatters/console-formatter.test.ts +++ b/src/outputs/formatters/console-formatter.test.ts @@ -1,32 +1,29 @@ -import {RequisitionModel} from '../../models/outputs/requisition-model'; -import {ConsoleFormatter, entryPoint} from './console-formatter'; +import { TaskModel } from '../../models/outputs/task-model'; +import { ConsoleFormatter, entryPoint } from './console-formatter'; describe('ConsoleFormatter', () => { + it('Should stringify it', () => { + const test: TaskModel = { + name: 'name', + valid: true, + tests: [] + } as any; + const format = new ConsoleFormatter().format(test); - it('Should stringify it', () => { - const test: RequisitionModel = { - name: 'name', - valid: true, - tests: [] - }; - const format = new ConsoleFormatter().format(test); + expect(typeof format).toBe('string'); + }); - expect(typeof (format)).toBe('string'); - }); - - it('Should export an entry point', done => { - const mainInstance: any = { - reportFormatterManager: { - addReportFormatter: (createFunction: any, ...tags: any) => { - expect(createFunction()).toBeInstanceOf(ConsoleFormatter); - expect(tags).toEqual(['console', 'stdout']); - done(); - } - } - }; - - entryPoint(mainInstance); - - }); + it('Should export an entry point', done => { + const mainInstance: any = { + reportFormatterManager: { + addReportFormatter: (createFunction: any, ...tags: any) => { + expect(createFunction()).toBeInstanceOf(ConsoleFormatter); + expect(tags).toEqual(['console', 'stdout']); + done(); + } + } + }; + entryPoint(mainInstance); + }); }); diff --git a/src/outputs/formatters/console-formatter.ts b/src/outputs/formatters/console-formatter.ts index 059e1a65..076e57cd 100644 --- a/src/outputs/formatters/console-formatter.ts +++ b/src/outputs/formatters/console-formatter.ts @@ -1,16 +1,15 @@ -import {ReportFormatter} from './report-formatter'; -import {RequisitionModel} from '../../models/outputs/requisition-model'; -import {MainInstance} from '../../plugins/main-instance'; -import {ObjectDecycler} from '../../object-parser/object-decycler'; -import {prettifyJson} from '../prettify-json'; +import { ReportFormatter } from './report-formatter'; +import { TaskModel } from '../../models/outputs/task-model'; +import { MainInstance } from '../../plugins/main-instance'; +import { ObjectDecycler } from '../../object-parser/object-decycler'; +import { prettifyJson } from '../prettify-json'; export class ConsoleFormatter implements ReportFormatter { - - public format(report: RequisitionModel): string { - return prettifyJson(new ObjectDecycler().decycle(report)); - } + public format(report: TaskModel): string { + return prettifyJson(new ObjectDecycler().decycle(report)); + } } export function entryPoint(mainInstance: MainInstance): void { - mainInstance.reportFormatterManager.addReportFormatter(() => new ConsoleFormatter(), 'console', 'stdout'); + mainInstance.reportFormatterManager.addReportFormatter(() => new ConsoleFormatter(), 'console', 'stdout'); } diff --git a/src/outputs/formatters/json-formatter.test.ts b/src/outputs/formatters/json-formatter.test.ts index 0243f9e0..931c79c2 100644 --- a/src/outputs/formatters/json-formatter.test.ts +++ b/src/outputs/formatters/json-formatter.test.ts @@ -1,43 +1,41 @@ -import {RequisitionModel} from '../../models/outputs/requisition-model'; -import {entryPoint, JsonReportFormatter} from './json-formatter'; -import {JsonObjectParser} from '../../object-parser/json-object-parser'; +import { TaskModel } from '../../models/outputs/task-model'; +import { entryPoint, JsonReportFormatter } from './json-formatter'; +import { JsonObjectParser } from '../../object-parser/json-object-parser'; describe('JsonReportFormatter', () => { + it('Should stringify it', () => { + const test: TaskModel = { + name: 'name', + valid: true, + tests: [] + } as any; + const format = new JsonReportFormatter().format(test); - it('Should stringify it', () => { - const test: RequisitionModel = { - name: 'name', - valid: true, - tests: [] - }; - const format = new JsonReportFormatter().format(test); + expect(typeof format).toBe('string'); + expect(format).toBe(new JsonObjectParser().stringify(test)); + }); - expect(typeof (format)).toBe('string'); - expect(format).toBe(new JsonObjectParser().stringify(test)); - }); + it('Should throw', () => { + const test: TaskModel = { + name: 'name', + valid: true, + tests: [] + } as any; + test.cycle = test; + expect(() => new JsonReportFormatter().format(test)).toThrow(); + }); - it('Should throw', () => { - const test: RequisitionModel = { - name: 'name', - valid: true, - tests: [] - }; - test.cycle = test; - expect(() => new JsonReportFormatter().format(test)).toThrow(); - }); - - it('Should export an entry point', done => { - const mainInstance: any = { - reportFormatterManager: { - addReportFormatter: (createFunction: any, ...tags: any) => { - expect(createFunction()).toBeInstanceOf(JsonReportFormatter); - expect(tags).toEqual(['json']); - done(); - } - } - }; - - entryPoint(mainInstance); - }); + it('Should export an entry point', done => { + const mainInstance: any = { + reportFormatterManager: { + addReportFormatter: (createFunction: any, ...tags: any) => { + expect(createFunction()).toBeInstanceOf(JsonReportFormatter); + expect(tags).toEqual(['json']); + done(); + } + } + }; + entryPoint(mainInstance); + }); }); diff --git a/src/outputs/formatters/json-formatter.ts b/src/outputs/formatters/json-formatter.ts index a622ad00..ca1ee7c0 100644 --- a/src/outputs/formatters/json-formatter.ts +++ b/src/outputs/formatters/json-formatter.ts @@ -1,19 +1,19 @@ -import {ReportFormatter} from './report-formatter'; -import {RequisitionModel} from '../../models/outputs/requisition-model'; -import {MainInstance} from '../../plugins/main-instance'; -import {Logger} from '../../loggers/logger'; +import { ReportFormatter } from './report-formatter'; +import { TaskModel } from '../../models/outputs/task-model'; +import { MainInstance } from '../../plugins/main-instance'; +import { Logger } from '../../loggers/logger'; export class JsonReportFormatter implements ReportFormatter { - public format(report: RequisitionModel): string { - try { - return JSON.stringify(report, null, 2); - } catch (e) { - Logger.warning(e); - throw e; - } + public format(report: TaskModel): string { + try { + return JSON.stringify(report, null, 2); + } catch (err) { + Logger.warning(`Json formatter errored: ` + err); + throw err; } + } } export function entryPoint(mainInstance: MainInstance): void { - mainInstance.reportFormatterManager.addReportFormatter(() => new JsonReportFormatter(), 'json'); + mainInstance.reportFormatterManager.addReportFormatter(() => new JsonReportFormatter(), 'json'); } diff --git a/src/outputs/formatters/report-formatter.ts b/src/outputs/formatters/report-formatter.ts index 45b86336..cd043b7c 100644 --- a/src/outputs/formatters/report-formatter.ts +++ b/src/outputs/formatters/report-formatter.ts @@ -1,5 +1,5 @@ -import {RequisitionModel} from '../../models/outputs/requisition-model'; +import { TaskModel } from '../../models/outputs/task-model'; export interface ReportFormatter { - format(report: RequisitionModel): string; + format(report: TaskModel): string; } diff --git a/src/outputs/formatters/yml-formatter.test.ts b/src/outputs/formatters/yml-formatter.test.ts index 9e8d45bb..1f944652 100644 --- a/src/outputs/formatters/yml-formatter.test.ts +++ b/src/outputs/formatters/yml-formatter.test.ts @@ -1,34 +1,31 @@ -import {entryPoint, YmlReportFormatter} from './yml-formatter'; -import {RequisitionModel} from '../../models/outputs/requisition-model'; -import {YmlObjectParser} from '../../object-parser/yml-object-parser'; +import { entryPoint, YmlReportFormatter } from './yml-formatter'; +import { TaskModel } from '../../models/outputs/task-model'; +import { YmlObjectParser } from '../../object-parser/yml-object-parser'; describe('YmlReportFormatter', () => { + it('Should stringify it', () => { + const test: TaskModel = { + name: 'name', + valid: true, + tests: [] + } as any; + const format = new YmlReportFormatter().format(test); - it('Should stringify it', () => { - const test: RequisitionModel = { - name: 'name', - valid: true, - tests: [] - }; - const format = new YmlReportFormatter().format(test); + expect(typeof format).toBe('string'); + expect(format).toBe(new YmlObjectParser().stringify(test)); + }); - expect(typeof (format)).toBe('string'); - expect(format).toBe(new YmlObjectParser().stringify(test)); - }); - - it('Should export an entry point', done => { - const mainInstance: any = { - reportFormatterManager: { - addReportFormatter: (createFunction: any, ...tags: any) => { - expect(createFunction()).toBeInstanceOf(YmlReportFormatter); - expect(tags).toEqual(['yml', 'yaml']); - done(); - } - } - }; - - entryPoint(mainInstance); - - }); + it('Should export an entry point', done => { + const mainInstance: any = { + reportFormatterManager: { + addReportFormatter: (createFunction: any, ...tags: any) => { + expect(createFunction()).toBeInstanceOf(YmlReportFormatter); + expect(tags).toEqual(['yml', 'yaml']); + done(); + } + } + }; + entryPoint(mainInstance); + }); }); diff --git a/src/outputs/formatters/yml-formatter.ts b/src/outputs/formatters/yml-formatter.ts index c00cb784..88d7adbc 100644 --- a/src/outputs/formatters/yml-formatter.ts +++ b/src/outputs/formatters/yml-formatter.ts @@ -1,14 +1,14 @@ -import {ReportFormatter} from './report-formatter'; -import {RequisitionModel} from '../../models/outputs/requisition-model'; -import {MainInstance} from '../../plugins/main-instance'; -import {YmlObjectParser} from '../../object-parser/yml-object-parser'; +import { ReportFormatter } from './report-formatter'; +import { TaskModel } from '../../models/outputs/task-model'; +import { MainInstance } from '../../plugins/main-instance'; +import { YmlObjectParser } from '../../object-parser/yml-object-parser'; export class YmlReportFormatter implements ReportFormatter { - public format(report: RequisitionModel): string { - return new YmlObjectParser().stringify(report); - } + public format(report: TaskModel): string { + return new YmlObjectParser().stringify(report); + } } export function entryPoint(mainInstance: MainInstance): void { - mainInstance.reportFormatterManager.addReportFormatter(() => new YmlReportFormatter(), 'yml', 'yaml'); + mainInstance.reportFormatterManager.addReportFormatter(() => new YmlReportFormatter(), 'yml', 'yaml'); } diff --git a/src/outputs/multi-tests-output.test.ts b/src/outputs/multi-tests-output.test.ts index 05b51745..4a1ede26 100644 --- a/src/outputs/multi-tests-output.test.ts +++ b/src/outputs/multi-tests-output.test.ts @@ -1,16 +1,16 @@ -import {MultiTestsOutput} from './multi-tests-output'; -import {SummaryTestOutput} from './summary-test-output'; -import {ProtocolManager} from '../plugins/protocol-manager'; -import {ReportFormatterManager} from '../plugins/report-formatter-manager'; -import {DynamicModulesManager} from '../plugins/dynamic-modules-manager'; +import { MultiTestsOutput } from './multi-tests-output'; +import { SummaryTestOutput } from './summary-test-output'; +import { ProtocolManager } from '../plugins/protocol-manager'; +import { ReportFormatterManager } from '../plugins/report-formatter-manager'; +import { DynamicModulesManager } from '../plugins/dynamic-modules-manager'; jest.mock('../plugins/dynamic-modules-manager'); // @ts-ignore DynamicModulesManager.getInstance.mockImplementation(() => { - return { - getProtocolManager: () => new ProtocolManager(), - getReportFormatterManager: () => new ReportFormatterManager() - }; + return { + getProtocolManager: () => new ProtocolManager(), + getReportFormatterManager: () => new ReportFormatterManager() + }; }); jest.mock('../plugins/report-formatter-manager'); @@ -19,73 +19,71 @@ jest.mock('./summary-test-output'); const print = jest.fn(); let constructorSummary = jest.fn(() => { - return { - print: print - }; + return { + print: print + }; }); // @ts-ignore SummaryTestOutput.mockImplementation(constructorSummary); -const publishMock = jest.fn(); +const actMock = jest.fn(); let format = jest.fn(); const create = jest.fn(() => { - return { - format: format - }; + return { + format: format + }; }); -const createPublisherMock = jest.fn(() => { - return { - publish: publishMock, - }; +const createActuatorMock = jest.fn(() => { + return { + act: actMock + }; }); // @ts-ignore ProtocolManager.mockImplementation(() => { - return { - createPublisher: createPublisherMock - }; + return { + createActuator: createActuatorMock + }; }); const formatMock = jest.fn(); const createReportFormatterMock = jest.fn(() => { - return { - format: formatMock, - }; + return { + format: formatMock + }; }); // @ts-ignore ReportFormatterManager.mockImplementation(() => { - return { - createReportFormatter: createReportFormatterMock - }; + return { + createReportFormatter: createReportFormatterMock + }; }); describe('MultiTestsOutput', () => { - beforeEach(() => { - print.mockClear(); - constructorSummary.mockClear(); - // @ts-ignore - SummaryTestOutput.mockClear(); - publishMock.mockClear(); - format.mockClear(); - create.mockClear(); - createPublisherMock.mockClear(); - }); + beforeEach(() => { + print.mockClear(); + constructorSummary.mockClear(); + // @ts-ignore + SummaryTestOutput.mockClear(); + actMock.mockClear(); + format.mockClear(); + create.mockClear(); + createActuatorMock.mockClear(); + }); - it('Should create an output and a createFunction', () => { - const output = {type: 'output', format: 'createFunction'}; - // @ts-ignore - const multiTestsOutput = new MultiTestsOutput([output]); + it('Should create an output and a createFunction', () => { + const output = { type: 'output', format: 'createFunction' }; + // @ts-ignore + const multiTestsOutput = new MultiTestsOutput([output]); - expect(createPublisherMock).toHaveBeenCalledWith(output); - }); + expect(createActuatorMock).toHaveBeenCalledWith(output); + }); - it('Should format before printing', async () => { - - const report = {}; - const output = {type: 'output', format: 'createFunction'}; - // @ts-ignore - await new MultiTestsOutput([output]).publishReport(report); - - expect(publishMock).toHaveBeenCalledWith(); - }); + it('Should format before printing', async () => { + const report = {}; + const output = { type: 'output', format: 'createFunction' }; + // @ts-ignore + await new MultiTestsOutput([output]).publishReport(report); + expect(actMock).toHaveBeenCalledWith(); + }); }); diff --git a/src/outputs/multi-tests-output.ts b/src/outputs/multi-tests-output.ts index dc0b21bf..f73b81b0 100644 --- a/src/outputs/multi-tests-output.ts +++ b/src/outputs/multi-tests-output.ts @@ -1,36 +1,37 @@ -import {Logger} from '../loggers/logger'; -import {RequisitionModel} from '../models/outputs/requisition-model'; -import {PublisherModel} from '../models/inputs/publisher-model'; -import {Publisher} from '../publishers/publisher'; -import {ReportFormatter} from './formatters/report-formatter'; -import {DynamicModulesManager} from '../plugins/dynamic-modules-manager'; +import { Logger } from '../loggers/logger'; +import { TaskModel } from '../models/outputs/task-model'; +import { ActuatorModel } from '../models/inputs/actuator-model'; +import { Actuator } from '../actuators/actuator'; +import { ReportFormatter } from './formatters/report-formatter'; +import { DynamicModulesManager } from '../plugins/dynamic-modules-manager'; export class MultiTestsOutput { - private outputs: Publisher[] = []; + private outputs: Actuator[] = []; - public constructor(outputs: PublisherModel[]) { - (outputs || []).forEach((output: PublisherModel) => { - Logger.debug(`Instantiating output '${output.type}' and format '${output.format}'`); - const publisher = DynamicModulesManager.getInstance() - .getProtocolManager().createPublisher(output); - publisher.formatter = DynamicModulesManager.getInstance() - .getReportFormatterManager().createReportFormatter(output.format); - publisher.format = output.format; - this.outputs.push(publisher); - }); - } + public constructor(outputs: ActuatorModel[]) { + (outputs || []).forEach((output: ActuatorModel) => { + Logger.debug(`Instantiating output '${output.type}' and format '${output.format}'`); + const actuator = DynamicModulesManager.getInstance().getProtocolManager().createActuator(output); + actuator.formatter = DynamicModulesManager.getInstance() + .getReportFormatterManager() + .createReportFormatter(output.format); + actuator.format = output.format; + this.outputs.push(actuator); + }); + } - public async publishReport(report: RequisitionModel) { - await Promise.all(this.outputs - .map(publisher => { - try { - const formatter = publisher.formatter as ReportFormatter; - Logger.trace(`Formatting as ${publisher.format}`); - publisher.payload = formatter.format(report); - return publisher.publish(); - } catch (err) { - Logger.warning(`Error publishing report: ${JSON.stringify(report)}: ${err}`); - } - })); - } + public async publishReport(report: TaskModel) { + await Promise.all( + this.outputs.map(actuator => { + try { + const formatter = actuator.formatter as ReportFormatter; + Logger.trace(`Formatting as ${actuator.format}`); + actuator.payload = formatter.format(report); + return actuator.act(); + } catch (err) { + Logger.warning(`Error publishing report: ${JSON.stringify(report)}: ${err}`); + } + }) + ); + } } diff --git a/src/outputs/prettify-json.ts b/src/outputs/prettify-json.ts index 6aa933b9..26de1991 100644 --- a/src/outputs/prettify-json.ts +++ b/src/outputs/prettify-json.ts @@ -1,15 +1,15 @@ const configuration = { - defaultIndentation: 4, - inlineArrays: true, - emptyArrayMsg: '-', - keysColor: 'green', - dashColor: 'grey' + defaultIndentation: 2, + inlineArrays: true, + emptyArrayMsg: '-', + keysColor: 'green', + dashColor: 'grey' }; export function prettifyJson(value: object): string { - try { - return require('prettyjson').render(value, configuration); - } catch (e) { - return JSON.stringify(value, null, configuration.defaultIndentation); - } + try { + return require('prettyjson').render(value, configuration); + } catch (e) { + return JSON.stringify(value, null, configuration.defaultIndentation); + } } diff --git a/src/outputs/summary-test-output.test.ts b/src/outputs/summary-test-output.test.ts index 1229b22f..bcd18381 100644 --- a/src/outputs/summary-test-output.test.ts +++ b/src/outputs/summary-test-output.test.ts @@ -1,261 +1,301 @@ -import {SummaryTestOutput} from './summary-test-output'; +import { SummaryTestOutput } from './summary-test-output'; -let consoleLogMock = jest.fn((message) => console.warn(message)); +let consoleLogMock = jest.fn(message => console.warn(message)); console.log = consoleLogMock; describe('SummaryTestOutput', () => { - - beforeEach(() => { - consoleLogMock.mockClear(); - }); - - it('should print report name', () => { - new SummaryTestOutput({ - name: 'name', - valid: true, - hooks: {}, - }).print(); - - expect(consolePrintedTimes('name')).toBe(1); - expect(consolePrintedTimes('testName')).toBe(0); - }); - - it('should print failed tests name', () => { - new SummaryTestOutput({ - name: 'name', + beforeEach(() => { + consoleLogMock.mockClear(); + }); + + it('should print report name', () => { + new SummaryTestOutput({ + name: 'name', + valid: true, + hooks: {} + }).print(); + + expect(consolePrintedTimes('name')).toBe(1); + expect(consolePrintedTimes('testName')).toBe(0); + }); + + it('should print failed tests name', () => { + new SummaryTestOutput( + { + name: 'name', + valid: false, + hooks: { + onEvent: { valid: false, - hooks: { - onEvent: { - valid: false, - tests: [{ - name: 'failing', - valid: false, - description: '' - }, { - name: 'passingTest', - valid: true, - description: '' - }] - - } - }, - }, {printFailingTests: true, level: 0, printChildren: false}).print(); - - expect(consolePrintedTimes('failing')).toBe(1); - expect(consolePrintedTimes('passingTest')).toBe(0); - }); - - it('should print PASS', () => { - new SummaryTestOutput({ - name: 'name', + tests: [ + { + name: 'failing', + valid: false, + description: '' + }, + { + name: 'passingTest', + valid: true, + description: '' + } + ] + } + } + }, + { printFailingTests: true, level: 0, printChildren: false } + ).print(); + + expect(consolePrintedTimes('failing')).toBe(1); + expect(consolePrintedTimes('passingTest')).toBe(0); + }); + + it('should print PASS', () => { + new SummaryTestOutput( + { + name: 'name', + valid: true, + hooks: { + onStuff: { valid: true, - hooks: { - onStuff: { - valid: true, - tests: [{ - name: 'any', - valid: true, - description: '' - }] - } - } - }, {printChildren: false}).print(); - - expect(consolePrintedTimes('PASS')).toBe(1); - expect(consolePrintedTimes('FAIL')).toBe(0); - expect(consolePrintedTimes('NULL')).toBe(0); - expect(consolePrintedTimes('SKIP')).toBe(0); - }); - - it('should print FAIL', () => { - new SummaryTestOutput({ - name: 'name', + tests: [ + { + name: 'any', + valid: true, + description: '' + } + ] + } + } + }, + { printChildren: false } + ).print(); + + expect(consolePrintedTimes('PASS')).toBe(1); + expect(consolePrintedTimes('FAIL')).toBe(0); + expect(consolePrintedTimes('NULL')).toBe(0); + expect(consolePrintedTimes('SKIP')).toBe(0); + }); + + it('should print FAIL', () => { + new SummaryTestOutput( + { + name: 'name', + valid: false, + hooks: { + onStuff: { valid: false, - hooks: { - onStuff: { - valid: false, - - tests: [{ - name: 'any', - valid: false, - description: '' - }] - } - } - }, {printChildren: false}).print(); - - expect(consolePrintedTimes('NULL')).toBe(0); - expect(consolePrintedTimes('FAIL')).toBe(1); - expect(consolePrintedTimes('PASS')).toBe(0); - expect(consolePrintedTimes('SKIP')).toBe(0); - }); - - it('should print failed tests', () => { - new SummaryTestOutput( - { - name: 'name', + + tests: [ + { + name: 'any', valid: false, - hooks: { - onStuff: { - valid: false, - - tests: [{ - name: 'any', - valid: false, - description: 'description' - }, { - name: 'second', - valid: false, - description: 'secDesc' - }] - } - } - }, {printChildren: false}).print(); - - expect(consolePrintedTimes('FAIL')).toBe(1); - // expect(consolePrintedTimes('name')).toBe(3); - - expect(consolePrintedTimes('any')).toBe(1); - expect(consolePrintedTimes('description')).toBe(1); - - expect(consolePrintedTimes('second')).toBe(1); - expect(consolePrintedTimes('secDesc')).toBe(1); - }); - - it('should print SKIP when ignored', () => { - new SummaryTestOutput({ - name: 'name', - valid: true, - ignored: true, - hooks: { - onStuff: { - valid: false, - - tests: [{ - name: 'any', - valid: false, - description: '' - }] - } - } - }, {printChildren: false}).print(); - - expect(consolePrintedTimes('SKIP')).toBe(1); - expect(consolePrintedTimes('NULL')).toBe(0); - expect(consolePrintedTimes('PASS')).toBe(0); - expect(consolePrintedTimes('FAIL')).toBe(0); - }); - - it('should print SKIP when every test is ignored', () => { - new SummaryTestOutput({ - name: 'name', - valid: true, - hooks: { - onStuff: { - valid: true, - - tests: [{ - name: 'any', - ignored: true, - valid: true, - description: '' - }] - } - } - }, {printChildren: false}).print(); - - expect(consolePrintedTimes('SKIP')).toBe(1); - expect(consolePrintedTimes('NULL')).toBe(0); - expect(consolePrintedTimes('PASS')).toBe(0); - expect(consolePrintedTimes('FAIL')).toBe(0); - }); - - it('should print SKIP when child is ignored', () => { - new SummaryTestOutput({ - name: 'name', - valid: true, - hooks: { - onStuff: { - valid: true, - - tests: [{ - name: 'any', - ignored: true, - valid: false, - description: '' - }] - } - } - }, {printChildren: false}).print(); - - expect(consolePrintedTimes('SKIP')).toBe(1); - expect(consolePrintedTimes('NULL')).toBe(0); - expect(consolePrintedTimes('PASS')).toBe(0); - expect(consolePrintedTimes('FAIL')).toBe(0); - }); - - it('should print NULL when there is no test', () => { - new SummaryTestOutput({ - name: 'name', - valid: true, hooks: { - onStuff: { - valid: true, - - tests: [] - } - } - }, {printChildren: false}).print(); - - expect(consolePrintedTimes('NULL')).toBe(1); - expect(consolePrintedTimes('SKIP')).toBe(0); - expect(consolePrintedTimes('PASS')).toBe(0); - expect(consolePrintedTimes('FAIL')).toBe(0); - }); - - it('should print total time', () => { - const totalTime = 400; - new SummaryTestOutput({ - name: 'name', - valid: true, - time: { - totalTime: totalTime - }, - }).print(); + description: '' + } + ] + } + } + }, + { printChildren: false } + ).print(); + + expect(consolePrintedTimes('NULL')).toBe(0); + expect(consolePrintedTimes('FAIL')).toBe(1); + expect(consolePrintedTimes('PASS')).toBe(0); + expect(consolePrintedTimes('SKIP')).toBe(0); + }); + + it('should print failed tests', () => { + new SummaryTestOutput( + { + name: 'name', + valid: false, + hooks: { + onStuff: { + valid: false, - expect(consolePrintedTimes(totalTime)).toBe(1); - }); + tests: [ + { + name: 'any', + valid: false, + description: 'description' + }, + { + name: 'second', + valid: false, + description: 'secDesc' + } + ] + } + } + }, + { printChildren: false } + ).print(); + + expect(consolePrintedTimes('FAIL')).toBe(1); + // expect(consolePrintedTimes('name')).toBe(3); + + expect(consolePrintedTimes('any')).toBe(1); + expect(consolePrintedTimes('description')).toBe(1); + + expect(consolePrintedTimes('second')).toBe(1); + expect(consolePrintedTimes('secDesc')).toBe(1); + }); + + it('should print SKIP when ignored', () => { + new SummaryTestOutput( + { + name: 'name', + valid: true, + ignored: true, + hooks: { + onStuff: { + valid: false, - it('should print children but not recursive', () => { - new SummaryTestOutput({ - name: 'name', + tests: [ + { + name: 'any', + valid: false, + description: '' + } + ] + } + } + }, + { printChildren: false } + ).print(); + + expect(consolePrintedTimes('SKIP')).toBe(1); + expect(consolePrintedTimes('NULL')).toBe(0); + expect(consolePrintedTimes('PASS')).toBe(0); + expect(consolePrintedTimes('FAIL')).toBe(0); + }); + + it('should print SKIP when every test is ignored', () => { + new SummaryTestOutput( + { + name: 'name', + valid: true, + hooks: { + onStuff: { valid: true, - tests: [], - publishers: [{name: 'publisher'}], - subscriptions: [{name: 'subscription'}], - requisitions: [{name: 'requisition'}], - }).print(); - - expect(consolePrintedTimes('publisher')).toBe(1); - expect(consolePrintedTimes('subscription')).toBe(1); - expect(consolePrintedTimes('requisition')).toBe(0); - }); - it('should not print children if it is deeper than max', () => { + tests: [ + { + name: 'any', + ignored: true, + valid: true, + description: '' + } + ] + } + } + }, + { printChildren: false } + ).print(); + + expect(consolePrintedTimes('SKIP')).toBe(1); + expect(consolePrintedTimes('NULL')).toBe(0); + expect(consolePrintedTimes('PASS')).toBe(0); + expect(consolePrintedTimes('FAIL')).toBe(0); + }); + + it('should print SKIP when child is ignored', () => { + new SummaryTestOutput( + { + name: 'name', + valid: true, + hooks: { + onStuff: { + valid: true, - new SummaryTestOutput({ - name: 'name', + tests: [ + { + name: 'any', + ignored: true, + valid: false, + description: '' + } + ] + } + } + }, + { printChildren: false } + ).print(); + + expect(consolePrintedTimes('SKIP')).toBe(1); + expect(consolePrintedTimes('NULL')).toBe(0); + expect(consolePrintedTimes('PASS')).toBe(0); + expect(consolePrintedTimes('FAIL')).toBe(0); + }); + + it('should print NULL when there is no test', () => { + new SummaryTestOutput( + { + name: 'name', + valid: true, + hooks: { + onStuff: { valid: true, - publishers: [{name: 'publisher', valid: false}], - subscriptions: [{name: 'subscription', valid: false}], - }, {maxLevel: 0, level: 0}).print(); - - expect(consolePrintedTimes('name')).toBe(1); - expect(consolePrintedTimes('publisher')).toBe(0); - expect(consolePrintedTimes('subscription')).toBe(0); - expect(consolePrintedTimes('requisition')).toBe(0); - }); - - const consolePrintedTimes = (text: any): number => { - return consoleLogMock.mock.calls.reduce((acc: number, mockCall: any) => acc + mockCall[0].includes(text), 0); - }; + + tests: [] + } + } + }, + { printChildren: false } + ).print(); + + expect(consolePrintedTimes('NULL')).toBe(1); + expect(consolePrintedTimes('SKIP')).toBe(0); + expect(consolePrintedTimes('PASS')).toBe(0); + expect(consolePrintedTimes('FAIL')).toBe(0); + }); + + it('should print total time', () => { + const totalTime = 400; + new SummaryTestOutput({ + name: 'name', + valid: true, + time: { + totalTime: totalTime + } + }).print(); + + expect(consolePrintedTimes(totalTime)).toBe(1); + }); + + it('should print children but not recursive', () => { + new SummaryTestOutput({ + name: 'name', + valid: true, + tests: [], + actuators: [{ name: 'actuator' }], + sensors: [{ name: 'sensor' }], + tasks: [{ name: 'task' }] + }).print(); + + expect(consolePrintedTimes('actuator')).toBe(1); + expect(consolePrintedTimes('sensor')).toBe(1); + expect(consolePrintedTimes('task')).toBe(0); + }); + + it('should not print children if it is deeper than max', () => { + new SummaryTestOutput( + { + name: 'name', + valid: true, + actuators: [{ name: 'actuator', valid: false }], + sensors: [{ name: 'sensor', valid: false }] + }, + { maxLevel: 0, level: 0 } + ).print(); + + expect(consolePrintedTimes('name')).toBe(1); + expect(consolePrintedTimes('actuator')).toBe(0); + expect(consolePrintedTimes('sensor')).toBe(0); + expect(consolePrintedTimes('task')).toBe(0); + }); + + const consolePrintedTimes = (text: any): number => { + return consoleLogMock.mock.calls.reduce((acc: number, mockCall: any) => acc + mockCall[0].includes(text), 0); + }; }); diff --git a/src/outputs/summary-test-output.ts b/src/outputs/summary-test-output.ts index b759ca7d..2cb2f838 100644 --- a/src/outputs/summary-test-output.ts +++ b/src/outputs/summary-test-output.ts @@ -1,194 +1,213 @@ -import {TestsAnalyzer} from './tests-analyzer'; -import {TestModel, testModelIsFailing, testModelIsNotFailing, testModelIsPassing} from '../models/outputs/test-model'; -import {RequisitionModel} from '../models/outputs/requisition-model'; -import {PublisherModel} from '../models/outputs/publisher-model'; -import {SubscriptionModel} from '../models/outputs/subscription-model'; -import {ReportModel} from '../models/outputs/report-model'; -import {HookModel} from '../models/outputs/hook-model'; -import colorizer from './colorizer'; +import { TestsAnalyzer } from './tests-analyzer'; +import { TestModel, testModelIsFailing, testModelIsPassing } from '../models/outputs/test-model'; +import { TaskModel } from '../models/outputs/task-model'; +import { ActuatorModel } from '../models/outputs/actuator-model'; +import { SensorModel } from '../models/outputs/sensor-model'; +import { ReportModel } from '../models/outputs/report-model'; +import { HookModel } from '../models/outputs/hook-model'; +import cgalk from 'chalk'; +import { Configuration } from '../configurations/configuration'; +import chalk from 'chalk'; export type SummaryOptions = { - maxLevel: number, - level: number, - tabulationPerLevel: number, - summarySpacing: number, - showPassingTests: boolean - printChildren: boolean, + maxLevel: number; + level: number; + tabulationPerLevel: number; + summarySpacing: number; + showPassingTests: boolean; + printChildren: boolean; }; export class SummaryTestOutput { - private readonly report: ReportModel; - private readonly options: SummaryOptions = { - level: 0, - maxLevel: 100, - tabulationPerLevel: 6, - summarySpacing: 90, - showPassingTests: false, - printChildren: true - }; - - public constructor(report: ReportModel, options?: any) { - this.report = report; - if (options && options.level !== undefined) { - this.options.level = options.level; - } else if (report.level) { - this.options.level = report.level; - } - this.options = Object.assign({}, this.options, options); + private readonly report: ReportModel; + private readonly options: SummaryOptions = { + level: 0, + maxLevel: 100, + tabulationPerLevel: 3, + summarySpacing: 90, + showPassingTests: false, + printChildren: true + }; + + public constructor(report: ReportModel, options?: any) { + this.report = report; + if (options && options.level !== undefined) { + this.options.level = options.level; + } else if (report.level) { + this.options.level = report.level; } - - public print(): void { - if (this.options.maxLevel === undefined || this.options.level <= this.options.maxLevel) { - this.printSelf(); - if (this.options.printChildren) { - this.printChildren(); - } - } + this.options = Object.assign({}, this.options, options); + } + + public print(): void { + if (this.options.maxLevel === undefined || this.options.level <= this.options.maxLevel) { + this.printSelf(); + if (this.options.printChildren) { + this.printChildren(); + } } - - private printChildren(): void { - const reportLeaves = (this.report.subscriptions || []) - .concat(this.report.publishers || []) - .concat(this.buildLeavesFromHooks() || []) - .concat(this.buildLeavesFromAssertion()); - for (const leaf of reportLeaves) { - const options = Object.assign({}, this.options, - { - level: this.options.level + 1, - }); - new SummaryTestOutput(leaf, options).print(); - } + } + + private printChildren(): void { + const reportLeaves = (this.report.sensors || []) + .concat(this.report.actuators || []) + .concat(this.buildLeavesFromHooks() || []) + .concat(this.buildLeavesFromAssertion()); + for (const leaf of reportLeaves) { + const options = Object.assign({}, this.options, { + level: this.options.level + 1 + }); + new SummaryTestOutput(leaf, options).print(); } - - private buildLeavesFromHooks() { - return Object.keys(this.report.hooks || {}).reduce((acc, key) => { - const leaf: any = { - ...this.report.hooks![key], - name: key, - }; - return acc.concat(leaf); - }, []); + } + + private buildLeavesFromHooks() { + return Object.keys(this.report.hooks || {}).reduce((acc, key) => { + const leaf: any = { + ...this.report.hooks![key], + name: key + }; + return acc.concat(leaf); + }, []); + } + + private buildLeavesFromAssertion() { + const reduce = (this.report.tests || []).reduce((acc: ReportModel[], test: TestModel) => { + const leaf: any = { + ...test, + isAssertion: true + }; + return acc.concat(leaf); + }, []); + return reduce; + } + + private printSelf() { + const testAnalyzer = new TestsAnalyzer().addTest(this.report); + console.log(this.formatTitle(testAnalyzer) + this.createSummary(testAnalyzer)); + this.printFailingTests(this.report as any, []); + } + + private formatTitle(testAnalyzer: TestsAnalyzer): string { + const initialTabulation = this.createEmptyStringSized(2 * this.options.tabulationPerLevel); + const tagTitleSeparation = this.createEmptyStringSized(this.options.level * this.options.tabulationPerLevel); + const titleSizeSeparation: number = + initialTabulation.length + 6 + tagTitleSeparation.length + this.report.name.length; + let formattedString = initialTabulation; + let nameColorFunction: any; + if (testAnalyzer.getTests().length === 0 && !this.report.isAssertion) { + formattedString += `${chalk.black.bgHex('#999999')('[NULL]')} `; + nameColorFunction = chalk.hex('#999999'); + } else if ( + this.report.ignored || + (testAnalyzer.getIgnoredList().length === testAnalyzer.getTests().length && !this.report.isAssertion) + ) { + formattedString += `${chalk.black.bgYellow('[SKIP]')} `; + nameColorFunction = chalk.yellow; + } else if (testAnalyzer.getFailingTests().length > 0 || !testModelIsPassing(this.report)) { + formattedString += `${chalk.black.bgRed('[FAIL]')} `; + nameColorFunction = chalk.red; + } else { + //if (this.report.valid) + formattedString += `${chalk.black.bgGreen('[PASS]')} `; + nameColorFunction = chalk.green; } - - private buildLeavesFromAssertion() { - const reduce = (this.report.tests || []).reduce((acc: ReportModel[], test: TestModel) => { - const leaf: any = { - ...test, - isAssertion: true - }; - return acc.concat(leaf); - }, []); - return reduce; + formattedString += tagTitleSeparation; + const iterationCounter: string = this.report.totalIterations > 1 ? ` [${this.report.iteration}]` : ''; + formattedString += nameColorFunction(this.report.name + iterationCounter); + formattedString += this.createEmptyStringSized(this.options.summarySpacing - titleSizeSeparation); + return formattedString; + } + + private createEmptyStringSized(length: number): string { + let blank = ''; + while (--length > 0) { + blank = blank.concat(' '); } + return blank; + } - private printSelf() { - const testAnalyzer = new TestsAnalyzer().addTest(this.report); - console.log(this.formatTitle(testAnalyzer) + this.createSummary(testAnalyzer)); - this.printFailingTests(this.report as any, []); + private createSummary(testAnalyzer: TestsAnalyzer): string { + if (this.report.isAssertion) { + return ''; } - - private formatTitle(testAnalyzer: TestsAnalyzer): string { - const initialTabulation = this.createEmptyStringSized(2 * this.options.tabulationPerLevel); - const tagTitleSeparation = this.createEmptyStringSized(this.options.level * this.options.tabulationPerLevel); - const titleSizeSeparation: number = initialTabulation.length + 6 + tagTitleSeparation.length + this.report.name.length; - let formattedString = initialTabulation; - let nameColorFunction: any; - if (testAnalyzer.getTests().length === 0 && !this.report.isAssertion) { - formattedString += `${colorizer.black.bgHex('#999999')('[NULL]')} `; - nameColorFunction = colorizer.hex('#999999'); - } else if (this.report.ignored || (testAnalyzer.getIgnoredList().length === testAnalyzer.getTests().length && !this.report.isAssertion)) { - formattedString += `${colorizer.black.bgYellow('[SKIP]')} `; - nameColorFunction = colorizer.yellow; - } else if (testAnalyzer.getFailingTests().length > 0 || !testModelIsPassing(this.report)) { - formattedString += `${colorizer.black.bgRed('[FAIL]')} `; - nameColorFunction = colorizer.red; - } else { //if (this.report.valid) - formattedString += `${colorizer.black.bgGreen('[PASS]')} `; - nameColorFunction = colorizer.green; - } - formattedString += tagTitleSeparation; - const iterationCounter: string = (this.report.totalIterations > 1) ? ` [${this.report.iteration}]` : ''; - formattedString += nameColorFunction(this.report.name + iterationCounter); - formattedString += this.createEmptyStringSized(this.options.summarySpacing - titleSizeSeparation); - return formattedString; + const percentage = testAnalyzer.getPercentage(); + const testsNumber = testAnalyzer.getNotIgnoredTests().length; + let message = `${testAnalyzer.getPassingTests().length}\t tests passing of ${testsNumber}\t (${percentage}%)`; + if (this.report.time) { + const totalTime = this.report.time.totalTime; + message += `\t| executed in ${totalTime}ms`; } - - private createEmptyStringSized(length: number): string { - let blank = ''; - while (--length > 0) { - blank = blank.concat(' '); - } - return blank; + if (Configuration.getInstance().getShowExplicitTestsOnly()) { + message += `\t| (${testAnalyzer.getPassingTests().filter(test => !test.implicit).length}/${ + testAnalyzer.getNotIgnoredTests().filter(test => !test.implicit).length + } of explicitly declared)`; } - - private createSummary(testAnalyzer: TestsAnalyzer): string { - if (this.report.isAssertion) { - return ''; - } - const percentage = testAnalyzer.getPercentage(); - const testsNumber = testAnalyzer.getNotIgnoredTests().length; - let message = `${testAnalyzer.getPassingTests().length}\t tests passing of ${testsNumber}\t (${percentage}%)`; - if (this.report.time) { - const totalTime = this.report.time.totalTime; - message += `\t| executed in ${totalTime}ms`; - } - const ignoredTests = testAnalyzer.getIgnoredList(); - if (ignoredTests.length > 0) { - message += colorizer.yellow(`\t| ${ignoredTests.length} tests ignored`); - } - return this.getColor(percentage)(message); - } - - private printFailingTests(report: ReportModel, hierarchy: string[]) { - const failing = testModelIsFailing(report); - if (failing || this.options.showPassingTests) { - Object.keys(report.hooks || {}) - .forEach((key: string) => this.printHookTests(report.hooks![key], key, hierarchy)); - - (report.subscriptions || []) - .concat(report.publishers || []) - .concat(report.requisitions || []) - .forEach(((leaf: RequisitionModel | PublisherModel | SubscriptionModel) => { - const iterationCounter: string = (leaf.totalIterations > 1) ? ` [${leaf.iteration}]` : ''; - this.printFailingTests(leaf, hierarchy.concat(leaf.name + iterationCounter)); - })); - - } + const ignoredTests = testAnalyzer.getIgnoredList(); + if (ignoredTests.length > 0) { + message += chalk.yellow(`\t| ${ignoredTests.length} tests ignored`); } - - private printHookTests(hook: HookModel, hookName: string, hierarchy: string[]) { - hook.tests - .filter((test: TestModel) => !testModelIsPassing(test) || this.options.showPassingTests) - .forEach((test: TestModel, index: number) => { - const initialTabulation = this.createEmptyStringSized((this.options.level + 4) * this.options.tabulationPerLevel); - let color: any; - if (test.ignored) { - color = colorizer.yellow; - } else if (test.valid) { - color = colorizer.green; - } else { - color = colorizer.red; - } - if (index === 0) { - const hierarchyMessage = initialTabulation + hierarchy - .map(level => color(level) + colorizer.gray(' â€ē ')) - .concat(hookName) - .join(''); - console.log(hierarchyMessage); - } - console.log(color(`${initialTabulation}${this.createEmptyStringSized(this.options.tabulationPerLevel)}${test.name}`)); - console.log(colorizer.reset(`${initialTabulation}${this - .createEmptyStringSized(2 * this.options.tabulationPerLevel)}${test.description}\n`)); - }); + return this.getColor(percentage)(message); + } + + private printFailingTests(report: ReportModel, hierarchy: string[]) { + const failing = testModelIsFailing(report); + if (failing || this.options.showPassingTests) { + Object.keys(report.hooks || {}).forEach((key: string) => this.printHookTests(report.hooks![key], key, hierarchy)); + + (report.sensors || []) + .concat(report.actuators || []) + .concat(report.tasks || []) + .forEach((leaf: TaskModel | ActuatorModel | SensorModel) => { + const iterationCounter: string = leaf.totalIterations > 1 ? ` [${leaf.iteration}]` : ''; + this.printFailingTests(leaf, hierarchy.concat(leaf.name + iterationCounter)); + }); } - - private getColor(percentage: number): Function { - if (percentage == 100) { - return colorizer.green; - } else if (percentage > 50) { - return colorizer.yellow; + } + + private printHookTests(hook: HookModel, hookName: string, hierarchy: string[]) { + hook.tests + .filter((test: TestModel) => !testModelIsPassing(test) || this.options.showPassingTests) + .forEach((test: TestModel, index: number) => { + const initialTabulation = this.createEmptyStringSized( + (this.options.level + 4) * this.options.tabulationPerLevel + ); + let color: any; + if (test.ignored) { + color = chalk.yellow; + } else if (test.valid) { + color = chalk.green; + } else { + color = chalk.red; + } + if (index === 0) { + const hierarchyMessage = + initialTabulation + + hierarchy + .map(level => color(level) + chalk.gray(' â€ē ')) + .concat(hookName) + .join(''); + console.log(hierarchyMessage); } - return colorizer.red; + console.log( + color(`${initialTabulation}${this.createEmptyStringSized(this.options.tabulationPerLevel)}${test.name}`) + ); + console.log( + chalk.reset( + `${initialTabulation}${this.createEmptyStringSized(2 * this.options.tabulationPerLevel)}${ + test.description + }\n` + ) + ); + }); + } + + private getColor(percentage: number): Function { + if (percentage == 100) { + return chalk.green; + } else if (percentage > 50) { + return chalk.yellow; } - + return chalk.red; + } } diff --git a/src/outputs/tests-analyzer.test.ts b/src/outputs/tests-analyzer.test.ts index 5e95ef52..6c7c8f4a 100644 --- a/src/outputs/tests-analyzer.test.ts +++ b/src/outputs/tests-analyzer.test.ts @@ -1,177 +1,180 @@ -import {TestsAnalyzer} from './tests-analyzer'; -import {RequisitionModel} from '../models/outputs/requisition-model'; -import {ReportModel} from '../models/outputs/report-model'; -import {TestModel} from '../models/outputs/test-model'; +import { TestsAnalyzer } from './tests-analyzer'; +import { TaskModel } from '../models/outputs/task-model'; +import { ReportModel } from '../models/outputs/report-model'; +import { TestModel } from '../models/outputs/test-model'; describe('TestsAnalyzer', () => { - const createTest = (valid: boolean, ignored?: boolean): TestModel => { - return { - valid, - ignored, - description: 'description', - name: 'name' - }; + const createTest = (valid: boolean, ignored?: boolean): TestModel => { + return { + valid, + ignored, + description: 'description', + name: 'name' }; + }; - it('Percentage should be zero when there are no tests', () => { - const test: ReportModel = { - name: 'name', - valid: true, - tests: [] - }; - - const testsAnalyzer = new TestsAnalyzer().addTest(test); - - expect(testsAnalyzer.getFailingTests().length).toBe(0); - expect(testsAnalyzer.getTests().length).toBe(0); - expect(testsAnalyzer.getPercentage()).toBe(100); - }); - - it('Should trunc percentage to two decimals number', () => { - const test: RequisitionModel = { - name: 'name', - valid: true, - hooks: { - onInit: {valid: true, tests: [{valid: true, description: 'description', name: 'name'}]} - }, - requisitions: [{ - name: 'name', - valid: true, - // @ts-ignore - time: {}, - // @ts-ignore - publishers: [{ - name: 'name', - valid: false, - hooks: { - onInit: {valid: false, tests: [createTest(false)]}, - onFinish: {valid: true, tests: [createTest(true)]} - } - }] - }] - }; - - const testsAnalyzer = new TestsAnalyzer().addTest(test); - - expect(testsAnalyzer.getTests().length).toBe(3); - expect(testsAnalyzer.getFailingTests().length).toBe(1); - expect(testsAnalyzer.getPercentage()).toBe(66.66); - }); - - it('Should count inner tests', () => { - - const test: ReportModel = { - name: 'name', - valid: true, - hooks: { - onInit: {valid: true, tests: [createTest(true)]} - }, - - }; - - const testsAnalyzer = new TestsAnalyzer().addTest(test); - - expect(testsAnalyzer.getFailingTests().length).toBe(0); - expect(testsAnalyzer.getTests().length).toBe(1); - expect(testsAnalyzer.getPercentage()).toBe(100); - }); - - it('Should ignore ignored test to calculate percentage', () => { - - const test: ReportModel = { - name: 'name', - valid: false, - hooks: { - onInit: {valid: true, tests: [createTest(false, true), createTest(true, true)]}, - onFinish: {valid: false, tests: [createTest(true, false), createTest(false, undefined)]} - }, - }; - - const testsAnalyzer = new TestsAnalyzer().addTest(test); - - expect(testsAnalyzer.getFailingTests().length).toBe(1); - expect(testsAnalyzer.getTests().length).toBe(4); - expect(testsAnalyzer.getPassingTests().length).toBe(1); - expect(testsAnalyzer.getNotIgnoredTests().length).toBe(2); - expect(testsAnalyzer.getPercentage()).toBe(50); - }); - - it('Should ignore ignored test to validate', () => { - - const test: ReportModel = { - name: 'name', - valid: true, - tests: [createTest(true), createTest(true, true), createTest(true, true)], - }; - - const testsAnalyzer = new TestsAnalyzer().addTest(test); - - expect(testsAnalyzer.isValid()).toBeTruthy(); - }); - - it('Should get filtered tests', () => { - const test: ReportModel = { - name: 'name', - description: 'name', - hooks: - { - onEvent: { - valid: false, - tests: [ - createTest(true), - createTest(true), - createTest(true), - createTest(false), - createTest(false, true)] - } - }, - valid: false, - }; - - const testsAnalyzer = new TestsAnalyzer().addTest(test); - - expect(testsAnalyzer.getFailingTests().length).toBe(1); - expect(testsAnalyzer.getPassingTests().length).toBe(3); - expect(testsAnalyzer.getIgnoredList().length).toBe(1); - expect(testsAnalyzer.getTests().length).toBe(5); - expect(testsAnalyzer.getNotIgnoredTests().length).toBe(4); - }); - - it('Should ignore even when there is test child', () => { - const test: ReportModel = { - name: 'name', - valid: true, - ignored: true, - hooks: { - onInit: { - valid: false, tests: [createTest(false)] - } + it('Percentage should be zero when there are no tests', () => { + const test: ReportModel = { + name: 'name', + valid: true, + tests: [] + }; + + const testsAnalyzer = new TestsAnalyzer().addTest(test); + + expect(testsAnalyzer.getFailingTests().length).toBe(0); + expect(testsAnalyzer.getTests().length).toBe(0); + expect(testsAnalyzer.getPercentage()).toBe(100); + }); + + it('Should trunc percentage to two decimals number', () => { + const test: TaskModel = { + name: 'name', + valid: true, + hooks: { + onInit: { + valid: true, + tests: [{ valid: true, description: 'description', name: 'name' }] + } + }, + tasks: [ + { + name: 'name', + valid: true, + // @ts-ignore + time: {}, + actuators: [ + // @ts-ignore + { + name: 'name', + valid: false, + hooks: { + onInit: { valid: false, tests: [createTest(false)] }, + onFinish: { valid: true, tests: [createTest(true)] } + } } - }; + ] + } + ] + }; - const testsAnalyzer = new TestsAnalyzer().addTest(test); + const testsAnalyzer = new TestsAnalyzer().addTest(test); - expect(testsAnalyzer.getFailingTests().length).toBe(0); - expect(testsAnalyzer.getPassingTests().length).toBe(0); - expect(testsAnalyzer.getIgnoredList().length).toBe(1); - expect(testsAnalyzer.getTests().length).toBe(1); - expect(testsAnalyzer.getNotIgnoredTests().length).toBe(0); - }); + expect(testsAnalyzer.getTests().length).toBe(3); + expect(testsAnalyzer.getFailingTests().length).toBe(1); + expect(testsAnalyzer.getPercentage()).toBe(66.66); + }); - it('Should print regular tests', () => { - const test: ReportModel = { - name: 'name', - valid: false, - tests: [createTest(false), createTest(true)] - }; + it('Should count inner tests', () => { + const test: ReportModel = { + name: 'name', + valid: true, + hooks: { + onInit: { valid: true, tests: [createTest(true)] } + } + }; - const testsAnalyzer = new TestsAnalyzer().addTest(test); + const testsAnalyzer = new TestsAnalyzer().addTest(test); + + expect(testsAnalyzer.getFailingTests().length).toBe(0); + expect(testsAnalyzer.getTests().length).toBe(1); + expect(testsAnalyzer.getPercentage()).toBe(100); + }); + + it('Should ignore ignored test to calculate percentage', () => { + const test: ReportModel = { + name: 'name', + valid: false, + hooks: { + onInit: { + valid: true, + tests: [createTest(false, true), createTest(true, true)] + }, + onFinish: { + valid: false, + tests: [createTest(true, false), createTest(false, undefined)] + } + } + }; + + const testsAnalyzer = new TestsAnalyzer().addTest(test); + + expect(testsAnalyzer.getFailingTests().length).toBe(1); + expect(testsAnalyzer.getTests().length).toBe(4); + expect(testsAnalyzer.getPassingTests().length).toBe(1); + expect(testsAnalyzer.getNotIgnoredTests().length).toBe(2); + expect(testsAnalyzer.getPercentage()).toBe(50); + }); + + it('Should ignore ignored test to validate', () => { + const test: ReportModel = { + name: 'name', + valid: true, + tests: [createTest(true), createTest(true, true), createTest(true, true)] + }; + + const testsAnalyzer = new TestsAnalyzer().addTest(test); + + expect(testsAnalyzer.isValid()).toBeTruthy(); + }); + + it('Should get filtered tests', () => { + const test: ReportModel = { + name: 'name', + description: 'name', + hooks: { + onEvent: { + valid: false, + tests: [createTest(true), createTest(true), createTest(true), createTest(false), createTest(false, true)] + } + }, + valid: false + }; + + const testsAnalyzer = new TestsAnalyzer().addTest(test); + + expect(testsAnalyzer.getFailingTests().length).toBe(1); + expect(testsAnalyzer.getPassingTests().length).toBe(3); + expect(testsAnalyzer.getIgnoredList().length).toBe(1); + expect(testsAnalyzer.getTests().length).toBe(5); + expect(testsAnalyzer.getNotIgnoredTests().length).toBe(4); + }); + + it('Should ignore even when there is test child', () => { + const test: ReportModel = { + name: 'name', + valid: true, + ignored: true, + hooks: { + onInit: { + valid: false, + tests: [createTest(false)] + } + } + }; + + const testsAnalyzer = new TestsAnalyzer().addTest(test); + + expect(testsAnalyzer.getFailingTests().length).toBe(0); + expect(testsAnalyzer.getPassingTests().length).toBe(0); + expect(testsAnalyzer.getIgnoredList().length).toBe(1); + expect(testsAnalyzer.getTests().length).toBe(1); + expect(testsAnalyzer.getNotIgnoredTests().length).toBe(0); + }); + + it('Should print regular tests', () => { + const test: ReportModel = { + name: 'name', + valid: false, + tests: [createTest(false), createTest(true)] + }; - expect(testsAnalyzer.getFailingTests().length).toBe(1); - expect(testsAnalyzer.getPassingTests().length).toBe(1); - expect(testsAnalyzer.getIgnoredList().length).toBe(0); - expect(testsAnalyzer.getTests().length).toBe(2); - expect(testsAnalyzer.getNotIgnoredTests().length).toBe(2); - }); + const testsAnalyzer = new TestsAnalyzer().addTest(test); + expect(testsAnalyzer.getFailingTests().length).toBe(1); + expect(testsAnalyzer.getPassingTests().length).toBe(1); + expect(testsAnalyzer.getIgnoredList().length).toBe(0); + expect(testsAnalyzer.getTests().length).toBe(2); + expect(testsAnalyzer.getNotIgnoredTests().length).toBe(2); + }); }); diff --git a/src/outputs/tests-analyzer.ts b/src/outputs/tests-analyzer.ts index 30bfb456..750123d6 100644 --- a/src/outputs/tests-analyzer.ts +++ b/src/outputs/tests-analyzer.ts @@ -1,75 +1,74 @@ -import {RequisitionModel} from '../models/outputs/requisition-model'; -import {ReportModel} from '../models/outputs/report-model'; -import {TestModel, testModelIsPassing} from '../models/outputs/test-model'; -import {HookModel} from '../models/outputs/hook-model'; +import { TaskModel } from '../models/outputs/task-model'; +import { ReportModel } from '../models/outputs/report-model'; +import { TestModel, testModelIsPassing } from '../models/outputs/test-model'; +import { HookModel } from '../models/outputs/hook-model'; export class TestsAnalyzer { - private tests: TestModel[] = []; + private tests: TestModel[] = []; - public addTest(report: ReportModel): TestsAnalyzer { - this.findRequisitions(report); - return this; - } + public addTest(report: ReportModel): TestsAnalyzer { + this.findTasks(report); + return this; + } - public isValid(): boolean { - return this.getNotIgnoredTests().every(test => test.valid); - } + public isValid(): boolean { + return this.getNotIgnoredTests().every(test => test.valid); + } - public getTests(): TestModel[] { - return this.tests; - } + public getTests(): TestModel[] { + return this.tests; + } - public getNotIgnoredTests(): TestModel[] { - return this.tests.filter(test => test.ignored === false || test.ignored === undefined); - } + public getNotIgnoredTests(): TestModel[] { + return this.tests.filter(test => test.ignored === false || test.ignored === undefined); + } - public getIgnoredList(): TestModel[] { - return this.tests.filter(test => test.ignored === true && test.ignored !== undefined); - } + public getIgnoredList(): TestModel[] { + return this.tests.filter(test => test.ignored === true && test.ignored !== undefined); + } - public getPassingTests(): TestModel[] { - return this.tests.filter(test => test.valid === true && (test.ignored === false || test.ignored === undefined)); - } + public getPassingTests(): TestModel[] { + return this.tests.filter(test => test.valid === true && (test.ignored === false || test.ignored === undefined)); + } - public getFailingTests(): TestModel[] { - return this.tests.filter(test => test.valid === false && (test.ignored === false || test.ignored === undefined)); - } + public getFailingTests(): TestModel[] { + return this.tests.filter(test => test.valid === false && (test.ignored === false || test.ignored === undefined)); + } - public getPercentage(): number { - const notIgnoredTestsLength = this.getNotIgnoredTests().length; - if (notIgnoredTestsLength === 0) { - return 100; - } - return Math.trunc(10000 * this.getPassingTests().length / notIgnoredTestsLength) / 100; + public getPercentage(): number { + const notIgnoredTestsLength = this.getNotIgnoredTests().length; + if (notIgnoredTestsLength === 0) { + return 100; } + return Math.trunc((10000 * this.getPassingTests().length) / notIgnoredTestsLength) / 100; + } - private findRequisitions(requisition: ReportModel) { - this.findTests(requisition); - (requisition.requisitions || []).forEach((child: RequisitionModel) => { - this.findRequisitions(child); - }); - } + private findTasks(task: ReportModel) { + this.findTests(task); + (task.tasks || []).forEach((child: TaskModel) => { + this.findTasks(child); + }); + } - private findTests(requisition: ReportModel) { - this.computeComponent(requisition as RequisitionModel); - for (const child of (requisition.subscriptions || []).concat(requisition.publishers || [])) { - this.computeComponent(child); - } + private findTests(task: ReportModel) { + this.computeComponent(task as TaskModel); + for (const child of (task.sensors || []).concat(task.actuators || [])) { + this.computeComponent(child); } + } - private computeComponent(reportModel: ReportModel): void { - if (reportModel.ignored) { - this.tests.push({ - ...reportModel, - description: reportModel.description || 'Ignored' - }); - } else { - Object.keys(reportModel.hooks || {}).forEach((key: string) => { - const hook = reportModel.hooks![key] as HookModel; - this.tests = this.tests.concat(hook.tests || []); - }); - this.tests = this.tests.concat(reportModel.tests || []); - } + private computeComponent(reportModel: ReportModel): void { + if (reportModel.ignored) { + this.tests.push({ + ...reportModel, + description: reportModel.description || 'Ignored' + }); + } else { + Object.keys(reportModel.hooks || {}).forEach((key: string) => { + const hook = reportModel.hooks![key] as HookModel; + this.tests = this.tests.concat(hook.tests || []); + }); + this.tests = this.tests.concat(reportModel.tests || []); } - + } } diff --git a/src/outputs/tests-flattener.test.ts b/src/outputs/tests-flattener.test.ts index 5e78ef07..7cceb98d 100644 --- a/src/outputs/tests-flattener.test.ts +++ b/src/outputs/tests-flattener.test.ts @@ -1,146 +1,181 @@ -import {TestsFlattener} from './tests-flattener'; -import {ReportModel} from '../models/outputs/report-model'; +import { TestsFlattener } from './tests-flattener'; +import { ReportModel } from '../models/outputs/report-model'; describe('TestsFlattener', () => { - it('Flattens requisitions', () => { - const requisition: ReportModel = { - name: 'requisition', - valid: true, - hooks: { - onHook: { - valid: true, - tests: [{ - description: 'description', - name: 'test', - valid: true, - }] - } + it('Flattens tasks', () => { + const task: ReportModel = { + name: 'task', + valid: true, + hooks: { + onHook: { + valid: true, + tests: [ + { + description: 'description', + name: 'test', + valid: true } - }; + ] + } + } + }; - const flattenTests = new TestsFlattener().flatten(requisition); - expect(flattenTests).toEqual([{'description': 'description', 'hierarchy': ['requisition', 'onHook'], 'name': 'test', 'valid': true}]); - }); + const flattenTests = new TestsFlattener().flatten(task); + expect(flattenTests).toEqual([ + { + description: 'description', + hierarchy: ['task', 'onHook'], + name: 'test', + valid: true + } + ]); + }); - it('Concatenates requisition #', () => { - const requisition: ReportModel = { - name: 'requisition', - iteration: 4, - totalIterations: 5, - valid: true, - hooks: { - onHook: { - valid: true, - tests: [{ - description: 'description', - name: 'test', - valid: true, - }] - } + it('Concatenates task #', () => { + const task: ReportModel = { + name: 'task', + iteration: 4, + totalIterations: 5, + valid: true, + hooks: { + onHook: { + valid: true, + tests: [ + { + description: 'description', + name: 'test', + valid: true } - }; + ] + } + } + }; - const flattenTests = new TestsFlattener().flatten(requisition); - expect(flattenTests).toEqual([{'description': 'description', 'hierarchy': ['requisition [4]', 'onHook'], 'name': 'test', 'valid': true}]); - }); + const flattenTests = new TestsFlattener().flatten(task); + expect(flattenTests).toEqual([ + { + description: 'description', + hierarchy: ['task [4]', 'onHook'], + name: 'test', + valid: true + } + ]); + }); - it('Handle empty hooks', () => { - const requisition: ReportModel = { - name: 'requisition', - iteration: 4, - totalIterations: 5, - valid: true, - hooks: {} - }; + it('Handle empty hooks', () => { + const task: ReportModel = { + name: 'task', + iteration: 4, + totalIterations: 5, + valid: true, + hooks: {} + }; - const flattenTests = new TestsFlattener().flatten(requisition); - expect(flattenTests).toEqual([]); - }); + const flattenTests = new TestsFlattener().flatten(task); + expect(flattenTests).toEqual([]); + }); - it('Flatten deeper publishers', () => { - const requisition: ReportModel = { - name: 'requisition', - valid: true, - publishers: [{ - name: 'publisher', - hooks: { - onHook: { - valid: true, - tests: [{ - description: 'description', - name: 'test', - valid: true, - }] - } + it('Flatten deeper actuators', () => { + const task: ReportModel = { + name: 'task', + valid: true, + actuators: [ + { + name: 'actuator', + hooks: { + onHook: { + valid: true, + tests: [ + { + description: 'description', + name: 'test', + valid: true } - }] - }; + ] + } + } + } + ] + }; - const flattenTests = new TestsFlattener().flatten(requisition); - expect(flattenTests).toEqual([{ - 'description': 'description', - 'hierarchy': ['requisition', 'publisher', 'onHook'], - 'name': 'test', - 'valid': true - }]); - }); + const flattenTests = new TestsFlattener().flatten(task); + expect(flattenTests).toEqual([ + { + description: 'description', + hierarchy: ['task', 'actuator', 'onHook'], + name: 'test', + valid: true + } + ]); + }); - it('Flatten deeper subscriptions', () => { - const requisition: ReportModel = { - name: 'requisition', - valid: true, - subscriptions: [{ - name: 'subscription', - hooks: { - onHook: { - valid: true, - tests: [{ - description: 'description', - name: 'test', - valid: true, - }] - } + it('Flatten deeper sensors', () => { + const task: ReportModel = { + name: 'task', + valid: true, + sensors: [ + { + name: 'sensor', + hooks: { + onHook: { + valid: true, + tests: [ + { + description: 'description', + name: 'test', + valid: true } - }] - }; + ] + } + } + } + ] + }; - const flattenTests = new TestsFlattener().flatten(requisition); - expect(flattenTests).toEqual([{ - 'description': 'description', - 'hierarchy': ['requisition', 'subscription', 'onHook'], - 'name': 'test', - 'valid': true - }]); - }); + const flattenTests = new TestsFlattener().flatten(task); + expect(flattenTests).toEqual([ + { + description: 'description', + hierarchy: ['task', 'sensor', 'onHook'], + name: 'test', + valid: true + } + ]); + }); - it('Flatten deeper requisitions', () => { - const requisition: ReportModel = { - name: 'requisition', - valid: true, - requisitions: [{ - name: 'requisition', - iteration: 3, - totalIterations: 5, - hooks: { - onHook: { - valid: true, - tests: [{ - description: 'description', - name: 'test', - valid: true, - }] - } + it('Flatten deeper tasks', () => { + const task: ReportModel = { + name: 'task', + valid: true, + tasks: [ + { + name: 'task', + iteration: 3, + totalIterations: 5, + hooks: { + onHook: { + valid: true, + tests: [ + { + description: 'description', + name: 'test', + valid: true } - }] - }; - - const flattenTests = new TestsFlattener().flatten(requisition); - expect(flattenTests).toEqual([{ - 'description': 'description', - 'hierarchy': ['requisition', 'requisition [3]', 'onHook'], - 'name': 'test', - 'valid': true - }]); - }); + ] + } + } + } + ] + }; + const flattenTests = new TestsFlattener().flatten(task); + expect(flattenTests).toEqual([ + { + description: 'description', + hierarchy: ['task', 'task [3]', 'onHook'], + name: 'test', + valid: true + } + ]); + }); }); diff --git a/src/outputs/tests-flattener.ts b/src/outputs/tests-flattener.ts index f65bae9d..2da4be58 100644 --- a/src/outputs/tests-flattener.ts +++ b/src/outputs/tests-flattener.ts @@ -1,46 +1,49 @@ -import {ReportModel} from '../models/outputs/report-model'; -import {TestModel} from '../models/outputs/test-model'; +import { ReportModel } from '../models/outputs/report-model'; +import { TestModel } from '../models/outputs/test-model'; export interface FlattenTest extends TestModel { - hierarchy: string[]; + hierarchy: string[]; } export class TestsFlattener { - public flatten(node: ReportModel): FlattenTest[] { - const iterationCounter = (node.totalIterations > 1) ? ` [${node.iteration}]` : ''; - const name = node.name + iterationCounter; - return this.goDeep({...node, name}, [name]); - } + public flatten(node: ReportModel): FlattenTest[] { + const iterationCounter = node.totalIterations > 1 ? ` [${node.iteration}]` : ''; + const name = node.name + iterationCounter; + return this.goDeep({ ...node, name }, [name]); + } - private goDeep(node: ReportModel, hierarchy: string[]): FlattenTest[] { - const tests = this.getTests(node, hierarchy); - const nested = this.deepTests(node, hierarchy); - return tests.concat(nested); - } + private goDeep(node: ReportModel, hierarchy: string[]): FlattenTest[] { + const tests = this.getTests(node, hierarchy); + const nested = this.deepTests(node, hierarchy); + return tests.concat(nested); + } - private deepTests(node: ReportModel, hierarchy: string[]) { - return (node.subscriptions || []) - .concat(node.publishers || []) - .concat(node.requisitions || []) - .reduce((acc: FlattenTest[], component: ReportModel) => { - const iterationCounter = (component.totalIterations > 1) ? ` [${component.iteration}]` : ''; - const name = component.name + iterationCounter; - return acc.concat(this.goDeep({...component, name}, hierarchy.concat(name))); - }, []); - } + private deepTests(node: ReportModel, hierarchy: string[]) { + return (node.sensors || []) + .concat(node.actuators || []) + .concat(node.tasks || []) + .reduce((acc: FlattenTest[], component: ReportModel) => { + const iterationCounter = component.totalIterations > 1 ? ` [${component.iteration}]` : ''; + const name = component.name + iterationCounter; + return acc.concat(this.goDeep({ ...component, name }, hierarchy.concat(name))); + }, []); + } - private getTests(node: ReportModel, hierarchy: string[]): FlattenTest[] { - if (!node.hooks) { - return []; - } - return Object.keys(node.hooks) - .reduce((acc, hookName) => acc - .concat(node.hooks![hookName].tests - .map((test: TestModel) => { - return { - ...test, - hierarchy: hierarchy.concat(hookName) - } as FlattenTest; - })), [] as FlattenTest[]); + private getTests(node: ReportModel, hierarchy: string[]): FlattenTest[] { + if (!node.hooks) { + return []; } + return Object.keys(node.hooks).reduce( + (acc, hookName) => + acc.concat( + node.hooks![hookName].tests.map((test: TestModel) => { + return { + ...test, + hierarchy: hierarchy.concat(hookName) + } as FlattenTest; + }) + ), + [] as FlattenTest[] + ); + } } diff --git a/src/plugins/asserter-manager.test.ts b/src/plugins/asserter-manager.test.ts index 7e432c26..a8c9d59f 100644 --- a/src/plugins/asserter-manager.test.ts +++ b/src/plugins/asserter-manager.test.ts @@ -1,6 +1,6 @@ -import {AsserterManager} from './asserter-manager'; -import {ExpectToBeEqualToAsserter} from '../asserters/expect-to-be-equal-to-asserter'; -import {NullAsserter} from '../asserters/null-asserter'; +import { AsserterManager } from './asserter-manager'; +import { ExpectToBeEqualToAsserter } from '../asserters/expect-to-be-equal-to-asserter'; +import { NullAsserter } from '../asserters/null-asserter'; import prettyjson from 'prettyjson'; jest.mock('prettyjson'); @@ -10,136 +10,165 @@ const render = jest.fn(); prettyjson.render.mockImplementation(render); describe('AsserterManager', () => { - it('should create proper asserter', () => { - const asserterManager = new AsserterManager(); - asserterManager.addAsserter({equals: {description: 'some', type: 'string'}}, - () => new ExpectToBeEqualToAsserter()); - - const asserter = asserterManager.createAsserter({name: 'any', equals: true}); - - expect(asserter).toBeInstanceOf(ExpectToBeEqualToAsserter); - }); - - it('should create proper asserter ignoring not required ones', () => { - const asserterManager = new AsserterManager(); - asserterManager.addAsserter({ - equals: { - description: 'some', - type: 'string' - }, - not: { - required: false, - description: 'some', - type: 'null' - } - }, - () => new ExpectToBeEqualToAsserter()); - - const asserter = asserterManager.createAsserter({name: 'any', equals: true}); - - expect(asserter).toBeInstanceOf(ExpectToBeEqualToAsserter); - }); - - it('should create Null asserter', () => { - const asserterManager = new AsserterManager(); - asserterManager.addAsserter({equals: {description: 'some', type: 'string'}}, - () => new ExpectToBeEqualToAsserter()); - - const asserter = asserterManager.createAsserter({name: 'any'}); - - expect(asserter).toBeInstanceOf(NullAsserter); - }); - - it('should describe every matching asserter', () => { - const asserterManager = new AsserterManager(); - - asserterManager - .addAsserter({toBeEqualTo: {description: 'some', type: 'string'}}, - () => new ExpectToBeEqualToAsserter()); - asserterManager - .addAsserter({toBeGreaterThan: {description: 'some', type: 'string'}}, - () => new ExpectToBeEqualToAsserter()); - asserterManager - .addAsserter({toBeLessThan: {description: 'some', type: 'string'}}, - () => new ExpectToBeEqualToAsserter()); - - const status = asserterManager.describeMatchingAsserters('than'); - - expect(status).toBeTruthy(); - expect(render).toHaveBeenCalledWith( - { - 'asserters': [{ - 'toBeLessThan': { - 'description': 'some', - 'required': true, - 'type': 'string' - } - }, { - 'toBeGreaterThan': { - 'description': 'some', - 'required': true, - 'type': 'string' - } - }] - }, expect.anything()); - }); - - it('should not describe', () => { - const asserterManager = new AsserterManager(); - - asserterManager - .addAsserter({toBeEqualTo: {description: 'some', type: 'string'}}, - () => new ExpectToBeEqualToAsserter()); - asserterManager - .addAsserter({toBeGreaterThan: {description: 'some', type: 'string'}}, - () => new ExpectToBeEqualToAsserter()); - asserterManager - .addAsserter({toBeLessThan: {description: 'some', type: 'string'}}, - () => new ExpectToBeEqualToAsserter()); - - const status = asserterManager.describeMatchingAsserters('does not match'); - - expect(status).toBeFalsy(); - expect(render).toHaveBeenCalledWith({'asserters': []}, expect.anything()); + it('should create proper asserter', () => { + const asserterManager = new AsserterManager(); + asserterManager.addAsserter( + { equals: { description: 'some', type: 'string' } }, + () => new ExpectToBeEqualToAsserter() + ); + + const asserter = asserterManager.createAsserter({ + name: 'any', + equals: true }); - it('should describe every asserter', () => { - const asserterManager = new AsserterManager(); - - asserterManager - .addAsserter({toBeEqualTo: {description: 'some', type: 'string'}}, - () => new ExpectToBeEqualToAsserter()); - asserterManager - .addAsserter({toBeGreaterThan: {description: 'some', type: 'string'}}, - () => new ExpectToBeEqualToAsserter()); - asserterManager - .addAsserter({toBeLessThan: {description: 'some'}}, - () => new ExpectToBeEqualToAsserter()); - - const status = asserterManager.describeMatchingAsserters(true); - - expect(status).toBeTruthy(); - expect(render).toHaveBeenCalledWith({ - 'asserters': [{ - 'toBeLessThan': { - 'description': 'some', - 'required': true, - 'type': 'any' - } - }, { - 'toBeGreaterThan': { - 'description': 'some', - 'required': true, - 'type': 'string' - } - }, { - 'toBeEqualTo': { - 'description': 'some', - 'required': true, - 'type': 'string' - } - }] - }, expect.anything()); + expect(asserter).toBeInstanceOf(ExpectToBeEqualToAsserter); + }); + + it('should create proper asserter ignoring not required ones', () => { + const asserterManager = new AsserterManager(); + asserterManager.addAsserter( + { + equals: { + description: 'some', + type: 'string' + }, + not: { + required: false, + description: 'some', + type: 'null' + } + }, + () => new ExpectToBeEqualToAsserter() + ); + + const asserter = asserterManager.createAsserter({ + name: 'any', + equals: true }); + expect(asserter).toBeInstanceOf(ExpectToBeEqualToAsserter); + }); + + it('should create Null asserter', () => { + const asserterManager = new AsserterManager(); + asserterManager.addAsserter( + { equals: { description: 'some', type: 'string' } }, + () => new ExpectToBeEqualToAsserter() + ); + + const asserter = asserterManager.createAsserter({ name: 'any' }); + + expect(asserter).toBeInstanceOf(NullAsserter); + }); + + it('should describe every matching asserter', () => { + const asserterManager = new AsserterManager(); + + asserterManager.addAsserter( + { toBeEqualTo: { description: 'some', type: 'string' } }, + () => new ExpectToBeEqualToAsserter() + ); + asserterManager.addAsserter( + { toBeGreaterThan: { description: 'some', type: 'string' } }, + () => new ExpectToBeEqualToAsserter() + ); + asserterManager.addAsserter( + { toBeLessThan: { description: 'some', type: 'string' } }, + () => new ExpectToBeEqualToAsserter() + ); + + const status = asserterManager.describeMatchingAsserters('than'); + + expect(status).toBeTruthy(); + expect(render).toHaveBeenCalledWith( + { + asserters: [ + { + toBeLessThan: { + description: 'some', + required: true, + type: 'string' + } + }, + { + toBeGreaterThan: { + description: 'some', + required: true, + type: 'string' + } + } + ] + }, + expect.anything() + ); + }); + + it('should not describe', () => { + const asserterManager = new AsserterManager(); + + asserterManager.addAsserter( + { toBeEqualTo: { description: 'some', type: 'string' } }, + () => new ExpectToBeEqualToAsserter() + ); + asserterManager.addAsserter( + { toBeGreaterThan: { description: 'some', type: 'string' } }, + () => new ExpectToBeEqualToAsserter() + ); + asserterManager.addAsserter( + { toBeLessThan: { description: 'some', type: 'string' } }, + () => new ExpectToBeEqualToAsserter() + ); + + const status = asserterManager.describeMatchingAsserters('does not match'); + + expect(status).toBeFalsy(); + expect(render).toHaveBeenCalledWith({ asserters: [] }, expect.anything()); + }); + + it('should describe every asserter', () => { + const asserterManager = new AsserterManager(); + + asserterManager.addAsserter( + { toBeEqualTo: { description: 'some', type: 'string' } }, + () => new ExpectToBeEqualToAsserter() + ); + asserterManager.addAsserter( + { toBeGreaterThan: { description: 'some', type: 'string' } }, + () => new ExpectToBeEqualToAsserter() + ); + asserterManager.addAsserter({ toBeLessThan: { description: 'some' } }, () => new ExpectToBeEqualToAsserter()); + + const status = asserterManager.describeMatchingAsserters(true); + + expect(status).toBeTruthy(); + expect(render).toHaveBeenCalledWith( + { + asserters: [ + { + toBeLessThan: { + description: 'some', + required: true, + type: 'any' + } + }, + { + toBeGreaterThan: { + description: 'some', + required: true, + type: 'string' + } + }, + { + toBeEqualTo: { + description: 'some', + required: true, + type: 'string' + } + } + ] + }, + expect.anything() + ); + }); }); diff --git a/src/plugins/asserter-manager.ts b/src/plugins/asserter-manager.ts index c45b9465..3a1ff94b 100644 --- a/src/plugins/asserter-manager.ts +++ b/src/plugins/asserter-manager.ts @@ -1,66 +1,72 @@ -import {Assertion} from '../models/events/assertion'; -import {Logger} from '../loggers/logger'; -import {Asserter} from '../asserters/asserter'; -import {NullAsserter} from '../asserters/null-asserter'; -import {prettifyJson} from '../outputs/prettify-json'; +import { Assertion } from '../models/events/assertion'; +import { Logger } from '../loggers/logger'; +import { Asserter } from '../asserters/asserter'; +import { NullAsserter } from '../asserters/null-asserter'; +import { prettifyJson } from '../outputs/prettify-json'; -type AssertionFieldTypes = ('string' | 'number' | 'boolean' | 'array' | 'null' | 'any'); +type AssertionFieldTypes = 'string' | 'number' | 'boolean' | 'array' | 'null' | 'any' | 'list'; type AssertionField = { - required?: boolean; - type?: AssertionFieldTypes[] | AssertionFieldTypes; - description?: string; + required?: boolean; + type?: AssertionFieldTypes[] | AssertionFieldTypes; + description?: string; }; const defaultAssertionField: AssertionField = { - required: true, - type: 'any' + required: true, + type: 'any' }; export interface AssertionTemplate { - [assertionField: string]: AssertionField; + [assertionField: string]: AssertionField; } interface AddedAsserter { - template: AssertionTemplate; - createFunction: () => Asserter; + template: AssertionTemplate; + createFunction: () => Asserter; } export class AsserterManager { - private addedAsserters: AddedAsserter[] = []; + private addedAsserters: AddedAsserter[] = []; - public createAsserter(assertion: Assertion): Asserter { - const matching = this.addedAsserters - .filter((added: AddedAsserter) => Object - .keys(added.template) - .filter(key => added.template[key].required || added.template[key].required === undefined) - .every(requiredKey => assertion[requiredKey] !== undefined)); - if (matching.length > 0) { - return matching[0].createFunction(); - } - Logger.error(`No asserter was found with '${JSON.stringify(assertion, null, 2)}', using default one`); - return new NullAsserter(); + public createAsserter(assertion: Assertion): Asserter { + const matching = this.addedAsserters.filter((added: AddedAsserter) => + Object.keys(added.template) + .filter(key => added.template[key].required || added.template[key].required === undefined) + .every(requiredKey => assertion[requiredKey] !== undefined) + ); + if (matching.length > 0) { + return matching[0].createFunction(); } + Logger.error(`No asserter was found with '${JSON.stringify(assertion, null, 2)}', using default one`); + return new NullAsserter(); + } - public addAsserter(templateAssertion: AssertionTemplate, createFunction: () => Asserter): void { - Object.keys(templateAssertion) - .forEach(key => templateAssertion[key] = Object.assign({}, defaultAssertionField, templateAssertion[key])); - this.addedAsserters.unshift({template: templateAssertion, createFunction}); - } + public addAsserter(templateAssertion: AssertionTemplate, createFunction: () => Asserter): void { + Object.keys(templateAssertion).forEach( + key => (templateAssertion[key] = Object.assign({}, defaultAssertionField, templateAssertion[key])) + ); + this.addedAsserters.unshift({ + template: templateAssertion, + createFunction + }); + } - public describeMatchingAsserters(data: any): boolean { - const matchingAsserters = this.getMatchingAsserters(data); - console.log(prettifyJson(matchingAsserters)); - return matchingAsserters.asserters.length > 0; - } + public describeMatchingAsserters(data: any): boolean { + const matchingAsserters = this.getMatchingAsserters(data); + console.log(`Describing asserters matching: ${data}`); + console.log(prettifyJson(matchingAsserters)); + return matchingAsserters.asserters.length > 0; + } - public getMatchingAsserters(field: string | true): { asserters: AssertionTemplate[] } { - let matching: AddedAsserter[] = this.addedAsserters; - if (typeof field === 'string') { - matching = this.addedAsserters - .filter((added: AddedAsserter) => Object - .keys(added.template) - .some(key => key.toUpperCase().indexOf(field.toUpperCase()) !== -1)); - } - return {asserters: matching.map(added => added.template).sort()}; + public getMatchingAsserters(field: string | true): { + asserters: AssertionTemplate[]; + } { + let matching: AddedAsserter[] = this.addedAsserters; + if (typeof field === 'string') { + matching = this.addedAsserters.filter((added: AddedAsserter) => + Object.keys(added.template).some(key => key.toUpperCase().indexOf(field.toUpperCase()) !== -1) + ); } + return { asserters: matching.map(added => added.template).sort() }; + } } diff --git a/src/plugins/dynamic-modules-manager.test.ts b/src/plugins/dynamic-modules-manager.test.ts index 49fd3406..82e76bfe 100644 --- a/src/plugins/dynamic-modules-manager.test.ts +++ b/src/plugins/dynamic-modules-manager.test.ts @@ -1,92 +1,105 @@ -import {DynamicModulesManager} from './dynamic-modules-manager'; -import {entryPoint} from '../outputs/formatters/json-formatter'; -import {ProtocolManager} from './protocol-manager'; -import {ReportFormatterManager} from './report-formatter-manager'; -import {ObjectParserManager} from './object-parser-manager'; +import { DynamicModulesManager } from './dynamic-modules-manager'; +import { entryPoint } from '../outputs/formatters/json-formatter'; +import { ProtocolManager } from './protocol-manager'; +import { ReportFormatterManager } from './report-formatter-manager'; +import { ObjectParserManager } from './object-parser-manager'; import prettyjson from 'prettyjson'; jest.mock('../outputs/formatters/json-formatter'); jest.mock('prettyjson'); describe('DynamicModulesManager', () => { - beforeEach(() => { - // @ts-ignore - delete DynamicModulesManager.instance; - }); - - it('should load every file with an entryPoint function exported', () => { - const dirname = __dirname.substr(0, __dirname.lastIndexOf('/')); - const expectedBuiltInModules = [ - 'publishers/custom-publisher', - 'publishers/file-publisher', - 'publishers/http-publisher', - 'publishers/standard-output-publisher', - 'publishers/stream-publisher', - 'publishers/udp-publisher', - 'subscriptions/custom-subscription', - 'subscriptions/filename-watcher-subscription', - 'subscriptions/http-subscription', - 'subscriptions/standard-input-subscription', - 'subscriptions/stream-subscription', - 'subscriptions/udp-subscription', - 'outputs/formatters/console-formatter', - 'outputs/formatters/json-formatter', - 'outputs/formatters/yml-formatter', - 'object-parser/json-object-parser', - 'object-parser/csv-object-parser', - 'object-parser/file-object-parser', - 'object-parser/yml-object-parser', - 'asserters/expect-to-be-equal-to-asserter', - 'asserters/expect-to-be-greater-than-asserter', - 'asserters/expect-to-be-greater-than-or-equal-to-asserter', - 'asserters/expect-to-be-less-than-asserter', - 'asserters/expect-to-be-less-than-or-equal-to-asserter', - 'asserters/expect-to-contain-asserter', - 'asserters/expect-to-be-truthy-asserter', - 'asserters/expect-to-be-falsy-asserter', - 'asserters/expect-to-be-defined-asserter', - 'asserters/expect-to-be-undefined-asserter', - ].map(expected => dirname + '/' + expected); - const actualList: string[] = DynamicModulesManager.getInstance().getBuiltInModules(); + beforeEach(() => { + // @ts-ignore + delete DynamicModulesManager.instance; + }); - expect(actualList.sort()).toEqual(expectedBuiltInModules.sort()); - }); + it('should load every file with an entryPoint function exported', () => { + const dirname = __dirname.substr(0, __dirname.lastIndexOf('/')); + const expectedBuiltInModules = [ + 'actuators/custom-actuator', + 'actuators/file-actuator', + 'actuators/http-actuator', + 'actuators/standard-output-actuator', + 'actuators/stream-actuator', + 'actuators/udp-actuator', + 'sensors/custom-sensor', + 'sensors/filename-watcher-sensor', + 'sensors/http-sensor', + 'sensors/standard-input-sensor', + 'sensors/stream-sensor', + 'sensors/udp-sensor', + 'outputs/formatters/console-formatter', + 'outputs/formatters/json-formatter', + 'outputs/formatters/yml-formatter', + 'object-parser/json-object-parser', + 'object-parser/csv-object-parser', + 'object-parser/file-object-parser', + 'object-parser/yml-object-parser', + 'asserters/expect-to-be-equal-to-asserter', + 'asserters/expect-to-be-greater-than-asserter', + 'asserters/expect-to-be-greater-than-or-equal-to-asserter', + 'asserters/expect-to-be-less-than-asserter', + 'asserters/expect-to-be-less-than-or-equal-to-asserter', + 'asserters/expect-to-contain-asserter', + 'asserters/expect-to-be-truthy-asserter', + 'asserters/expect-to-be-falsy-asserter', + 'asserters/expect-to-be-any-of-asserter', + 'asserters/expect-to-be-defined-asserter', + 'asserters/expect-to-be-undefined-asserter' + ].map(expected => dirname + '/' + expected); + const actualList: string[] = DynamicModulesManager.getInstance().getBuiltInModules(); - it('should call entryPoint function', () => { - const entryPointMock: any = jest.fn(); - // @ts-ignore - entryPoint.mockImplementationOnce(entryPointMock); + expect(actualList.sort()).toEqual(expectedBuiltInModules.sort()); + }); - const dynamicModulesManager = DynamicModulesManager.getInstance(); + it('should call entryPoint function', () => { + const entryPointMock: any = jest.fn(); + // @ts-ignore + entryPoint.mockImplementationOnce(entryPointMock); - // @ts-ignore - const mainInstance = entryPoint.mock.calls[0][0]; + const dynamicModulesManager = DynamicModulesManager.getInstance(); - expect(entryPointMock).toHaveBeenCalled(); - expect(mainInstance.protocolManager).toBeInstanceOf(ProtocolManager); - expect(mainInstance.reportFormatterManager).toBeInstanceOf(ReportFormatterManager); - expect(mainInstance.objectParserManager).toBeInstanceOf(ObjectParserManager); - }); + // @ts-ignore + const mainInstance = entryPoint.mock.calls[0][0]; - it('should print loaded modules', done => { - // @ts-ignore - prettyjson.render.mockImplementation((printedModules) => { - expect(Array.isArray(printedModules.explicit)).toBeTruthy(); - expect(Array.isArray(printedModules.implicit)).toBeTruthy(); - done(); - }); + expect(entryPointMock).toHaveBeenCalled(); + expect(mainInstance.protocolManager).toBeInstanceOf(ProtocolManager); + expect(mainInstance.reportFormatterManager).toBeInstanceOf(ReportFormatterManager); + expect(mainInstance.objectParserManager).toBeInstanceOf(ObjectParserManager); + }); - DynamicModulesManager.getInstance().describeLoadedModules(); + it('should print loaded modules', done => { + // @ts-ignore + prettyjson.render.mockImplementation(printedModules => { + expect(Array.isArray(printedModules.explicit)).toBeTruthy(); + expect(Array.isArray(printedModules.implicit)).toBeTruthy(); + done(); }); - it('should check enqueuer version of plugins', () => { - // @ts-ignore - expect(DynamicModulesManager.versionMatches({dependencies: {enqueuer: '^5.0.0'}})).toBeTruthy(); - // @ts-ignore - expect(DynamicModulesManager.versionMatches({devDependencies: {enqueuer: '5.0.0'}})).toBeTruthy(); - // @ts-ignore - expect(DynamicModulesManager.versionMatches({peerDependencies: {enqueuer: '5.0.0'}})).toBeTruthy(); - // @ts-ignore - expect(DynamicModulesManager.versionMatches({})).toBeFalsy(); - }); + DynamicModulesManager.getInstance().describeLoadedModules(); + }); + + it('should check enqueuer version of plugins', () => { + expect( + // @ts-ignore + DynamicModulesManager.versionMatches({ + dependencies: { enqueuer: '^6.0.0' } + }) + ).toBeTruthy(); + expect( + // @ts-ignore + DynamicModulesManager.versionMatches({ + devDependencies: { enqueuer: '6.0.0' } + }) + ).toBeTruthy(); + expect( + // @ts-ignore + DynamicModulesManager.versionMatches({ + peerDependencies: { enqueuer: '6.0.0' } + }) + ).toBeTruthy(); + // @ts-ignore + expect(DynamicModulesManager.versionMatches({})).toBeFalsy(); + }); }); diff --git a/src/plugins/dynamic-modules-manager.ts b/src/plugins/dynamic-modules-manager.ts index df1e40b9..c413dba4 100644 --- a/src/plugins/dynamic-modules-manager.ts +++ b/src/plugins/dynamic-modules-manager.ts @@ -1,194 +1,205 @@ -import {MainInstance} from './main-instance'; -import {ProtocolManager} from './protocol-manager'; -import {ReportFormatterManager} from './report-formatter-manager'; -import {Configuration} from '../configurations/configuration'; -import {Logger} from '../loggers/logger'; +import { MainInstance } from './main-instance'; +import { ProtocolManager } from './protocol-manager'; +import { ReportFormatterManager } from './report-formatter-manager'; +import { Configuration } from '../configurations/configuration'; +import { Logger } from '../loggers/logger'; import * as path from 'path'; -import {ObjectParserManager} from './object-parser-manager'; -import {AsserterManager} from './asserter-manager'; +import { ObjectParserManager } from './object-parser-manager'; +import { AsserterManager } from './asserter-manager'; import * as os from 'os'; import * as glob from 'glob'; import * as fs from 'fs'; -import {prettifyJson} from '../outputs/prettify-json'; +import { prettifyJson } from '../outputs/prettify-json'; const enqueuerPackageJson = require('../../package.json'); export class DynamicModulesManager { - private static instance: DynamicModulesManager; - private readonly protocolManager: ProtocolManager; - private readonly reportFormatterManager: ReportFormatterManager; - private readonly objectParserManager: ObjectParserManager; - private readonly asserterManager: AsserterManager; - private readonly builtInModules: string[]; - private readonly implicitModules: string[]; - private readonly explicitModules: string[]; - - private constructor() { - this.protocolManager = new ProtocolManager(); - this.reportFormatterManager = new ReportFormatterManager(); - this.objectParserManager = new ObjectParserManager(); - this.asserterManager = new AsserterManager(); - this.builtInModules = this.findEveryEntryPointableBuiltInModule(); - this.implicitModules = this.findEveryEnqueuerImplicitPluginPackage(os.homedir() + '/.nqr/node_modules/*') - .concat(this.findEveryEnqueuerImplicitPluginPackage(__dirname + '/../../node_modules/*')); - this.explicitModules = []; - this.initialModulesLoad(); - } - - public static getInstance() { - if (!DynamicModulesManager.instance) { - DynamicModulesManager.instance = new DynamicModulesManager(); - } - return DynamicModulesManager.instance; - } - - public getBuiltInModules(): string[] { - return this.builtInModules; - } - - public getImplicitModules(): string[] { - return this.implicitModules; - } - - public getLoadedModules(): { - implicit: string[], - explicit: string[] - } { - return { - implicit: this.implicitModules, - explicit: this.explicitModules, - }; - } - - public getProtocolManager(): ProtocolManager { - return this.protocolManager; - } - - public getAsserterManager(): AsserterManager { - return this.asserterManager; - } - - public getReportFormatterManager(): ReportFormatterManager { - return this.reportFormatterManager; - } - - public getObjectParserManager(): ObjectParserManager { - return this.objectParserManager; - } - - public describeLoadedModules(): void { - console.log(prettifyJson(this.getLoadedModules())); - } - - public loadModuleExplicitly(module: string): boolean { - if (this.loadModule(module)) { - Logger.info(`Success to load '${path.basename(module)}' as dynamic module`); - this.explicitModules.push(module); - return true; - } else { - Logger.error(`Fail to load '${module}' as dynamic module`); - return false; - } - } - - public findEveryEnqueuerImplicitPluginPackage(pattern: string): string[] { - try { - return (glob.sync(pattern, {}) || []) - .map(module => module.replace(/\.js/, '')) - .filter(module => { - try { - const packageJsonPath = module + '/package.json'; - if (fs.existsSync(packageJsonPath)) { - const packageJson = JSON.parse(fs.readFileSync(packageJsonPath).toString()); - const keyWordsMatch = (packageJson.keywords || []) - .find((keyword: string) => keyword.toLowerCase() === 'enqueuer' || keyword.toLowerCase() === 'nqr'); - if (keyWordsMatch) { - const versionMatches = DynamicModulesManager.versionMatches(packageJson); - if (versionMatches) { - return require(module).entryPoint !== undefined; - } - } - } - } catch (err) { - Logger.trace(err); - } - return false; - }); - } catch (err) { - - } - return []; - } - - private static versionMatches(packageJson: { name: string, dependencies: any, devDependencies: any, peerDependencies: any }): boolean { - const regexp = /[^\d]*(\d+)/; - const currentMajorVersion = (process.env.npm_package_version || enqueuerPackageJson.version).match(regexp)[0]; - const enqueuerVersion = (packageJson.dependencies || {}).enqueuer || - (packageJson.devDependencies || {}).enqueuer || - (packageJson.peerDependencies || {}).enqueuer || - '0.0.0'; - const pluginMajorEnqueuerVersion = enqueuerVersion.match(regexp)[1]; - - Logger.trace(`name: ${packageJson.name} => currentMajorVersion (${ - +currentMajorVersion}) <= pluginMajorEnqueuerVersion (${ - +pluginMajorEnqueuerVersion}): ${ - +currentMajorVersion <= +pluginMajorEnqueuerVersion}`); - return +currentMajorVersion <= +pluginMajorEnqueuerVersion; - } - - private loadModule(module: string): boolean { + private static instance: DynamicModulesManager; + private readonly protocolManager: ProtocolManager; + private readonly reportFormatterManager: ReportFormatterManager; + private readonly objectParserManager: ObjectParserManager; + private readonly asserterManager: AsserterManager; + private readonly builtInModules: string[]; + private readonly implicitModules: string[]; + private readonly explicitModules: string[]; + + private constructor() { + this.protocolManager = new ProtocolManager(); + this.reportFormatterManager = new ReportFormatterManager(); + this.objectParserManager = new ObjectParserManager(); + this.asserterManager = new AsserterManager(); + this.builtInModules = this.findEveryEntryPointableBuiltInModule(); + this.implicitModules = this.findEveryEnqueuerImplicitPluginPackage(os.homedir() + '/.nqr/node_modules/*').concat( + this.findEveryEnqueuerImplicitPluginPackage(__dirname + '/../../node_modules/*') + ); + this.explicitModules = []; + this.initialModulesLoad(); + } + + public static getInstance() { + if (!DynamicModulesManager.instance) { + DynamicModulesManager.instance = new DynamicModulesManager(); + } + return DynamicModulesManager.instance; + } + + public getBuiltInModules(): string[] { + return this.builtInModules; + } + + public getImplicitModules(): string[] { + return this.implicitModules; + } + + public getLoadedModules(): { + implicit: string[]; + explicit: string[]; + } { + return { + implicit: this.implicitModules, + explicit: this.explicitModules + }; + } + + public getProtocolManager(): ProtocolManager { + return this.protocolManager; + } + + public getAsserterManager(): AsserterManager { + return this.asserterManager; + } + + public getReportFormatterManager(): ReportFormatterManager { + return this.reportFormatterManager; + } + + public getObjectParserManager(): ObjectParserManager { + return this.objectParserManager; + } + + public describeLoadedModules(): void { + console.log(prettifyJson(this.getLoadedModules())); + } + + public loadModuleExplicitly(module: string): boolean { + if (this.loadModule(module)) { + Logger.info(`Success to load '${path.basename(module)}' as dynamic module`); + this.explicitModules.push(module); + return true; + } else { + Logger.error(`Fail to load '${module}' as dynamic module`); + return false; + } + } + + public findEveryEnqueuerImplicitPluginPackage(pattern: string): string[] { + try { + return (glob.sync(pattern, {}) || []) + .map(module => module.replace(/\.js/, '')) + .filter(module => !module.includes('.test')) + .filter(module => { + const packageJsonPath = module + '/package.json'; + try { + if (fs.existsSync(packageJsonPath)) { + const packageJson = JSON.parse(fs.readFileSync(packageJsonPath).toString()); + const keyWords = + typeof packageJson.keywords != 'object' + ? (packageJson.keywords?.toString().split(',') ?? []) + : (packageJson.keywords ?? []); + const keyWordsMatch = (keyWords || []).find( + (keyword: string) => keyword.toLowerCase() === 'enqueuer' || keyword.toLowerCase() === 'nqr' + ); + if (keyWordsMatch) { + const versionMatches = DynamicModulesManager.versionMatches(packageJson); + if (versionMatches) { + return require(module).entryPoint !== undefined; + } + } + } + } catch (err) { + Logger.trace(`Dynamic modules manager: ` + err); + } + return false; + }); + } catch (err) {} + return []; + } + + private static versionMatches(packageJson: { + name: string; + dependencies: any; + devDependencies: any; + peerDependencies: any; + }): boolean { + const regexp = /[^\d]*(\d+)/; + const currentMajorVersion = (process.env.npm_package_version || enqueuerPackageJson.version).match(regexp)[0]; + const enqueuerVersion = + (packageJson.dependencies || {}).enqueuer || + (packageJson.devDependencies || {}).enqueuer || + (packageJson.peerDependencies || {}).enqueuer || + '0.0.0'; + const pluginMajorEnqueuerVersion = enqueuerVersion.match(regexp)[1]; + + Logger.trace( + `name: ${packageJson.name} => currentMajorVersion (${+currentMajorVersion}) <= pluginMajorEnqueuerVersion (${+pluginMajorEnqueuerVersion}): ${+currentMajorVersion <= +pluginMajorEnqueuerVersion}` + ); + return +currentMajorVersion <= +pluginMajorEnqueuerVersion; + } + + private loadModule(module: string): boolean { + try { + require(module).entryPoint({ + protocolManager: this.protocolManager, + reportFormatterManager: this.reportFormatterManager, + objectParserManager: this.objectParserManager, + asserterManager: this.asserterManager + } as MainInstance); + return true; + } catch (err) { + Logger.error(`Fail to load '${module}': ${err}`); + } + return false; + } + + private findEveryEntryPointableBuiltInModule(): string[] { + const pattern = __dirname + '/../**/*.+(ts|d.ts|js)'; + const files = glob + .sync(pattern, {}) + .map(module => module.replace('./src/', '../')) + .map(module => module.replace(/\.d\.ts/, '')) + .map(module => module.replace(/\.ts/, '')) + .map(module => module.replace(/\.js/, '')) + .filter(module => !module.includes('.test')) + .filter(module => { try { - require(module) - .entryPoint( - { - protocolManager: this.protocolManager, - reportFormatterManager: this.reportFormatterManager, - objectParserManager: this.objectParserManager, - asserterManager: this.asserterManager - } as MainInstance); - return true; + return require(module).entryPoint !== undefined; } catch (err) { - Logger.error(`Fail to load '${module}': ${err}`); + return false; } - return false; - } - - private findEveryEntryPointableBuiltInModule(): string[] { - const pattern = __dirname + '/../**/*\.+(ts|d.ts|js)'; - const files = glob.sync(pattern, {}) - .map(module => module.replace('./src/', '../')) - .map(module => module.replace(/\.d\.ts/, '')) - .map(module => module.replace(/\.ts/, '')) - .map(module => module.replace(/\.js/, '')) - .filter(module => { - try { - return require(module).entryPoint !== undefined; - } catch (err) { - return false; - } - }); - const plugins: Set = new Set(files); - return Array.from(plugins.values()); - } - - private initialModulesLoad() { - Logger.info(`Loading built in modules`); - this.builtInModules - .forEach(module => this.loadModule(module) ? - Logger.debug(`Success to load '${path.basename(module)}' as built in module`) : - Logger.trace(`Fail to load '${module}' as built in module`)); - - Logger.info(`Loading ${this.implicitModules.length} implicitly declared plugins`); - this.implicitModules - .forEach(module => this.loadModule(module) ? - Logger.info(`Success to load '${path.basename(module)}' as dynamic module`) : - Logger.error(`Fail to load '${module}' as dynamic module`)); - - const configurationPlugins = Configuration.getInstance().getPlugins(); - Logger.info(`Loading ${configurationPlugins.length} explicitly declared plugins`); - configurationPlugins - .filter(module => !this.explicitModules.includes(module)) - .forEach(module => this.loadModuleExplicitly(module)); - } - + }); + const plugins: Set = new Set(files); + return Array.from(plugins.values()); + } + + private initialModulesLoad() { + Logger.debug(`Loading built in modules`); + this.builtInModules.forEach(module => + this.loadModule(module) + ? Logger.trace(`Success to load '${path.basename(module)}' as built in module`) + : Logger.warning(`Fail to load '${module}' as built in module`) + ); + + Logger.info(`Loading ${this.implicitModules.length} implicitly declared plugins`); + this.implicitModules.forEach(module => + this.loadModule(module) + ? Logger.info(`Success to load '${path.basename(module)}' as dynamic module`) + : Logger.error(`Fail to load '${module}' as dynamic module`) + ); + + const configurationPlugins = Configuration.getInstance().getPlugins(); + Logger.info(`Loading ${configurationPlugins.length} explicitly declared plugins`); + configurationPlugins + .filter(module => !this.explicitModules.includes(module)) + .forEach(module => this.loadModuleExplicitly(module)); + } } diff --git a/src/plugins/main-instance.ts b/src/plugins/main-instance.ts index 5b1baa90..b6123dc5 100644 --- a/src/plugins/main-instance.ts +++ b/src/plugins/main-instance.ts @@ -1,11 +1,11 @@ -import {ProtocolManager} from './protocol-manager'; -import {ReportFormatterManager} from './report-formatter-manager'; -import {ObjectParserManager} from './object-parser-manager'; -import {AsserterManager} from './asserter-manager'; +import { ProtocolManager } from './protocol-manager'; +import { ReportFormatterManager } from './report-formatter-manager'; +import { ObjectParserManager } from './object-parser-manager'; +import { AsserterManager } from './asserter-manager'; export interface MainInstance { - readonly protocolManager: ProtocolManager; - readonly reportFormatterManager: ReportFormatterManager; - readonly objectParserManager: ObjectParserManager; - readonly asserterManager: AsserterManager; + readonly protocolManager: ProtocolManager; + readonly reportFormatterManager: ReportFormatterManager; + readonly objectParserManager: ObjectParserManager; + readonly asserterManager: AsserterManager; } diff --git a/src/plugins/object-parser-manager.test.ts b/src/plugins/object-parser-manager.test.ts index f05266e0..a7d29ed1 100644 --- a/src/plugins/object-parser-manager.test.ts +++ b/src/plugins/object-parser-manager.test.ts @@ -1,4 +1,4 @@ -import {ObjectParserManager} from './object-parser-manager'; +import { ObjectParserManager } from './object-parser-manager'; import prettyjson from 'prettyjson'; jest.mock('prettyjson'); @@ -8,104 +8,99 @@ const render = jest.fn(); prettyjson.render.mockImplementation(render); describe('ObjectParserManager', () => { - - beforeEach(() => { - render.mockClear(); - }); - - it('should return undefined', () => { - expect(new ObjectParserManager().createParser('undefined')).toBeUndefined(); - }); - - it('should ignore case', () => { - const myObject = 'myObject'; - const tags = 'tagsLowerUpperCase'; - - // @ts-ignore - const objectParserManager = new ObjectParserManager(); - objectParserManager.addObjectParser(() => myObject, tags); - - expect(objectParserManager.createParser(tags.toUpperCase())).toBe(myObject); - }); - - it('should tryToParseWithParsers', () => { - const parsed = 'parsed'; - const objectParserManager = new ObjectParserManager(); - // @ts-ignore - objectParserManager.addObjectParser(() => { - return { - parse: () => parsed - }; - }, 'first'); - - expect(objectParserManager.tryToParseWithParsers('stuff', ['first'])).toBe(parsed); - }); - - it('should tryToParseWithEveryParser subset error', () => { - const objectParserManager = new ObjectParserManager(); - // @ts-ignore - objectParserManager.addObjectParser(() => { - return { - parse: () => { - throw 'error'; - } - }; - }, 'first'); - - // @ts-ignore - objectParserManager.addObjectParser(() => { - return { - parse: () => { - throw 'other error'; - } - }; - }, 'other'); - - try { - objectParserManager.tryToParseWithParsers('stuff', ['other']); - expect(true).toBeFalsy(); - } catch (err) { - expect(err).toEqual({ - other: 'other error' - }); + beforeEach(() => { + render.mockClear(); + }); + + it('should return undefined', () => { + expect(new ObjectParserManager().createParser('undefined')).toBeUndefined(); + }); + + it('should ignore case', () => { + const myObject = 'myObject' as any; + const tags = 'tagsLowerUpperCase'; + + // @ts-ignore + const objectParserManager = new ObjectParserManager(); + objectParserManager.addObjectParser(() => myObject, tags); + + expect(objectParserManager.createParser(tags.toUpperCase())).toBe(myObject); + }); + + it('should tryToParseWithParsers', () => { + const parsed = 'parsed'; + const objectParserManager = new ObjectParserManager(); + // @ts-ignore + objectParserManager.addObjectParser(() => { + return { + parse: () => parsed + }; + }, 'first'); + + expect(objectParserManager.tryToParseWithParsers('stuff', ['first'])).toBe(parsed); + }); + + it('should tryToParseWithEveryParser subset error', () => { + const objectParserManager = new ObjectParserManager(); + // @ts-ignore + objectParserManager.addObjectParser(() => { + return { + parse: () => { + throw 'error'; } - - }); - - it('should describe every ObjectParser', () => { - const parserList = ['ob1', 'ob2']; - const objectParserManager = new ObjectParserManager(); - // @ts-ignore - parserList.forEach(op => objectParserManager.addObjectParser(() => op, 'whatever')); - - const status = objectParserManager.describeMatchingObjectParsers(true); - - expect(status).toBeTruthy(); - expect(render).toHaveBeenCalledWith({'parsers': [['whatever'], ['whatever']]}, expect.anything()); - }); - - it('should describe given ObjectParser', () => { - const parserList = ['ob1', 'ob2']; - const objectParserManager = new ObjectParserManager(); - // @ts-ignore - parserList.forEach(op => objectParserManager.addObjectParser(() => op, op)); - - const status = objectParserManager.describeMatchingObjectParsers(parserList[1]); - - expect(status).toBeTruthy(); - expect(render).toHaveBeenCalledWith({'parsers': [['ob2']]}, expect.anything()); - }); - - it('should error when describing no given ObjectParser', () => { - const parserList = ['ob1', 'ob2']; - const objectParserManager = new ObjectParserManager(); - // @ts-ignore - parserList.forEach(op => objectParserManager.addObjectParser(() => op, op)); - - const status = objectParserManager.describeMatchingObjectParsers('unknown'); - - expect(status).toBeFalsy(); - expect(render).toHaveBeenCalledWith({'parsers': []}, expect.anything()); - }); - + }; + }, 'first'); + + // @ts-ignore + objectParserManager.addObjectParser(() => { + return { + parse: () => { + throw 'other error'; + } + }; + }, 'other'); + + try { + objectParserManager.tryToParseWithParsers('stuff', ['other']); + expect(true).toBeFalsy(); + } catch (err) { + expect(err).toBe('OTHER error: other error'); + } + }); + + it('should describe every ObjectParser', () => { + const parserList = ['ob1', 'ob2']; + const objectParserManager = new ObjectParserManager(); + // @ts-ignore + parserList.forEach(op => objectParserManager.addObjectParser(() => op, 'whatever')); + + const status = objectParserManager.describeMatchingObjectParsers(true); + + expect(status).toBeTruthy(); + expect(render).toHaveBeenCalledWith({ parsers: [['whatever'], ['whatever']] }, expect.anything()); + }); + + it('should describe given ObjectParser', () => { + const parserList = ['ob1', 'ob2']; + const objectParserManager = new ObjectParserManager(); + // @ts-ignore + parserList.forEach(op => objectParserManager.addObjectParser(() => op, op)); + + const status = objectParserManager.describeMatchingObjectParsers(parserList[1]); + + expect(status).toBeTruthy(); + expect(render).toHaveBeenCalledWith({ parsers: [['ob2']] }, expect.anything()); + }); + + it('should error when describing no given ObjectParser', () => { + const parserList = ['ob1', 'ob2']; + const objectParserManager = new ObjectParserManager(); + // @ts-ignore + parserList.forEach(op => objectParserManager.addObjectParser(() => op, op)); + + const status = objectParserManager.describeMatchingObjectParsers('unknown'); + + expect(status).toBeFalsy(); + expect(render).toHaveBeenCalledWith({ parsers: [] }, expect.anything()); + }); }); diff --git a/src/plugins/object-parser-manager.ts b/src/plugins/object-parser-manager.ts index 0dac7792..ddd90956 100644 --- a/src/plugins/object-parser-manager.ts +++ b/src/plugins/object-parser-manager.ts @@ -1,63 +1,67 @@ -import {ObjectParser} from '../object-parser/object-parser'; -import {Logger} from '../loggers/logger'; -import {prettifyJson} from '../outputs/prettify-json'; +import { ObjectParser } from '../object-parser/object-parser'; +import { Logger } from '../loggers/logger'; +import { prettifyJson } from '../outputs/prettify-json'; interface AddedObjectParser { - tags: string[]; - createFunction: () => ObjectParser; + tags: string[]; + createFunction: () => ObjectParser; } export class ObjectParserManager { - private addedObjectParsers: AddedObjectParser[] = []; + private addedObjectParsers: AddedObjectParser[] = []; - public addObjectParser(createFunction: () => ObjectParser, firstTag: string, ...tags: string[]): void { - const strings = [firstTag].concat(tags); - this.addedObjectParsers.unshift({tags: strings, createFunction}); - } + public addObjectParser(createFunction: () => ObjectParser, firstTag: string, ...tags: string[]): void { + const strings = [firstTag].concat(tags); + this.addedObjectParsers.unshift({ tags: strings, createFunction }); + } - public getMatchingObjectParsers(describeObjectParsers: string | true): any { - return { - parsers: this.addedObjectParsers - .filter((objectParser: AddedObjectParser) => typeof (describeObjectParsers) === 'string' ? (objectParser.tags || []) - .some((tag: string) => tag.toLowerCase() === describeObjectParsers.toLowerCase()) : true) - .map((objectParser: AddedObjectParser) => objectParser.tags) - }; - } + public getMatchingObjectParsers(describeObjectParsers: string | true): any { + return { + parsers: this.addedObjectParsers + .filter((objectParser: AddedObjectParser) => + typeof describeObjectParsers === 'string' + ? (objectParser.tags || []).some((tag: string) => tag.toLowerCase() === describeObjectParsers.toLowerCase()) + : true + ) + .map((objectParser: AddedObjectParser) => objectParser.tags) + }; + } - public describeMatchingObjectParsers(data: any): boolean { - const matchingObjectParsers = this.getMatchingObjectParsers(data); - console.log(prettifyJson(matchingObjectParsers)); - return matchingObjectParsers.parsers.length > 0; - } + public describeMatchingObjectParsers(data: any): boolean { + const matchingObjectParsers = this.getMatchingObjectParsers(data); + console.log(`Describing object parsers matching: ${data}`); + console.log(prettifyJson(matchingObjectParsers)); + return matchingObjectParsers.parsers.length > 0; + } - public createParser(tag: string): ObjectParser | undefined { - const matchingObjectParsers = this.addedObjectParsers - .filter((addedFormatter: AddedObjectParser) => (addedFormatter.tags || []) - .some((parserKey: string) => parserKey.toLowerCase() === tag.toLowerCase())) - .map((addedFormatter: AddedObjectParser) => addedFormatter.createFunction()); - if (matchingObjectParsers.length > 0) { - return matchingObjectParsers[0]; - } - Logger.warning(`No object parser was found with '${tag}'`); + public createParser(tag: string): ObjectParser | undefined { + const matchingObjectParsers = this.addedObjectParsers + .filter((addedFormatter: AddedObjectParser) => + (addedFormatter.tags || []).some((parserKey: string) => parserKey.toLowerCase() === tag.toLowerCase()) + ) + .map((addedFormatter: AddedObjectParser) => addedFormatter.createFunction()); + if (matchingObjectParsers.length > 0) { + return matchingObjectParsers[0]; } + Logger.warning(`No object parser was found with '${tag}'`); + } - public tryToParseWithParsers(fileBufferContent: string, tags: string[] = []): object { - const errorResult: any = {}; - for (const tag of tags) { - const objectParser = this.createParser(tag); - if (objectParser) { - try { - const parsed = objectParser.parse(fileBufferContent); - Logger.debug(`Content parsed as ${tag}`); - return parsed; - } catch (err) { - errorResult[tag] = err.toString(); - } - } else { - errorResult[tag] = `No object parser was found with '${tag}'`; - } + public tryToParseWithParsers(fileBufferContent: string, tags: string[] = []): object { + const errorMessages: string[] = []; + for (const tag of tags) { + const objectParser = this.createParser(tag); + if (objectParser) { + try { + const parsed = objectParser.parse(fileBufferContent); + Logger.debug(`Content parsed as ${tag}`); + return parsed; + } catch (err) { + errorMessages.push(`${tag.toLocaleUpperCase()} error: ${err}`); } - throw errorResult; + } else { + errorMessages.push(`No parser was found with: ${tag}`); + } } - + throw errorMessages.join(';\n'); + } } diff --git a/src/plugins/protocol-manager.test.ts b/src/plugins/protocol-manager.test.ts index 0cf58a66..dc17ea2f 100644 --- a/src/plugins/protocol-manager.test.ts +++ b/src/plugins/protocol-manager.test.ts @@ -1,9 +1,9 @@ -import {ProtocolManager} from './protocol-manager'; +import { ProtocolManager } from './protocol-manager'; import prettyjson from 'prettyjson'; -import {NullPublisher} from '../publishers/null-publisher'; -import {NullSubscription} from '../subscriptions/null-subscription'; -import {SubscriptionProtocol} from '../protocols/subscription-protocol'; -import {PublisherProtocol} from '../protocols/publisher-protocol'; +import { NullActuator } from '../actuators/null-actuator'; +import { NullSensor } from '../sensors/null-sensor'; +import { SensorProtocol } from '../protocols/sensor-protocol'; +import { ActuatorProtocol } from '../protocols/actuator-protocol'; jest.mock('prettyjson'); @@ -12,353 +12,475 @@ const render = jest.fn(); prettyjson.render.mockImplementation(render); describe('ProtocolManager', () => { + beforeEach(() => { + render.mockClear(); + }); - beforeEach(() => { - render.mockClear(); - }); + it('describeMatchingProtocols', () => { + const protocolManager = new ProtocolManager(); + protocolManager.addProtocol( + // @ts-ignore + new ActuatorProtocol('mine', () => { + /*not empty*/ + }) + ); + expect(protocolManager.describeMatchingProtocols()).toBeTruthy(); + expect(render).toHaveBeenCalledWith( + { + actuators: expect.anything(), + sensors: expect.anything() + }, + expect.anything() + ); + }); - it('describeMatchingProtocols', () => { - const protocolManager = new ProtocolManager(); - // @ts-ignore - protocolManager.addProtocol(new PublisherProtocol('mine', () => {/*not empty*/ - })); - expect(protocolManager.describeMatchingProtocols()).toBeTruthy(); - expect(render).toHaveBeenCalledWith({ - publishers: expect.anything(), - subscriptions: expect.anything() - }, expect.anything()); - }); + it('should create right Actuator', () => { + const actuator: any = {}; + // @ts-ignore + const protocolManager = new ProtocolManager(); + protocolManager.addProtocol( + new ActuatorProtocol('mine', arg => { + actuator.arg = arg; + return actuator; + }) + ); + // @ts-ignore + const actual = protocolManager.createActuator({ type: 'mine' }); + expect(actual).toEqual({ arg: { type: 'mine' } }); + }); - it('should create right Publisher', () => { - const publisher: any = {}; - // @ts-ignore - const protocolManager = new ProtocolManager(); - protocolManager.addProtocol(new PublisherProtocol('mine', (arg) => { - publisher.arg = arg; - return publisher; - })); - // @ts-ignore - const actual = protocolManager.createPublisher({type: 'mine'}); - expect(actual).toEqual({arg: {type: 'mine'}}); - }); - - it('should create right Subscription', () => { - const subscription: any = {}; - // @ts-ignore - const protocolManager = new ProtocolManager(); - protocolManager.addProtocol(new SubscriptionProtocol('mine', (arg) => { - subscription.arg = arg; - return subscription; - })); - // @ts-ignore - const actual = protocolManager.createSubscription({type: 'mine'}); - expect(actual).toEqual({arg: {type: 'mine'}}); - }); + it('should create right Sensor', () => { + const sensor: any = {}; + // @ts-ignore + const protocolManager = new ProtocolManager(); + protocolManager.addProtocol( + new SensorProtocol('mine', arg => { + sensor.arg = arg; + return sensor; + }) + ); + // @ts-ignore + const actual = protocolManager.createSensor({ type: 'mine' }); + expect(actual).toEqual({ arg: { type: 'mine' } }); + }); - it('should create NullPublisher', () => { - // @ts-ignore - const publisher = new ProtocolManager().createPublisher({}); - expect(publisher).toBeInstanceOf(NullPublisher); - }); + it('should create NullActuator', () => { + // @ts-ignore + const actuator = new ProtocolManager().createActuator({}); + expect(actuator).toBeInstanceOf(NullActuator); + }); - it('should create NullSubscription', () => { - // @ts-ignore - const publisher = new ProtocolManager().createSubscription({}); - expect(publisher).toBeInstanceOf(NullSubscription); - }); + it('should create NullSensor', () => { + // @ts-ignore + const actuator = new ProtocolManager().createSensor({}); + expect(actuator).toBeInstanceOf(NullSensor); + }); - it('describe given publisher Protocol', () => { - // @ts-ignore - const protocolManager = new ProtocolManager(); - // @ts-ignore - protocolManager.addProtocol(new PublisherProtocol('pub', () => {/*not empty*/ - }) - .addAlternativeName('virgs') - .setLibrary('request')); - expect(protocolManager.describeMatchingProtocols('virgs')).toBeTruthy(); - expect(render).toHaveBeenCalledWith({ - 'publishers': [{ - 'name': 'pub', - 'schema': { - 'attributes': { - 'type': { - 'description': 'Protocol identifier', - 'required': true, - 'type': 'string' - }, - 'ignore': { - 'defaultValue': false, - 'description': 'Defines if the component should be ignored', - 'required': false, - 'type': 'boolean' - }, 'name': {'description': 'Defines the component name', 'required': false, 'type': 'string'} - }, - 'hooks': { - 'onFinish': { - 'arguments': { - 'elapsedTime': {'description': 'Number of milliseconds since the instantiation of the component'}, - 'this': {'description': 'Pointer to the component'} - }, 'description': 'Executed when the component is about to finish' - }, - 'onInit': { - 'arguments': { - 'elapsedTime': {'description': 'Number of milliseconds since the instantiation of the component'}, - 'this': {'description': 'Pointer to the component'} - }, 'description': 'Executed as soon as the component is initialized' - } - } - } - }], 'subscriptions': [] - }, expect.anything() - ); - }); + it('describe given actuator Protocol', () => { + // @ts-ignore + const protocolManager = new ProtocolManager(); + protocolManager.addProtocol( + // @ts-ignore + new ActuatorProtocol('pub', () => { + /*not empty*/ + }) + .addAlternativeName('virgs') + .setLibrary('request') + ); + expect(protocolManager.describeMatchingProtocols('virgs')).toBeTruthy(); + expect(render).toHaveBeenCalledWith( + { + actuators: [ + { + name: 'pub', + schema: { + attributes: { + type: { + description: 'Protocol identifier', + required: true, + type: 'string' + }, + ignore: { + defaultValue: false, + description: 'Defines if the component should be ignored', + required: false, + type: 'boolean' + }, + name: { + description: 'Defines the component name', + required: false, + type: 'string' + } + }, + hooks: { + onFinish: { + arguments: { + elapsedTime: { + description: 'Number of milliseconds since the instantiation of the component' + }, + this: { description: 'Pointer to the component' } + }, + description: 'Executed when the component is about to finish' + }, + onInit: { + arguments: { + elapsedTime: { + description: 'Number of milliseconds since the instantiation of the component' + }, + this: { description: 'Pointer to the component' } + }, + description: 'Executed as soon as the component is initialized' + } + } + } + } + ], + sensors: [] + }, + expect.anything() + ); + }); - it('describe given publisher Protocol not string param', () => { - // @ts-ignore - const protocolManager = new ProtocolManager(); - // @ts-ignore - protocolManager.addProtocol(new PublisherProtocol('pub', () => {/*not empty*/ - })); - // @ts-ignore - protocolManager.addProtocol(new PublisherProtocol('other', () => {/*not empty*/ - })); - expect(protocolManager.describeMatchingProtocols()).toBeTruthy(); - expect(render).toHaveBeenCalledWith({ - 'publishers': [{ - 'name': 'pub', - 'schema': { - 'attributes': { - 'type': { - 'description': 'Protocol identifier', - 'required': true, - 'type': 'string' - }, - 'ignore': { - 'defaultValue': false, - 'description': 'Defines if the component should be ignored', - 'required': false, - 'type': 'boolean' - }, 'name': {'description': 'Defines the component name', 'required': false, 'type': 'string'} - }, - 'hooks': { - 'onFinish': { - 'arguments': { - 'elapsedTime': {'description': 'Number of milliseconds since the instantiation of the component'}, - 'this': {'description': 'Pointer to the component'} - }, 'description': 'Executed when the component is about to finish' - }, - 'onInit': { - 'arguments': { - 'elapsedTime': {'description': 'Number of milliseconds since the instantiation of the component'}, - 'this': {'description': 'Pointer to the component'} - }, 'description': 'Executed as soon as the component is initialized' - } - } - } - }, { - 'name': 'other', - 'schema': { - 'attributes': { - 'type': { - 'description': 'Protocol identifier', - 'required': true, - 'type': 'string' - }, - 'ignore': { - 'defaultValue': false, - 'description': 'Defines if the component should be ignored', - 'required': false, - 'type': 'boolean' - }, 'name': {'description': 'Defines the component name', 'required': false, 'type': 'string'} - }, - 'hooks': { - 'onFinish': { - 'arguments': { - 'elapsedTime': {'description': 'Number of milliseconds since the instantiation of the component'}, - 'this': {'description': 'Pointer to the component'} - }, 'description': 'Executed when the component is about to finish' - }, - 'onInit': { - 'arguments': { - 'elapsedTime': {'description': 'Number of milliseconds since the instantiation of the component'}, - 'this': {'description': 'Pointer to the component'} - }, 'description': 'Executed as soon as the component is initialized' - } - } - } - }], 'subscriptions': [] - }, expect.anything() - ); - }); + it('describe given actuator Protocol not string param', () => { + // @ts-ignore + const protocolManager = new ProtocolManager(); + protocolManager.addProtocol( + // @ts-ignore + new ActuatorProtocol('pub', () => { + /*not empty*/ + }) + ); + protocolManager.addProtocol( + // @ts-ignore + new ActuatorProtocol('other', () => { + /*not empty*/ + }) + ); + expect(protocolManager.describeMatchingProtocols()).toBeTruthy(); + expect(render).toHaveBeenCalledWith( + { + actuators: [ + { + name: 'pub', + schema: { + attributes: { + type: { + description: 'Protocol identifier', + required: true, + type: 'string' + }, + ignore: { + defaultValue: false, + description: 'Defines if the component should be ignored', + required: false, + type: 'boolean' + }, + name: { + description: 'Defines the component name', + required: false, + type: 'string' + } + }, + hooks: { + onFinish: { + arguments: { + elapsedTime: { + description: 'Number of milliseconds since the instantiation of the component' + }, + this: { description: 'Pointer to the component' } + }, + description: 'Executed when the component is about to finish' + }, + onInit: { + arguments: { + elapsedTime: { + description: 'Number of milliseconds since the instantiation of the component' + }, + this: { description: 'Pointer to the component' } + }, + description: 'Executed as soon as the component is initialized' + } + } + } + }, + { + name: 'other', + schema: { + attributes: { + type: { + description: 'Protocol identifier', + required: true, + type: 'string' + }, + ignore: { + defaultValue: false, + description: 'Defines if the component should be ignored', + required: false, + type: 'boolean' + }, + name: { + description: 'Defines the component name', + required: false, + type: 'string' + } + }, + hooks: { + onFinish: { + arguments: { + elapsedTime: { + description: 'Number of milliseconds since the instantiation of the component' + }, + this: { description: 'Pointer to the component' } + }, + description: 'Executed when the component is about to finish' + }, + onInit: { + arguments: { + elapsedTime: { + description: 'Number of milliseconds since the instantiation of the component' + }, + this: { description: 'Pointer to the component' } + }, + description: 'Executed as soon as the component is initialized' + } + } + } + } + ], + sensors: [] + }, + expect.anything() + ); + }); - it('error describing Protocol', () => { - // @ts-ignore - const protocolManager = new ProtocolManager(); - expect(protocolManager.describeMatchingProtocols()).toBeFalsy(); - expect(render).toHaveBeenCalled(); - }); + it('error describing Protocol', () => { + // @ts-ignore + const protocolManager = new ProtocolManager(); + expect(protocolManager.describeMatchingProtocols()).toBeFalsy(); + expect(render).toHaveBeenCalled(); + }); - it('describe given subscription Protocol', () => { + it('describe given sensor Protocol', () => { + // @ts-ignore + const protocolManager = new ProtocolManager(); + protocolManager.addProtocol( + new SensorProtocol( + 'sub', // @ts-ignore - const protocolManager = new ProtocolManager(); - // @ts-ignore - protocolManager.addProtocol(new SubscriptionProtocol('sub', () => {/*not empty*/ - }, ['value']).addAlternativeName('altName') - .setLibrary('express')); - expect(protocolManager.describeMatchingProtocols('sub')).toBeTruthy(); - expect(render).toHaveBeenCalledWith({ - 'publishers': [], 'subscriptions': [{ - '0': 'value', 'name': 'sub', 'schema': { - 'attributes': { - 'type': { - 'description': 'Protocol identifier', - 'required': true, - 'type': 'string' - }, + () => { + /*not empty*/ + }, + ['value'] + ) + .addAlternativeName('altName') + .setLibrary('express') + ); + expect(protocolManager.describeMatchingProtocols('sub')).toBeTruthy(); + expect(render).toHaveBeenCalledWith( + { + actuators: [], + sensors: [ + { + '0': 'value', + name: 'sub', + schema: { + attributes: { + type: { + description: 'Protocol identifier', + required: true, + type: 'string' + }, - 'avoid': { - 'defaultValue': false, - 'description': 'Defines if the subscription should be avoided', - 'required': false, - 'type': 'boolean' - }, - 'ignore': { - 'defaultValue': false, - 'description': 'Defines if the component should be ignored', - 'required': false, - 'type': 'boolean' - }, - 'name': {'description': 'Defines the component name', 'required': false, 'type': 'string'}, - 'timeout': { - 'defaultValue': 3000, - 'description': 'Defines the subscription time out', - 'required': false, - 'suffix': 'ms', - 'type': 'int' - } + avoid: { + defaultValue: false, + description: 'Defines if the sensor should be avoided', + required: false, + type: 'boolean' + }, + ignore: { + defaultValue: false, + description: 'Defines if the component should be ignored', + required: false, + type: 'boolean' + }, + name: { + description: 'Defines the component name', + required: false, + type: 'string' + }, + timeout: { + defaultValue: 3000, + description: 'Defines the sensor time out', + required: false, + suffix: 'ms', + type: 'int' + } + }, + hooks: { + onFinish: { + arguments: { + elapsedTime: { + description: 'Number of milliseconds since the instantiation of the component' }, - 'hooks': { - 'onFinish': { - 'arguments': { - 'elapsedTime': {'description': 'Number of milliseconds since the instantiation of the component'}, - 'this': {'description': 'Pointer to the component'} - }, 'description': 'Executed when the component is about to finish' - }, - 'onInit': { - 'arguments': { - 'elapsedTime': {'description': 'Number of milliseconds since the instantiation of the component'}, - 'this': {'description': 'Pointer to the component'} - }, 'description': 'Executed as soon as the component is initialized' - } - } + this: { description: 'Pointer to the component' } + }, + description: 'Executed when the component is about to finish' + }, + onInit: { + arguments: { + elapsedTime: { + description: 'Number of milliseconds since the instantiation of the component' + }, + this: { description: 'Pointer to the component' } + }, + description: 'Executed as soon as the component is initialized' } - }] - }, expect.anything()); - }); + } + } + } + ] + }, + expect.anything() + ); + }); - it('describe given subscription Protocol not string param', () => { - // @ts-ignore - const protocolManager = new ProtocolManager(); - // @ts-ignore - protocolManager.addProtocol(new SubscriptionProtocol('sub', () => { /*not empty*/ - })); - // @ts-ignore - protocolManager.addProtocol(new SubscriptionProtocol('sub2', () => { /*not empty*/ - })); - expect(protocolManager.describeMatchingProtocols()).toBeTruthy(); - expect(render).toHaveBeenCalledWith({ - 'publishers': [], 'subscriptions': [{ - 'name': 'sub', 'schema': { - 'attributes': { - 'type': { - 'description': 'Protocol identifier', - 'required': true, - 'type': 'string' - }, + it('describe given sensor Protocol not string param', () => { + // @ts-ignore + const protocolManager = new ProtocolManager(); + protocolManager.addProtocol( + // @ts-ignore + new SensorProtocol('sub', () => { + /*not empty*/ + }) + ); + protocolManager.addProtocol( + // @ts-ignore + new SensorProtocol('sub2', () => { + /*not empty*/ + }) + ); + expect(protocolManager.describeMatchingProtocols()).toBeTruthy(); + expect(render).toHaveBeenCalledWith( + { + actuators: [], + sensors: [ + { + name: 'sub', + schema: { + attributes: { + type: { + description: 'Protocol identifier', + required: true, + type: 'string' + }, - 'avoid': { - 'defaultValue': false, - 'description': 'Defines if the subscription should be avoided', - 'required': false, - 'type': 'boolean' - }, - 'ignore': { - 'defaultValue': false, - 'description': 'Defines if the component should be ignored', - 'required': false, - 'type': 'boolean' - }, - 'name': {'description': 'Defines the component name', 'required': false, 'type': 'string'}, - 'timeout': { - 'defaultValue': 3000, - 'description': 'Defines the subscription time out', - 'required': false, - 'suffix': 'ms', - 'type': 'int' - } + avoid: { + defaultValue: false, + description: 'Defines if the sensor should be avoided', + required: false, + type: 'boolean' + }, + ignore: { + defaultValue: false, + description: 'Defines if the component should be ignored', + required: false, + type: 'boolean' + }, + name: { + description: 'Defines the component name', + required: false, + type: 'string' + }, + timeout: { + defaultValue: 3000, + description: 'Defines the sensor time out', + required: false, + suffix: 'ms', + type: 'int' + } + }, + hooks: { + onFinish: { + arguments: { + elapsedTime: { + description: 'Number of milliseconds since the instantiation of the component' }, - 'hooks': { - 'onFinish': { - 'arguments': { - 'elapsedTime': {'description': 'Number of milliseconds since the instantiation of the component'}, - 'this': {'description': 'Pointer to the component'} - }, 'description': 'Executed when the component is about to finish' - }, - 'onInit': { - 'arguments': { - 'elapsedTime': {'description': 'Number of milliseconds since the instantiation of the component'}, - 'this': {'description': 'Pointer to the component'} - }, 'description': 'Executed as soon as the component is initialized' - } - } + this: { description: 'Pointer to the component' } + }, + description: 'Executed when the component is about to finish' + }, + onInit: { + arguments: { + elapsedTime: { + description: 'Number of milliseconds since the instantiation of the component' + }, + this: { description: 'Pointer to the component' } + }, + description: 'Executed as soon as the component is initialized' } - }, { - 'name': 'sub2', 'schema': { - 'attributes': { - 'type': { - 'description': 'Protocol identifier', - 'required': true, - 'type': 'string' - }, + } + } + }, + { + name: 'sub2', + schema: { + attributes: { + type: { + description: 'Protocol identifier', + required: true, + type: 'string' + }, - 'avoid': { - 'defaultValue': false, - 'description': 'Defines if the subscription should be avoided', - 'required': false, - 'type': 'boolean' - }, - 'ignore': { - 'defaultValue': false, - 'description': 'Defines if the component should be ignored', - 'required': false, - 'type': 'boolean' - }, - 'name': {'description': 'Defines the component name', 'required': false, 'type': 'string'}, - 'timeout': { - 'defaultValue': 3000, - 'description': 'Defines the subscription time out', - 'required': false, - 'suffix': 'ms', - 'type': 'int' - } + avoid: { + defaultValue: false, + description: 'Defines if the sensor should be avoided', + required: false, + type: 'boolean' + }, + ignore: { + defaultValue: false, + description: 'Defines if the component should be ignored', + required: false, + type: 'boolean' + }, + name: { + description: 'Defines the component name', + required: false, + type: 'string' + }, + timeout: { + defaultValue: 3000, + description: 'Defines the sensor time out', + required: false, + suffix: 'ms', + type: 'int' + } + }, + hooks: { + onFinish: { + arguments: { + elapsedTime: { + description: 'Number of milliseconds since the instantiation of the component' }, - 'hooks': { - 'onFinish': { - 'arguments': { - 'elapsedTime': {'description': 'Number of milliseconds since the instantiation of the component'}, - 'this': {'description': 'Pointer to the component'} - }, 'description': 'Executed when the component is about to finish' - }, - 'onInit': { - 'arguments': { - 'elapsedTime': {'description': 'Number of milliseconds since the instantiation of the component'}, - 'this': {'description': 'Pointer to the component'} - }, 'description': 'Executed as soon as the component is initialized' - } - } + this: { description: 'Pointer to the component' } + }, + description: 'Executed when the component is about to finish' + }, + onInit: { + arguments: { + elapsedTime: { + description: 'Number of milliseconds since the instantiation of the component' + }, + this: { description: 'Pointer to the component' } + }, + description: 'Executed as soon as the component is initialized' } - }] - }, expect.anything()); - }); - + } + } + } + ] + }, + expect.anything() + ); + }); }); diff --git a/src/plugins/protocol-manager.ts b/src/plugins/protocol-manager.ts index 74d05bc9..09da34ee 100644 --- a/src/plugins/protocol-manager.ts +++ b/src/plugins/protocol-manager.ts @@ -1,64 +1,68 @@ -import {PublisherModel} from '../models/inputs/publisher-model'; -import {Publisher} from '../publishers/publisher'; -import {SubscriptionModel} from '../models/inputs/subscription-model'; -import {Subscription} from '../subscriptions/subscription'; -import {NullSubscription} from '../subscriptions/null-subscription'; -import {NullPublisher} from '../publishers/null-publisher'; -import {Protocol} from '../protocols/protocol'; -import {PublisherProtocol} from '../protocols/publisher-protocol'; -import {SubscriptionProtocol} from '../protocols/subscription-protocol'; -import {Logger} from '../loggers/logger'; -import {prettifyJson} from '../outputs/prettify-json'; +import { ActuatorModel } from '../models/inputs/actuator-model'; +import { Actuator } from '../actuators/actuator'; +import { SensorModel } from '../models/inputs/sensor-model'; +import { Sensor } from '../sensors/sensor'; +import { NullSensor } from '../sensors/null-sensor'; +import { NullActuator } from '../actuators/null-actuator'; +import { Protocol } from '../protocols/protocol'; +import { ActuatorProtocol } from '../protocols/actuator-protocol'; +import { SensorProtocol } from '../protocols/sensor-protocol'; +import { Logger } from '../loggers/logger'; +import { prettifyJson } from '../outputs/prettify-json'; export class ProtocolManager { - private protocols: Protocol[] = []; + private protocols: Protocol[] = []; - public createPublisher(publisherModel: PublisherModel): Publisher { - const matchingPublishers = this.protocols - .filter((protocol: Protocol) => protocol.isPublisher()) - .filter((protocol: Protocol) => protocol.matches(publisherModel.type)) - .map((protocol: Protocol) => (protocol as PublisherProtocol).create(publisherModel)); - if (matchingPublishers.length > 0) { - return matchingPublishers[0]; - } - Logger.error(`No publisher was found with '${publisherModel.type}'`); - return new NullPublisher(publisherModel); + public createActuator(actuatorModel: ActuatorModel): Actuator { + const matchingActuators = this.protocols + .filter((protocol: Protocol) => protocol.isActuator()) + .filter((protocol: Protocol) => protocol.matches(actuatorModel.type)) + .map((protocol: Protocol) => (protocol as ActuatorProtocol).create(actuatorModel)); + if (matchingActuators.length > 0) { + return matchingActuators[0]; } + Logger.error(`No actuator was found with '${actuatorModel.type}'`); + return new NullActuator(actuatorModel); + } - public createSubscription(subscriptionModel: SubscriptionModel): Subscription { - const matchingSubscriptions = this.protocols - .filter((protocol: Protocol) => protocol.isSubscription()) - .filter((protocol: Protocol) => protocol.matches(subscriptionModel.type)) - .map((protocol: Protocol) => (protocol as SubscriptionProtocol).create(subscriptionModel)); - if (matchingSubscriptions.length > 0) { - return matchingSubscriptions[0]; - } - Logger.error(`No subscription was found with '${subscriptionModel.type}'`); - return new NullSubscription(subscriptionModel); + public createSensor(sensorModel: SensorModel): Sensor { + const matchingSensors = this.protocols + .filter((protocol: Protocol) => protocol.isSensor()) + .filter((protocol: Protocol) => protocol.matches(sensorModel.type)) + .map((protocol: Protocol) => (protocol as SensorProtocol).create(sensorModel)); + if (matchingSensors.length > 0) { + return matchingSensors[0]; } + Logger.error(`No sensor was found with '${sensorModel.type}'`); + return new NullSensor(sensorModel); + } - public addProtocol(protocol: Protocol): void { - this.protocols.push(protocol); - } + public addProtocol(protocol: Protocol): void { + this.protocols.push(protocol); + } - public describeMatchingProtocols(description: string = ''): boolean { - const matchingProtocols = this.getProtocolsDescription(description); - console.log(prettifyJson(matchingProtocols)); - return matchingProtocols.publishers.length + matchingProtocols.subscriptions.length > 0; - } + public describeMatchingProtocols(description: string = ''): boolean { + const matchingProtocols = this.getProtocolsDescription(description); + console.log(`Describing protocols matching: ${description}`); + console.log(prettifyJson(matchingProtocols)); + return matchingProtocols.actuators.length + matchingProtocols.sensors.length > 0; + } - public getProtocolsDescription(protocol: string = ''): { publishers: {}[], subscriptions: {}[] } { - return { - publishers: this.protocols - //NOTE: function check for retro compatibilities proposes - .filter((protocol: Protocol) => protocol.isPublisher && protocol.isPublisher()) - .filter((publisher: Protocol) => publisher.matches(protocol)) - .map(protocol => protocol.getDescription()), - subscriptions: this.protocols - //NOTE: function check for retro compatibilities proposes - .filter((protocol: Protocol) => protocol.isSubscription && protocol.isSubscription()) - .filter((subscription: Protocol) => subscription.matches(protocol)) - .map(protocol => protocol.getDescription()) - }; - } + public getProtocolsDescription(protocol: string = ''): { + actuators: {}[]; + sensors: {}[]; + } { + return { + actuators: this.protocols + //NOTE: function check for retro compatibilities proposes + .filter((protocol: Protocol) => protocol.isActuator && protocol.isActuator()) + .filter((actuator: Protocol) => actuator.matches(protocol)) + .map(protocol => protocol.getDescription()), + sensors: this.protocols + //NOTE: function check for retro compatibilities proposes + .filter((protocol: Protocol) => protocol.isSensor && protocol.isSensor()) + .filter((sensor: Protocol) => sensor.matches(protocol)) + .map(protocol => protocol.getDescription()) + }; + } } diff --git a/src/plugins/report-formatter-manager.test.ts b/src/plugins/report-formatter-manager.test.ts index baf28ad3..a4fa00aa 100644 --- a/src/plugins/report-formatter-manager.test.ts +++ b/src/plugins/report-formatter-manager.test.ts @@ -1,6 +1,6 @@ import prettyjson from 'prettyjson'; -import {ReportFormatterManager} from './report-formatter-manager'; -import {JsonReportFormatter} from '../outputs/formatters/json-formatter'; +import { ReportFormatterManager } from './report-formatter-manager'; +import { JsonReportFormatter } from '../outputs/formatters/json-formatter'; jest.mock('prettyjson'); @@ -9,80 +9,110 @@ const render = jest.fn(); prettyjson.render.mockImplementation(render); describe('ReportFormatterManager', () => { + beforeEach(() => { + render.mockClear(); + }); - beforeEach(() => { - render.mockClear(); - }); + it('describeFormats', () => { + const reportFormatterManager = new ReportFormatterManager(); + reportFormatterManager.addReportFormatter( + // @ts-ignore + () => { + /**/ + }, + 'first', + '1st' + ); + // @ts-ignore + reportFormatterManager.addReportFormatter(() => { + /**/ + }, 'second'); + // @ts-ignore - it('describeFormats', () => { - const reportFormatterManager = new ReportFormatterManager(); - // @ts-ignore - reportFormatterManager.addReportFormatter(() => {/**/ - }, 'first', '1st'); - // @ts-ignore - reportFormatterManager.addReportFormatter(() => {/**/ - }, 'second'); - // @ts-ignore + expect(reportFormatterManager.describeMatchingReportFormatters(true)).toBeTruthy(); + expect(render).toHaveBeenCalledWith( + { + formatters: [['first', '1st'], ['second']] + }, + expect.anything() + ); + }); - expect(reportFormatterManager.describeMatchingReportFormatters(true)).toBeTruthy(); - expect(render).toHaveBeenCalledWith({ - formatters: [['first', '1st'], ['second']] - }, expect.anything()); - }); + it('should create right formatter', done => { + // @ts-ignore + const reportFormatterManager = new ReportFormatterManager(); + reportFormatterManager.addReportFormatter(() => done(), 'tag'); + reportFormatterManager.createReportFormatter('tag'); + }); - it('should create right formatter', done => { - // @ts-ignore - const reportFormatterManager = new ReportFormatterManager(); - reportFormatterManager.addReportFormatter(() => done(), 'tag'); - reportFormatterManager.createReportFormatter('tag'); - }); + it('should create right formatter ignoring case', done => { + // @ts-ignore + const reportFormatterManager = new ReportFormatterManager(); + reportFormatterManager.addReportFormatter(() => done(), 'TaG'); + reportFormatterManager.createReportFormatter('tag'); + }); - it('should create right formatter ignoring case', done => { - // @ts-ignore - const reportFormatterManager = new ReportFormatterManager(); - reportFormatterManager.addReportFormatter(() => done(), 'TaG'); - reportFormatterManager.createReportFormatter('tag'); - }); + it('should create Default formatter', () => { + const formatter = new ReportFormatterManager().createReportFormatter('unknown'); + expect(formatter).toBeInstanceOf(JsonReportFormatter); + }); - it('should create Default formatter', () => { - const formatter = new ReportFormatterManager().createReportFormatter('unknown'); - expect(formatter).toBeInstanceOf(JsonReportFormatter); - }); + it('describe given formatter', () => { + const reportFormatterManager = new ReportFormatterManager(); + reportFormatterManager.addReportFormatter( + // @ts-ignore + () => { + /**/ + }, + 'tag', + 'another' + ); + // @ts-ignore + reportFormatterManager.addReportFormatter(() => { + /**/ + }, 'second'); + expect(reportFormatterManager.describeMatchingReportFormatters('tag')).toBeTruthy(); + expect(render).toHaveBeenCalledWith( + { + formatters: [['tag', 'another']] + }, + expect.anything() + ); + }); - it('describe given formatter', () => { - const reportFormatterManager = new ReportFormatterManager(); - // @ts-ignore - reportFormatterManager.addReportFormatter(() => {/**/ - }, 'tag', 'another'); - // @ts-ignore - reportFormatterManager.addReportFormatter(() => {/**/ - }, 'second'); - expect(reportFormatterManager.describeMatchingReportFormatters('tag')).toBeTruthy(); - expect(render).toHaveBeenCalledWith({ - formatters: [['tag', 'another']] - }, expect.anything()); - }); - - it('describe given formatter not string param', () => { - const reportFormatterManager = new ReportFormatterManager(); - // @ts-ignore - reportFormatterManager.addReportFormatter(() => {/**/ - }, 'tag', 'another'); - // @ts-ignore - reportFormatterManager.addReportFormatter(() => {/**/ - }, 'second'); - expect(reportFormatterManager.describeMatchingReportFormatters(true)).toBeTruthy(); - expect(render).toHaveBeenCalledWith({ - formatters: [['tag', 'another'], ['second']] - }, expect.anything()); - }); - - it('error describe given formatter', () => { - const reportFormatterManager = new ReportFormatterManager(); - // @ts-ignore - reportFormatterManager.addReportFormatter(() => {/**/ - }, 'tag', 'another'); - expect(reportFormatterManager.describeMatchingReportFormatters('unknown')).toBeFalsy(); - }); + it('describe given formatter not string param', () => { + const reportFormatterManager = new ReportFormatterManager(); + reportFormatterManager.addReportFormatter( + // @ts-ignore + () => { + /**/ + }, + 'tag', + 'another' + ); + // @ts-ignore + reportFormatterManager.addReportFormatter(() => { + /**/ + }, 'second'); + expect(reportFormatterManager.describeMatchingReportFormatters(true)).toBeTruthy(); + expect(render).toHaveBeenCalledWith( + { + formatters: [['tag', 'another'], ['second']] + }, + expect.anything() + ); + }); + it('error describe given formatter', () => { + const reportFormatterManager = new ReportFormatterManager(); + reportFormatterManager.addReportFormatter( + // @ts-ignore + () => { + /**/ + }, + 'tag', + 'another' + ); + expect(reportFormatterManager.describeMatchingReportFormatters('unknown')).toBeFalsy(); + }); }); diff --git a/src/plugins/report-formatter-manager.ts b/src/plugins/report-formatter-manager.ts index 49ee7264..1e11d3fc 100644 --- a/src/plugins/report-formatter-manager.ts +++ b/src/plugins/report-formatter-manager.ts @@ -1,44 +1,51 @@ -import {ReportFormatter} from '../outputs/formatters/report-formatter'; -import {JsonReportFormatter} from '../outputs/formatters/json-formatter'; -import {Logger} from '../loggers/logger'; -import {prettifyJson} from '../outputs/prettify-json'; +import { ReportFormatter } from '../outputs/formatters/report-formatter'; +import { JsonReportFormatter } from '../outputs/formatters/json-formatter'; +import { Logger } from '../loggers/logger'; +import { prettifyJson } from '../outputs/prettify-json'; interface AddedReportFormatter { - tags: string[]; - createFunction: () => ReportFormatter; + tags: string[]; + createFunction: () => ReportFormatter; } export class ReportFormatterManager { - private formatters: AddedReportFormatter[] = []; + private formatters: AddedReportFormatter[] = []; - public createReportFormatter(format: string): ReportFormatter { - const matchingFormatters = this.formatters - .filter((addedFormatter: AddedReportFormatter) => (addedFormatter.tags || []) - .some((tag: string) => tag.toLowerCase() === format.toLowerCase())) - .map((addedFormatter: AddedReportFormatter) => addedFormatter.createFunction()); - if (matchingFormatters.length > 0) { - return matchingFormatters[0]; - } - Logger.error(`No report formatter was found with '${format}', using default one`); - return new JsonReportFormatter(); + public createReportFormatter(format: string): ReportFormatter { + const matchingFormatters = this.formatters + .filter((addedFormatter: AddedReportFormatter) => + (addedFormatter.tags || []).some((tag: string) => tag.toLowerCase() === format.toLowerCase()) + ) + .map((addedFormatter: AddedReportFormatter) => addedFormatter.createFunction()); + if (matchingFormatters.length > 0) { + return matchingFormatters[0]; } + Logger.error(`No report formatter was found with '${format}', using default one`); + return new JsonReportFormatter(); + } - public addReportFormatter(createFunction: () => ReportFormatter, firstTag: string, ...tags: string[]): void { - this.formatters.push({tags: [firstTag].concat(tags), createFunction}); - } + public addReportFormatter(createFunction: () => ReportFormatter, firstTag: string, ...tags: string[]): void { + this.formatters.push({ tags: [firstTag].concat(tags), createFunction }); + } - public getMatchingReportFormatters(describeFormatters: string | true): { formatters: string[][] } { - return { - formatters: this.formatters - .filter((addedFormatter: AddedReportFormatter) => typeof (describeFormatters) === 'string' ? (addedFormatter.tags || []) - .some((tag: string) => tag.toLowerCase() === describeFormatters.toLowerCase()) : true) - .map((formatter: AddedReportFormatter) => formatter.tags) - }; - } + public getMatchingReportFormatters(describeFormatters: string | true): { + formatters: string[][]; + } { + return { + formatters: this.formatters + .filter((addedFormatter: AddedReportFormatter) => + typeof describeFormatters === 'string' + ? (addedFormatter.tags || []).some((tag: string) => tag.toLowerCase() === describeFormatters.toLowerCase()) + : true + ) + .map((formatter: AddedReportFormatter) => formatter.tags) + }; + } - public describeMatchingReportFormatters(describeFormatters: string | true): boolean { - const matchingReportFormatters = this.getMatchingReportFormatters(describeFormatters); - console.log(prettifyJson(matchingReportFormatters)); - return matchingReportFormatters.formatters.length > 0; - } + public describeMatchingReportFormatters(describeFormatters: string | true): boolean { + const matchingReportFormatters = this.getMatchingReportFormatters(describeFormatters); + console.log(`Describing report formatters matching: ${describeFormatters}`); + console.log(prettifyJson(matchingReportFormatters)); + return matchingReportFormatters.formatters.length > 0; + } } diff --git a/src/pools/http-actuator-fetcher.test.ts b/src/pools/http-actuator-fetcher.test.ts new file mode 100644 index 00000000..53f32e54 --- /dev/null +++ b/src/pools/http-actuator-fetcher.test.ts @@ -0,0 +1,53 @@ +import { HttpActuatorFetcher } from './http-actuator-fetcher'; + +jest.spyOn(global, 'fetch'); + +const fetchMock: jest.Mock = fetch as jest.Mock; + +describe('HttpActuatorFetcher', () => { + beforeEach(() => { + fetchMock.mockClear(); + }); + + it('Should resolve request result', async () => { + (fetch as jest.Mock).mockImplementationOnce(() => { + const headers = new Headers(); + headers.set('headerA', 'a'); + headers.set('header-1', '1'); + + return Promise.resolve({ + text: () => Promise.resolve('body'), + headers: headers, + status: 200 + }); + }); + + const result = await new HttpActuatorFetcher('url', 'method', { key: 'value' }, 'body', 1000).request(); + expect(result).toEqual({ + body: 'body', + headers: { headera: 'a', 'header-1': '1' }, + status: 200, + statusCode: 200 + }); + expect(fetchMock).toHaveBeenCalledWith('url', { + method: 'method', + headers: { + key: 'value', + 'Content-Length': 4 + }, + body: 'body', + agent: expect.any(Object), + signal: expect.any(Object) + }); + }); + + it('Should reject request result', async () => { + const requestMock = jest.fn((options, cb) => { + expect(options.headers).toEqual({ 'Content-Length': 0 }); + expect(options.timeout).toBe(3000); + cb('error'); + }); + (fetch as jest.Mock).mockImplementationOnce(requestMock); + await expect(new HttpActuatorFetcher('', '', undefined, '').request()).rejects.toBeDefined(); + }); +}); diff --git a/src/pools/http-actuator-fetcher.ts b/src/pools/http-actuator-fetcher.ts new file mode 100644 index 00000000..5ffbaf69 --- /dev/null +++ b/src/pools/http-actuator-fetcher.ts @@ -0,0 +1,91 @@ +import { Logger } from '../loggers/logger'; +import https from 'https'; + +const DEFAULT_TIMEOUT = 5000; + +export class HttpActuatorFetcher { + private readonly url: string; + private readonly method: string; + private readonly headers: any; + private readonly timeout: number; + private body: any; + + constructor(url: string, method: string, headers: any = {}, body: any, timeout: number = DEFAULT_TIMEOUT) { + this.url = url; + this.method = method; + this.headers = headers; + this.body = body; + this.timeout = timeout; + } + + public async request(): Promise { + Logger.info(`Hitting (${this.method.toUpperCase()}) - ${this.url}`); + const options = this.createOptions(); + try { + const response = await fetch(this.url, options); + const headers: any = {}; + response.headers.forEach((value, key) => { + headers[key] = value; + }); + const result = { + status: response.status, + statusCode: response.status, + body: await response.text(), + headers: headers + }; + return result; + } catch (err) { + const error = err as Error; + //@ts-expect-error + throw `Http request error: ${err}: ${error?.cause ?? error?.message}`; + } + } + + private createOptions(): RequestInit { + const payload = this.handleObjectPayload(); + const headers: any = {}; + if (this.method.toUpperCase() != 'GET') { + headers['Content-Length'] = this.setContentLength(payload); + } + const newLocal = { + method: this.method, + signal: AbortSignal.timeout(this.timeout), + headers: { + ...headers, + ...this.headers + }, + body: payload, + agent: new https.Agent({ + keepAlive: true, + maxSockets: Infinity, + keepAliveMsecs: 20000 + }) + }; + return newLocal; + } + + private setContentLength(value: string = ''): number { + if (Buffer.isBuffer(value)) { + return value.length; + } else { + return Buffer.from(value, 'utf8').byteLength; + } + } + + private handleObjectPayload(): string | undefined { + if (this.method.toUpperCase() == 'GET') { + return undefined; + } + try { + JSON.parse(this.body); + return this.body; + } catch (exc) { + //do nothing + } + if (typeof this.body != 'string') { + this.body = JSON.stringify(this.body); + } + + return this.body; + } +} diff --git a/src/pools/http-container-pool.test.ts b/src/pools/http-container-pool.test.ts index 7f2c1664..5eb87c4a 100644 --- a/src/pools/http-container-pool.test.ts +++ b/src/pools/http-container-pool.test.ts @@ -1,71 +1,59 @@ -import {HttpContainerPool} from './http-container-pool'; -import {HttpContainer} from './http-container'; -import {Logger} from '../loggers/logger'; +import { HttpContainerPool } from './http-container-pool'; +import { HttpContainer } from './http-container'; +import { Logger } from '../loggers/logger'; let acquireMock = jest.fn(() => Promise.resolve('acquireReturn')); -let releaseMock = jest.fn((cb) => cb()); +let releaseMock = jest.fn(cb => cb()); jest.mock('./http-container'); let constructorHttpContainer = jest.fn(() => { - return { - acquire: acquireMock, - release: releaseMock - }; + return { + acquire: acquireMock, + release: releaseMock + }; }); +// @ts-expect-error HttpContainer.mockImplementation(constructorHttpContainer); jest.mock('../loggers/logger'); const warningLogMock = jest.fn(); +// @ts-expect-error Logger.warning.mockImplementation(warningLogMock); describe('HttpContainerPool', () => { + beforeEach(() => { + acquireMock.mockClear(); + releaseMock.mockClear(); + constructorHttpContainer.mockClear(); + }); - beforeEach(() => { - acquireMock.mockClear(); - releaseMock.mockClear(); - constructorHttpContainer.mockClear(); - }); + it('create new App', async () => { + const port = 987; + const credentials = { key: 'value' }; - it('create new App', done => { - const port = 987; - const credentials = {key: 'value'}; + const result = await HttpContainerPool.getApp(port, credentials); - const appPromise = HttpContainerPool.getApp(port, true, credentials); + expect(result).toEqual('acquireReturn'); + expect(constructorHttpContainer).toHaveBeenCalledWith(port, credentials); + expect(acquireMock).toHaveBeenCalled(); + }); - appPromise.then((some) => { - expect(some).toEqual('acquireReturn'); - done(); - }); - expect(constructorHttpContainer).toHaveBeenCalledWith(port, credentials); - expect(acquireMock).toHaveBeenCalled(); - }); + it('reuse App', async () => { + const port = 987; + const credentials = { key: 'value' }; - it('reuse App', done => { + await HttpContainerPool.getApp(port, credentials); + const result = await HttpContainerPool.getApp(port, credentials); - const port = 987; - const secure = true; - const credentials = {key: 'value'}; + expect(result).toBe('acquireReturn'); + expect(constructorHttpContainer).not.toHaveBeenCalled(); + expect(acquireMock).toHaveBeenCalledTimes(2); + }); - HttpContainerPool.getApp(port, secure, credentials).then(() => { - const appPromise = HttpContainerPool.getApp(port, secure, credentials); - - appPromise.then((some) => { - expect(some).toBe('acquireReturn'); - done(); - }); - expect(constructorHttpContainer).toHaveBeenCalledTimes(1); - expect(acquireMock).toHaveBeenCalledTimes(2); - }); - }); - - it('release non existent App', done => { - const port = 3245612; - HttpContainerPool.releaseApp(port).then(() => { - - expect(releaseMock).not.toHaveBeenCalled(); - done(); - }); - - }); + it('release non existent App', async () => { + const port = 3245612; + await HttpContainerPool.releaseApp(port); + expect(releaseMock).not.toHaveBeenCalled(); + }); }); diff --git a/src/pools/http-container-pool.ts b/src/pools/http-container-pool.ts index fcdfe90c..5cb93c7d 100644 --- a/src/pools/http-container-pool.ts +++ b/src/pools/http-container-pool.ts @@ -1,45 +1,45 @@ -import {Logger} from '../loggers/logger'; -import {HttpContainer} from './http-container'; +import { Logger } from '../loggers/logger'; +import { HttpContainer } from './http-container'; import * as core from 'express-serve-static-core'; export class HttpContainerPool { - private static instance: HttpContainerPool; - private containers: { [propName: number]: HttpContainer } = {}; + private static instance: HttpContainerPool; + private containers: { [propName: number]: HttpContainer } = {}; - public static async getApp(port: number, secure: boolean = false, credentials?: any): Promise { - const self = HttpContainerPool.getInstance(); - Logger.trace(`Getting a Http server ${port}`); - let httpContainer: HttpContainer = self.containers[port]; - if (!httpContainer) { - Logger.trace(`Creating a new Http server ${port}`); - httpContainer = new HttpContainer(port, credentials); - self.containers[port] = httpContainer; - return await httpContainer.acquire(); - } else { - Logger.trace(`Reusing Http server ${port}`); - return await httpContainer.acquire(); - } + public static async getApp(port: number, credentials?: any): Promise { + const self = HttpContainerPool.getInstance(); + Logger.trace(`Getting a Http server ${port}`); + let httpContainer: HttpContainer = self.containers[port]; + if (!httpContainer) { + Logger.trace(`Creating a new Http server ${port}`); + httpContainer = new HttpContainer(port, credentials); + self.containers[port] = httpContainer; + return await httpContainer.acquire(); + } else { + Logger.trace(`Reusing Http server ${port}`); + return await httpContainer.acquire(); } + } - public static async releaseApp(port: number): Promise { - const self = HttpContainerPool.getInstance(); + public static async releaseApp(port: number): Promise { + const self = HttpContainerPool.getInstance(); - Logger.trace(`Current containers: {${Object.keys(self.containers)}}`); - const httpContainer = self.containers[port]; - if (httpContainer) { - const number = await httpContainer.release(); - if (number === 0) { - delete self.containers[port]; - } - } else { - Logger.trace(`No bound http-container to be released (${port})`); - } + Logger.trace(`Current containers: {${Object.keys(self.containers)}}`); + const httpContainer = self.containers[port]; + if (httpContainer) { + const number = await httpContainer.release(); + if (number === 0) { + delete self.containers[port]; + } + } else { + Logger.trace(`No bound http-container to be released (${port})`); } + } - private static getInstance(): HttpContainerPool { - if (!HttpContainerPool.instance) { - HttpContainerPool.instance = new HttpContainerPool(); - } - return HttpContainerPool.instance; + private static getInstance(): HttpContainerPool { + if (!HttpContainerPool.instance) { + HttpContainerPool.instance = new HttpContainerPool(); } + return HttpContainerPool.instance; + } } diff --git a/src/pools/http-container.test.ts b/src/pools/http-container.test.ts index e49d3b01..54d01fde 100644 --- a/src/pools/http-container.test.ts +++ b/src/pools/http-container.test.ts @@ -1,95 +1,97 @@ -import {HandlerListener} from '../handlers/handler-listener'; -import {HttpContainer} from './http-container'; +import { HandlerListener } from '../handlers/handler-listener'; +import { HttpContainer } from './http-container'; import express = require('express'); jest.mock('express'); jest.mock('../handlers/handler-listener'); describe('HttpContainer', () => { - it('Should create http server', () => { - const mockApp = { - use: () => { - } - }; - express.mockImplementationOnce(() => { - return mockApp; - }); - - new HttpContainer(123, false); - - expect(express).toBeCalledWith(); + it('Should create http server', () => { + const mockApp = { + use: () => {} + }; + // @ts-expect-error + express.mockImplementationOnce(() => { + return mockApp; }); - it('Should call handleListen when no server exists', done => { - const appReturn = { - use: () => { - } - }; - express.mockImplementationOnce(() => appReturn); - const mockListen = jest.fn(); - HandlerListener.mockImplementationOnce(() => { - return { - listen: mockListen - }; - }); + new HttpContainer(123, false); + + expect(express).toBeCalledWith(); + }); - new HttpContainer(123, false).acquire().then((app) => { - expect(mockListen).toHaveBeenCalled(); - expect(mockListen).toHaveBeenCalledTimes(1); - expect(app).toEqual(appReturn); - done(); - }); + it('Should call handleListen when no server exists', done => { + const appReturn = { + use: () => {} + }; + // @ts-expect-error + express.mockImplementationOnce(() => appReturn); + const mockListen = jest.fn(); + // @ts-expect-error + HandlerListener.mockImplementationOnce(() => { + return { + listen: mockListen + }; }); - it('Should handle handleListen fail', done => { - const appReturn = { - use: () => { - } - }; - express.mockImplementationOnce(() => appReturn); - HandlerListener.mockImplementationOnce(() => { - return { - listen: jest.fn(() => Promise.reject('reason')) - }; - }); + new HttpContainer(123, false).acquire().then(app => { + expect(mockListen).toHaveBeenCalled(); + expect(mockListen).toHaveBeenCalledTimes(1); + expect(app).toEqual(appReturn); + done(); + }); + }); - new HttpContainer(123, false).acquire().catch((err) => { - expect(err).toEqual('reason'); - done(); - }); + it('Should handle handleListen fail', done => { + const appReturn = { + use: () => {} + }; + // @ts-expect-error + express.mockImplementationOnce(() => appReturn); + // @ts-expect-error + HandlerListener.mockImplementationOnce(() => { + return { + listen: jest.fn(() => Promise.reject('reason')) + }; }); - it('Should not recall handleListen when no server exists', done => { - const appReturn = { - use: () => { - } - }; - express.mockImplementationOnce(() => appReturn); - const mockListen = jest.fn(); - HandlerListener.mockImplementationOnce(() => { - return { - listen: mockListen - }; - }); + new HttpContainer(123, false).acquire().catch(err => { + expect(err).toEqual('reason'); + done(); + }); + }); - const httpContainer = new HttpContainer(123, false); - httpContainer.acquire().then(() => { - httpContainer.acquire().then(() => { + it('Should not recall handleListen when no server exists', done => { + const appReturn = { + use: () => {} + }; + // @ts-expect-error + express.mockImplementationOnce(() => appReturn); + const mockListen = jest.fn(); + // @ts-expect-error + HandlerListener.mockImplementationOnce(() => { + return { + listen: mockListen + }; + }); - expect(mockListen).toHaveBeenCalledTimes(1); - done(); - }); - }); + const httpContainer = new HttpContainer(123, false); + httpContainer.acquire().then(() => { + httpContainer.acquire().then(() => { + expect(mockListen).toHaveBeenCalledTimes(1); + done(); + }); }); + }); - it('Should return number of existent instances', async () => { - express.mockImplementationOnce(() => { - return { - use: () => { - } - }; - }); - const httpContainer = new HttpContainer(123, false); - expect(await httpContainer.release()).toBe(-1); + it('Should return number of existent instances', async () => { + // @ts-expect-error + express.mockImplementationOnce(() => { + return { + use: () => {} + }; }); + const httpContainer = new HttpContainer(123, false); + expect(await httpContainer.release()).toBe(-1); + }); }); diff --git a/src/pools/http-container.ts b/src/pools/http-container.ts index 40c6682d..f0a2b5ea 100644 --- a/src/pools/http-container.ts +++ b/src/pools/http-container.ts @@ -1,94 +1,93 @@ -import {Logger} from '../loggers/logger'; +import { Logger } from '../loggers/logger'; import express from 'express'; import https from 'https'; -import http, {Server} from 'http'; -import {HandlerListener} from '../handlers/handler-listener'; +import http, { Server } from 'http'; +import { HandlerListener } from '../handlers/handler-listener'; import * as core from 'express-serve-static-core'; export class HttpContainer { - private readonly port: number; - private readonly app: core.Express; - private server: Server; - private counter: number = 0; - private sockets: Set = new Set(); - private listenPromise?: Promise; - private closePromise?: Promise; + private readonly port: number; + private readonly app: core.Express; + private server: Server; + private counter: number = 0; + private sockets: Set = new Set(); + private listenPromise?: Promise; + private closePromise?: Promise; - public constructor(port: number, credentials?: any) { - this.port = port; - this.app = this.createApp(); - this.server = this.createServer(credentials); - this.handleNewSocketConnections(); - } - - public async acquire(): Promise { - Logger.debug(`Acquiring container ${this.port} (${this.counter})`); - ++this.counter; - if (this.counter == 1) { - if (this.closePromise) { - await this.closePromise; - } - this.listenPromise = new HandlerListener(this.server).listen(this.port); - } - await this.listenPromise; - Logger.trace(`container acquired ${this.port} (${this.counter})`); - return this.app; - } + public constructor(port: number, credentials?: any) { + this.port = port; + this.app = this.createApp(); + this.server = this.createServer(credentials); + this.handleNewSocketConnections(); + } - public async release(): Promise { - --this.counter; - Logger.trace(`Releasing container ${this.port} (${this.counter})`); - if (this.counter == 0) { - Logger.trace(`Closing container ${this.port}`); - this.closePromise = new Promise(((resolve, reject) => { - this.sockets.forEach((socket: any) => socket.destroy()); - this.server.close((err: any) => { - if (err) { - reject(`Error closing server ${this.port}: ${err}`); - } - Logger.trace(`Container ${this.port} is closed`); - resolve(); - }); - })); - } else { - this.closePromise = Promise.resolve(); - } + public async acquire(): Promise { + Logger.trace(`Acquiring container ${this.port} (${this.counter})`); + ++this.counter; + if (this.counter == 1) { + if (this.closePromise) { await this.closePromise; - return this.counter; + } + this.listenPromise = new HandlerListener(this.server).listen(this.port); } + await this.listenPromise; + Logger.trace(`container acquired ${this.port} (${this.counter})`); + return this.app; + } - private handleNewSocketConnections(): void { - this.server.on('connection', (socket: any) => { - Logger.trace(`Container ${this.port} got a new connection`); - this.sockets.add(socket); - socket.on('close', () => { - this.sockets.delete(socket); - }); + public async release(): Promise { + --this.counter; + Logger.trace(`Releasing container ${this.port} (${this.counter})`); + if (this.counter == 0) { + Logger.trace(`Closing container ${this.port}`); + this.closePromise = new Promise((resolve, reject) => { + this.sockets.forEach((socket: any) => socket.destroy()); + this.server.close((err: any) => { + if (err) { + reject(`Error closing server ${this.port}: ${err}`); + } + Logger.trace(`Container ${this.port} is closed`); + resolve(0); }); + }); + } else { + this.closePromise = Promise.resolve(); } + await this.closePromise; + return this.counter; + } - private createServer(credentials?: any): Server { - if (credentials) { - return https.createServer(credentials, this.app); - } - return http.createServer(this.app); - } + private handleNewSocketConnections(): void { + this.server.on('connection', (socket: any) => { + Logger.trace(`Container ${this.port} got a new connection`); + this.sockets.add(socket); + socket.on('close', () => { + this.sockets.delete(socket); + }); + }); + } - private createApp(): any { - const app = express(); - app.use((req: any, res: any, next: any) => { - req.setEncoding('utf8'); - req.rawBody = ''; - req.on('data', (chunk: any) => { - req.rawBody += chunk; - }); - req.on('end', () => { - next(); - }); - res.header('Access-Control-Allow-Origin', '*'); - res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept'); - }); - return app; + private createServer(credentials?: any): Server { + if (credentials) { + return https.createServer(credentials, this.app); } + return http.createServer(this.app); + } + private createApp(): core.Express { + const app = express(); + app.use((req: any, res: any, next: any) => { + req.setEncoding('utf8'); + req.rawBody = ''; + req.on('data', (chunk: any) => { + req.rawBody += chunk; + }); + req.on('end', () => { + next(); + }); + res.header('Access-Control-Allow-Origin', '*'); + res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept'); + }); + return app; + } } diff --git a/src/pools/http-requester.test.ts b/src/pools/http-requester.test.ts deleted file mode 100644 index 945cddf4..00000000 --- a/src/pools/http-requester.test.ts +++ /dev/null @@ -1,43 +0,0 @@ -import request from 'request'; -import {HttpRequester} from "./http-requester"; - -jest.mock("request"); - -describe('HttpRequester', () => { - - it('Should resolve request result', done => { - const url = 'url'; - const method = 'method'; - const headers = {key: 'value'}; - const body = { - body: true - }; - const timeout = 100; - - const requestMock = jest.fn((options, cb) => { - expect(options.url).toBe(url); - expect(options.method).toBe(method); - expect(options.timeout).toBe(timeout); - cb(undefined, 'response'); - }); - request.mockImplementationOnce(requestMock); - new HttpRequester(url, method, headers, body, timeout).request().then((result) => { - expect(result).toBe('response'); - done(); - }) - }); - - it('Should reject request result', done => { - const requestMock = jest.fn((options, cb) => { - expect(options.headers).toEqual({"Content-Length": 0}); - expect(options.timeout).toBe(3000); - cb('error') - }); - request.mockImplementationOnce(requestMock); - new HttpRequester('', '', undefined, '').request().catch((result) => { - expect(result).toBe("Http request error: error"); - done(); - }) - }); - -}); \ No newline at end of file diff --git a/src/pools/http-requester.ts b/src/pools/http-requester.ts deleted file mode 100644 index 7c9216b1..00000000 --- a/src/pools/http-requester.ts +++ /dev/null @@ -1,74 +0,0 @@ -import request from 'request'; -import {Logger} from '../loggers/logger'; - -export class HttpRequester { - private readonly url: string; - private readonly method: string; - private readonly headers: any; - private readonly timeout: number; - private body: any; - - constructor(url: string, method: string, headers: any = {}, body: any, timeout: number = 3000) { - this.url = url; - this.method = method; - this.headers = headers; - this.body = body; - this.timeout = timeout; - } - - public request(): Promise { - return new Promise((resolve, reject) => { - Logger.info(`Hitting (${this.method.toUpperCase()}) - ${this.url}`); - const options = this.createOptions(); - request(options, - (error: any, response: any) => { - if (error) { - reject('Http request error: ' + error); - } else { - resolve(response); - } - }); - }); - } - - private createOptions() { - let options: any = { - url: this.url, - method: this.method, - timeout: this.timeout, - headers: this.headers, - rejectUnauthorized: false - }; - options.data = options.body = this.handleObjectPayload(); - if (this.method.toUpperCase() != 'GET') { - options.headers['Content-Length'] = options.headers['Content-Length'] || this.setContentLength(options.data); - } - return options; - } - - private setContentLength(value: string): number { - if (Buffer.isBuffer(value)) { - return value.length; - } else { - return Buffer.from(value, 'utf8').byteLength; - } - } - - private handleObjectPayload(): string | undefined { - if (this.method.toUpperCase() == 'GET') { - return undefined; - } - try { - JSON.parse(this.body); - return this.body; - } - catch (exc) { - //do nothing - } - if (typeof(this.body) != 'string') { - this.body = JSON.stringify(this.body); - } - - return this.body; - } -} diff --git a/src/protocols/actuator-protocol.ts b/src/protocols/actuator-protocol.ts new file mode 100644 index 00000000..80cb2b02 --- /dev/null +++ b/src/protocols/actuator-protocol.ts @@ -0,0 +1,21 @@ +import { Protocol, ProtocolType } from './protocol'; +import { ActuatorModel } from '../models/inputs/actuator-model'; +import { Actuator } from '../actuators/actuator'; +import { ProtocolDocumentation } from './protocol-documentation'; + +export class ActuatorProtocol extends Protocol { + private readonly createFunction: (actuatorModel: ActuatorModel) => Actuator; + + public constructor( + name: string, + createFunction: (actuatorModel: ActuatorModel) => Actuator, + documentation?: ProtocolDocumentation + ) { + super(name, ProtocolType.ACTUATOR, documentation); + this.createFunction = createFunction; + } + + public create(actuator: ActuatorModel): Actuator { + return this.createFunction(actuator); + } +} diff --git a/src/protocols/protocol-documentation.ts b/src/protocols/protocol-documentation.ts index 955f6791..744238cc 100644 --- a/src/protocols/protocol-documentation.ts +++ b/src/protocols/protocol-documentation.ts @@ -1,39 +1,39 @@ export interface ProtocolDocumentation { - description?: string; - homepage?: string; - libraryHomepage?: string; - schema?: ProtocolSchema; + description?: string; + homepage?: string; + libraryHomepage?: string; + schema?: ProtocolSchema; } export interface ProtocolSchema { - attributes?: { - [attributeName: string]: ProtocolAttribute; - }; - hooks?: { - [hookName: string]: ProtocolEventHook - }; + attributes?: { + [attributeName: string]: ProtocolAttribute; + }; + hooks?: { + [hookName: string]: ProtocolEventHook; + }; } export interface ProtocolEventHook { - description?: string; - arguments?: { - [argumentName: string]: { - description?: string; - } + description?: string; + arguments?: { + [argumentName: string]: { + description?: string; }; + }; } interface ProtocolAttributeObject { - [attributeName: string]: ProtocolAttribute; + [attributeName: string]: ProtocolAttribute; } export interface ProtocolAttribute { - type: 'string' | 'number' | 'text' | 'boolean' | ProtocolAttributeObject | 'object' | 'any' | 'int' | 'list'; - description?: string; - required?: boolean; - defaultValue?: any; - suffix?: string; - label?: string; - example?: any; - listValues?: any[]; + type: 'string' | 'number' | 'text' | 'boolean' | ProtocolAttributeObject | 'object' | 'any' | 'int' | 'list'; + description?: string; + required?: boolean; + defaultValue?: any; + suffix?: string; + label?: string; + example?: any; + listValues?: any[]; } diff --git a/src/protocols/protocol.test.ts b/src/protocols/protocol.test.ts index b6d2aaca..b099c678 100644 --- a/src/protocols/protocol.test.ts +++ b/src/protocols/protocol.test.ts @@ -1,190 +1,227 @@ -import {Protocol, ProtocolType} from './protocol'; -import {ProtocolDocumentation} from './protocol-documentation'; +import { Protocol, ProtocolType } from './protocol'; +import { ProtocolDocumentation } from './protocol-documentation'; describe('Protocol', () => { + it('getName', () => { + const name = 'gui'; + const match = createProtocol(name, undefined).getName(); + expect(match).toBe(name); + }); - it('getName', () => { - const name = 'gui'; - const match = new Protocol(name, undefined).getName(); - expect(match).toBe(name); - }); - - it('namesMatchExactly name', () => { - const match = new Protocol('gui', undefined).matches('gui'); - expect(match).toBeTruthy(); - }); + it('namesMatchExactly name', () => { + const match = createProtocol('gui', undefined).matches('gui'); + expect(match).toBeTruthy(); + }); - it('should ignore case going', () => { - const match = new Protocol('gui', undefined).matches('GUI'); - expect(match).toBeTruthy(); - }); + it('should ignore case going', () => { + const match = createProtocol('gui', undefined).matches('GUI'); + expect(match).toBeTruthy(); + }); - it('should ignore case coming', () => { - const match = new Protocol('GUI', undefined).matches('gui'); - expect(match).toBeTruthy(); - }); + it('should ignore case coming', () => { + const match = createProtocol('GUI', undefined).matches('gui'); + expect(match).toBeTruthy(); + }); - it('namesMatch alternative', () => { - const match = new Protocol('', undefined,).addAlternativeName('gui').matches('gui'); - expect(match).toBeTruthy(); - }); + it('namesMatch alternative', () => { + const match = createProtocol('', undefined).addAlternativeName('gui').matches('gui'); + expect(match).toBeTruthy(); + }); - it('names dont Match alternative', () => { - const match = new Protocol('', undefined,).addAlternativeName('one', 'two').matches('gui', 0); - expect(match).toBeFalsy(); - }); + it('names dont Match alternative', () => { + const match = createProtocol('', undefined).addAlternativeName('one', 'two').matches('gui'); + expect(match).toBeFalsy(); + }); - it('alternative names are unique', () => { - const match = new Protocol('one', undefined).addAlternativeName('one', 'two').addAlternativeName('two', 'three'); - // @ts-ignore - expect(match.alternativeNames).toEqual(['one', 'two', 'three']); - }); + it('alternative names are unique', () => { + const match = createProtocol('one', undefined).addAlternativeName('one', 'two').addAlternativeName('two', 'three'); + // @ts-ignore + expect(match.alternativeNames).toEqual(['one', 'two', 'three']); + }); - it('isLibraryInstalled', () => { - // @ts-ignore - const available = new Protocol('', undefined).setLibrary('express').library.installed; - expect(available).toBeTruthy(); - }); + it('isLibraryInstalled', () => { + // @ts-ignore + const available = createProtocol('', undefined).setLibrary('express').library.installed; + expect(available).toBeTruthy(); + }); - it('isLibraryInstalled false', () => { - const available = new Protocol('', undefined).setLibrary('zero-mq-not-defined-at-least-I-hope').isLibraryInstalled(); - expect(available).toBeFalsy(); - }); + it('isLibraryInstalled false', () => { + const available = createProtocol('', undefined) + .setLibrary('zero-mq-not-defined-at-least-I-hope') + // @ts-expect-error + .isLibraryInstalled(); + expect(available).toBeFalsy(); + }); - it('get deep property', () => { - const doc: ProtocolDocumentation = { - homepage: 'homepage', - description: 'description', - libraryHomepage: 'libraryHomepage', - schema: { - attributes: { - attributeName: { - description: 'AttributeDescription', - type: 'string' - } - }, - hooks: { - onEvent: { - arguments: {'hookArg': {}} - } - } - } - }; - // @ts-ignore - const property = new Protocol('protocol', ProtocolType.SUBSCRIPTION, doc) - .addAlternativeName('alternativeName') - .setLibrary('express') - .getDescription(); - expect(property).toEqual({ - 'description': 'description', 'homepage': 'homepage', 'libraryHomepage': 'libraryHomepage', 'name': 'protocol', 'schema': { - 'attributes': { - 'attributeName': {'description': 'AttributeDescription', 'type': 'string'}, - 'type': { - 'description': 'Protocol identifier', - 'required': true, - 'type': 'string' - }, - 'avoid': { - 'defaultValue': false, - 'description': 'Defines if the subscription should be avoided', - 'required': false, - 'type': 'boolean' - }, - 'ignore': { - 'defaultValue': false, - 'description': 'Defines if the component should be ignored', - 'required': false, - 'type': 'boolean' - }, - 'name': {'description': 'Defines the component name', 'required': false, 'type': 'string'}, - 'timeout': { - 'defaultValue': 3000, - 'description': 'Defines the subscription time out', - 'required': false, - 'suffix': 'ms', - 'type': 'int' - } - }, - 'hooks': { - 'onEvent': { - 'arguments': { - 'hookArg': {}, - 'elapsedTime': { - 'description': 'Number of milliseconds since the instantiation of the component' - }, - 'this': { - 'description': 'Pointer to the component' - } - } - }, - 'onFinish': { - 'arguments': { - 'elapsedTime': {'description': 'Number of milliseconds since the instantiation of the component'}, - 'this': {'description': 'Pointer to the component'} - }, 'description': 'Executed when the component is about to finish' - }, - 'onInit': { - 'arguments': { - 'elapsedTime': {'description': 'Number of milliseconds since the instantiation of the component'}, - 'this': {'description': 'Pointer to the component'} - }, 'description': 'Executed as soon as the component is initialized' - } - } + it('get deep property', () => { + const doc: ProtocolDocumentation = { + homepage: 'homepage', + description: 'description', + libraryHomepage: 'libraryHomepage', + schema: { + attributes: { + attributeName: { + description: 'AttributeDescription', + type: 'string' + } + }, + hooks: { + onEvent: { + arguments: { hookArg: {} } + } + } + } + }; + const property = createProtocol('protocol', ProtocolType.SENSOR, doc) + .addAlternativeName('alternativeName') + .setLibrary('express') + .getDescription(); + expect(property).toEqual({ + description: 'description', + homepage: 'homepage', + libraryHomepage: 'libraryHomepage', + name: 'protocol', + schema: { + attributes: { + attributeName: { + description: 'AttributeDescription', + type: 'string' + }, + type: { + description: 'Protocol identifier', + required: true, + type: 'string' + }, + avoid: { + defaultValue: false, + description: 'Defines if the sensor should be avoided', + required: false, + type: 'boolean' + }, + ignore: { + defaultValue: false, + description: 'Defines if the component should be ignored', + required: false, + type: 'boolean' + }, + name: { + description: 'Defines the component name', + required: false, + type: 'string' + }, + timeout: { + defaultValue: 3000, + description: 'Defines the sensor time out', + required: false, + suffix: 'ms', + type: 'int' + } + }, + hooks: { + onEvent: { + arguments: { + hookArg: {}, + elapsedTime: { + description: 'Number of milliseconds since the instantiation of the component' + }, + this: { + description: 'Pointer to the component' + } } - }); + }, + onFinish: { + arguments: { + elapsedTime: { + description: 'Number of milliseconds since the instantiation of the component' + }, + this: { description: 'Pointer to the component' } + }, + description: 'Executed when the component is about to finish' + }, + onInit: { + arguments: { + elapsedTime: { + description: 'Number of milliseconds since the instantiation of the component' + }, + this: { description: 'Pointer to the component' } + }, + description: 'Executed as soon as the component is initialized' + } + } + } }); + }); - it('get deep with nothing', () => { - // @ts-ignore - const property = new Protocol('protocol', ProtocolType.SUBSCRIPTION, {}) - .addAlternativeName('alternativeName') - .setLibrary('express') - .getDescription(); - expect(property).toEqual({ - 'name': 'protocol', 'schema': { - 'attributes': { - 'type': { - 'description': 'Protocol identifier', - 'required': true, - 'type': 'string' - }, - 'avoid': { - 'defaultValue': false, - 'description': 'Defines if the subscription should be avoided', - 'required': false, - 'type': 'boolean' - }, - 'ignore': { - 'defaultValue': false, - 'description': 'Defines if the component should be ignored', - 'required': false, - 'type': 'boolean' - }, - 'name': {'description': 'Defines the component name', 'required': false, 'type': 'string'}, - 'timeout': { - 'defaultValue': 3000, - 'description': 'Defines the subscription time out', - 'required': false, - 'suffix': 'ms', - 'type': 'int' - } - }, - 'hooks': { - 'onFinish': { - 'arguments': { - 'elapsedTime': {'description': 'Number of milliseconds since the instantiation of the component'}, - 'this': {'description': 'Pointer to the component'} - }, 'description': 'Executed when the component is about to finish' - }, - 'onInit': { - 'arguments': { - 'elapsedTime': {'description': 'Number of milliseconds since the instantiation of the component'}, - 'this': {'description': 'Pointer to the component'} - }, 'description': 'Executed as soon as the component is initialized' - } - } - } - }); + it('get deep with nothing', () => { + // @ts-ignore + const property = createProtocol('protocol', ProtocolType.SENSOR, {}) + .addAlternativeName('alternativeName') + .setLibrary('express') + .getDescription(); + expect(property).toEqual({ + name: 'protocol', + schema: { + attributes: { + type: { + description: 'Protocol identifier', + required: true, + type: 'string' + }, + avoid: { + defaultValue: false, + description: 'Defines if the sensor should be avoided', + required: false, + type: 'boolean' + }, + ignore: { + defaultValue: false, + description: 'Defines if the component should be ignored', + required: false, + type: 'boolean' + }, + name: { + description: 'Defines the component name', + required: false, + type: 'string' + }, + timeout: { + defaultValue: 3000, + description: 'Defines the sensor time out', + required: false, + suffix: 'ms', + type: 'int' + } + }, + hooks: { + onFinish: { + arguments: { + elapsedTime: { + description: 'Number of milliseconds since the instantiation of the component' + }, + this: { description: 'Pointer to the component' } + }, + description: 'Executed when the component is about to finish' + }, + onInit: { + arguments: { + elapsedTime: { + description: 'Number of milliseconds since the instantiation of the component' + }, + this: { description: 'Pointer to the component' } + }, + description: 'Executed as soon as the component is initialized' + } + } + } }); + }); + const createProtocol = ( + protocolName: string, + type?: ProtocolType, + protocolDocumentation?: ProtocolDocumentation + ): Protocol => { + //@ts-expect-error + return new Protocol(protocolName, type, protocolDocumentation); + }; }); diff --git a/src/protocols/protocol.ts b/src/protocols/protocol.ts index 7af5326b..e4518c97 100644 --- a/src/protocols/protocol.ts +++ b/src/protocols/protocol.ts @@ -1,178 +1,178 @@ -import {Logger} from '../loggers/logger'; -import {DefaultHookEvents} from '../models/events/event'; -import {ProtocolDocumentation} from './protocol-documentation'; -import {SubscriptionReporter} from '../reporters/subscription/subscription-reporter'; +import { Logger } from '../loggers/logger'; +import { DefaultHookEvents } from '../models/events/event'; +import { ProtocolDocumentation } from './protocol-documentation'; +import { SensorReporter } from '../reporters/sensor/sensor-reporter'; type Library = { - name: string; - installed: boolean; + name: string; + installed: boolean; }; export enum ProtocolType { - PUBLISHER, - SUBSCRIPTION + ACTUATOR, + SENSOR } export abstract class Protocol { - private readonly type: ProtocolType; - private readonly name: string; - private readonly documentation: ProtocolDocumentation; - private alternativeNames?: string[]; - private library?: Library; - - protected constructor(name: string, type: ProtocolType, protocolDocumentation?: ProtocolDocumentation) { - this.name = name; - this.type = type; - this.documentation = this.createDefaultDocumentation(); - if (protocolDocumentation) { - this.documentation = protocolDocumentation; - } - this.addDefaultValues(type); + private readonly type: ProtocolType; + private readonly name: string; + private readonly documentation: ProtocolDocumentation; + private alternativeNames?: string[]; + private library?: Library; + + protected constructor(name: string, type: ProtocolType, protocolDocumentation?: ProtocolDocumentation) { + this.name = name; + this.type = type; + this.documentation = this.createDefaultDocumentation(); + if (protocolDocumentation) { + this.documentation = protocolDocumentation; } + this.addDefaultValues(type); + } - private addDefaultValues(type: ProtocolType) { - if (!this.documentation.schema) { - this.documentation.schema = {}; - } - if (!this.documentation.schema.hooks) { - this.documentation.schema.hooks = {}; - } - this.documentation.schema.hooks = Object.assign({}, this.documentation.schema.hooks, { - [DefaultHookEvents.ON_INIT]: { - arguments: {}, - description: 'Executed as soon as the component is initialized', - }, - [DefaultHookEvents.ON_FINISH]: { - arguments: {}, - description: 'Executed when the component is about to finish', - } - }); - Object.keys(this.documentation.schema.hooks).forEach((key: string) => { - const hookArguments = this.documentation.schema!.hooks![key].arguments; - this.documentation.schema!.hooks![key].arguments = { - ...hookArguments, - elapsedTime: { - description: 'Number of milliseconds since the instantiation of the component' - }, - this: { - description: 'Pointer to the component' - } - }; - }); - if (!this.documentation.schema.attributes) { - this.documentation.schema.attributes = {}; - } - this.documentation.schema.attributes.ignore = { - type: 'boolean', - required: false, - defaultValue: false, - description: 'Defines if the component should be ignored' - }; - this.documentation.schema.attributes.name = { - type: 'string', - required: false, - description: 'Defines the component name' - }; - this.documentation.schema.attributes.type = { - type: 'string', - required: true, - description: 'Protocol identifier' - }; - if (type == ProtocolType.SUBSCRIPTION) { - this.documentation.schema.attributes.timeout = { - type: 'int', - suffix: 'ms', - required: false, - defaultValue: SubscriptionReporter.DEFAULT_TIMEOUT, - description: 'Defines the subscription time out' - }; - this.documentation.schema.attributes.avoid = { - type: 'boolean', - required: false, - defaultValue: false, - description: 'Defines if the subscription should be avoided' - }; - } + private addDefaultValues(type: ProtocolType) { + if (!this.documentation.schema) { + this.documentation.schema = {}; } - - private createDefaultDocumentation() { - return { - schema: { - attributes: {}, - hooks: { - [DefaultHookEvents.ON_INIT]: { - arguments: {} - }, - [DefaultHookEvents.ON_FINISH]: { - arguments: {} - } - } - } - }; + if (!this.documentation.schema.hooks) { + this.documentation.schema.hooks = {}; } - - public isSubscription(): boolean { - return this.type === ProtocolType.SUBSCRIPTION; + this.documentation.schema.hooks = Object.assign({}, this.documentation.schema.hooks, { + [DefaultHookEvents.ON_INIT]: { + arguments: {}, + description: 'Executed as soon as the component is initialized' + }, + [DefaultHookEvents.ON_FINISH]: { + arguments: {}, + description: 'Executed when the component is about to finish' + } + }); + Object.keys(this.documentation.schema.hooks).forEach((key: string) => { + const hookArguments = this.documentation.schema!.hooks![key].arguments; + this.documentation.schema!.hooks![key].arguments = { + ...hookArguments, + elapsedTime: { + description: 'Number of milliseconds since the instantiation of the component' + }, + this: { + description: 'Pointer to the component' + } + }; + }); + if (!this.documentation.schema.attributes) { + this.documentation.schema.attributes = {}; } - - public isPublisher(): boolean { - return this.type === ProtocolType.PUBLISHER; + this.documentation.schema.attributes.ignore = { + type: 'boolean', + required: false, + defaultValue: false, + description: 'Defines if the component should be ignored' + }; + this.documentation.schema.attributes.name = { + type: 'string', + required: false, + description: 'Defines the component name' + }; + this.documentation.schema.attributes.type = { + type: 'string', + required: true, + description: 'Protocol identifier' + }; + if (type == ProtocolType.SENSOR) { + this.documentation.schema.attributes.timeout = { + type: 'int', + suffix: 'ms', + required: false, + defaultValue: SensorReporter.DEFAULT_TIMEOUT, + description: 'Defines the sensor time out' + }; + this.documentation.schema.attributes.avoid = { + type: 'boolean', + required: false, + defaultValue: false, + description: 'Defines if the sensor should be avoided' + }; } + } + + private createDefaultDocumentation() { + return { + schema: { + attributes: {}, + hooks: { + [DefaultHookEvents.ON_INIT]: { + arguments: {} + }, + [DefaultHookEvents.ON_FINISH]: { + arguments: {} + } + } + } + }; + } - public getName(): string { - return this.name; - } + public isSensor(): boolean { + return this.type === ProtocolType.SENSOR; + } - public getDescription(): {} { - if (Object.keys(this.documentation).length > 0) { - return {...this.documentation, name: this.name}; - } - return {name: this.name}; - } + public isActuator(): boolean { + return this.type === ProtocolType.ACTUATOR; + } - public addAlternativeName(...alternativeNames: string[]): Protocol { - let uniqueAlternativeNames; - if (this.alternativeNames) { - uniqueAlternativeNames = new Set(this.alternativeNames.concat(alternativeNames)); - } else { - uniqueAlternativeNames = new Set(alternativeNames); - } - this.alternativeNames = Array.from(uniqueAlternativeNames); - return this; - } + public getName(): string { + return this.name; + } - public setLibrary(libraryName: string): Protocol { - this.library = this.createLibrary(libraryName); - return this; + public getDescription(): {} { + if (Object.keys(this.documentation).length > 0) { + return { ...this.documentation, name: this.name }; } - - public matches(type: string): boolean { - if (typeof type === 'string') { - try { - return [this.name].concat(this.alternativeNames || []) - .some((name: string) => name.toUpperCase().includes(type.toUpperCase())); - } catch (exc) { - Logger.warning(`Error comparing protocols with given type '${type}': ${exc}`); - } - } - return false; + return { name: this.name }; + } + + public addAlternativeName(...alternativeNames: string[]): Protocol { + let uniqueAlternativeNames; + if (this.alternativeNames) { + uniqueAlternativeNames = new Set(this.alternativeNames.concat(alternativeNames)); + } else { + uniqueAlternativeNames = new Set(alternativeNames); } - - private isLibraryInstalled(libraryName: string): boolean { - try { - require.resolve(libraryName); - return true; - } catch (e) { - /* do nothing */ - } - return false; + this.alternativeNames = Array.from(uniqueAlternativeNames); + return this; + } + + public setLibrary(libraryName: string): Protocol { + this.library = this.createLibrary(libraryName); + return this; + } + + public matches(type: string): boolean { + if (typeof type === 'string') { + try { + return [this.name] + .concat(this.alternativeNames || []) + .some((name: string) => name.toUpperCase().includes(type.toUpperCase())); + } catch (exc) { + Logger.warning(`Error comparing protocols with given type '${type}': ${exc}`); + } } - - private createLibrary(name: string): Library | undefined { - return { - name: name, - installed: this.isLibraryInstalled(name) - }; + return false; + } + + private isLibraryInstalled(libraryName: string): boolean { + try { + require.resolve(libraryName); + return true; + } catch (e) { + /* do nothing */ } - + return false; + } + + private createLibrary(name: string): Library | undefined { + return { + name: name, + installed: this.isLibraryInstalled(name) + }; + } } diff --git a/src/protocols/publisher-protocol.ts b/src/protocols/publisher-protocol.ts deleted file mode 100644 index 516b5731..00000000 --- a/src/protocols/publisher-protocol.ts +++ /dev/null @@ -1,19 +0,0 @@ -import {Protocol, ProtocolType} from './protocol'; -import {PublisherModel} from '../models/inputs/publisher-model'; -import {Publisher} from '../publishers/publisher'; -import {ProtocolDocumentation} from './protocol-documentation'; - -export class PublisherProtocol extends Protocol { - private readonly createFunction: (publisherModel: PublisherModel) => Publisher; - - public constructor(name: string, - createFunction: (publisherModel: PublisherModel) => Publisher, - documentation?: ProtocolDocumentation) { - super(name, ProtocolType.PUBLISHER, documentation); - this.createFunction = createFunction; - } - - public create(publisher: PublisherModel): Publisher { - return this.createFunction(publisher); - } -} diff --git a/src/protocols/sensor-protocol.ts b/src/protocols/sensor-protocol.ts new file mode 100644 index 00000000..1a9feaf0 --- /dev/null +++ b/src/protocols/sensor-protocol.ts @@ -0,0 +1,21 @@ +import { Protocol, ProtocolType } from './protocol'; +import { SensorModel } from '../models/inputs/sensor-model'; +import { Sensor } from '../sensors/sensor'; +import { ProtocolDocumentation } from './protocol-documentation'; + +export class SensorProtocol extends Protocol { + private readonly createFunction: (sensorModel: SensorModel) => Sensor; + + public constructor( + name: string, + createFunction: (sensorModel: SensorModel) => Sensor, + documentation?: ProtocolDocumentation + ) { + super(name, ProtocolType.SENSOR, documentation); + this.createFunction = createFunction; + } + + public create(sensor: SensorModel): Sensor { + return this.createFunction(sensor); + } +} diff --git a/src/protocols/subscription-protocol.ts b/src/protocols/subscription-protocol.ts deleted file mode 100644 index 1b5f8c7c..00000000 --- a/src/protocols/subscription-protocol.ts +++ /dev/null @@ -1,20 +0,0 @@ -import {Protocol, ProtocolType} from './protocol'; -import {SubscriptionModel} from '../models/inputs/subscription-model'; -import {Subscription} from '../subscriptions/subscription'; -import {ProtocolDocumentation} from './protocol-documentation'; - -export class SubscriptionProtocol extends Protocol { - private readonly createFunction: (subscriptionModel: SubscriptionModel) => Subscription; - - public constructor(name: string, - createFunction: (subscriptionModel: SubscriptionModel) => Subscription, - documentation?: ProtocolDocumentation) { - super(name, ProtocolType.SUBSCRIPTION, documentation); - this.createFunction = createFunction; - } - - public create(subscription: SubscriptionModel): Subscription { - return this.createFunction(subscription); - } - -} diff --git a/src/publishers/custom-publisher.ts b/src/publishers/custom-publisher.ts deleted file mode 100644 index 85ddf3eb..00000000 --- a/src/publishers/custom-publisher.ts +++ /dev/null @@ -1,34 +0,0 @@ -import {Publisher} from './publisher'; -import {Store} from '../configurations/store'; -import {Logger} from '../loggers/logger'; -import {PublisherModel} from '../models/inputs/publisher-model'; -import * as fs from 'fs'; -import requireFromString from 'require-from-string'; -import {MainInstance} from '../plugins/main-instance'; -import {PublisherProtocol} from '../protocols/publisher-protocol'; - -class CustomPublisher extends Publisher { - - constructor(model: PublisherModel) { - super(model); - this['model'] = model; - } - - public async publish(): Promise { - try { - const moduleString: string = fs.readFileSync(this.module).toString(); - const module = requireFromString(moduleString); - const custom = new module.Publisher(this); - return await custom.publish({store: Store.getData(), logger: Logger}); - } catch (err) { - Logger.error(`Error loading module '${this.module}': ${err}`); - } - } -} - -export function entryPoint(mainInstance: MainInstance): void { - const protocol = new PublisherProtocol('custom', - (publisherModel: PublisherModel) => new CustomPublisher(publisherModel)); - - mainInstance.protocolManager.addProtocol(protocol); -} diff --git a/src/publishers/file-publisher.ts b/src/publishers/file-publisher.ts deleted file mode 100644 index cd0668ca..00000000 --- a/src/publishers/file-publisher.ts +++ /dev/null @@ -1,76 +0,0 @@ -import {Publisher} from './publisher'; -import {PublisherModel} from '../models/inputs/publisher-model'; -import {IdGenerator} from '../strings/id-generator'; -import * as fs from 'fs'; -import {MainInstance} from '../plugins/main-instance'; -import {PublisherProtocol} from '../protocols/publisher-protocol'; -import {DateController} from '../timers/date-controller'; - -class FilePublisher extends Publisher { - - constructor(publisherAttributes: PublisherModel) { - super(publisherAttributes); - this['filenameExtension'] = this.filenameExtension || 'enq'; - } - - public publish(): Promise { - const filename = this.getFileName(); - let value = this.payload; - - if (typeof (value) === 'object') { - value = JSON.stringify(value, null, 2); - } - - fs.writeFileSync(filename, value); - return Promise.resolve(); - } - - private getFileName() { - if (this.filename) { - return this.filename; - } - return this.createFileName(); - } - - private createFileName() { - let filename = this.filenamePrefix + new IdGenerator(this.payload || new DateController().getStringOnlyNumbers()).generateId(); - const needsToInsertDot = filename.lastIndexOf('.') == -1 && this.filenameExtension.lastIndexOf('.') == -1; - if (needsToInsertDot) { - filename += '.'; - } - return filename + this.filenameExtension; - } -} - -export function entryPoint(mainInstance: MainInstance): void { - const protocol = new PublisherProtocol('file', - (publisherModel: PublisherModel) => new FilePublisher(publisherModel), { - description: 'The file publisher provides an implementation of filesystem writers', - libraryHomepage: 'https://nodejs.org/api/fs.html', - schema: { - attributes: { - filenameExtension: { - description: 'Used when there is no file name defined, succeeds the auto generated file name', - required: false, - type: 'string' - }, - filenamePrefix: { - description: 'Used when there is no file name defined, precedes the auto generated file name', - required: false, - type: 'string' - }, - filename: { - description: 'The file name', - required: false, - type: 'string' - }, - payload: { - type: 'text', - required: true - }, - } - } - } - ); - mainInstance.protocolManager.addProtocol(protocol); -} diff --git a/src/publishers/http-publisher.ts b/src/publishers/http-publisher.ts deleted file mode 100644 index 4b54c431..00000000 --- a/src/publishers/http-publisher.ts +++ /dev/null @@ -1,113 +0,0 @@ -import {Publisher} from './publisher'; -import {Logger} from '../loggers/logger'; -import {PublisherModel} from '../models/inputs/publisher-model'; -import {HttpRequester} from '../pools/http-requester'; -import {MainInstance} from '../plugins/main-instance'; -import {PublisherProtocol} from '../protocols/publisher-protocol'; -import {HttpAuthenticationFactory} from '../http-authentications/http-authentication-factory'; - -class HttpPublisher extends Publisher { - - constructor(publish: PublisherModel) { - super(publish); - this['method'] = publish.method || 'get'; - this.payload = this.payload || ''; - this['headers'] = this.headers || {}; - this['timeout'] = this.timeout || 3000; - } - - public async publish(): Promise { - this.insertAuthentication(); - - const response = await new HttpRequester(this.url, - this.method.toLowerCase(), - this.headers, - this.payload, - this.timeout) - .request(); - this.executeHookEvent('onResponseReceived', response); - //NOTE: Deprecation purpose - this.executeHookEvent('onMessageReceived', response); - return this.processResponseToBePrinted(response); - } - - private processResponseToBePrinted(response: any): object { - const toBePrinted: any = { - statusCode: response.statusCode, - headers: response.headers - }; - try { - if (response.body) { - toBePrinted.body = JSON.parse(response.body); - } - } catch (e) { - toBePrinted.body = response.body; - } - return toBePrinted; - } - - private insertAuthentication() { - if (this.authentication) { - const authenticator = new HttpAuthenticationFactory().create(this.authentication); - const authentication = authenticator.generate(); - if (authentication) { - this['headers'] = Object.assign(this.headers, authentication); - } else { - Logger.warning(`No http authentication method was generated from: ${this.authentication}`); - } - } - } -} - -export function entryPoint(mainInstance: MainInstance): void { - const protocol = new PublisherProtocol('http', - (publisherModel: PublisherModel) => new HttpPublisher(publisherModel), - { - description: 'The HTTP publisher provides an implementation of http requisitions', - libraryHomepage: 'https://github.com/request/request', - schema: { - attributes: { - url: { - required: true, - type: 'string', - example: 'https://github.com/enqueuer-land/enqueuer' - }, - method: { - required: false, - type: 'string', - defaultValue: 'GET', - listValues: ['GET', 'POST', 'PATCH', 'PUT', 'OPTIONS', 'HEAD', 'DELETE'] - }, - payload: { - required: true, - type: 'text' - }, - timeout: { - required: false, - type: 'int', - defaultValue: 3000, - suffix: 'ms' - }, - headers: { - description: '', - type: 'object', - defaultValue: {} - }, - }, - hooks: { - onResponseReceived: { - description: 'Hook called when the publisher gets a response from the server', - arguments: { - statusCode: {}, - headers: {}, - body: {}, - } - } - } - } - }) - .addAlternativeName('http-client', 'https', 'https-client') - .setLibrary('request'); - - mainInstance.protocolManager.addProtocol(protocol); -} diff --git a/src/publishers/null-publisher.ts b/src/publishers/null-publisher.ts deleted file mode 100644 index a20823ba..00000000 --- a/src/publishers/null-publisher.ts +++ /dev/null @@ -1,13 +0,0 @@ -import {Publisher} from './publisher'; -import {PublisherModel} from '../models/inputs/publisher-model'; - -export class NullPublisher extends Publisher { - - public constructor(publisherModel: PublisherModel) { - super(publisherModel); - } - - public publish(): Promise { - return Promise.reject(`Undefined publisher: '${this.type}'`); - } -} diff --git a/src/publishers/publisher.ts b/src/publishers/publisher.ts deleted file mode 100644 index 86118295..00000000 --- a/src/publishers/publisher.ts +++ /dev/null @@ -1,40 +0,0 @@ -import {PublisherModel} from '../models/inputs/publisher-model'; -import {Event} from '../models/events/event'; -import {Logger} from '../loggers/logger'; - -export abstract class Publisher { - public type: string; - public payload: any; - public name: string; - public onMessageReceived?: Event; - public onInit?: Event; - public onFinish?: Event; - public messageReceived?: any; - public ignore: boolean = false; - - [propName: string]: any; - - protected constructor(publisherAttributes: PublisherModel) { - Object.keys(publisherAttributes).forEach(key => { - this[key] = publisherAttributes[key]; - }); - this.type = publisherAttributes.type; - this.payload = publisherAttributes.payload; - this.name = publisherAttributes.name; - } - - public abstract publish(): Promise; - - public registerHookEventExecutor(hookEventExecutor: (eventName: string, args: any) => void) { - this['hookEventExecutor'] = hookEventExecutor; - } - - protected executeHookEvent(hookName: string, args: any = {}) { - if (this['hookEventExecutor']) { - this['hookEventExecutor'](hookName, args); - } else { - Logger.warning(`Hook event executor not registered in publisher`); - } - } - -} diff --git a/src/publishers/standard-output-publisher.ts b/src/publishers/standard-output-publisher.ts deleted file mode 100644 index 4f31f3e0..00000000 --- a/src/publishers/standard-output-publisher.ts +++ /dev/null @@ -1,36 +0,0 @@ -import {Publisher} from './publisher'; -import {PublisherModel} from '../models/inputs/publisher-model'; -import {MainInstance} from '../plugins/main-instance'; -import {PublisherProtocol} from '../protocols/publisher-protocol'; - -class StandardOutputPublisher extends Publisher { - - public constructor(publisherProperties: PublisherModel) { - super(publisherProperties); - } - - public publish(): Promise { - if (typeof (this.payload) === 'object') { - this.payload = JSON.stringify(this.payload, null, 2); - } - console.log(this.payload); - return Promise.resolve(); - } -} - -export function entryPoint(mainInstance: MainInstance): void { - const protocol = new PublisherProtocol('stdout', - (publisherModel: PublisherModel) => new StandardOutputPublisher(publisherModel), { - schema: { - attributes: { - payload: { - type: 'text', - required: true - } - } - } - }) - .addAlternativeName('standard-output'); - - mainInstance.protocolManager.addProtocol(protocol); -} diff --git a/src/publishers/stream-publisher.ts b/src/publishers/stream-publisher.ts deleted file mode 100644 index c1c088b4..00000000 --- a/src/publishers/stream-publisher.ts +++ /dev/null @@ -1,218 +0,0 @@ -import {Publisher} from './publisher'; -import {PublisherModel} from '../models/inputs/publisher-model'; -import * as net from 'net'; -import {Logger} from '../loggers/logger'; -import {Store} from '../configurations/store'; -import * as tls from 'tls'; -import {Timeout} from '../timers/timeout'; -import {MainInstance} from '../plugins/main-instance'; -import {PublisherProtocol} from '../protocols/publisher-protocol'; -import {ProtocolDocumentation} from '../protocols/protocol-documentation'; - -class StreamPublisher extends Publisher { - - private readonly loadedStream: any; - - constructor(publisherAttributes: PublisherModel) { - super(publisherAttributes); - this['timeout'] = this.timeout || 1000; - if (this.loadStream) { - Logger.debug(`Loading ${this.type} client: ${this.loadStream}`); - this.loadedStream = Store.getData()[this.loadStream]; - } - } - - public publish(): Promise { - return new Promise((resolve, reject) => { - if (this.loadStream) { - this.sendReusingStream(resolve, reject); - } else { - this.sendCreatingStream(resolve, reject); - } - - }); - } - - private sendReusingStream(resolve: any, reject: any) { - Logger.info(`${this.type} client is trying to reuse stream ${this.loadStream}`); - if (!this.loadedStream) { - Logger.error(`There is no ${this.type} stream able to be loaded named ${this.loadStream}`); - this.sendCreatingStream(resolve, reject); - } else { - Logger.debug(`Client is reusing ${this.type} stream`); - this.publishData(this.loadedStream, resolve, reject); - } - } - - private sendCreatingStream(resolve: any, reject: any) { - Logger.info(`${this.type} client trying to connect`); - this.createStream() - .then((stream: any) => { - Logger.debug(`${this.type} client connected to: ${this.serverAddress}:${this.port || this.path}`); - this.publishData(stream, resolve, reject); - }).catch(err => { - reject(err); - }); - } - - private createStream(): Promise { - return new Promise((resolve, reject) => { - if ('tcp' === (this.type || '').toLowerCase()) { - this.createTcpStream(resolve, reject); - } else if ('ssl' === (this.type || '').toLowerCase()) { - this.createSslStream(resolve, reject); - } else { - resolve(net.createConnection(this.path)); - } - }); - } - - private createSslStream(resolve: any, reject: any): void { - const stream: any = tls.connect(this.port, this.serverAddress, this.options, () => resolve(stream)); - stream.on('error', (error: any) => { - Logger.error(`${this.type} client error: ${error}`); - reject(error); - }); - } - - private createTcpStream(resolve: any, reject: any): void { - const stream = new net.Socket(); - stream.connect(this.port, this.serverAddress, () => resolve(stream)); - stream.on('error', (error: any) => { - Logger.error(`${this.type} client error: ${error}`); - reject(error); - }); - } - - private publishData(stream: any, resolve: any, reject: any) { - Logger.debug(`${this.type} client publishing`); - stream.once('error', (data: any) => { - this.finalize(stream); - reject(data); - }); - const stringifyPayload = this.stringifyPayload(); - stream.write(stringifyPayload, () => { - Logger.debug(`${this.type} client published`); - this.registerEvents(stream, resolve); - if (this.saveStream) { - Logger.debug(`Persisting publisher stream ${this.saveStream}`); - Store.getData()[this.saveStream] = stream; - } - }); - } - - private registerEvents(stream: any, resolve: any) { - new Timeout(() => { - this.finalize(stream); - Logger.debug(`${this.type} client timed out`); - stream.removeAllListeners('data'); - resolve(); - }).start(this.timeout); - - stream.once('end', () => { - Logger.debug(`${this.type} client ended`); - this.finalize(stream); - resolve(); - }).on('data', (msg: Buffer) => { - Logger.debug(`${this.type} client got data '${msg.toString()}'`); - if (!this.messageReceived) { - this.messageReceived = { - payload: '', - stream: stream.address() - }; - } - this.messageReceived.payload += msg; - }); - } - - private finalize(stream: any) { - if (this.messageReceived) { - this.executeHookEvent('onMessageReceived', this.messageReceived); - } - - if (!this.saveStream) { - Logger.trace(`Ending writable stream`); - stream.end(); - } - if (stream.close) { - Logger.trace(`Closing writable stream`); - stream.close(); - } - } - - private stringifyPayload() { - if (typeof (this.payload) != 'string' && !Buffer.isBuffer(this.payload)) { - return JSON.stringify(this.payload); - } - return this.payload; - } -} - -export function entryPoint(mainInstance: MainInstance): void { - const createFunction = (publisherModel: PublisherModel) => new StreamPublisher(publisherModel); - const docs: ProtocolDocumentation = { - description: 'The stream subscription provides implementations of TCP/UDS servers', - libraryHomepage: 'https://nodejs.org/api/net.html', - schema: { - attributes: { - payload: { - required: true, - type: 'text' - }, - serverAddress: { - required: false, - type: 'string' - }, - port: { - description: 'Defined when using TCP', - required: false, - type: 'int' - }, - path: { - description: 'Defined when using UDS', - required: false, - type: 'string' - }, - saveStream: { - description: 'Set it when you want to reuse this stream', - required: false, - type: 'string' - }, - loadStream: { - description: 'Set it when you want to reuse an opened stream', - required: false, - type: 'string' - }, - timeout: { - description: 'Timeout to stop listening after the first byte is read', - required: false, - type: 'int' - }, - options: { - description: 'Defined when using SSL. https://nodejs.org/api/net.html#net_net_createserver_options_connectionlistener', - required: false, - type: 'object' - }, - }, - hooks: { - onMessageReceived: { - arguments: { - payload: {}, - stream: {} - } - } - } - } - }; - - const tcp = new PublisherProtocol('tcp', - createFunction, docs); - const uds = new PublisherProtocol('uds', - createFunction, docs); - const ssl = new PublisherProtocol('ssl', - createFunction, docs); - - mainInstance.protocolManager.addProtocol(tcp); - mainInstance.protocolManager.addProtocol(uds); - mainInstance.protocolManager.addProtocol(ssl); -} diff --git a/src/publishers/udp-publisher.ts b/src/publishers/udp-publisher.ts deleted file mode 100644 index 6f5eebaf..00000000 --- a/src/publishers/udp-publisher.ts +++ /dev/null @@ -1,58 +0,0 @@ -import {Publisher} from './publisher'; -import {PublisherModel} from '../models/inputs/publisher-model'; -import {Logger} from '../loggers/logger'; -import * as dgram from 'dgram'; -import {PublisherProtocol} from '../protocols/publisher-protocol'; -import {MainInstance} from '../plugins/main-instance'; - -class UdpPublisher extends Publisher { - constructor(publisherAttributes: PublisherModel) { - super(publisherAttributes); - } - - public publish(): Promise { - return new Promise((resolve, reject) => { - const client = dgram.createSocket('udp4'); - Logger.debug('Udp client trying to send message'); - - client.send(Buffer.from(this.payload), this.port, this.serverAddress, (error: any) => { - if (error) { - client.close(); - reject(error); - return; - } - Logger.debug('Udp client sent message'); - resolve(); - }); - - }); - } -} - -export function entryPoint(mainInstance: MainInstance): void { - const protocol = new PublisherProtocol('udp', - (publisherModel: PublisherModel) => new UdpPublisher(publisherModel), - { - description: 'The udp publisher provides an implementation of UDP Datagram sockets clients', - libraryHomepage: 'https://nodejs.org/api/dgram.html', - schema: { - attributes: { - payload: { - required: true, - type: 'text' - }, - serverAddress: { - required: true, - type: 'string' - }, - port: { - required: true, - type: 'int' - }, - } - }, - } - ).addAlternativeName('udp-client'); - - mainInstance.protocolManager.addProtocol(protocol); -} diff --git a/src/reporters/actuator/actuator-reporter.test.ts b/src/reporters/actuator/actuator-reporter.test.ts new file mode 100644 index 00000000..da1292fa --- /dev/null +++ b/src/reporters/actuator/actuator-reporter.test.ts @@ -0,0 +1,139 @@ +import { ActuatorReporter } from './actuator-reporter'; +import { ProtocolManager } from '../../plugins/protocol-manager'; +import { DynamicModulesManager } from '../../plugins/dynamic-modules-manager'; +import { EventExecutor } from '../../events/event-executor'; +import { DefaultHookEvents } from '../../models/events/event'; + +jest.mock('../../plugins/dynamic-modules-manager'); +// @ts-ignore +DynamicModulesManager.getInstance.mockImplementation(() => { + return { + getProtocolManager: () => new ProtocolManager() + }; +}); + +let actMock = jest.fn(() => Promise.resolve('publishResult')) as any; +let actuatorMock = jest.fn(() => { + return { + act: actMock, + registerHookEventExecutor: () => ({}) + }; +}); + +jest.mock('../../plugins/protocol-manager'); +// @ts-ignore +ProtocolManager.mockImplementation(() => { + return { + createActuator: actuatorMock + }; +}); + +jest.mock('../../events/event-executor'); +// @ts-expect-error +EventExecutor.mockImplementation(() => ({ + execute: () => [], + isDebugMode: () => [], + addArgument: () => {} +})); + +const actuator = { + name: 'pubName', + id: 'id' +} as any; + +describe('ActuatorReporter', () => { + beforeEach(() => { + actMock = jest.fn(() => Promise.resolve(true)); + }); + + it('Should call actuator constructor', () => { + new ActuatorReporter(actuator); + + expect(actuatorMock).toHaveBeenCalledWith(actuator); + }); + + it('Should call onInit', () => { + // @ts-expect-error + EventExecutor.mockImplementation = jest.fn(); + + new ActuatorReporter(actuator); + + expect(EventExecutor).toHaveBeenCalledWith(actuator, 'onInit', 'actuator'); + }); + + it('Should resolve onMessageReceived', done => { + const actuatorReporter = new ActuatorReporter(actuator); + actuatorReporter.act().then(() => { + done(); + }); + }); + + it('Should reject onMessageReceived', done => { + const reason = 'reasonMessage'; + actMock = jest.fn(() => Promise.reject(reason)); + const actuatorReporter = new ActuatorReporter(actuator); + actuatorReporter.act().catch(err => { + expect(err).toBe(reason); + + done(); + }); + }); + + it('Should keep id', done => { + const actuatorReporter = new ActuatorReporter(actuator as any); + actuatorReporter.act().then(() => { + const report = actuatorReporter.getReport(); + expect(report.id).toBe(actuator.id); + done(); + }); + }); + + it('Should add Actuator test - success', done => { + const actuatorReporter = new ActuatorReporter(actuator as any); + actuatorReporter.act().then(() => { + actuatorReporter.onFinish(); + const report = actuatorReporter.getReport(); + expect(report.name).toBe(actuator.name); + const actuatorTest = report.hooks![DefaultHookEvents.ON_FINISH].tests[0]; + expect(actuatorTest.name).toBe('Published'); + expect(actuatorTest.valid).toBeTruthy(); + + done(); + }); + }); + + it('Should add Actuator test - fail', done => { + const reason = 'reasonMessage'; + actMock = jest.fn(() => Promise.reject(reason)); + const actuatorReporter = new ActuatorReporter(actuator as any); + actuatorReporter.act().catch(() => { + actuatorReporter.onFinish(); + const report = actuatorReporter.getReport(); + expect(report.name).toBe(actuator.name); + const actuatorTest = report.hooks![DefaultHookEvents.ON_FINISH].tests[0]; + expect(actuatorTest.name).toBe('Published'); + expect(actuatorTest.valid).toBeFalsy(); + + done(); + }); + }); + + it('Should call onFinish', async () => { + // @ts-expect-error + EventExecutor.mockClear(); + // @ts-expect-error + EventExecutor.mockImplementation = jest.fn(); + + await new ActuatorReporter(actuator).onFinish(); + + expect(EventExecutor).toHaveBeenNthCalledWith( + 2, + { + act: expect.any(Function), + registerHookEventExecutor: expect.any(Function) + }, + 'onFinish', + 'actuator' + ); + }); +}); diff --git a/src/reporters/actuator/actuator-reporter.ts b/src/reporters/actuator/actuator-reporter.ts new file mode 100644 index 00000000..332c1149 --- /dev/null +++ b/src/reporters/actuator/actuator-reporter.ts @@ -0,0 +1,146 @@ +import { Actuator } from '../../actuators/actuator'; +import { DateController } from '../../timers/date-controller'; +import * as output from '../../models/outputs/actuator-model'; +import { ActuatorModel } from '../../models/outputs/actuator-model'; +import * as input from '../../models/inputs/actuator-model'; +import { Logger } from '../../loggers/logger'; +import { DynamicModulesManager } from '../../plugins/dynamic-modules-manager'; +import { EventExecutor } from '../../events/event-executor'; +import { DefaultHookEvents } from '../../models/events/event'; +import { ObjectDecycler } from '../../object-parser/object-decycler'; +import { TestModel, testModelIsPassing } from '../../models/outputs/test-model'; +import { NotificationEmitter } from '../../notifications/notification-emitter'; +import { HookModel } from '../../models/outputs/hook-model'; +import { Notifications } from '../../notifications/notifications'; +import { HookReporter } from '../hook-reporter'; + +export class ActuatorReporter { + private readonly report: output.ActuatorModel; + private readonly actuator: Actuator; + private readonly startTime: Date; + private readonly executedHooks: { [propName: string]: string[] }; + private acted: boolean = false; + + constructor(actuator: input.ActuatorModel) { + this.report = { + id: actuator.id, + name: actuator.name, + ignored: actuator.ignore, + valid: true, + hooks: { + [DefaultHookEvents.ON_INIT]: { valid: true, tests: [] }, + [DefaultHookEvents.ON_FINISH]: { valid: true, tests: [] } + }, + type: actuator.type + }; + this.executedHooks = {}; + this.startTime = new Date(); + this.executeOnInitFunction(actuator); + Logger.debug(`Trying to instantiate actuator from '${actuator.type}'`); + this.actuator = DynamicModulesManager.getInstance().getProtocolManager().createActuator(actuator); + this.actuator.registerHookEventExecutor((eventName: string, args: any) => + this.executeHookEvent(eventName, { ...args, elapsedTime: new Date().getTime() - this.startTime.getTime() }) + ); + } + + public async act(): Promise { + try { + if (this.actuator.ignore) { + Logger.trace(`Ignoring actuator ${this.report.name}`); + } else { + Logger.trace(`Publishing ${this.report.name}`); + const response = await this.actuator.act(); + Logger.debug(`${this.report.name} acted`); + this.report.messageSentInstant = new DateController().toString(); + this.acted = true; + this.report.hooks![DefaultHookEvents.ON_FINISH].tests.push({ + name: 'Published', + valid: this.acted, + description: this.processMessage(response), + implicit: true + }); + } + } catch (err) { + Logger.error(`'${this.report.name}' fail publishing: ${err}`); + this.report.hooks![DefaultHookEvents.ON_FINISH].tests.push({ + name: 'Published', + valid: false, + description: '' + err, + implicit: true + }); + this.report.valid = false; + throw err; + } + } + + private processMessage(messageReceived: any): string { + if (messageReceived) { + if (typeof messageReceived === 'object') { + return JSON.stringify(new ObjectDecycler().decycle(messageReceived), null, 2); + } + return messageReceived; + } + return 'Published successfully'; + } + + public getReport(): ActuatorModel { + return this.report; + } + + public onFinish(): void { + if (!this.actuator.ignore) { + this.executeHookEvent(DefaultHookEvents.ON_FINISH, { + executedHooks: this.executedHooks, + elapsedTime: new Date().getTime() - this.startTime.getTime() + }); + this.report.valid = this.report.valid && this.acted; + NotificationEmitter.emit(Notifications.PUBLISHER_FINISHED, { + actuator: this.report + }); + } + } + + private executeHookEvent(eventName: string, args: any = {}, actuator: any = this.actuator): void { + if (!actuator.ignore) { + this.executedHooks[eventName] = Object.keys(args); + args.elapsedTime = new Date().getTime() - this.startTime.getTime(); + const eventExecutor = new EventExecutor(actuator, eventName, 'actuator'); + Object.keys(args).forEach((key: string) => { + eventExecutor.addArgument(key, args[key]); + }); + const previousHook = this.report.hooks![eventName]; + let previousTests: TestModel[] = previousHook?.tests ?? []; + const tests = previousTests.concat(eventExecutor.execute()); + const valid = tests.every((test: TestModel) => testModelIsPassing(test)); + const decycledArgs = new ObjectDecycler().decycle(args); + const hookModel: HookModel = { + arguments: decycledArgs, + tests: tests, + valid: valid + }; + if (eventExecutor.isDebugMode()) { + console.table(Object.keys(decycledArgs).join('; ')); + // console.table(decycledArgs); + } + //TODO investigate why this line wasn't added this file originally + const hookResult = new HookReporter(this.report.hooks![eventName]).addValues(hookModel); + this.report.hooks![eventName] = hookModel; + NotificationEmitter.emit(Notifications.HOOK_FINISHED, { + hookName: eventName, + hook: hookResult, + actuator: this.actuator + }); + + this.report.valid = this.report.valid && valid; + } + } + + private executeOnInitFunction(actuator: input.ActuatorModel) { + if (!actuator.ignore) { + NotificationEmitter.emit(Notifications.PUBLISHER_STARTED, { + actuator: actuator + }); + this.executeHookEvent(DefaultHookEvents.ON_INIT, {}, actuator); + } + } +} diff --git a/src/reporters/actuator/multi-actuators-reporter.test.ts b/src/reporters/actuator/multi-actuators-reporter.test.ts new file mode 100644 index 00000000..a4fd402d --- /dev/null +++ b/src/reporters/actuator/multi-actuators-reporter.test.ts @@ -0,0 +1,101 @@ +import { ActuatorReporter } from './actuator-reporter'; +import { MultiActuatorsReporter } from './multi-actuators-reporter'; + +jest.mock('./actuator-reporter'); + +let act = jest.fn(); +let onFinishMock = jest.fn(() => { + return 'onFinishMockStr'; +}); +let getReportMock = jest.fn(() => { + return 'getReportMockStr'; +}); + +const recreateMock = () => { + // @ts-expect-error + ActuatorReporter.mockImplementation(() => { + return { + act: act, + onFinish: onFinishMock, + getReport: getReportMock + }; + }); +}; + +let clearMock = function () { + // @ts-expect-error + ActuatorReporter.mockClear(); + act.mockClear(); + onFinishMock.mockClear(); + getReportMock.mockReset(); +}; + +describe('MultiActuatorsReporter', () => { + beforeEach(() => { + recreateMock(); + }); + + afterEach(() => { + clearMock(); + }); + + it('Call publishReporter constructors', () => { + const actuators = [{ name: 'first' }, { name: 'second' }] as any; + new MultiActuatorsReporter(actuators); + + expect(ActuatorReporter).toHaveBeenCalledTimes(actuators.length); + expect(ActuatorReporter).toHaveBeenCalledWith({ name: 'first' }); + expect(ActuatorReporter).toHaveBeenCalledWith({ name: 'second' }); + }); + + it('Call publishReporter constructors empty actuators', () => { + new MultiActuatorsReporter([]); + + expect(ActuatorReporter).toHaveBeenCalledTimes(0); + }); + + it('should handle success', done => { + act.mockImplementation(() => Promise.resolve()); + recreateMock(); + + const actuators = [{}, {}] as any; + new MultiActuatorsReporter(actuators).act().then(() => { + done(); + }); + + expect(act).toHaveBeenCalledTimes(actuators.length); + }); + + it('should handle be success when no actuator is given', done => { + new MultiActuatorsReporter([]).act().then(() => { + done(); + }); + + expect(act).toHaveBeenCalledTimes(0); + }); + + it('should handle fail publishing', async () => { + act.mockImplementationOnce(() => Promise.resolve()); + act.mockImplementationOnce(() => Promise.reject('err reason')); + recreateMock(); + + const actuators = [{}, {}] as any; + await new MultiActuatorsReporter(actuators).act(); + expect(act).toHaveBeenCalledTimes(actuators.length); + }); + + it('should call onFinish', () => { + const actuators = [{}, {}] as any; + + new MultiActuatorsReporter(actuators).onFinish(); + expect(onFinishMock).toHaveBeenCalledTimes(actuators.length); + }); + + it('should call getReport', () => { + const actuators = [{}, {}] as any; + + const report = new MultiActuatorsReporter(actuators).getReport(); + expect(report.length).toBe(actuators.length); + expect(getReportMock).toHaveBeenCalledTimes(actuators.length); + }); +}); diff --git a/src/reporters/actuator/multi-actuators-reporter.ts b/src/reporters/actuator/multi-actuators-reporter.ts new file mode 100644 index 00000000..42dd7657 --- /dev/null +++ b/src/reporters/actuator/multi-actuators-reporter.ts @@ -0,0 +1,40 @@ +import { ActuatorReporter } from './actuator-reporter'; +import * as output from '../../models/outputs/actuator-model'; +import * as input from '../../models/inputs/actuator-model'; +import { Logger } from '../../loggers/logger'; +import { ComponentImporter } from '../../task-runners/component-importer'; + +export class MultiActuatorsReporter { + private actuators: ActuatorReporter[]; + + constructor(actuators: input.ActuatorModel[]) { + Logger.debug(`Instantiating actuators`); + this.actuators = actuators.map(actuator => new ActuatorReporter(new ComponentImporter().importActuator(actuator))); + } + + public async act(): Promise { + if (this.actuators.length > 0) { + Logger.debug(`Actuators are acting`); + + await Promise.all( + this.actuators.map(async actuator => { + try { + await actuator.act(); + } catch (err) { + Logger.error(`Actuators errored: ` + err); + } + }) + ); + Logger.debug(`Actuators have acted`); + } + } + + public onFinish(): void { + //sync forEach + this.actuators.map(actuator => actuator.onFinish()); + } + + public getReport(): output.ActuatorModel[] { + return this.actuators.map(actuator => actuator.getReport()); + } +} diff --git a/src/reporters/hook-reporter.test.ts b/src/reporters/hook-reporter.test.ts index 96261a25..bc043a8c 100644 --- a/src/reporters/hook-reporter.test.ts +++ b/src/reporters/hook-reporter.test.ts @@ -1,74 +1,83 @@ -import {HookReporter} from './hook-reporter'; +import { HookReporter } from './hook-reporter'; describe('HookReporter', () => { - it('Empty constructor', () => { - const hookReporter: HookReporter = new HookReporter(); - const hookModel = hookReporter.addValues({ - tests: [ - { - description: 'some', - valid: false, - name: 'a' - }, - { - description: 'some2', - valid: true, - name: 'b' - }], - valid: true, - arguments: {a: 1} - }); - - expect(hookModel).toEqual({ - 'arguments': {'a': 1}, - 'tests': [{'description': 'some', 'name': 'a', 'valid': false}, {'description': 'some2', 'name': 'b', 'valid': true}], - 'valid': false - }); - + it('Empty constructor', () => { + const hookReporter: HookReporter = new HookReporter(); + const hookModel = hookReporter.addValues({ + tests: [ + { + description: 'some', + valid: false, + name: 'a' + }, + { + description: 'some2', + valid: true, + name: 'b' + } + ], + valid: true, + arguments: { a: 1 } }); - it('Full constructor', () => { - const hookReporter: HookReporter = new HookReporter( - { - tests: [ - { - description: 'some', - valid: false, - name: 'c' - }], - valid: true, - arguments: {c: 1} - } - ); - const hookModel = hookReporter.addValues({ - tests: [ - { - description: 'some', - valid: true, - name: 'a' - }, - { - description: 'some', - valid: true, - name: 'b' - }], - valid: true, - arguments: {a: 1} - }); - expect(hookModel).toEqual({ - 'arguments': {'a': 1, 'c': 1}, - 'tests': [ - { - 'description': 'some', 'name': 'c', 'valid': false - }, { - 'description': 'some', - 'name': 'a', - 'valid': true - }, { - 'description': 'some', 'name': 'b', 'valid': true - }], - 'valid': false - }); + expect(hookModel).toEqual({ + arguments: { a: 1 }, + tests: [ + { description: 'some', name: 'a', valid: false }, + { description: 'some2', name: 'b', valid: true } + ], + valid: false + }); + }); + it('Full constructor', () => { + const hookReporter: HookReporter = new HookReporter({ + tests: [ + { + description: 'some', + valid: false, + name: 'c' + } + ], + valid: true, + arguments: { c: 1 } + }); + const hookModel = hookReporter.addValues({ + tests: [ + { + description: 'some', + valid: true, + name: 'a' + }, + { + description: 'some', + valid: true, + name: 'b' + } + ], + valid: true, + arguments: { a: 1 } + }); + expect(hookModel).toEqual({ + arguments: { a: 1, c: 1 }, + tests: [ + { + description: 'some', + name: 'c', + valid: false + }, + { + description: 'some', + name: 'a', + valid: true + }, + { + description: 'some', + name: 'b', + valid: true + } + ], + valid: false }); + }); }); diff --git a/src/reporters/hook-reporter.ts b/src/reporters/hook-reporter.ts index 3f46971b..73df9d5f 100644 --- a/src/reporters/hook-reporter.ts +++ b/src/reporters/hook-reporter.ts @@ -1,22 +1,22 @@ -import {HookModel} from '../models/outputs/hook-model'; -import {testModelIsNotFailing} from '../models/outputs/test-model'; +import { HookModel } from '../models/outputs/hook-model'; +import { testModelIsNotFailing } from '../models/outputs/test-model'; export class HookReporter { - private readonly hook: HookModel; + private readonly hook: HookModel; - public constructor(hookModel?: HookModel) { - this.hook = { - valid: true, - tests: [], - arguments: {} - }; - this.addValues(hookModel); - } + public constructor(hookModel?: HookModel) { + this.hook = { + valid: true, + tests: [], + arguments: {} + }; + this.addValues(hookModel); + } - public addValues(hookModel: HookModel = {valid: true, tests: [], arguments: {}}): HookModel { - this.hook.tests = this.hook.tests.concat(hookModel.tests); - this.hook.arguments = Object.assign({}, this.hook.arguments, hookModel.arguments); - this.hook.valid = this.hook.tests.every(test => testModelIsNotFailing(test)); - return this.hook; - } + public addValues(hookModel: HookModel = { valid: true, tests: [], arguments: {} }): HookModel { + this.hook.tests = this.hook.tests.concat(hookModel.tests); + this.hook.arguments = Object.assign({}, this.hook.arguments, hookModel.arguments); + this.hook.valid = this.hook.tests.every(test => testModelIsNotFailing(test)); + return this.hook; + } } diff --git a/src/reporters/publishers/multi-publishers-reporter.test.ts b/src/reporters/publishers/multi-publishers-reporter.test.ts deleted file mode 100644 index 5777dbd3..00000000 --- a/src/reporters/publishers/multi-publishers-reporter.test.ts +++ /dev/null @@ -1,103 +0,0 @@ -import {PublisherReporter} from './publisher-reporter'; -import {MultiPublishersReporter} from './multi-publishers-reporter'; - -jest.mock('./publisher-reporter'); - -let publish = jest.fn(); -let onFinishMock = jest.fn(() => { - return 'onFinishMockStr'; -}); -let getReportMock = jest.fn(() => { - return 'getReportMockStr'; -}); - -const recreateMock = () => { - PublisherReporter.mockImplementation(() => { - return { - publish: publish, - onFinish: onFinishMock, - getReport: getReportMock - }; - }); -}; - -let clearMock = function () { - PublisherReporter.mockClear(); - publish.mockClear(); - onFinishMock.mockClear(); - getReportMock.mockReset(); -}; - -describe('MultiPublishersReporter', () => { - beforeEach(() => { - recreateMock(); - }); - - afterEach(() => { - clearMock(); - }); - - it('Call publishReporter constructors', () => { - const publishers = [{name: 'first'}, {name: 'second'}]; - new MultiPublishersReporter(publishers); - - expect(PublisherReporter).toHaveBeenCalledTimes(publishers.length); - expect(PublisherReporter).toHaveBeenCalledWith({name: 'first'}); - expect(PublisherReporter).toHaveBeenCalledWith({name: 'second'}); - }); - - it('Call publishReporter constructors empty publishers', () => { - new MultiPublishersReporter([]); - - expect(PublisherReporter).toHaveBeenCalledTimes(0); - }); - - it('should handle success', done => { - publish.mockImplementation(() => Promise.resolve()); - recreateMock(); - - const publishers = [{}, {}]; - new MultiPublishersReporter(publishers) - .publish() - .then(() => { - done(); - }); - - expect(publish).toHaveBeenCalledTimes(publishers.length); - }); - - it('should handle be success when no publisher is given', done => { - new MultiPublishersReporter([]) - .publish() - .then(() => { - done(); - }); - - expect(publish).toHaveBeenCalledTimes(0); - }); - - it('should handle fail publishing', async () => { - publish.mockImplementationOnce(() => Promise.resolve()); - publish.mockImplementationOnce(() => Promise.reject('err reason')); - recreateMock(); - - const publishers = [{}, {}]; - await new MultiPublishersReporter(publishers).publish(); - expect(publish).toHaveBeenCalledTimes(publishers.length); - }); - - it('should call onFinish', () => { - const publishers = [{}, {}]; - - new MultiPublishersReporter(publishers).onFinish(); - expect(onFinishMock).toHaveBeenCalledTimes(publishers.length); - }); - - it('should call getReport', () => { - const publishers = [{}, {}]; - - const report = new MultiPublishersReporter(publishers).getReport(); - expect(report.length).toBe(publishers.length); - expect(getReportMock).toHaveBeenCalledTimes(publishers.length); - }); -}); diff --git a/src/reporters/publishers/multi-publishers-reporter.ts b/src/reporters/publishers/multi-publishers-reporter.ts deleted file mode 100644 index 5d788bde..00000000 --- a/src/reporters/publishers/multi-publishers-reporter.ts +++ /dev/null @@ -1,39 +0,0 @@ -import {PublisherReporter} from './publisher-reporter'; -import * as output from '../../models/outputs/publisher-model'; -import * as input from '../../models/inputs/publisher-model'; -import {Logger} from '../../loggers/logger'; -import {ComponentImporter} from '../../requisition-runners/component-importer'; - -export class MultiPublishersReporter { - private publishers: PublisherReporter[]; - - constructor(publishers: input.PublisherModel[]) { - Logger.debug(`Instantiating publishers`); - this.publishers = publishers.map(publisher => new PublisherReporter(new ComponentImporter().importPublisher(publisher))); - } - - public async publish(): Promise { - if (this.publishers.length > 0) { - Logger.debug(`Publishers are publishing messages`); - - await Promise.all(this.publishers.map(async publisher => { - try { - await publisher.publish(); - } catch (err) { - Logger.error(err); - } - })); - Logger.debug(`Publishers have published their messages`); - } - } - - public onFinish(): void { - //sync forEach - this.publishers.map(publisher => publisher.onFinish()); - } - - public getReport(): output.PublisherModel[] { - return this.publishers.map(publisher => publisher.getReport()); - } - -} diff --git a/src/reporters/publishers/publisher-reporter.test.ts b/src/reporters/publishers/publisher-reporter.test.ts deleted file mode 100644 index 44a50341..00000000 --- a/src/reporters/publishers/publisher-reporter.test.ts +++ /dev/null @@ -1,136 +0,0 @@ -import {PublisherReporter} from './publisher-reporter'; -import {ProtocolManager} from '../../plugins/protocol-manager'; -import {DynamicModulesManager} from '../../plugins/dynamic-modules-manager'; -import {EventExecutor} from '../../events/event-executor'; -import {DefaultHookEvents} from '../../models/events/event'; - -jest.mock('../../plugins/dynamic-modules-manager'); -// @ts-ignore -DynamicModulesManager.getInstance.mockImplementation(() => { - return { - getProtocolManager: () => new ProtocolManager() - }; -}); - -let publishMock = jest.fn(() => Promise.resolve('publishResult')); -let publisherMock = jest.fn(() => { - return { - publish: publishMock, - registerHookEventExecutor: () => ({}) - }; -}); - -jest.mock('../../plugins/protocol-manager'); -// @ts-ignore -ProtocolManager.mockImplementation(() => { - return { - createPublisher: publisherMock - }; -}); - -jest.mock('../../events/event-executor'); -EventExecutor.mockImplementation(() => ({ - execute: () => [], - addArgument: () => { - } -})); - -const publisher = { - name: 'pubName', - id: 'id' -}; - -describe('PublisherReporter', () => { - beforeEach(() => { - publishMock = jest.fn(() => Promise.resolve(true)); - }); - - it('Should call publisher constructor', () => { - new PublisherReporter(publisher); - - expect(publisherMock).toHaveBeenCalledWith(publisher); - }); - - it('Should call onInit', () => { - EventExecutor.mockImplementation = jest.fn(); - - new PublisherReporter(publisher); - - expect(EventExecutor).toHaveBeenCalledWith(publisher, 'onInit', 'publisher'); - }); - - it('Should resolve onMessageReceived', done => { - const publisherReporter = new PublisherReporter(publisher); - publisherReporter.publish().then(() => { - done(); - }); - - }); - - it('Should reject onMessageReceived', done => { - const reason = 'reasonMessage'; - publishMock = jest.fn(() => Promise.reject(reason)); - const publisherReporter = new PublisherReporter(publisher); - publisherReporter.publish().catch((err) => { - expect(err).toBe(reason); - - done(); - }); - - }); - - it('Should keep id', done => { - const publisherReporter = new PublisherReporter(publisher); - publisherReporter.publish().then(() => { - const report = publisherReporter.getReport(); - expect(report.id).toBe(publisher.id); - done(); - }); - - }); - - it('Should add Publisher test - success', done => { - const publisherReporter = new PublisherReporter(publisher); - publisherReporter.publish().then(() => { - publisherReporter.onFinish(); - const report = publisherReporter.getReport(); - expect(report.name).toBe(publisher.name); - const publisherTest = report.hooks[DefaultHookEvents.ON_FINISH].tests[0]; - expect(publisherTest.name).toBe('Published'); - expect(publisherTest.valid).toBeTruthy(); - - done(); - }); - - }); - - it('Should add Publisher test - fail', done => { - const reason = 'reasonMessage'; - publishMock = jest.fn(() => Promise.reject(reason)); - const publisherReporter = new PublisherReporter(publisher); - publisherReporter.publish().catch(() => { - publisherReporter.onFinish(); - const report = publisherReporter.getReport(); - expect(report.name).toBe(publisher.name); - const publisherTest = report.hooks[DefaultHookEvents.ON_FINISH].tests[0]; - expect(publisherTest.name).toBe('Published'); - expect(publisherTest.valid).toBeFalsy(); - - done(); - }); - - }); - - it('Should call onFinish', async () => { - EventExecutor.mockClear(); - EventExecutor.mockImplementation = jest.fn(); - - await new PublisherReporter(publisher).onFinish(); - - expect(EventExecutor).toHaveBeenNthCalledWith(2, { - publish: expect.any(Function), - registerHookEventExecutor: expect.any(Function), - }, 'onFinish', 'publisher'); - }); - -}); diff --git a/src/reporters/publishers/publisher-reporter.ts b/src/reporters/publishers/publisher-reporter.ts deleted file mode 100644 index 07d18bb0..00000000 --- a/src/reporters/publishers/publisher-reporter.ts +++ /dev/null @@ -1,128 +0,0 @@ -import {Publisher} from '../../publishers/publisher'; -import {DateController} from '../../timers/date-controller'; -import * as output from '../../models/outputs/publisher-model'; -import {PublisherModel} from '../../models/outputs/publisher-model'; -import * as input from '../../models/inputs/publisher-model'; -import {Logger} from '../../loggers/logger'; -import {DynamicModulesManager} from '../../plugins/dynamic-modules-manager'; -import {EventExecutor} from '../../events/event-executor'; -import {DefaultHookEvents} from '../../models/events/event'; -import {ObjectDecycler} from '../../object-parser/object-decycler'; -import {TestModel, testModelIsPassing} from '../../models/outputs/test-model'; -import {NotificationEmitter} from '../../notifications/notification-emitter'; -import {HookModel} from '../../models/outputs/hook-model'; -import {Notifications} from '../../notifications/notifications'; - -export class PublisherReporter { - private readonly report: output.PublisherModel; - private readonly publisher: Publisher; - private readonly startTime: Date; - private readonly executedHooks: string[]; - private published: boolean = false; - - constructor(publisher: input.PublisherModel) { - this.report = { - id: publisher.id, - name: publisher.name, - ignored: publisher.ignore, - valid: true, - hooks: { - [DefaultHookEvents.ON_INIT]: {valid: true, tests: []}, - [DefaultHookEvents.ON_FINISH]: {valid: true, tests: []} - }, - type: publisher.type - }; - this.executedHooks = []; - this.startTime = new Date(); - this.executeOnInitFunction(publisher); - Logger.debug(`Trying to instantiate publisher from '${publisher.type}'`); - this.publisher = DynamicModulesManager.getInstance().getProtocolManager().createPublisher(publisher); - this.publisher.registerHookEventExecutor((eventName: string, args: any) => this.executeHookEvent(eventName, args)); - } - - public async publish(): Promise { - try { - if (this.publisher.ignore) { - Logger.trace(`Ignoring publisher ${this.report.name}`); - } else { - Logger.trace(`Publishing ${this.report.name}`); - const response = await this.publisher.publish(); - Logger.debug(`${this.report.name} published`); - this.report.publishTime = new DateController().toString(); - this.published = true; - this.report.hooks![DefaultHookEvents.ON_FINISH].tests.push({ - name: 'Published', valid: this.published, description: this.processMessage(response) - }); - - } - } catch (err) { - Logger.error(`'${this.report.name}' fail publishing: ${err}`); - this.report.hooks![DefaultHookEvents.ON_FINISH].tests.push({ - name: 'Published', valid: false, description: err.toString() - }); - this.report.valid = false; - throw err; - } - } - - private processMessage(messageReceived: any): string { - if (messageReceived) { - if (typeof messageReceived === 'object') { - return JSON.stringify(new ObjectDecycler().decycle(messageReceived), null, 2); - } - return messageReceived; - } - return 'Published successfully'; - } - - public getReport(): PublisherModel { - return this.report; - } - - public onFinish(): void { - if (!this.publisher.ignore) { - this.executeHookEvent(DefaultHookEvents.ON_FINISH, {executedHooks: this.executedHooks}); - this.report.valid = this.report.valid && this.published; - NotificationEmitter.emit(Notifications.PUBLISHER_FINISHED, {publisher: this.report}); - } - } - - private executeHookEvent(eventName: string, args: any = {}, publisher: any = this.publisher): void { - if (!publisher.ignore) { - this.executedHooks.push(eventName); - args.elapsedTime = new Date().getTime() - this.startTime.getTime(); - const eventExecutor = new EventExecutor(publisher, eventName, 'publisher'); - Object.keys(args).forEach((key: string) => { - eventExecutor.addArgument(key, args[key]); - }); - const previousHook = this.report.hooks![eventName]; - let previousTests: TestModel[] = []; - if (previousHook && previousHook.tests) { - previousTests = previousHook.tests || []; - } - const tests = previousTests.concat(eventExecutor.execute()); - const valid = tests.every((test: TestModel) => testModelIsPassing(test)); - const hookResult: HookModel = { - arguments: new ObjectDecycler().decycle(args), - tests: tests, - valid: valid - }; - this.report.hooks![eventName] = hookResult; - NotificationEmitter.emit(Notifications.HOOK_FINISHED, { - hookName: eventName, - hook: hookResult, - publisher: this.publisher - }); - - this.report.valid = this.report.valid && valid; - } - } - - private executeOnInitFunction(publisher: input.PublisherModel) { - if (!publisher.ignore) { - NotificationEmitter.emit(Notifications.PUBLISHER_STARTED, {publisher: publisher}); - this.executeHookEvent(DefaultHookEvents.ON_INIT, {}, publisher); - } - } - -} diff --git a/src/reporters/requisition-report-generator.test.ts b/src/reporters/requisition-report-generator.test.ts deleted file mode 100644 index f078067d..00000000 --- a/src/reporters/requisition-report-generator.test.ts +++ /dev/null @@ -1,140 +0,0 @@ -import {RequisitionReportGenerator} from './requisition-report-generator'; -import {DefaultHookEvents} from '../models/events/event'; - -let sleep = (millisecondsToWait: number): void => { - const waitTill = new Date(new Date().getTime() + millisecondsToWait); - while (waitTill > new Date()) { - //wait - } -}; - -describe('RequisitionReportGenerator', () => { - - it('Create default report', () => { - const report = new RequisitionReportGenerator({name: 'testName'}).getReport(); - expect(report.time).toBeDefined(); - delete report.time; - expect(report).toEqual({ - 'hooks': { - 'onFinish': {arguments: {}, 'tests': [], 'valid': true}, - 'onInit': {arguments: {}, 'tests': [], 'valid': true} - }, - 'id': undefined, - 'ignored': undefined, - 'level': undefined, - 'name': 'testName', - 'publishers': [], - 'requisitions': [], - 'subscriptions': [], - 'valid': true - }); - }); - - it('Time report with timeout', () => { - const timeout = 1000; - const reportGenerator = new RequisitionReportGenerator({name: 'someName'}, timeout); - const firstReport = reportGenerator.getReport(); - const firstStartTime = new Date(firstReport.time.startTime.valueOf()).getTime(); - - sleep(20); - reportGenerator.finish(); - - const secondReport = reportGenerator.getReport(); - expect(new Date(secondReport.time.startTime).getTime()).toBeGreaterThanOrEqual(firstStartTime); - expect(secondReport.time.timeout).toBeGreaterThanOrEqual(timeout); - delete secondReport.time; - delete secondReport.tests; - - expect(secondReport).toEqual({ - 'hooks': { - 'onFinish': {arguments: {}, 'tests': [], 'valid': true}, - 'onInit': {arguments: {}, 'tests': [], 'valid': true} - }, - 'id': undefined, - 'ignored': undefined, - 'level': undefined, - 'name': 'someName', - 'publishers': [], - 'requisitions': [], - 'subscriptions': [], - 'valid': true - }); - }); - - it('Time out test fail', () => { - const timeout = 10; - const reportGenerator = new RequisitionReportGenerator({name: 'someName'}, timeout); - sleep(50); - reportGenerator.finish(); - - const report = reportGenerator.getReport(); - expect(report.valid).toBeFalsy(); - expect(report.hooks[DefaultHookEvents.ON_FINISH].tests.length).toBe(1); - expect(report.hooks[DefaultHookEvents.ON_FINISH].valid).toBeFalsy(); - expect(report.hooks[DefaultHookEvents.ON_FINISH].tests[0].name).toBe('No time out'); - expect(report.hooks[DefaultHookEvents.ON_FINISH].tests[0].valid).toBeFalsy(); - expect(report.hooks[DefaultHookEvents.ON_FINISH].tests[0].description).toBeDefined(); - }); - - it('Time report without timeout', () => { - const reportGenerator = new RequisitionReportGenerator({name: 'someName'}); - - reportGenerator.finish(); - - const time = reportGenerator.getReport().time; - - expect(time.startTime).toBeDefined(); - expect(time.endTime).toBeDefined(); - expect(time.totalTime).toBe(new Date(time.endTime).getTime() - new Date(time.startTime).getTime()); - expect(time.timeout).toBeUndefined(); - }); - - it('Adding publisher report', () => { - const reportGenerator = new RequisitionReportGenerator({name: 'someName'}); - - let report = reportGenerator.getReport(); - expect(report.valid).toBeTruthy(); - expect(report.publishers.length).toBe(0); - - reportGenerator.setPublishersReport([{valid: false}]); - reportGenerator.finish(); - report = reportGenerator.getReport(); - - expect(report.valid).toBeFalsy(); - expect(report.publishers.length).toBe(1); - expect(report.publishers[0].valid).toBeFalsy(); - }); - - it('Adding subscription report', () => { - const reportGenerator = new RequisitionReportGenerator({name: 'someName'}); - - let report = reportGenerator.getReport(); - expect(report.valid).toBeTruthy(); - expect(report.subscriptions.length).toBe(0); - - reportGenerator.setSubscriptionsReport([{valid: false}]); - reportGenerator.finish(); - report = reportGenerator.getReport(); - - expect(report.valid).toBeFalsy(); - expect(report.subscriptions.length).toBe(1); - expect(report.subscriptions[0].valid).toBeFalsy(); - }); - - it('Adding tests', () => { - const reportGenerator = new RequisitionReportGenerator({name: 'someName'}); - - reportGenerator.addTest('hookName', {valid: false, arguments: {a: 1}, tests: [{name: 'a', valid: false}]}); - reportGenerator.addTest('hookName', {valid: true, arguments: {b: 3}, tests: [{name: 'b', valid: true}]}); - reportGenerator.finish(); - const report = reportGenerator.getReport(); - - expect(report.valid).toBeFalsy(); - expect(report.hooks.hookName).toEqual({ - 'arguments': {'a': 1, 'b': 3}, - 'tests': [{'name': 'a', 'valid': false}, {'name': 'b', 'valid': true}], - 'valid': false - }); - }); - -}); diff --git a/src/reporters/requisition-report-generator.ts b/src/reporters/requisition-report-generator.ts deleted file mode 100644 index 49895d4a..00000000 --- a/src/reporters/requisition-report-generator.ts +++ /dev/null @@ -1,74 +0,0 @@ -import {DateController} from '../timers/date-controller'; -import * as input from '../models/inputs/requisition-model'; -import * as output from '../models/outputs/requisition-model'; -import {RequisitionModel} from '../models/outputs/requisition-model'; -import {SubscriptionModel} from '../models/outputs/subscription-model'; -import {PublisherModel} from '../models/outputs/publisher-model'; -import {RequisitionDefaultReports} from '../models-defaults/outputs/requisition-default-reports'; -import {DefaultHookEvents} from '../models/events/event'; -import {HookModel} from '../models/outputs/hook-model'; -import {HookReporter} from './hook-reporter'; - -export class RequisitionReportGenerator { - - private startTime: DateController = new DateController(); - private readonly timeout?: number; - private readonly report: output.RequisitionModel; - - public constructor(requisitionAttributes: input.RequisitionModel, timeout?: number) { - this.report = RequisitionDefaultReports.createDefaultReport(requisitionAttributes); - this.report.id = requisitionAttributes.id; - this.startTime = new DateController(); - this.timeout = timeout; - } - - public setPublishersReport(publishersReport: PublisherModel[]): void { - this.report.publishers = publishersReport; - } - - public setSubscriptionsReport(subscriptionReport: SubscriptionModel[]): void { - this.report.subscriptions = subscriptionReport; - } - - public getReport(): RequisitionModel { - this.report.valid = (this.report.subscriptions || []).every(report => report.valid) && - (this.report.publishers || []).every(report => report.valid) && - Object.keys(this.report.hooks || {}).every((key: string) => this.report.hooks ? this.report.hooks[key].valid : true); - return this.report; - } - - public finish(): void { - this.addTimesReport(); - } - - public addTest(hookName: string, hook: HookModel) { - this.report.hooks![hookName] = new HookReporter(this.report.hooks![hookName]).addValues(hook); - } - - private addTimesReport(): void { - this.report.time = this.generateTimesReport(); - if (this.timeout) { - this.report.time.timeout = this.timeout; - if (this.report.time.totalTime > this.report.time.timeout) { - this.addTest(DefaultHookEvents.ON_FINISH, { - valid: false, tests: [{ - valid: false, - name: 'No time out', - description: `Requisition has timed out: ${this.report.time.totalTime} > ${this.timeout}` - }] - }); - } - - } - } - - private generateTimesReport() { - const endDate = new DateController(); - return { - startTime: this.startTime.toString(), - endTime: endDate.toString(), - totalTime: endDate.getTime() - this.startTime.getTime() - }; - } - -} diff --git a/src/reporters/requisition-reporter.ts b/src/reporters/requisition-reporter.ts deleted file mode 100644 index 6301814b..00000000 --- a/src/reporters/requisition-reporter.ts +++ /dev/null @@ -1,144 +0,0 @@ -import {RequisitionReportGenerator} from './requisition-report-generator'; -import {Logger} from '../loggers/logger'; -import * as input from '../models/inputs/requisition-model'; -import {RequisitionModel} from '../models/inputs/requisition-model'; -import * as output from '../models/outputs/requisition-model'; -import {MultiSubscriptionsReporter} from './subscription/multi-subscriptions-reporter'; -import {MultiPublishersReporter} from './publishers/multi-publishers-reporter'; -import {EventExecutor} from '../events/event-executor'; -import {DefaultHookEvents} from '../models/events/event'; -import {TestModel} from '../models/outputs/test-model'; -import {NotificationEmitter} from '../notifications/notification-emitter'; -import {Notifications} from '../notifications/notifications'; - -export class RequisitionReporter { - public static readonly DEFAULT_TIMEOUT = 5 * 1000; - private readonly timeout?: number; - private readonly requisitionAttributes: RequisitionModel; - private readonly startTime: Date; - private reportGenerator: RequisitionReportGenerator; - private multiSubscriptionsReporter: MultiSubscriptionsReporter; - private multiPublishersReporter: MultiPublishersReporter; - private hasFinished: boolean = false; - - constructor(requisitionAttributes: input.RequisitionModel) { - this.requisitionAttributes = requisitionAttributes; - if (this.requisitionAttributes.timeout === undefined) { - this.timeout = RequisitionReporter.DEFAULT_TIMEOUT; - } else if (this.requisitionAttributes.timeout > 0) { - this.timeout = this.requisitionAttributes.timeout; - } - this.reportGenerator = new RequisitionReportGenerator(this.requisitionAttributes, this.timeout); - this.startTime = new Date(); - this.executeOnInitFunction(); - this.multiSubscriptionsReporter = new MultiSubscriptionsReporter(this.requisitionAttributes.subscriptions); - this.multiPublishersReporter = new MultiPublishersReporter(this.requisitionAttributes.publishers); - } - - public async delay(): Promise { - const delay = this.requisitionAttributes.delay || 0; - if (delay > 0) { - Logger.info(`Delaying requisition '${this.requisitionAttributes.name}' for ${delay}ms`); - return await new Promise((resolve) => setTimeout(() => resolve(), delay)); - } - } - - public async startTimeout(): Promise { - return new Promise(async (resolve) => { - if (this.timeout) { - Logger.debug('Starting requisition time out'); - await new Promise((resolve) => setTimeout(() => resolve(), this.timeout)); - if (!this.hasFinished) { - Logger.info(`Requisition '${this.requisitionAttributes.name}' timed out`); - await this.onRequisitionFinish(); - resolve(this.reportGenerator.getReport()); - } - } - }); - } - - public async execute(): Promise { - try { - this.multiSubscriptionsReporter.start(); - await this.multiSubscriptionsReporter.subscribe(); - await Promise.all([this.multiSubscriptionsReporter.receiveMessage(), this.multiPublishersReporter.publish()]); - } catch (err) { - Logger.error(`Requisition error: ${err}`); - this.reportGenerator.addTest(DefaultHookEvents.ON_FINISH, - {valid: false, tests: [{name: 'Requisition error', description: err, valid: false}]}); - } - if (!this.hasFinished) { - await this.onRequisitionFinish(); - } - return this.reportGenerator.getReport(); - } - - public async interrupt(): Promise { - if (!this.hasFinished) { - await this.onRequisitionFinish(); - this.reportGenerator.addTest(DefaultHookEvents.ON_FINISH, - { - valid: false, - tests: [{ - valid: false, - name: 'Requisition interrupted', - description: 'Not finished yet. There was not enough time to finish the requisition' - }] - }); - } - return this.reportGenerator.getReport(); - } - - private async onRequisitionFinish(): Promise { - this.hasFinished = true; - await this.multiSubscriptionsReporter.unsubscribe(); - await this.executeOnFinishFunction(); - Logger.info(`Start gathering reports`); - - this.reportGenerator.setPublishersReport(this.multiPublishersReporter.getReport()); - this.reportGenerator.setSubscriptionsReport(this.multiSubscriptionsReporter.getReport()); - this.reportGenerator.finish(); - - } - - private executeOnInitFunction(): TestModel[] { - Logger.debug(`Executing requisition onInit hook function`); - const eventExecutor = new EventExecutor(this.requisitionAttributes, DefaultHookEvents.ON_INIT, 'requisition'); - const elapsedTime = new Date().getTime() - this.startTime.getTime(); - eventExecutor.addArgument('elapsedTime', elapsedTime); - const testModels = eventExecutor.execute(); - const hookResult = { - valid: testModels.every(test => test.valid), - tests: testModels, - arguments: {elapsedTime: elapsedTime} - }; - this.reportGenerator.addTest(DefaultHookEvents.ON_INIT, hookResult); - NotificationEmitter.emit(Notifications.HOOK_FINISHED, { - hookName: DefaultHookEvents.ON_INIT, - hook: hookResult, - requisition: this.requisitionAttributes - }); - return testModels; - } - - private async executeOnFinishFunction(): Promise { - this.multiSubscriptionsReporter.onFinish(); - const onFinishEventExecutor = new EventExecutor(this.requisitionAttributes, DefaultHookEvents.ON_FINISH, 'requisition'); - const elapsedTime = new Date().getTime() - this.startTime.getTime(); - onFinishEventExecutor.addArgument('elapsedTime', elapsedTime); - const testModels = onFinishEventExecutor.execute(); - const hookResult = { - valid: testModels.every(test => test.valid), - tests: testModels, - arguments: {elapsedTime: elapsedTime} - }; - this.reportGenerator.addTest(DefaultHookEvents.ON_FINISH, hookResult); - NotificationEmitter.emit(Notifications.HOOK_FINISHED, { - hookName: DefaultHookEvents.ON_FINISH, - hook: hookResult, - requisition: this.requisitionAttributes - }); - this.multiPublishersReporter.onFinish(); - } - -} diff --git a/src/reporters/sensor/multi-sensors-reporter.test.ts b/src/reporters/sensor/multi-sensors-reporter.test.ts new file mode 100644 index 00000000..0d9f1364 --- /dev/null +++ b/src/reporters/sensor/multi-sensors-reporter.test.ts @@ -0,0 +1,167 @@ +import { SensorReporter } from './sensor-reporter'; +import { MultiSensorsReporter } from './multi-sensors-reporter'; + +let startTimeoutMock = jest.fn(() => {}); +let onFinishMock = jest.fn(); +let hasFinishedMock = jest.fn(); +// @ts-expect-error +let mountMock = jest.fn(() => new Promise()); +// @ts-expect-error +let receiveMessageMock = jest.fn(() => new Promise()); +let getReportMock: any; +let SensorReporterMock = jest.fn(() => { + return { + startTimeout: startTimeoutMock, + hasFinished: hasFinishedMock, + mount: mountMock, + onFinish: onFinishMock, + receiveMessage: receiveMessageMock, + getReport: getReportMock + }; +}); + +jest.mock('../sensor/sensor-reporter'); +// @ts-expect-error +SensorReporter.mockImplementation(SensorReporterMock); + +describe('MultiSensorsReporter', () => { + let constructorArgument: any; + + beforeEach(() => { + constructorArgument = [ + { + name: 'subName', + type: 'subType' + }, + { + name: 'subName2', + type: 'subType2' + } + ]; + startTimeoutMock = jest.fn(() => {}); + mountMock = jest.fn(() => new Promise(() => {})); + receiveMessageMock = jest.fn(() => new Promise(() => {})); + }); + + afterEach(() => { + // @ts-expect-error + SensorReporter.mockImplementation(SensorReporterMock); + }); + + it('Should call subReporter constructor', () => { + new MultiSensorsReporter(constructorArgument); + + expect(SensorReporterMock).toHaveBeenNthCalledWith(1, constructorArgument[0]); + expect(SensorReporterMock).toHaveBeenNthCalledWith(2, constructorArgument[1]); + }); + + it('Should call getReport of each', () => { + getReportMock = jest.fn(() => { + return { + type: 'iei', + valid: false, + tests: [{ valid: true }] + }; + }); + const multi = new MultiSensorsReporter(constructorArgument); + + const report = multi.getReport(); + + expect(report).toEqual([ + { tests: [{ valid: true }], type: 'iei', valid: false }, + { + tests: [{ valid: true }], + type: 'iei', + valid: false + } + ]); + expect(getReportMock).toHaveBeenCalledTimes(2); + }); + + it('Should call onFinish of each', () => { + onFinishMock = jest.fn(); + const multi = new MultiSensorsReporter(constructorArgument); + + multi.onFinish(); + + expect(onFinishMock).toHaveBeenCalledTimes(2); + }); + + it('Sub timeout before mounted', async () => { + // @ts-expect-error + startTimeoutMock = jest.fn((cb: any) => setTimeout(cb, 2000)); + hasFinishedMock = jest.fn(() => true); + + const multi = new MultiSensorsReporter(constructorArgument); + + multi.start(); + const sensorResult = await multi.mount(); + console.log(sensorResult); + }); + + it('Subtimeout before receiving message', async () => { + receiveMessageMock = jest.fn(() => Promise.resolve()); + + const sensors = [{}] as any; + const multiSensorsReporter = new MultiSensorsReporter(sensors); + await multiSensorsReporter.receiveMessage(); + expect(receiveMessageMock).toHaveBeenCalledTimes(sensors.length); + }); + + it('Sub mounted', done => { + mountMock = jest.fn(() => Promise.resolve()); + let timeoutCb = jest.fn(); + + const multi = new MultiSensorsReporter(constructorArgument); + + multi.start(); + multi.mount().then(() => { + expect(startTimeoutMock).toHaveBeenCalled(); + expect(timeoutCb).not.toHaveBeenCalled(); + done(); + }); + }); + + it('Handling receiveMessage no sensor', done => { + const multi = new MultiSensorsReporter([]); + + multi.receiveMessage().then(() => { + done(); + }); + }); + + it('Handling receiveMessage success', done => { + expect.assertions(0); + receiveMessageMock = jest.fn(() => Promise.resolve()); + + // @ts-expect-error + new MultiSensorsReporter([{}]).receiveMessage().then(() => { + done(); + }); + }); + + it('Should receiveMessage be success when there is no sensor', done => { + expect.assertions(0); + receiveMessageMock = jest.fn(() => Promise.resolve()); + + new MultiSensorsReporter([]).receiveMessage().then(() => { + done(); + }); + }); + + it('Handling happy path', done => { + expect.assertions(0); + mountMock = jest.fn(() => Promise.resolve()); + receiveMessageMock.mockImplementationOnce(() => Promise.resolve()); + // @ts-expect-error + const multi = new MultiSensorsReporter([{}]); + multi + // @ts-expect-error + .mount(() => {}) + .then(() => { + multi.receiveMessage().then(() => { + done(); + }); + }); + }); +}); diff --git a/src/reporters/sensor/multi-sensors-reporter.ts b/src/reporters/sensor/multi-sensors-reporter.ts new file mode 100644 index 00000000..e4860d6a --- /dev/null +++ b/src/reporters/sensor/multi-sensors-reporter.ts @@ -0,0 +1,78 @@ +import * as input from '../../models/inputs/sensor-model'; +import * as output from '../../models/outputs/sensor-model'; +import { SensorReporter } from './sensor-reporter'; +import { Logger } from '../../loggers/logger'; +import { ComponentImporter } from '../../task-runners/component-importer'; + +export class MultiSensorsReporter { + private sensors: SensorReporter[] = []; + private timeoutPromise: Promise; + + constructor(sensors: input.SensorModel[]) { + Logger.debug(`Instantiating sensors`); + this.sensors = sensors.map(sensor => new SensorReporter(new ComponentImporter().importSensor(sensor))); + this.timeoutPromise = Promise.resolve(); + } + + public start(): void { + this.timeoutPromise = new Promise(resolve => { + this.sensors.forEach(sensor => { + sensor.startTimeout(() => { + if (this.sensors.every(sensor => sensor.hasFinished())) { + const message = `Every sensor has finished its job`; + Logger.debug(message); + resolve(message); + } + }); + }); + }); + } + + public async mount(): Promise { + Logger.debug(`Sensors are getting ready`); + return Promise.race([ + Promise.all( + this.sensors.map(async sensor => { + try { + await sensor.mount(); + } catch (err) { + Logger.error(`Error getting ready: ${err}`); + } + }) + ), + this.timeoutPromise + ]); + } + + public async receiveMessage(): Promise { + Logger.debug(`Sensors are waiting for messages`); + await Promise.race([ + Promise.all( + this.sensors.map(async sensor => { + try { + await sensor.receiveMessage(); + Logger.debug(`A sensor received a message`); + } catch (err) { + Logger.error(`Error receiving message: ${err}`); + } + }) + ), + this.timeoutPromise + ]); + + Logger.debug(`Sensors are no longer waiting for messages`); + } + + public async unmount(): Promise { + Logger.debug(`Sensors are closing`); + return await Promise.all(this.sensors.map(sensor => sensor.unmount())); + } + + public getReport(): output.SensorModel[] { + return this.sensors.map(sensor => sensor.getReport()); + } + + public onFinish(): void { + this.sensors.forEach(sensorHandler => sensorHandler.onFinish()); + } +} diff --git a/src/reporters/sensor/sensor-final-reporter.test.ts b/src/reporters/sensor/sensor-final-reporter.test.ts new file mode 100644 index 00000000..3e002f26 --- /dev/null +++ b/src/reporters/sensor/sensor-final-reporter.test.ts @@ -0,0 +1,299 @@ +import { SensorFinalReporter } from './sensor-final-reporter'; + +describe('SensorFinalReporter', () => { + it('No mounted, no avoidable, no message, no timeout', () => { + const mounted = false; + const avoidable = false; + const finalReporter: SensorFinalReporter = new SensorFinalReporter({ + mounted: mounted, + avoidable: avoidable + }); + + const report = finalReporter.getReport(); + + expect(report.length).toBe(1); + expect(report[0].valid).toBeFalsy(); + expect(report[0].implicit).toBeTruthy(); + expect(report[0].name).toBe('Prepared'); + }); + + it('No mounted, no avoidable, no message, timeout', () => { + const mounted = false; + const avoidable = false; + const errorDescription = 'error'; + const finalReporter: SensorFinalReporter = new SensorFinalReporter({ + mounted: mounted, + avoidable: avoidable, + mountError: errorDescription + }); + + const report = finalReporter.getReport(); + + expect(report.length).toBe(1); + expect(report[0].valid).toBeFalsy(); + expect(report[0].name).toBe('Prepared'); + expect(report[0].description).toContain(errorDescription); + expect(report[0].implicit).toBeTruthy(); + }); + + it('No mounted, no avoidable, message, no timeout', () => { + const mounted = false; + const avoidable = false; + const finalReporter: SensorFinalReporter = new SensorFinalReporter({ + mounted: mounted, + avoidable: avoidable, + messageReceived: 'messageReceived' + }); + + const report = finalReporter.getReport(); + + expect(report.length).toBe(1); + expect(report[0].valid).toBeFalsy(); + expect(report[0].name).toBe('Prepared'); + expect(report[0].implicit).toBeTruthy(); + }); + + it('No mounted, no avoidable, message, timeout', () => { + const mounted = false; + const avoidable = false; + const finalReporter: SensorFinalReporter = new SensorFinalReporter({ + mounted: mounted, + avoidable: avoidable, + messageReceived: 'messageReceived', + time: { timeout: 1000, totalTime: 1001 } + }); + + const report = finalReporter.getReport(); + + expect(report.length).toBe(1); + expect(report[0].valid).toBeFalsy(); + expect(report[0].name).toBe('Prepared'); + expect(report[0].implicit).toBeTruthy(); + }); + + it('No mounted, avoidable, no message, no timeout', () => { + const mounted = false; + const avoidable = true; + const finalReporter: SensorFinalReporter = new SensorFinalReporter({ + mounted: mounted, + avoidable: avoidable + }); + + const report = finalReporter.getReport(); + + expect(report.length).toBe(1); + expect(report[0].valid).toBeFalsy(); + expect(report[0].name).toBe('Prepared'); + expect(report[0].implicit).toBeTruthy(); + }); + + it('No mounted, avoidable, no message, timeout', () => { + const mounted = false; + const avoidable = true; + const finalReporter: SensorFinalReporter = new SensorFinalReporter({ + mounted: mounted, + avoidable: avoidable, + time: { timeout: 1000, totalTime: 1001 } + }); + + const report = finalReporter.getReport(); + + expect(report.length).toBe(1); + expect(report[0].valid).toBeFalsy(); + expect(report[0].name).toBe('Prepared'); + expect(report[0].implicit).toBeTruthy(); + }); + + it('No mounted, avoidable, message, no timeout', () => { + const mounted = false; + const avoidable = true; + const finalReporter: SensorFinalReporter = new SensorFinalReporter({ + mounted: mounted, + avoidable: avoidable, + messageReceived: 'messageReceived' + }); + + const report = finalReporter.getReport(); + + expect(report.length).toBe(1); + expect(report[0].valid).toBeFalsy(); + expect(report[0].name).toBe('Prepared'); + expect(report[0].implicit).toBeTruthy(); + }); + + it('No mounted, avoidable, message, timeout', () => { + const mounted = false; + const avoidable = true; + const finalReporter: SensorFinalReporter = new SensorFinalReporter({ + mounted: mounted, + avoidable: avoidable, + messageReceived: 'messageReceived', + time: { timeout: 1000, totalTime: 1001 } + }); + + const report = finalReporter.getReport(); + + expect(report.length).toBe(1); + expect(report[0].valid).toBeFalsy(); + expect(report[0].name).toBe('Prepared'); + expect(report[0].implicit).toBeTruthy(); + }); + + it('Prepared, no avoidable, no message, no timeout', () => { + const mounted = true; + const avoidable = false; + const finalReporter: SensorFinalReporter = new SensorFinalReporter({ + mounted: mounted, + avoidable: avoidable + }); + + const report = finalReporter.getReport(); + + expect(report.length).toBe(1); + expect(report[0].valid).toBeFalsy(); + expect(report[0].name).toBe('Message received'); + expect(report[0].implicit).toBeTruthy(); + }); + + it('Prepared, no avoidable, no message, timeout', () => { + const mounted = true; + const avoidable = false; + const finalReporter: SensorFinalReporter = new SensorFinalReporter({ + mounted: mounted, + avoidable: avoidable, + time: { timeout: 1000, totalTime: 1001 } + }); + + const report = finalReporter.getReport(); + + expect(report.length).toBe(2); + expect(report[0].valid).toBeFalsy(); + expect(report[0].name).toBe('Message received'); + expect(report[1].valid).toBeFalsy(); + expect(report[1].name).toBe('No time out'); + expect(report[0].implicit).toBeTruthy(); + }); + + it('Ignored', () => { + const mounted = true; + const avoidable = false; + const finalReporter: SensorFinalReporter = new SensorFinalReporter({ + ignore: true, + mounted: mounted, + avoidable: avoidable, + time: { timeout: 1000, totalTime: 1001 } + }); + + const report = finalReporter.getReport(); + + expect(report.length).toBe(0); + }); + + it('Prepared, no avoidable, message, no timeout', () => { + const mounted = true; + const avoidable = false; + const finalReporter: SensorFinalReporter = new SensorFinalReporter({ + mounted: mounted, + avoidable: avoidable, + messageReceived: 'messageReceived' + }); + + const report = finalReporter.getReport(); + + expect(report.length).toBe(1); + expect(report[0].valid).toBeTruthy(); + expect(report[0].name).toBe('Message received'); + expect(report[0].description).toBe('messageReceived'); + expect(report[0].implicit).toBeTruthy(); + }); + + it('Prepared, no avoidable, message, timeout', () => { + const mounted = true; + const avoidable = false; + const finalReporter: SensorFinalReporter = new SensorFinalReporter({ + mounted: mounted, + avoidable: avoidable, + messageReceived: 'messageReceived', + time: { timeout: 1000, totalTime: 1001 } + }); + + const report = finalReporter.getReport(); + + expect(report.length).toBe(2); + expect(report[0].valid).toBeTruthy(); + expect(report[0].name).toBe('Message received'); + expect(report[0].implicit).toBeTruthy(); + expect(report[0].description).toBe('messageReceived'); + expect(report[1].valid).toBeFalsy(); + expect(report[1].name).toBe('No time out'); + expect(report[1].implicit).toBeTruthy(); + }); + + it('Prepared, avoidable, no message, no timeout', () => { + const mounted = true; + const avoidable = true; + const finalReporter: SensorFinalReporter = new SensorFinalReporter({ + mounted: mounted, + avoidable: avoidable + }); + + const report = finalReporter.getReport(); + + expect(report.length).toBe(1); + expect(report[0].valid).toBeTruthy(); + expect(report[0].name).toBe('Sensor avoided'); + expect(report[0].implicit).toBeTruthy(); + }); + + it('Prepared, avoidable, no message, timeout', () => { + const mounted = true; + const avoidable = true; + const finalReporter: SensorFinalReporter = new SensorFinalReporter({ + mounted: mounted, + avoidable: avoidable, + time: { timeout: 1000, totalTime: 1001 } + }); + + const report = finalReporter.getReport(); + + expect(report.length).toBe(1); + expect(report[0].valid).toBeTruthy(); + expect(report[0].name).toBe('Sensor avoided'); + expect(report[0].implicit).toBeTruthy(); + }); + + it('Prepared, avoidable, message, no timeout', () => { + const mounted = true; + const avoidable = true; + const finalReporter: SensorFinalReporter = new SensorFinalReporter({ + mounted: mounted, + avoidable: avoidable, + messageReceived: 'messageReceived' + }); + + const report = finalReporter.getReport(); + + expect(report.length).toBe(1); + expect(report[0].valid).toBeFalsy(); + expect(report[0].name).toBe('Sensor avoided'); + expect(report[0].implicit).toBeTruthy(); + }); + + it('Prepared, avoidable, message, timeout', () => { + const mounted = true; + const avoidable = true; + const finalReporter: SensorFinalReporter = new SensorFinalReporter({ + mounted: mounted, + avoidable: avoidable, + messageReceived: 'messageReceived', + time: { timeout: 1000, totalTime: 1001 } + }); + + const report = finalReporter.getReport(); + + expect(report.length).toBe(1); + expect(report[0].valid).toBeFalsy(); + expect(report[0].name).toBe('Sensor avoided'); + expect(report[0].implicit).toBeTruthy(); + }); +}); diff --git a/src/reporters/sensor/sensor-final-reporter.ts b/src/reporters/sensor/sensor-final-reporter.ts new file mode 100644 index 00000000..a9506a64 --- /dev/null +++ b/src/reporters/sensor/sensor-final-reporter.ts @@ -0,0 +1,128 @@ +import { TestModel } from '../../models/outputs/test-model'; + +export type Time = { timeout?: number; totalTime: number }; +export type Summary = { + mounted: boolean; + avoidable: boolean; + messageReceived?: any; + time?: Time; + ignore?: boolean; + mountError?: string; +}; + +export class SensorFinalReporter { + private messageReceivedTestName: string = `Message received`; + private sensorAvoidedTestName: string = `Sensor avoided`; + private noTimeOutTestName: string = `No time out`; + private mountedTestName: string = `Prepared`; + + private readonly mounted: boolean; + private readonly avoidable: boolean; + private readonly messageReceived?: any = false; + private readonly ignore: boolean; + private readonly time?: Time; + private readonly mountError?: string; + + constructor(summary: Summary) { + this.mounted = summary.mounted; + this.avoidable = summary.avoidable; + this.messageReceived = summary.messageReceived; + this.time = summary.time; + this.mountError = summary.mountError; + this.ignore = !!summary.ignore; + } + + public getReport(): TestModel[] { + if (this.ignore) { + return []; + } + if (!this.mounted) { + return this.createNotPreparedTests(); + } + let tests: TestModel[] = []; + if (this.avoidable) { + tests = tests.concat(this.createAvoidableTests()); + } else { + tests = tests.concat(this.createMessageTests()); + } + return tests.concat(this.addTimeoutTests()); + } + + private addTimeoutTests(): TestModel[] { + if (this.time) { + if (!!this.time.timeout && this.time.totalTime > this.time.timeout) { + return this.createTimeoutTests(); + } + } + return []; + } + + private createNotPreparedTests(): TestModel[] { + return [ + { + implicit: true, + valid: false, + name: this.mountedTestName, + description: this.mountError || 'Sensor failed to mount' + } + ]; + } + + private createMessageTests(): TestModel[] { + if (this.messageReceived) { + return [ + { + implicit: true, + valid: true, + name: this.messageReceivedTestName, + description: this.messageReceived + } + ]; + } else { + return [ + { + implicit: true, + valid: false, + name: this.messageReceivedTestName, + description: `Sensor has not received its message` + } + ]; + } + } + + private createTimeoutTests(): TestModel[] { + if (!this.avoidable && this.time) { + return [ + { + implicit: true, + valid: false, + name: this.noTimeOutTestName, + description: `Not avoidable sensor has timed out: ${this.time.totalTime} > ${this.time.timeout}` + } + ]; + } + return []; + } + + private createAvoidableTests(): TestModel[] { + if (this.messageReceived) { + return [ + { + implicit: true, + valid: false, + name: this.sensorAvoidedTestName, + description: `Avoidable sensor should not receive messages` + } + ]; + } else { + return [ + { + valid: true, + implicit: true, + name: this.sensorAvoidedTestName, + description: `Avoidable sensor has not received any message` + } + ]; + } + } +} diff --git a/src/reporters/sensor/sensor-reporter.ts b/src/reporters/sensor/sensor-reporter.ts new file mode 100644 index 00000000..3903e3af --- /dev/null +++ b/src/reporters/sensor/sensor-reporter.ts @@ -0,0 +1,262 @@ +import { Logger } from '../../loggers/logger'; +import { DateController } from '../../timers/date-controller'; +import { Sensor } from '../../sensors/sensor'; +import { Timeout } from '../../timers/timeout'; +import * as input from '../../models/inputs/sensor-model'; +import { SensorModel } from '../../models/inputs/sensor-model'; +import * as output from '../../models/outputs/sensor-model'; +import { SensorFinalReporter } from './sensor-final-reporter'; +import { DynamicModulesManager } from '../../plugins/dynamic-modules-manager'; +import { EventExecutor } from '../../events/event-executor'; +import { DefaultHookEvents } from '../../models/events/event'; +import { ObjectDecycler } from '../../object-parser/object-decycler'; +import { TestModel, testModelIsPassing } from '../../models/outputs/test-model'; +import Signals = NodeJS.Signals; +import SignalsListener = NodeJS.SignalsListener; +import { HookReporter } from '../hook-reporter'; +import { NotificationEmitter } from '../../notifications/notification-emitter'; +import { Notifications } from '../../notifications/notifications'; + +export class SensorReporter { + public static readonly DEFAULT_TIMEOUT: number = 3 * 1000; + private readonly killListener: SignalsListener; + private readonly report: output.SensorModel; + private readonly startTime: DateController; + private readonly sensor: Sensor; + private readonly executedHooks: { [propName: string]: string[] }; + private mountError?: string; + private hasTimedOut: boolean = false; + private mounted: boolean = false; + private totalTime?: DateController; + + constructor(sensorAttributes: input.SensorModel) { + this.startTime = new DateController(); + this.report = { + id: sensorAttributes.id, + name: sensorAttributes.name, + ignored: sensorAttributes.ignore, + type: sensorAttributes.type, + hooks: { + [DefaultHookEvents.ON_INIT]: { valid: true, tests: [] }, + [DefaultHookEvents.ON_FINISH]: { valid: true, tests: [] } + }, + valid: true + }; + this.executedHooks = {}; + this.executeOnInitFunction(sensorAttributes); + + Logger.debug(`Instantiating sensor ${sensorAttributes.type}`); + this.sensor = DynamicModulesManager.getInstance().getProtocolManager().createSensor(sensorAttributes); + this.sensor.registerHookEventExecutor((eventName: string, args: any) => + this.executeHookEvent(eventName, { ...args, elapsedTime: new Date().getTime() - this.startTime.getTime() }) + ); + if (sensorAttributes.timeout === undefined) { + this.sensor.timeout = SensorReporter.DEFAULT_TIMEOUT; + } else if (sensorAttributes.timeout <= 0) { + delete this.sensor.timeout; + } + this.killListener = (signal: Signals) => this.handleKillSignal(signal, this.sensor.type || 'undefined'); + } + + public hasFinished(): boolean { + return this.sensor.ignore || this.sensor.messageReceived || this.hasTimedOut; + } + + public startTimeout(onTimeOutCallback: Function) { + if (this.sensor.timeout) { + Logger.debug(`Starting sensor '${this.sensor.name}' timeout`); + new Timeout(() => { + if (!this.sensor.messageReceived) { + this.totalTime = new DateController(); + const message = `Sensor '${this.sensor.name}' stopped waiting because it has timed out (${this.sensor.timeout}ms)`; + Logger.info(message); + this.hasTimedOut = true; + onTimeOutCallback(); + } + }).start(this.sensor.timeout); + } + } + + public async mount(): Promise { + if (this.sensor.ignore) { + Logger.trace(`Sensor '${this.sensor.name}' is ignored`); + } else { + try { + Logger.trace(`Starting '${this.sensor.name}' time out`); + Logger.trace(`Sensor '${this.sensor.name}' is getting ready`); + await this.sensor.mount(); + await this.handleSensor(); + } catch (err) { + const message = `Sensor '${this.sensor.name}' is unable to mount: ${err}`; + Logger.error(message); + this.mountError = `${err}`; + throw err; + } + } + } + + public async receiveMessage(): Promise { + if (!this.sensor.ignore) { + try { + const messageReceived = this.processMessage(await this.sensor.receiveMessage()); + this.sensor.messageReceived = messageReceived; + Logger.debug(`${this.sensor.name} received its message`); + this.handleMessageArrival(); + await this.sendSyncResponse(); + Logger.trace(`Sensor '${this.sensor.name}' has finished its job`); + } catch (err) { + this.sensor.unmount().catch(console.log.bind(console)); + Logger.error(`Sensor '${this.sensor.name}' is unable to receive message: ${err}`); + throw err; + } + } + } + + private processMessage(messageReceived: any): string { + if (messageReceived) { + if (typeof messageReceived === 'object') { + return JSON.stringify(new ObjectDecycler().decycle(messageReceived), null, 2); + } + return messageReceived; + } + return `Sensor has received its message`; + } + + private async handleSensor(): Promise { + process.once('SIGINT', this.killListener).once('SIGTERM', this.killListener); + if (this.hasTimedOut) { + const message = `Sensor '${this.sensor.name}' sensor because it has timed out`; + Logger.error(message); + return false; + } else { + this.report.sensorTime = new DateController().toString(); + this.mounted = true; + return true; + } + } + + private async sendSyncResponse(): Promise { + try { + Logger.debug(`Sensor ${this.sensor.type} sending synchronous response`); + await this.sensor.respond(); + } catch (err) { + Logger.warning(`Error ${this.sensor.type} synchronous response sending: ${err}`); + this.report.hooks![DefaultHookEvents.ON_FINISH].tests = this.report.hooks![ + DefaultHookEvents.ON_FINISH + ].tests.concat({ + valid: false, + name: 'Response sent', + description: `${err}` + }); + } + } + + public getReport(): output.SensorModel { + const time: any = { + timeout: this.sensor.timeout + }; + if (!this.totalTime) { + this.totalTime = new DateController(); + } + time.totalTime = this.totalTime.getTime() - this.startTime.getTime(); + const finalReporter = new SensorFinalReporter({ + mounted: this.mounted, + avoidable: this.sensor.avoid, + messageReceived: this.sensor.messageReceived, + time: time, + mountError: this.mountError, + ignore: this.sensor.ignore + }); + + const finalReport = finalReporter.getReport(); + this.report.hooks![DefaultHookEvents.ON_FINISH].tests = + this.report.hooks![DefaultHookEvents.ON_FINISH].tests.concat(finalReport); + this.report.hooks![DefaultHookEvents.ON_FINISH].valid = + this.report.hooks![DefaultHookEvents.ON_FINISH].valid && + finalReport.every((report: TestModel) => testModelIsPassing(report)); + + this.report.valid = + this.report.valid && + Object.keys(this.report.hooks || {}).every((key: string) => + this.report.hooks ? this.report.hooks[key].valid : true + ); + return this.report; + } + + public async unmount(): Promise { + process.removeListener('SIGINT', this.killListener).removeListener('SIGTERM', this.killListener); + + Logger.debug(`Closing sensor ${this.sensor.type}`); + if (this.mounted) { + return this.sensor.unmount(); + } + } + + public onFinish() { + if (!this.sensor.ignore) { + this.executeHookEvent(DefaultHookEvents.ON_FINISH, { + executedHooks: this.executedHooks, + elapsedTime: new Date().getTime() - this.startTime.getTime() + }); + NotificationEmitter.emit(Notifications.SUBSCRIPTION_FINISHED, { + sensor: this.report + }); + } + } + + private executeHookEvent(eventName: string, additionalArgs: any = {}, sensor: any = this.sensor): void { + if (!sensor.ignore) { + this.executedHooks[eventName] = Object.keys(additionalArgs); + const eventExecutor = new EventExecutor(sensor, eventName, 'sensor'); + if (typeof additionalArgs === 'object') { + Object.keys(additionalArgs).forEach((key: string) => { + eventExecutor.addArgument(key, additionalArgs[key]); + }); + } + const tests = eventExecutor.execute(); + const valid = tests.every((test: TestModel) => testModelIsPassing(test)); + const decycledArgs = new ObjectDecycler().decycle(additionalArgs); + const hookModel = { + arguments: decycledArgs, + tests: tests, + valid: valid + }; + if (eventExecutor.isDebugMode()) { + // console.table(decycledArgs); + console.table(Object.keys(decycledArgs).join('; ')); + } + const hookResult = new HookReporter(this.report.hooks![eventName]).addValues(hookModel); + this.report.hooks![eventName] = hookResult; + NotificationEmitter.emit(Notifications.HOOK_FINISHED, { + hookName: eventName, + hook: hookResult, + sensor: this.sensor + }); + } + } + + private handleMessageArrival() { + if (!this.hasTimedOut) { + Logger.debug(`${this.sensor.name} stop waiting because it has received its message`); + this.totalTime = new DateController(); + } else { + Logger.info(`${this.sensor.name} has received message in a unable time`); + } + Logger.debug(`${this.sensor.name} handled message arrival`); + } + + private executeOnInitFunction(sensorAttributes: SensorModel) { + if (!sensorAttributes.ignore) { + NotificationEmitter.emit(Notifications.SUBSCRIPTION_STARTED, { + sensor: sensorAttributes + }); + this.executeHookEvent(DefaultHookEvents.ON_INIT, {}, sensorAttributes); + } + } + + private async handleKillSignal(signal: Signals, type: string): Promise { + Logger.debug(`Sensor reporter '${type}' handling kill signal ${signal}`); + await this.unmount(); + Logger.debug(`Sensor reporter '${type}' closed`); + } +} diff --git a/src/reporters/subscription/multi-subscriptions-reporter.test.ts b/src/reporters/subscription/multi-subscriptions-reporter.test.ts deleted file mode 100644 index 88e666d5..00000000 --- a/src/reporters/subscription/multi-subscriptions-reporter.test.ts +++ /dev/null @@ -1,166 +0,0 @@ -import {SubscriptionReporter} from './subscription-reporter'; -import {MultiSubscriptionsReporter} from './multi-subscriptions-reporter'; - -let startTimeoutMock = jest.fn(() => { -}); -let onFinishMock = jest.fn(); -let hasFinishedMock = jest.fn(); -let subscribeMock = jest.fn(() => new Promise()); -let receiveMessageMock = jest.fn(() => new Promise()); -let getReportMock; -let SubscriptionReporterMock = jest.fn(() => { - return { - startTimeout: startTimeoutMock, - hasFinished: hasFinishedMock, - subscribe: subscribeMock, - onFinish: onFinishMock, - receiveMessage: receiveMessageMock, - getReport: getReportMock - }; -}); - -jest.mock('../subscription/subscription-reporter'); -SubscriptionReporter.mockImplementation(SubscriptionReporterMock); - -describe('MultiSubscriptionsReporter', () => { - - let constructorArgument; - - beforeEach(() => { - constructorArgument = [ - { - name: 'subName', - type: 'subType' - }, - { - name: 'subName2', - type: 'subType2' - } - ]; - startTimeoutMock = jest.fn(() => { - }); - subscribeMock = jest.fn(() => new Promise(() => { - })); - receiveMessageMock = jest.fn(() => new Promise(() => { - })); - }); - - afterEach(() => { - SubscriptionReporter.mockImplementation(SubscriptionReporterMock); - }); - - it('Should call subReporter constructor', () => { - new MultiSubscriptionsReporter(constructorArgument); - - expect(SubscriptionReporterMock).toHaveBeenNthCalledWith(1, constructorArgument[0]); - expect(SubscriptionReporterMock).toHaveBeenNthCalledWith(2, constructorArgument[1]); - }); - - it('Should call getReport of each', () => { - getReportMock = jest.fn(() => { - return { - type: 'iei', - valid: false, - tests: [{valid: true}] - }; - }); - const multi = new MultiSubscriptionsReporter(constructorArgument); - - const report = multi.getReport(); - - expect(report).toEqual([{'tests': [{'valid': true}], 'type': 'iei', 'valid': false}, { - 'tests': [{'valid': true}], - 'type': 'iei', - 'valid': false - }]); - expect(getReportMock).toHaveBeenCalledTimes(2); - }); - - it('Should call onFinish of each', () => { - onFinishMock = jest.fn(); - const multi = new MultiSubscriptionsReporter(constructorArgument); - - multi.onFinish(); - - expect(onFinishMock).toHaveBeenCalledTimes(2); - }); - - it('Sub timeout before subscribed', async () => { - startTimeoutMock = jest.fn((cb: any) => setTimeout(cb, 2000)); - hasFinishedMock = jest.fn(() => true); - - const multi = new MultiSubscriptionsReporter(constructorArgument); - - multi.start(); - const subscriptionResult = await multi.subscribe(); - console.log(subscriptionResult); - }); - - it('Subtimeout before receiving message', async () => { - receiveMessageMock = jest.fn(() => Promise.resolve()); - - const subscriptions = [{}]; - const multiSubscriptionsReporter = new MultiSubscriptionsReporter(subscriptions); - await multiSubscriptionsReporter.receiveMessage(); - expect(receiveMessageMock).toHaveBeenCalledTimes(subscriptions.length); - - }); - - it('Sub subscribed', done => { - subscribeMock = jest.fn(() => Promise.resolve()); - let timeoutCb = jest.fn(); - - const multi = new MultiSubscriptionsReporter(constructorArgument); - - multi.start(timeoutCb); - multi.subscribe().then(() => { - expect(startTimeoutMock).toHaveBeenCalled(); - expect(timeoutCb).not.toHaveBeenCalled(); - done(); - }); - }); - - it('Handling receiveMessage no subscription', done => { - const multi = new MultiSubscriptionsReporter([]); - - multi.receiveMessage().then(() => { - done(); - }); - }); - - it('Handling receiveMessage success', done => { - expect.assertions(0); - receiveMessageMock = jest.fn(() => Promise.resolve()); - - new MultiSubscriptionsReporter([{}]).receiveMessage().then(() => { - done(); - }); - - }); - - it('Should receiveMessage be success when there is no subscription', done => { - expect.assertions(0); - receiveMessageMock = jest.fn(() => Promise.resolve()); - - new MultiSubscriptionsReporter([]).receiveMessage().then(() => { - done(); - }); - - }); - - it('Handling happy path', done => { - expect.assertions(0); - subscribeMock = jest.fn(() => Promise.resolve()); - receiveMessageMock.mockImplementationOnce(() => Promise.resolve()); - - const multi = new MultiSubscriptionsReporter([{}]); - multi.subscribe(() => { - }).then(() => { - multi.receiveMessage().then(() => { - done(); - }); - - }); - }); - -}); diff --git a/src/reporters/subscription/multi-subscriptions-reporter.ts b/src/reporters/subscription/multi-subscriptions-reporter.ts deleted file mode 100644 index 2aa6ac20..00000000 --- a/src/reporters/subscription/multi-subscriptions-reporter.ts +++ /dev/null @@ -1,74 +0,0 @@ -import * as input from '../../models/inputs/subscription-model'; -import * as output from '../../models/outputs/subscription-model'; -import {SubscriptionReporter} from './subscription-reporter'; -import {Logger} from '../../loggers/logger'; -import {ComponentImporter} from '../../requisition-runners/component-importer'; - -export class MultiSubscriptionsReporter { - private subscriptions: SubscriptionReporter[] = []; - private timeoutPromise: Promise; - - constructor(subscriptions: input.SubscriptionModel[]) { - Logger.debug(`Instantiating subscriptions`); - this.subscriptions = subscriptions - .map(subscription => new SubscriptionReporter(new ComponentImporter().importSubscription(subscription))); - this.timeoutPromise = Promise.resolve(); - } - - public start(): void { - this.timeoutPromise = new Promise((resolve) => { - this.subscriptions.forEach(subscription => { - subscription.startTimeout(() => { - if (this.subscriptions.every(subscription => subscription.hasFinished())) { - const message = `Every subscription has finished its job`; - Logger.debug(message); - resolve(message); - } - }); - }); - }); - } - - public async subscribe(): Promise { - Logger.debug(`Subscriptions are subscribing`); - return Promise.race([ - Promise.all(this.subscriptions.map(async subscription => { - try { - await subscription.subscribe(); - } catch (err) { - Logger.error(`Error subscribing: ${err}`); - } - })), - this.timeoutPromise]); - } - - public async receiveMessage(): Promise { - Logger.debug(`Subscriptions are waiting for messages`); - await Promise.race([ - Promise.all(this.subscriptions.map(async subscription => { - try { - await subscription.receiveMessage(); - Logger.debug(`A subscription received a message`); - } catch (err) { - Logger.error(`Error receiving message: ${err}`); - } - })), - this.timeoutPromise]); - - Logger.debug(`Subscriptions are no longer waiting for messages`); - } - - public async unsubscribe(): Promise { - Logger.debug(`Subscriptions are unsubscribing`); - return await Promise.all(this.subscriptions.map(subscription => subscription.unsubscribe())); - } - - public getReport(): output.SubscriptionModel[] { - return this.subscriptions.map(subscription => subscription.getReport()); - } - - public onFinish(): void { - this.subscriptions.forEach(subscriptionHandler => subscriptionHandler.onFinish()); - } - -} diff --git a/src/reporters/subscription/subscription-final-reporter.test.ts b/src/reporters/subscription/subscription-final-reporter.test.ts deleted file mode 100644 index 2ac4d53c..00000000 --- a/src/reporters/subscription/subscription-final-reporter.test.ts +++ /dev/null @@ -1,285 +0,0 @@ -import {SubscriptionFinalReporter} from './subscription-final-reporter'; - -describe('SubscriptionFinalReporter', () => { - - it('No subscribed, no avoidable, no message, no timeout', () => { - const subscribed = false; - const avoidable = false; - const finalReporter: SubscriptionFinalReporter = new SubscriptionFinalReporter({ - subscribed: subscribed, - avoidable: avoidable - }, ); - - const report = finalReporter.getReport(); - - expect(report.length).toBe(1); - expect(report[0].valid).toBeFalsy(); - expect(report[0].name).toBe('Subscribed'); - }); - - it('No subscribed, no avoidable, no message, timeout', () => { - const subscribed = false; - const avoidable = false; - const errorDescription = 'error'; - const finalReporter: SubscriptionFinalReporter = new SubscriptionFinalReporter({ - subscribed: subscribed, - avoidable: avoidable, - subscribeError: errorDescription - }); - - const report = finalReporter.getReport(); - - expect(report.length).toBe(1); - expect(report[0].valid).toBeFalsy(); - expect(report[0].name).toBe('Subscribed'); - expect(report[0].description).toContain(errorDescription); - }); - - it('No subscribed, no avoidable, message, no timeout', () => { - const subscribed = false; - const avoidable = false; - const finalReporter: SubscriptionFinalReporter = new SubscriptionFinalReporter({ - subscribed: subscribed, - avoidable: avoidable, - messageReceived: 'messageReceived' - }); - - const report = finalReporter.getReport(); - - expect(report.length).toBe(1); - expect(report[0].valid).toBeFalsy(); - expect(report[0].name).toBe('Subscribed'); - }); - - it('No subscribed, no avoidable, message, timeout', () => { - const subscribed = false; - const avoidable = false; - const finalReporter: SubscriptionFinalReporter = new SubscriptionFinalReporter({ - subscribed: subscribed, - avoidable: avoidable, - messageReceived: 'messageReceived', - time: {timeout: 1000, totalTime: 1001} - }); - - const report = finalReporter.getReport(); - - expect(report.length).toBe(1); - expect(report[0].valid).toBeFalsy(); - expect(report[0].name).toBe('Subscribed'); - }); - - it('No subscribed, avoidable, no message, no timeout', () => { - const subscribed = false; - const avoidable = true; - const finalReporter: SubscriptionFinalReporter = new SubscriptionFinalReporter({ - subscribed: subscribed, - avoidable: avoidable - }); - - const report = finalReporter.getReport(); - - expect(report.length).toBe(1); - expect(report[0].valid).toBeFalsy(); - expect(report[0].name).toBe('Subscribed'); - }); - - it('No subscribed, avoidable, no message, timeout', () => { - const subscribed = false; - const avoidable = true; - const finalReporter: SubscriptionFinalReporter = new SubscriptionFinalReporter({ - subscribed: subscribed, - avoidable: avoidable, - time: {timeout: 1000, totalTime: 1001} - }); - - const report = finalReporter.getReport(); - - expect(report.length).toBe(1); - expect(report[0].valid).toBeFalsy(); - expect(report[0].name).toBe('Subscribed'); - }); - - it('No subscribed, avoidable, message, no timeout', () => { - const subscribed = false; - const avoidable = true; - const finalReporter: SubscriptionFinalReporter = new SubscriptionFinalReporter({ - subscribed: subscribed, - avoidable: avoidable, - messageReceived: 'messageReceived' - }); - - const report = finalReporter.getReport(); - - expect(report.length).toBe(1); - expect(report[0].valid).toBeFalsy(); - expect(report[0].name).toBe('Subscribed'); - }); - - it('No subscribed, avoidable, message, timeout', () => { - const subscribed = false; - const avoidable = true; - const finalReporter: SubscriptionFinalReporter = new SubscriptionFinalReporter({ - subscribed: subscribed, - avoidable: avoidable, - messageReceived: 'messageReceived', - time: {timeout: 1000, totalTime: 1001} - }); - - const report = finalReporter.getReport(); - - expect(report.length).toBe(1); - expect(report[0].valid).toBeFalsy(); - expect(report[0].name).toBe('Subscribed'); - }); - - it('Subscribed, no avoidable, no message, no timeout', () => { - const subscribed = true; - const avoidable = false; - const finalReporter: SubscriptionFinalReporter = new SubscriptionFinalReporter({ - subscribed: subscribed, - avoidable: avoidable - }); - - const report = finalReporter.getReport(); - - expect(report.length).toBe(1); - expect(report[0].valid).toBeFalsy(); - expect(report[0].name).toBe('Message received'); - }); - - it('Subscribed, no avoidable, no message, timeout', () => { - const subscribed = true; - const avoidable = false; - const finalReporter: SubscriptionFinalReporter = new SubscriptionFinalReporter({ - subscribed: subscribed, - avoidable: avoidable, - time: {timeout: 1000, totalTime: 1001} - }); - - const report = finalReporter.getReport(); - - expect(report.length).toBe(2); - expect(report[0].valid).toBeFalsy(); - expect(report[0].name).toBe('Message received'); - expect(report[1].valid).toBeFalsy(); - expect(report[1].name).toBe('No time out'); - }); - - it('Ignored', () => { - const subscribed = true; - const avoidable = false; - const finalReporter: SubscriptionFinalReporter = new SubscriptionFinalReporter({ - ignore: true, - subscribed: subscribed, - avoidable: avoidable, - time: {timeout: 1000, totalTime: 1001} - }); - - const report = finalReporter.getReport(); - - expect(report.length).toBe(0); - }); - - it('Subscribed, no avoidable, message, no timeout', () => { - const subscribed = true; - const avoidable = false; - const finalReporter: SubscriptionFinalReporter = new SubscriptionFinalReporter({ - subscribed: subscribed, - avoidable: avoidable, - messageReceived: 'messageReceived' - }); - - const report = finalReporter.getReport(); - - expect(report.length).toBe(1); - expect(report[0].valid).toBeTruthy(); - expect(report[0].name).toBe('Message received'); - expect(report[0].description).toBe('messageReceived'); - }); - - it('Subscribed, no avoidable, message, timeout', () => { - const subscribed = true; - const avoidable = false; - const finalReporter: SubscriptionFinalReporter = new SubscriptionFinalReporter({ - subscribed: subscribed, - avoidable: avoidable, - messageReceived: 'messageReceived', - time: {timeout: 1000, totalTime: 1001} - }); - - const report = finalReporter.getReport(); - - expect(report.length).toBe(2); - expect(report[0].valid).toBeTruthy(); - expect(report[0].name).toBe('Message received'); - expect(report[0].description).toBe('messageReceived'); - expect(report[1].valid).toBeFalsy(); - expect(report[1].name).toBe('No time out'); - - }); - - it('Subscribed, avoidable, no message, no timeout', () => { - const subscribed = true; - const avoidable = true; - const finalReporter: SubscriptionFinalReporter = new SubscriptionFinalReporter({ - subscribed: subscribed, - avoidable: avoidable - }); - - const report = finalReporter.getReport(); - - expect(report.length).toBe(1); - expect(report[0].valid).toBeTruthy(); - expect(report[0].name).toBe('Subscription avoided'); - }); - - it('Subscribed, avoidable, no message, timeout', () => { - const subscribed = true; - const avoidable = true; - const finalReporter: SubscriptionFinalReporter = new SubscriptionFinalReporter({ - subscribed: subscribed, - avoidable: avoidable, - time: {timeout: 1000, totalTime: 1001} - }); - - const report = finalReporter.getReport(); - - expect(report.length).toBe(1); - expect(report[0].valid).toBeTruthy(); - expect(report[0].name).toBe('Subscription avoided'); - }); - - it('Subscribed, avoidable, message, no timeout', () => { - const subscribed = true; - const avoidable = true; - const finalReporter: SubscriptionFinalReporter = new SubscriptionFinalReporter({ - subscribed: subscribed, - avoidable: avoidable, - messageReceived: 'messageReceived' - }); - - const report = finalReporter.getReport(); - - expect(report.length).toBe(1); - expect(report[0].valid).toBeFalsy(); - expect(report[0].name).toBe('Subscription avoided'); - }); - - it('Subscribed, avoidable, message, timeout', () => { - const subscribed = true; - const avoidable = true; - const finalReporter: SubscriptionFinalReporter = new SubscriptionFinalReporter({ - subscribed: subscribed, - avoidable: avoidable, - messageReceived: 'messageReceived', - time: {timeout: 1000, totalTime: 1001} - }); - - const report = finalReporter.getReport(); - - expect(report.length).toBe(1); - expect(report[0].valid).toBeFalsy(); - expect(report[0].name).toBe('Subscription avoided'); - }); - -}); diff --git a/src/reporters/subscription/subscription-final-reporter.ts b/src/reporters/subscription/subscription-final-reporter.ts deleted file mode 100644 index 550185d1..00000000 --- a/src/reporters/subscription/subscription-final-reporter.ts +++ /dev/null @@ -1,103 +0,0 @@ -import {TestModel} from '../../models/outputs/test-model'; - -export type Time = { timeout?: number; totalTime: number }; -export type Summary = { subscribed: boolean, avoidable: boolean, messageReceived?: any, time?: Time, ignore?: boolean, subscribeError?: string }; - -export class SubscriptionFinalReporter { - private messageReceivedTestName: string = `Message received`; - private subscriptionAvoidedTestName: string = `Subscription avoided`; - private noTimeOutTestName: string = `No time out`; - private subscribedTestName: string = `Subscribed`; - - private readonly subscribed: boolean; - private readonly avoidable: boolean; - private readonly messageReceived?: any = false; - private readonly ignore: boolean; - private readonly time?: Time; - private readonly subscribeError?: string; - - constructor(summary: Summary) { - this.subscribed = summary.subscribed; - this.avoidable = summary.avoidable; - this.messageReceived = summary.messageReceived; - this.time = summary.time; - this.subscribeError = summary.subscribeError; - this.ignore = !!summary.ignore; - } - - public getReport(): TestModel[] { - if (this.ignore) { - return []; - } - if (!this.subscribed) { - return this.createNotSubscribedTests(); - } - let tests: TestModel[] = []; - if (this.avoidable) { - tests = tests.concat(this.createAvoidableTests()); - } else { - tests = tests.concat(this.createMessageTests()); - } - return tests.concat(this.addTimeoutTests()); - } - - private addTimeoutTests(): TestModel[] { - if (this.time) { - if (!!this.time.timeout && this.time.totalTime > this.time.timeout) { - return this.createTimeoutTests(); - } - } - return []; - } - - private createNotSubscribedTests(): TestModel[] { - return [{ - valid: false, - name: this.subscribedTestName, - description: this.subscribeError || '' - }]; - } - - private createMessageTests(): TestModel[] { - if (this.messageReceived) { - return [{ - valid: true, - name: this.messageReceivedTestName, - description: this.messageReceived - }]; - } else { - return [{ - valid: false, - name: this.messageReceivedTestName, - description: `Subscription has not received its message` - }]; - } - } - - private createTimeoutTests(): TestModel[] { - if (!this.avoidable && this.time) { - return [{ - valid: false, - name: this.noTimeOutTestName, - description: `Not avoidable subscription has timed out: ${this.time.totalTime} > ${this.time.timeout}` - }]; - } - return []; - } - - private createAvoidableTests(): TestModel[] { - if (this.messageReceived) { - return [{ - valid: false, - name: this.subscriptionAvoidedTestName, - description: `Avoidable subscription should not receive messages` - }]; - } else { - return [{ - valid: true, - name: this.subscriptionAvoidedTestName, - description: `Avoidable subscription has not received any message` - }]; - } - } -} diff --git a/src/reporters/subscription/subscription-reporter.ts b/src/reporters/subscription/subscription-reporter.ts deleted file mode 100644 index 00209fae..00000000 --- a/src/reporters/subscription/subscription-reporter.ts +++ /dev/null @@ -1,248 +0,0 @@ -import {Logger} from '../../loggers/logger'; -import {DateController} from '../../timers/date-controller'; -import {Subscription} from '../../subscriptions/subscription'; -import {Timeout} from '../../timers/timeout'; -import * as input from '../../models/inputs/subscription-model'; -import {SubscriptionModel} from '../../models/inputs/subscription-model'; -import * as output from '../../models/outputs/subscription-model'; -import {SubscriptionFinalReporter} from './subscription-final-reporter'; -import {DynamicModulesManager} from '../../plugins/dynamic-modules-manager'; -import {EventExecutor} from '../../events/event-executor'; -import {DefaultHookEvents} from '../../models/events/event'; -import {ObjectDecycler} from '../../object-parser/object-decycler'; -import {TestModel, testModelIsPassing} from '../../models/outputs/test-model'; -import Signals = NodeJS.Signals; -import SignalsListener = NodeJS.SignalsListener; -import {HookReporter} from '../hook-reporter'; -import {NotificationEmitter} from '../../notifications/notification-emitter'; -import {Notifications} from '../../notifications/notifications'; - -export class SubscriptionReporter { - - public static readonly DEFAULT_TIMEOUT: number = 3 * 1000; - private readonly killListener: SignalsListener; - private readonly report: output.SubscriptionModel; - private readonly startTime: DateController; - private readonly subscription: Subscription; - private readonly executedHooks: string[]; - private subscribeError?: string; - private hasTimedOut: boolean = false; - private subscribed: boolean = false; - private totalTime?: DateController; - - constructor(subscriptionAttributes: input.SubscriptionModel) { - this.startTime = new DateController(); - this.report = { - id: subscriptionAttributes.id, - name: subscriptionAttributes.name, - ignored: subscriptionAttributes.ignore, - type: subscriptionAttributes.type, - hooks: { - [DefaultHookEvents.ON_INIT]: {valid: true, tests: []}, - [DefaultHookEvents.ON_FINISH]: {valid: true, tests: []} - }, - valid: true - }; - this.executedHooks = []; - this.executeOnInitFunction(subscriptionAttributes); - - Logger.debug(`Instantiating subscription ${subscriptionAttributes.type}`); - this.subscription = DynamicModulesManager.getInstance().getProtocolManager().createSubscription(subscriptionAttributes); - this.subscription.registerHookEventExecutor((eventName: string, args: any) => this.executeHookEvent(eventName, args)); - if (subscriptionAttributes.timeout === undefined) { - this.subscription.timeout = SubscriptionReporter.DEFAULT_TIMEOUT; - } else if (subscriptionAttributes.timeout <= 0) { - delete this.subscription.timeout; - } - this.killListener = (signal: Signals) => this.handleKillSignal(signal, this.subscription.type || 'undefined'); - } - - public hasFinished(): boolean { - return this.subscription.ignore || - this.subscription.messageReceived || - this.hasTimedOut; - } - - public startTimeout(onTimeOutCallback: Function) { - if (this.subscription.timeout) { - Logger.debug(`Starting subscription '${this.subscription.name}' timeout`); - new Timeout(() => { - if (!this.subscription.messageReceived) { - this.totalTime = new DateController(); - const message = `Subscription '${this.subscription.name}' stopped waiting because it has timed out`; - Logger.info(message); - this.hasTimedOut = true; - onTimeOutCallback(); - } - }).start(this.subscription.timeout); - } - } - - public async subscribe(): Promise { - if (this.subscription.ignore) { - Logger.trace(`Subscription '${this.subscription.name}' is ignored`); - } else { - try { - Logger.trace(`Starting '${this.subscription.name}' time out`); - Logger.trace(`Subscription '${this.subscription.name}' is subscribing`); - await this.subscription.subscribe(); - await this.handleSubscription(); - } catch (err) { - const message = `Subscription '${this.subscription.name}' is unable to subscribe: ${err}`; - Logger.error(message); - this.subscribeError = `${err}`; - throw err; - } - } - } - - public async receiveMessage(): Promise { - if (!this.subscription.ignore) { - try { - const messageReceived = this.processMessage(await this.subscription.receiveMessage()); - this.subscription.messageReceived = messageReceived; - Logger.debug(`${this.subscription.name} received its message`); - this.handleMessageArrival(); - await this.sendSyncResponse(); - Logger.trace(`Subscription '${this.subscription.name}' has finished its job`); - } catch (err) { - this.subscription.unsubscribe().catch(console.log.bind(console)); - Logger.error(`Subscription '${this.subscription.name}' is unable to receive message: ${err}`); - throw err; - } - } - } - - private processMessage(messageReceived: any): string { - if (messageReceived) { - if (typeof messageReceived === 'object') { - return JSON.stringify(new ObjectDecycler().decycle(messageReceived), null, 2); - } - return messageReceived; - } - return `Subscription has received its message`; - } - - private async handleSubscription(): Promise { - process.once('SIGINT', this.killListener) - .once('SIGTERM', this.killListener); - if (this.hasTimedOut) { - const message = `Subscription '${this.subscription.name}' subscription because it has timed out`; - Logger.error(message); - return false; - } else { - this.report.subscriptionTime = new DateController().toString(); - this.subscribed = true; - return true; - } - } - - private async sendSyncResponse(): Promise { - try { - Logger.debug(`Subscription ${this.subscription.type} sending synchronous response`); - await this.subscription.sendResponse(); - } catch (err) { - Logger.warning(`Error ${this.subscription.type} synchronous response sending: ${err}`); - this.report.hooks![DefaultHookEvents.ON_FINISH].tests = this.report.hooks![DefaultHookEvents.ON_FINISH] - .tests.concat({valid: false, name: 'Response sent', description: `${err}`}); - - } - } - - public getReport(): output.SubscriptionModel { - const time: any = { - timeout: this.subscription.timeout - }; - if (!this.totalTime) { - this.totalTime = new DateController(); - } - time.totalTime = this.totalTime.getTime() - this.startTime.getTime(); - const finalReporter = new SubscriptionFinalReporter({ - subscribed: this.subscribed, - avoidable: this.subscription.avoid, - messageReceived: this.subscription.messageReceived, - time: time, - subscribeError: this.subscribeError, - ignore: this.subscription.ignore - }); - - const finalReport = finalReporter.getReport(); - this.report.hooks![DefaultHookEvents.ON_FINISH].tests = this.report.hooks![DefaultHookEvents.ON_FINISH] - .tests.concat(finalReport); - this.report.hooks![DefaultHookEvents.ON_FINISH].valid = this.report.hooks![DefaultHookEvents.ON_FINISH].valid - && finalReport.every((report: TestModel) => testModelIsPassing(report)); - - this.report.valid = this.report.valid && Object.keys(this.report.hooks || {}) - .every((key: string) => this.report.hooks ? this.report.hooks[key].valid : true); - return this.report; - } - - public async unsubscribe(): Promise { - process.removeListener('SIGINT', this.killListener) - .removeListener('SIGTERM', this.killListener); - - Logger.debug(`Unsubscribing subscription ${this.subscription.type}`); - if (this.subscribed) { - return this.subscription.unsubscribe(); - } - } - - public onFinish() { - Logger.trace(`Executing subscription onFinish`); - if (!this.subscription.ignore) { - this.executeHookEvent(DefaultHookEvents.ON_FINISH, {executedHooks: this.executedHooks}); - NotificationEmitter.emit(Notifications.SUBSCRIPTION_FINISHED, {subscription: this.report}); - } - } - - private executeHookEvent(eventName: string, args: any = {}, subscription: any = this.subscription): void { - if (!subscription.ignore) { - this.executedHooks.push(eventName); - args.elapsedTime = new Date().getTime() - this.startTime.getTime(); - const eventExecutor = new EventExecutor(subscription, eventName, 'subscription'); - if (typeof args === 'object') { - Object.keys(args).forEach((key: string) => { - eventExecutor.addArgument(key, args[key]); - }); - } - const tests = eventExecutor.execute(); - const valid = tests.every((test: TestModel) => testModelIsPassing(test)); - const hookModel = { - arguments: new ObjectDecycler().decycle(args), - tests: tests, - valid: valid - }; - const hookResult = new HookReporter(this.report.hooks![eventName]).addValues(hookModel); - this.report.hooks![eventName] = hookResult; - NotificationEmitter.emit(Notifications.HOOK_FINISHED, { - hookName: eventName, - hook: hookResult, - subscription: this.subscription - }); - } - } - - private handleMessageArrival() { - if (!this.hasTimedOut) { - Logger.debug(`${this.subscription.name} stop waiting because it has received its message`); - this.totalTime = new DateController(); - } else { - Logger.info(`${this.subscription.name} has received message in a unable time`); - } - Logger.debug(`${this.subscription.name} handled message arrival`); - } - - private executeOnInitFunction(subscriptionAttributes: SubscriptionModel) { - if (!subscriptionAttributes.ignore) { - NotificationEmitter.emit(Notifications.SUBSCRIPTION_STARTED, {subscription: subscriptionAttributes}); - this.executeHookEvent(DefaultHookEvents.ON_INIT, {}, subscriptionAttributes); - } - } - - private async handleKillSignal(signal: Signals, type: string): Promise { - Logger.debug(`Subscription reporter '${type}' handling kill signal ${signal}`); - await this.unsubscribe(); - Logger.debug(`Subscription reporter '${type}' unsubscribed`); - } - -} diff --git a/src/reporters/task-report-generator.test.ts b/src/reporters/task-report-generator.test.ts new file mode 100644 index 00000000..3167edc7 --- /dev/null +++ b/src/reporters/task-report-generator.test.ts @@ -0,0 +1,184 @@ +import { TaskReportGenerator } from './task-report-generator'; +import { DefaultHookEvents } from '../models/events/event'; + +let sleep = (millisecondsToWait: number): void => { + const waitTill = new Date(new Date().getTime() + millisecondsToWait); + while (waitTill > new Date()) { + //wait + } +}; + +describe('TaskReportGenerator', () => { + it('Create default report', () => { + // @ts-expect-error + const report = new TaskReportGenerator({ + name: 'testName' + }).getReport(); + expect(report.time).toBeDefined(); + expect(report).toEqual({ + hooks: { + onFinish: { arguments: {}, tests: [], valid: true }, + onInit: { arguments: {}, tests: [], valid: true } + }, + iteration: undefined, + totalIterations: undefined, + time: { + endTime: expect.any(String), + startTime: expect.any(String), + totalTime: expect.any(Number) + }, + id: undefined, + ignored: undefined, + level: undefined, + name: 'testName', + actuators: [], + tasks: [], + sensors: [], + valid: true + }); + }); + + it('Time report with timeout', () => { + const timeout = 1000; + // @ts-expect-error + const reportGenerator = new TaskReportGenerator({ name: 'someName' }, timeout); + const firstReport = reportGenerator.getReport(); + const firstStartTime = new Date(firstReport.time.startTime.valueOf()).getTime(); + + sleep(20); + reportGenerator.finish(); + + const secondReport = reportGenerator.getReport(); + expect(new Date(secondReport.time.startTime).getTime()).toBeGreaterThanOrEqual(firstStartTime); + expect(secondReport.time.timeout).toBeGreaterThanOrEqual(timeout); + delete secondReport.tests; + + expect(secondReport).toEqual({ + hooks: { + onFinish: { arguments: {}, tests: [], valid: true }, + onInit: { arguments: {}, tests: [], valid: true } + }, + iteration: undefined, + totalIterations: undefined, + time: { + endTime: expect.any(String), + startTime: expect.any(String), + timeout: 1000, + totalTime: expect.any(Number) + }, + id: undefined, + ignored: undefined, + level: undefined, + name: 'someName', + actuators: [], + tasks: [], + sensors: [], + valid: true + }); + }); + + it('Time out test fail', () => { + const timeout = 10; + // @ts-expect-error + const reportGenerator = new TaskReportGenerator({ name: 'someName' }, timeout); + sleep(50); + reportGenerator.finish(); + + const report = reportGenerator.getReport(); + expect(report.valid).toBeFalsy(); + expect(report.hooks![DefaultHookEvents.ON_FINISH].tests.length).toBe(1); + expect(report.hooks![DefaultHookEvents.ON_FINISH].valid).toBeFalsy(); + expect(report.hooks![DefaultHookEvents.ON_FINISH].tests[0].name).toBe('No time out'); + expect(report.hooks![DefaultHookEvents.ON_FINISH].tests[0].implicit).toBeTruthy(); + expect(report.hooks![DefaultHookEvents.ON_FINISH].tests[0].valid).toBeFalsy(); + expect(report.hooks![DefaultHookEvents.ON_FINISH].tests[0].description).toBeDefined(); + }); + + it('Time report without timeout', () => { + // @ts-expect-error + const reportGenerator = new TaskReportGenerator({ + name: 'someName' + }); + + reportGenerator.finish(); + + const time = reportGenerator.getReport().time; + + expect(time.startTime).toBeDefined(); + expect(time.endTime).toBeDefined(); + expect(time.totalTime).toBe(new Date(time.endTime).getTime() - new Date(time.startTime).getTime()); + expect(time.timeout).toBeUndefined(); + }); + + it('Adding actuator report', () => { + // @ts-expect-error + const reportGenerator = new TaskReportGenerator({ + name: 'someName' + }); + + let report = reportGenerator.getReport(); + expect(report.valid).toBeTruthy(); + expect(report.actuators.length).toBe(0); + + // @ts-expect-error + reportGenerator.setActuatorsReport([{ valid: false }]); + reportGenerator.finish(); + report = reportGenerator.getReport(); + + expect(report.valid).toBeFalsy(); + expect(report.actuators.length).toBe(1); + expect(report.actuators[0].valid).toBeFalsy(); + }); + + it('Adding sensor report', () => { + // @ts-expect-error + const reportGenerator = new TaskReportGenerator({ + name: 'someName' + }); + + let report = reportGenerator.getReport(); + expect(report.valid).toBeTruthy(); + expect(report.sensors.length).toBe(0); + + // @ts-expect-error + reportGenerator.setSensorsReport([{ valid: false }]); + reportGenerator.finish(); + report = reportGenerator.getReport(); + + expect(report.valid).toBeFalsy(); + expect(report.sensors.length).toBe(1); + expect(report.sensors[0].valid).toBeFalsy(); + }); + + it('Adding tests', () => { + // @ts-expect-error + const reportGenerator = new TaskReportGenerator({ + name: 'someName' + }); + + reportGenerator.addTest('hookName', { + valid: false, + arguments: { a: 1 }, + // @ts-expect-error + tests: [{ name: 'a', valid: false }] + }); + reportGenerator.addTest('hookName', { + valid: true, + arguments: { b: 3 }, + // @ts-expect-error + tests: [{ name: 'b', valid: true }] + }); + reportGenerator.finish(); + const report = reportGenerator.getReport(); + + expect(report.valid).toBeFalsy(); + expect(report.hooks!.hookName).toEqual({ + arguments: { a: 1, b: 3 }, + tests: [ + { name: 'a', valid: false }, + { name: 'b', valid: true } + ], + valid: false + }); + }); +}); diff --git a/src/reporters/task-report-generator.ts b/src/reporters/task-report-generator.ts new file mode 100644 index 00000000..66e790d8 --- /dev/null +++ b/src/reporters/task-report-generator.ts @@ -0,0 +1,78 @@ +import { DateController } from '../timers/date-controller'; +import * as input from '../models/inputs/task-model'; +import * as output from '../models/outputs/task-model'; +import { TaskModel } from '../models/outputs/task-model'; +import { SensorModel } from '../models/outputs/sensor-model'; +import { ActuatorModel } from '../models/outputs/actuator-model'; +import { TaskDefaultReports } from '../models-defaults/outputs/task-default-reports'; +import { DefaultHookEvents } from '../models/events/event'; +import { HookModel } from '../models/outputs/hook-model'; +import { HookReporter } from './hook-reporter'; + +export class TaskReportGenerator { + private startTime: DateController = new DateController(); + private readonly timeout?: number; + private readonly report: output.TaskModel; + + public constructor(taskAttributes: input.TaskModel, timeout?: number) { + this.report = TaskDefaultReports.createDefaultReport(taskAttributes); + this.report.id = taskAttributes.id; + this.startTime = new DateController(); + this.timeout = timeout; + } + + public setActuatorsReport(actuatorsReport: ActuatorModel[]): void { + this.report.actuators = actuatorsReport; + } + + public setSensorsReport(sensorReport: SensorModel[]): void { + this.report.sensors = sensorReport; + } + + public getReport(): TaskModel { + this.report.valid = + (this.report.sensors || []).every(report => report.valid) && + (this.report.actuators || []).every(report => report.valid) && + Object.keys(this.report.hooks || {}).every((key: string) => + this.report.hooks ? this.report.hooks[key].valid : true + ); + return this.report; + } + + public finish(): void { + this.addTimesReport(); + } + + public addTest(hookName: string, hook: HookModel) { + this.report.hooks![hookName] = new HookReporter(this.report.hooks![hookName]).addValues(hook); + } + + private addTimesReport(): void { + this.report.time = this.generateTimesReport(); + if (this.timeout) { + this.report.time.timeout = this.timeout; + if (this.report.time.totalTime > this.report.time.timeout) { + this.addTest(DefaultHookEvents.ON_FINISH, { + valid: false, + tests: [ + { + valid: false, + implicit: true, + name: 'No time out', + description: `Task has timed out: ${this.report.time.totalTime} > ${this.timeout}` + } + ] + }); + } + } + } + + private generateTimesReport() { + const endDate = new DateController(); + return { + startTime: this.startTime.toString(), + endTime: endDate.toString(), + totalTime: endDate.getTime() - this.startTime.getTime() + }; + } +} diff --git a/src/reporters/task-reporter.ts b/src/reporters/task-reporter.ts new file mode 100644 index 00000000..8e3a1e4c --- /dev/null +++ b/src/reporters/task-reporter.ts @@ -0,0 +1,150 @@ +import { TaskReportGenerator } from './task-report-generator'; +import { Logger } from '../loggers/logger'; +import * as input from '../models/inputs/task-model'; +import { TaskModel } from '../models/inputs/task-model'; +import * as output from '../models/outputs/task-model'; +import { MultiSensorsReporter } from './sensor/multi-sensors-reporter'; +import { MultiActuatorsReporter } from './actuator/multi-actuators-reporter'; +import { EventExecutor } from '../events/event-executor'; +import { DefaultHookEvents } from '../models/events/event'; +import { TestModel } from '../models/outputs/test-model'; +import { NotificationEmitter } from '../notifications/notification-emitter'; +import { Notifications } from '../notifications/notifications'; + +export class TaskReporter { + public static readonly DEFAULT_TIMEOUT = 5 * 1000; + private readonly timeout?: number; + private readonly taskAttributes: TaskModel; + private readonly startTime: Date; + private reportGenerator: TaskReportGenerator; + private multiSensorsReporter: MultiSensorsReporter; + private multiActuatorsReporter: MultiActuatorsReporter; + private hasFinished: boolean = false; + + constructor(taskAttributes: input.TaskModel) { + this.taskAttributes = taskAttributes; + if (this.taskAttributes.timeout === undefined) { + this.timeout = TaskReporter.DEFAULT_TIMEOUT; + } else if (this.taskAttributes.timeout > 0) { + this.timeout = this.taskAttributes.timeout; + } + this.reportGenerator = new TaskReportGenerator(this.taskAttributes, this.timeout); + this.startTime = new Date(); + this.executeOnInitFunction(); + this.multiSensorsReporter = new MultiSensorsReporter(this.taskAttributes.sensors); + this.multiActuatorsReporter = new MultiActuatorsReporter(this.taskAttributes.actuators); + } + + public async delay(): Promise { + const delay = this.taskAttributes.delay || 0; + if (delay > 0) { + Logger.info(`Delaying task '${this.taskAttributes.name}' for ${delay}ms`); + return await this.sleep(delay); + } + } + + public async startTimeout(): Promise { + return new Promise(async resolve => { + const timeout = this.timeout; + if (timeout) { + Logger.debug('Starting task time out'); + await this.sleep(timeout); + if (!this.hasFinished) { + Logger.info(`Task '${this.taskAttributes.name}' has timed out (${timeout}ms)`); + await this.onTaskFinish(); + resolve(this.reportGenerator.getReport()); + } + } + }); + } + + public async execute(): Promise { + try { + this.multiSensorsReporter.start(); + await this.multiSensorsReporter.mount(); + await Promise.all([this.multiSensorsReporter.receiveMessage(), this.multiActuatorsReporter.act()]); + } catch (err) { + Logger.error(`Task error: ${err}`); + this.reportGenerator.addTest(DefaultHookEvents.ON_FINISH, { + valid: false, + tests: [{ name: 'Task error', description: '' + err, valid: false }] + }); + } + if (!this.hasFinished) { + await this.onTaskFinish(); + } + return this.reportGenerator.getReport(); + } + + public async interrupt(): Promise { + if (!this.hasFinished) { + await this.onTaskFinish(); + this.reportGenerator.addTest(DefaultHookEvents.ON_FINISH, { + valid: false, + tests: [ + { + valid: false, + name: 'Task interrupted', + description: `Task interrupted` + } + ] + }); + } + return this.reportGenerator.getReport(); + } + + private async sleep(delay: number): Promise { + return await new Promise(timeoutResolve => setTimeout(() => timeoutResolve(), delay)); + } + + private async onTaskFinish(): Promise { + this.hasFinished = true; + await this.multiSensorsReporter.unmount(); + await this.executeOnFinishFunction(); + Logger.info(`Start gathering reports`); + + this.reportGenerator.setActuatorsReport(this.multiActuatorsReporter.getReport()); + this.reportGenerator.setSensorsReport(this.multiSensorsReporter.getReport()); + this.reportGenerator.finish(); + } + + private executeOnInitFunction(): TestModel[] { + Logger.debug(`Executing task's 'onInit' hook function`); + const eventExecutor = new EventExecutor(this.taskAttributes, DefaultHookEvents.ON_INIT, 'task'); + const elapsedTime = new Date().getTime() - this.startTime.getTime(); + eventExecutor.addArgument('elapsedTime', elapsedTime); + const testModels = eventExecutor.execute(); + const hookResult = { + valid: testModels.every(test => test.valid), + tests: testModels, + arguments: { elapsedTime: elapsedTime } + }; + this.reportGenerator.addTest(DefaultHookEvents.ON_INIT, hookResult); + NotificationEmitter.emit(Notifications.HOOK_FINISHED, { + hookName: DefaultHookEvents.ON_INIT, + hook: hookResult, + task: this.taskAttributes + }); + return testModels; + } + + private async executeOnFinishFunction(): Promise { + this.multiSensorsReporter.onFinish(); + const onFinishEventExecutor = new EventExecutor(this.taskAttributes, DefaultHookEvents.ON_FINISH, 'task'); + const elapsedTime = new Date().getTime() - this.startTime.getTime(); + onFinishEventExecutor.addArgument('elapsedTime', elapsedTime); + const testModels = onFinishEventExecutor.execute(); + const hookModel = { + valid: testModels.every(test => test.valid), + tests: testModels, + arguments: { elapsedTime: elapsedTime } + }; + this.reportGenerator.addTest(DefaultHookEvents.ON_FINISH, hookModel); + NotificationEmitter.emit(Notifications.HOOK_FINISHED, { + hookName: DefaultHookEvents.ON_FINISH, + hook: hookModel, + task: this.taskAttributes + }); + this.multiActuatorsReporter.onFinish(); + } +} diff --git a/src/requisition-runners/component-importer.test.ts b/src/requisition-runners/component-importer.test.ts deleted file mode 100644 index 7a0c49a1..00000000 --- a/src/requisition-runners/component-importer.test.ts +++ /dev/null @@ -1,98 +0,0 @@ -import {ComponentImporter} from './component-importer'; - -describe('ComponentImporter', () => { - it('should return the same if there is no importRequisition', () => { - const requisition = {name: 'name', value: 1234}; - - // @ts-ignore - const imported = new ComponentImporter().importRequisition(requisition); - - expect(imported).toEqual(requisition); - }); - - it('should throw on not valid', () => { - const requisition = {import: 'throw file'}; - - // @ts-ignore - expect(() => new ComponentImporter().importRequisition(requisition)).toThrow(); - }); - - it('should merge requisitions (imported has higher priority)', () => { - const imported = { - onInit: { - script: 'imported' - }, - name: 'imported', - importedValue: 1234, - }; - const original = { - import: imported, - onInit: { - script: 'original' - }, - name: 'original', - iterations: 1 - }; - - // @ts-ignore - const merged = new ComponentImporter().importRequisition(original); - - expect(merged.name).toBe(imported.name); - expect(merged.import).toBeDefined(); - expect(merged.onInit!.script).toBe(imported.onInit.script); - expect(merged.importedValue).toBe(1234); - expect(merged.iterations).toBe(1); - }); - - it('should merge subscription (imported has higher priority)', () => { - const imported = { - onInit: { - script: 'imported' - }, - name: 'imported', - importedValue: 1234, - }; - const original = { - import: imported, - onInit: { - script: 'original' - }, - name: 'original', - type: 'http' - }; - - const merged = new ComponentImporter().importSubscription(original); - - expect(merged.name).toBe(imported.name); - expect(merged.import).toBeDefined(); - expect(merged.onInit!.script).toBe(imported.onInit.script); - expect(merged.importedValue).toBe(1234); - expect(merged.type).toBe(original.type); - }); - - it('should merge publisher (imported has higher priority)', () => { - const imported = { - onInit: { - script: 'imported' - }, - name: 'imported', - importedValue: 1234, - }; - const original = { - import: imported, - onInit: { - script: 'original' - }, - name: 'original', - type: 'http' - }; - - const merged = new ComponentImporter().importPublisher(original); - - expect(merged.name).toBe(imported.name); - expect(merged.import).toBeDefined(); - expect(merged.onInit!.script).toBe(imported.onInit.script); - expect(merged.importedValue).toBe(1234); - expect(merged.type).toBe(original.type); - }); -}); diff --git a/src/requisition-runners/component-importer.ts b/src/requisition-runners/component-importer.ts deleted file mode 100644 index 55916011..00000000 --- a/src/requisition-runners/component-importer.ts +++ /dev/null @@ -1,42 +0,0 @@ -import {RequisitionModel} from '../models/inputs/requisition-model'; -import {RequisitionAdopter} from '../components/requisition-adopter'; -import {RequisitionValidator} from './requisition-validator'; -import {Logger} from '../loggers/logger'; -import {SubscriptionModel} from '../models/inputs/subscription-model'; -import {PublisherModel} from '../models/inputs/publisher-model'; - -export class ComponentImporter { - - public importRequisition(requisition: RequisitionModel): RequisitionModel { - const importValue = requisition.import; - if (importValue) { - const imported: any = Array.isArray(importValue) ? {requisitions: importValue} : importValue; - const requisitionValidator = new RequisitionValidator(); - if (!requisitionValidator.validate(imported)) { - const message = `Error importing ${JSON.stringify(importValue)}: ${requisitionValidator.getErrorMessage()}`; - Logger.error(message); - throw message; - } - const merged = Object.assign({}, requisition, imported); - return new RequisitionAdopter(merged).getRequisition(); - } - return requisition; - } - - public importSubscription(subscription: SubscriptionModel): SubscriptionModel { - const importValue = subscription.import; - if (importValue) { - return Object.assign({}, subscription, importValue); - } - return subscription; - } - - public importPublisher(publisherModel: PublisherModel): PublisherModel { - const importValue = publisherModel.import; - if (importValue) { - return Object.assign({}, publisherModel, importValue); - } - return publisherModel; - } - -} diff --git a/src/requisition-runners/iterations-evaluator.test.ts b/src/requisition-runners/iterations-evaluator.test.ts deleted file mode 100644 index 7da24aac..00000000 --- a/src/requisition-runners/iterations-evaluator.test.ts +++ /dev/null @@ -1,34 +0,0 @@ -import {IterationsEvaluator} from './iterations-evaluator'; - -describe('IterationsEvaluator', () => { - - it('Should return 1 when undefined', () => { - - // @ts-ignore - expect(new IterationsEvaluator().iterations()).toBe(1); - }); - - it('Should return 1 when true', () => { - // @ts-ignore - expect(new IterationsEvaluator().iterations('1 === 1')).toBe(1); - }); - - it('Should return 0 when false', () => { - // @ts-ignore - expect(new IterationsEvaluator().iterations('1 === 0')).toBe(0); - }); - - it('Should parse string', () => { - // @ts-ignore - expect(new IterationsEvaluator().iterations('10')).toBe(10); - }); - - it('Should catch exceptions and return 0', () => { - // @ts-ignore - expect(new IterationsEvaluator().iterations('throw "gui"')).toBe(0); - }); - - it('Should accept number', () => { - expect(new IterationsEvaluator().iterations(100)).toBe(100); - }); -}); diff --git a/src/requisition-runners/iterations-evaluator.ts b/src/requisition-runners/iterations-evaluator.ts deleted file mode 100644 index 2a377cf4..00000000 --- a/src/requisition-runners/iterations-evaluator.ts +++ /dev/null @@ -1,25 +0,0 @@ -import {Logger} from '../loggers/logger'; - -export class IterationsEvaluator { - - public iterations(iterations: number): number { - if (iterations !== undefined) { - try { - //TODO be able to use 'requisition' as argument - const evaluated = eval(iterations.toString()); - switch (typeof evaluated) { - case 'boolean': - return evaluated ? 1 : 0; - case 'number': - default: - return evaluated; - } - } catch (err) { - Logger.warning(`Error evaluating iterations: $${iterations}: ${err}`); - return 0; - } - } - - return 1; - } -} diff --git a/src/requisition-runners/requisition-file-parser.test.ts b/src/requisition-runners/requisition-file-parser.test.ts deleted file mode 100644 index 126329c7..00000000 --- a/src/requisition-runners/requisition-file-parser.test.ts +++ /dev/null @@ -1,49 +0,0 @@ -import {DynamicModulesManager} from '../plugins/dynamic-modules-manager'; -import * as fs from 'fs'; -import {RequisitionFileParser} from './requisition-file-parser'; -import * as glob from 'glob'; - -jest.mock('fs'); -jest.mock('glob'); -// @ts-ignore -glob.sync.mockImplementation((pattern: string) => [pattern]); - -describe('RequisitionFileParser', () => { - beforeEach(() => { - // @ts-ignore - delete DynamicModulesManager.instance; - - }); - - it('Should parse array as just one', () => { - const requisitionInput = { - onInit: {}, - id: 0 - }; - DynamicModulesManager.getInstance().getObjectParserManager().addObjectParser(() => { - return { - parse: () => requisitionInput - }; - }, 'yml'); - - const fileContent = JSON.stringify(requisitionInput); - // @ts-ignore - fs.readFileSync.mockImplementationOnce(() => Buffer.from(fileContent)); - const filename = 'anyStuff'; - - const requisition = new RequisitionFileParser().parseFile(filename); - - expect(requisition.name).toBe(filename); - expect(requisition.id).toBe(requisitionInput.id); - expect(requisition.onInit).toEqual(requisitionInput.onInit); - }); - - it('Should throw', () => { - // @ts-ignore - fs.readFileSync.mockImplementationOnce(() => { - throw 'error'; - }); - expect(() => new RequisitionFileParser().parseFile('anyStuff')).toThrow(); - }); - -}); diff --git a/src/requisition-runners/requisition-file-parser.ts b/src/requisition-runners/requisition-file-parser.ts deleted file mode 100644 index 93439012..00000000 --- a/src/requisition-runners/requisition-file-parser.ts +++ /dev/null @@ -1,14 +0,0 @@ -import {RequisitionModel} from '../models/inputs/requisition-model'; -import {RequisitionParser} from './requisition-parser'; -import * as fs from 'fs'; - -export class RequisitionFileParser { - - public parseFile(filename: string): RequisitionModel { - const fileBufferContent = fs.readFileSync(filename).toString(); - const requisition = new RequisitionParser().parse(fileBufferContent); - requisition.name = requisition.name || filename; - return requisition; - } - -} diff --git a/src/requisition-runners/requisition-file-pattern-parser.test.ts b/src/requisition-runners/requisition-file-pattern-parser.test.ts deleted file mode 100644 index 67fa9db4..00000000 --- a/src/requisition-runners/requisition-file-pattern-parser.test.ts +++ /dev/null @@ -1,131 +0,0 @@ -import {RequisitionFilePatternParser} from './requisition-file-pattern-parser'; -import {DynamicModulesManager} from '../plugins/dynamic-modules-manager'; -import {YmlObjectParser} from '../object-parser/yml-object-parser'; -import * as fs from 'fs'; -import * as glob from 'glob'; - -jest.mock('fs'); -jest.mock('glob'); -// @ts-ignore -glob.sync.mockImplementation((pattern: string) => [pattern]); - -describe('RequisitionFilePatternParser', () => { - beforeEach(() => { - // @ts-ignore - delete DynamicModulesManager.instance; - }); - - it('Should parse array as just one', () => { - const requisitionsInput = [ - { - onInit: {}, - id: 0 - }, - { - publishers: [{type: true}], - name: 'named', - id: 1 - } - ]; - DynamicModulesManager.getInstance().getObjectParserManager().addObjectParser(() => { - return { - parse: () => requisitionsInput - }; - }, 'yml'); - - const fileContent = JSON.stringify(requisitionsInput); - // @ts-ignore - fs.readFileSync.mockImplementationOnce(() => Buffer.from(fileContent)); - const filename = 'anyStuff'; - - const requisition = new RequisitionFilePatternParser([filename]).parse(); - - expect(requisition[0].name).toBe(filename); - - expect(requisition[0].requisitions[0].id).toBe(requisitionsInput[0].id); - expect(requisition[0].requisitions[0].onInit).toEqual(requisitionsInput[0].onInit); - - expect(requisition[0].requisitions[1].name).toBe(requisitionsInput[1].name); - expect(requisition[0].requisitions[1].id).toBe(requisitionsInput[1].id); - expect(requisition[0].requisitions[1].publishers).toEqual(requisitionsInput[1].publishers); - - }); - - it('Should add invalid file error', () => { - // @ts-ignore - fs.readFileSync.mockImplementationOnce(() => { - throw 'error'; - }); - const parser: RequisitionFilePatternParser = new RequisitionFilePatternParser(['anyStuff']); - - parser.parse(); - - expect(parser.getFilesErrors()[0]).toEqual({'description': 'error', 'name': "Error parsing file 'anyStuff'", 'valid': false}); - }); - - it('Should no test found error', () => { - const parser: RequisitionFilePatternParser = new RequisitionFilePatternParser([]); - - parser.parse(); - - expect(parser.getFilesErrors()[0]).toEqual({ - 'description': 'No test file was found', - 'name': 'No test file was found', 'valid': false - }); - }); - - it('Should add if file is not yml nor json', () => { - const notYml = 'foo bar\nfoo: bar'; - - DynamicModulesManager.getInstance().getObjectParserManager().addObjectParser(() => { - return { - parse: (value) => new YmlObjectParser().parse(value) - }; - }, 'yml'); - - // @ts-ignore - fs.readFileSync.mockImplementationOnce(() => Buffer.from(notYml)); - - const parser = new RequisitionFilePatternParser(['anyStuff']); - parser.parse(); - - const parsedErrorDescription: any = parser.getFilesErrors()[0].description; - expect(parsedErrorDescription.json).toBeDefined(); - expect(parsedErrorDescription.yml).toBeDefined(); - }); - - it('Should add error if it is not a valid requisition', () => { - const notYml = 'hey: bar'; - - DynamicModulesManager.getInstance().getObjectParserManager().addObjectParser(() => { - return { - parse: (value) => new YmlObjectParser().parse(value) - }; - }, 'yml'); - - // @ts-ignore - fs.readFileSync.mockImplementationOnce(() => Buffer.from(notYml)); - - const parser = new RequisitionFilePatternParser(['anyStuff']); - parser.parse(); - - expect(parser.getFilesErrors()[0].name).toBe('Error parsing file \'anyStuff\''); - }); - - it('should add every not matching file to error', () => { - // @ts-ignore - glob.sync.mockReset(); - // @ts-ignore - glob.sync.mockImplementationOnce(() => []); - - const parser = new RequisitionFilePatternParser(['not-matching-pattern']); - parser.parse(); - - expect(parser.getFilesErrors()[0]).toEqual({ - 'description': "No file was found with: 'not-matching-pattern'", - 'name': "No file was found with: 'not-matching-pattern'", - 'valid': false - }); - }); - -}); diff --git a/src/requisition-runners/requisition-file-pattern-parser.ts b/src/requisition-runners/requisition-file-pattern-parser.ts deleted file mode 100644 index 965cfe7b..00000000 --- a/src/requisition-runners/requisition-file-pattern-parser.ts +++ /dev/null @@ -1,60 +0,0 @@ -import {Logger} from '../loggers/logger'; -import {TestModel} from '../models/outputs/test-model'; -import * as input from '../models/inputs/requisition-model'; -import {RequisitionModel} from '../models/inputs/requisition-model'; -import * as glob from 'glob'; -import {RequisitionFileParser} from './requisition-file-parser'; - -export class RequisitionFilePatternParser { - - private filesErrors: TestModel[] = []; - - constructor(private readonly patterns: string[]) { - - } - - public getFilesErrors(): TestModel[] { - return this.filesErrors; - } - - public parse(): RequisitionModel[] { - this.filesErrors = []; - const requisitions: input.RequisitionModel[] = []; - const matchingFiles = this.getMatchingFiles(); - matchingFiles.forEach((file: string) => { - try { - requisitions.push(new RequisitionFileParser().parseFile(file)); - } catch (err) { - this.addError(`Error parsing file '${file}'`, err); - } - }); - if (matchingFiles.length === 0) { - const title = `No test file was found`; - this.addError(title, title); - } - return requisitions; - } - - private getMatchingFiles(): string[] { - let result: string[] = []; - this.patterns.map((pattern: string) => { - const items = glob.sync(pattern, {nodir: true}); - if (items.length > 0) { - result = result.concat(items.sort()); - } else { - const message = `No file was found with: '${pattern}'`; - this.addError(message, message); - } - }); - result = [...new Set(result)]; - - Logger.info(`Files list: ${JSON.stringify(result, null, 2)}`); - return result; - } - - private addError(title: string, message: string) { - Logger.error(message); - this.filesErrors.push({name: title, valid: false, description: message}); - } - -} diff --git a/src/requisition-runners/requisition-parser.test.ts b/src/requisition-runners/requisition-parser.test.ts deleted file mode 100644 index 5977037a..00000000 --- a/src/requisition-runners/requisition-parser.test.ts +++ /dev/null @@ -1,83 +0,0 @@ -import {DynamicModulesManager} from '../plugins/dynamic-modules-manager'; -import {YmlObjectParser} from '../object-parser/yml-object-parser'; -import {RequisitionParser} from './requisition-parser'; - -describe('RequisitionParser', () => { - beforeEach(() => { - // @ts-ignore - delete DynamicModulesManager.instance; - - }); - - it('Should parse array as just one', () => { - const requisitionsInput = [ - { - onInit: {}, - id: 0 - }, - { - publishers: [{type: true}], - name: 'named', - id: 1 - } - ]; - DynamicModulesManager.getInstance().getObjectParserManager().addObjectParser(() => { - return { - parse: () => requisitionsInput - }; - }, 'yml'); - - const fileContent = JSON.stringify(requisitionsInput); - const requisition = new RequisitionParser().parse(fileContent); - - expect(requisition.requisitions[0].id).toBe(requisitionsInput[0].id); - expect(requisition.requisitions[0].onInit).toEqual(requisitionsInput[0].onInit); - - expect(requisition.requisitions[1].name).toBe(requisitionsInput[1].name); - expect(requisition.requisitions[1].id).toBe(requisitionsInput[1].id); - expect(requisition.requisitions[1].publishers).toEqual(requisitionsInput[1].publishers); - - }); - - it('Should throw', () => { - expect(() => new RequisitionParser().parse('anyStuff')).toThrow(); - }); - - it('Should add errors if file is not yml nor json', () => { - const notYml = 'foo bar\nfoo: bar'; - - DynamicModulesManager.getInstance().getObjectParserManager().addObjectParser(() => { - return { - parse: (value) => new YmlObjectParser().parse(value) - }; - }, 'yml'); - - try { - new RequisitionParser().parse('any:1\nany:1'); - expect(true).toBeFalsy(); - } catch (err) { - expect(err.json).toBeDefined(); - expect(err.yml).toBeDefined(); - } - - }); - - it('Should add error if it is not a valid requisition', () => { - const notYml = 'hey: bar'; - - DynamicModulesManager.getInstance().getObjectParserManager().addObjectParser(() => { - return { - parse: (value) => new YmlObjectParser().parse(value) - }; - }, 'yml'); - - try { - new RequisitionParser().parse(notYml); - expect(true).toBeFalsy(); - } catch (err) { - expect(err).toBe(`'hey: bar' is not a valid requisition.` + - ` Unable to find: 'onInit', 'onFinish', 'delay', 'requisitions', 'publishers', 'subscriptions' nor 'import'.`); - } - }); - -}); diff --git a/src/requisition-runners/requisition-parser.ts b/src/requisition-runners/requisition-parser.ts deleted file mode 100644 index ee2a916d..00000000 --- a/src/requisition-runners/requisition-parser.ts +++ /dev/null @@ -1,19 +0,0 @@ -import {DynamicModulesManager} from '../plugins/dynamic-modules-manager'; -import {RequisitionModel} from '../models/inputs/requisition-model'; -import {RequisitionValidator} from './requisition-validator'; - -export class RequisitionParser { - - public parse(content: string): RequisitionModel { - let fileContent: any = DynamicModulesManager - .getInstance().getObjectParserManager() - .tryToParseWithParsers(content, ['yml', 'json']); - - const requisition = Array.isArray(fileContent) ? {requisitions: fileContent} : fileContent; - const requisitionValidator = new RequisitionValidator(); - if (!requisitionValidator.validate(requisition)) { - throw '\'' + content + '\' is not a valid requisition. ' + requisitionValidator.getErrorMessage(); - } - return requisition; - } -} diff --git a/src/requisition-runners/requisition-runner.test.ts b/src/requisition-runners/requisition-runner.test.ts deleted file mode 100644 index 699ac41d..00000000 --- a/src/requisition-runners/requisition-runner.test.ts +++ /dev/null @@ -1,81 +0,0 @@ -import {RequisitionRunner} from './requisition-runner'; -import {RequisitionModel} from '../models/inputs/requisition-model'; -import {Store} from '../configurations/store'; - -describe('RequisitionRunner', () => { - - it('Should return requisition reporter skipped', async () => { - // @ts-ignore - const requisition: RequisitionModel = { - iterations: 0, - name: 'skipped', - id: 'id' - }; - - const reports = await new RequisitionRunner(requisition).run(); - const actual = reports[0]; - - expect(reports.length).toBe(1); - expect(actual.name).toBe(requisition.name); - expect(actual.id).toBe(requisition.id); - expect(actual.valid).toBeTruthy(); - expect(actual.hooks!.onInit.valid).toBeTruthy(); - expect(actual.hooks!.onFinish.valid).toBeTruthy(); - }); - - it('Should return requisition report collection', async () => { - const requisition: RequisitionModel = { - name: 'super cool', - iterations: 5, - requisitions: [], - publishers: [], - subscriptions: [], - }; - - const reports = await new RequisitionRunner(requisition).run(); - const report = reports[0]; - - expect(reports.length).toBe(requisition.iterations); - expect(report.time).toBeDefined(); - expect(report.name).toContain(requisition.name); - expect(report.valid).toBeTruthy(); - }); - - it('Should run children requisition report collection', async () => { - const requisition: RequisitionModel = { - name: 'super cool', - // @ts-ignore - requisitions: [{ - name: 'child', - publishers: [], - subscriptions: [], - requisitions: [] - }], - publishers: [], - subscriptions: [], - }; - - const reports = await new RequisitionRunner(requisition).run(); - const report = reports[0].requisitions[0]; - - expect(report.time).toBeDefined(); - expect(report.name).toContain('child'); - expect(report.valid).toBeTruthy(); - }); - - it('Should replace stuff', async () => { - const keyName = 'value'; - Store.getData().keyName = keyName; - // @ts-ignore - const requisition: RequisitionModel = { - name: '<>', - }; - - const reports = await new RequisitionRunner(requisition).run(); - const report = reports[0]; - - expect(reports.length).toBe(1); - expect(report.name).toBe(keyName); - }); - -}); diff --git a/src/requisition-runners/requisition-runner.ts b/src/requisition-runners/requisition-runner.ts deleted file mode 100644 index 06d833d5..00000000 --- a/src/requisition-runners/requisition-runner.ts +++ /dev/null @@ -1,165 +0,0 @@ -import {Logger} from '../loggers/logger'; -import {RequisitionReporter} from '../reporters/requisition-reporter'; -import * as input from '../models/inputs/requisition-model'; -import * as output from '../models/outputs/requisition-model'; -import {JsonPlaceholderReplacer} from 'json-placeholder-replacer'; -import {Store} from '../configurations/store'; -import {RequisitionDefaultReports} from '../models-defaults/outputs/requisition-default-reports'; -import {FileContentMapCreator} from '../configurations/file-content-map-creator'; -import {IterationsEvaluator} from './iterations-evaluator'; -import {ComponentParentBackupper} from '../components/component-parent-backupper'; -import {ComponentImporter} from './component-importer'; -import {RequisitionAdopter} from '../components/requisition-adopter'; -import {NotificationEmitter} from '../notifications/notification-emitter'; -import {testModelIsNotFailing} from '../models/outputs/test-model'; -import {Notifications} from '../notifications/notifications'; - -export class RequisitionRunner { - - private requisition: input.RequisitionModel; - private childrenRequisitionRunner: RequisitionRunner[] = []; - private requisitionReporter?: RequisitionReporter; - - public constructor(requisition: input.RequisitionModel) { - this.requisition = new RequisitionAdopter(requisition).getRequisition(); - } - - public async run(): Promise { - NotificationEmitter.emit(Notifications.REQUISITION_STARTED, {requisition: this.requisition}); - Logger.info(`Running requisition '${this.requisition.name}'`); - try { - this.importRequisition(); - } catch (err) { - Logger.error(`Error importing requisition`); - const report = RequisitionDefaultReports.createRunningError(this.requisition, err); - this.emitOnFinishNotification(report); - return [report]; - } - this.replaceVariables(); - const evaluatedIterations: number = new IterationsEvaluator().iterations(this.requisition.iterations); - if (evaluatedIterations <= 0) { - Logger.info(`Requisition will be skipped duo no iterations`); - const report = RequisitionDefaultReports.createSkippedReport(this.requisition); - this.emitOnFinishNotification(report); - return [report]; - } else if (this.requisition.ignore) { - Logger.info(`Requisition will be ignored`); - const report = RequisitionDefaultReports.createIgnoredReport(this.requisition); - this.emitOnFinishNotification(report); - return [report]; - } - return await this.iterateRequisition(evaluatedIterations); - } - - private async iterateRequisition(iterations: number): Promise { - const reports = []; - for (let iterationCounter = 0; iterationCounter < iterations; ++iterationCounter) { - try { - this.replaceVariables(); - this.requisition.iteration = iterationCounter; - this.requisition.totalIterations = iterations; - const iterationSuffix: string = (iterations > 1) ? ` [${iterationCounter}]` : ''; - Logger.trace(`Requisition runner starting requisition reporter for '${this.requisition.name + iterationSuffix}'`); - const report = await this.startRequisitionReporter(); - reports.push(report); - this.emitOnFinishNotification(report); - } catch (err) { - reports.push(RequisitionDefaultReports.createRunningError(this.requisition, err.toString())); - Logger.error(err); - } - } - return reports; - } - - private importRequisition() { - if (this.requisition.import) { - this.requisition = new ComponentImporter().importRequisition(this.requisition); - } - } - - private async interrupt(): Promise { - const report: output.RequisitionModel = await this.requisitionReporter!.interrupt(); - this.emitOnFinishNotification(report); - return report; - } - - private emitOnFinishNotification(report: output.RequisitionModel) { - NotificationEmitter.emit(Notifications.REQUISITION_FINISHED, {requisition: report}); - } - - private replaceVariables(): void { - Logger.debug(`Evaluating variables of requisition '${this.requisition.name}'`); - const componentParentBackupper = new ComponentParentBackupper(); - componentParentBackupper.removeParents(this.requisition); - const fileMapCreator = new FileContentMapCreator(this.requisition); - const fileReplaced = new JsonPlaceholderReplacer() - .addVariableMap(fileMapCreator.getMap()) - .replace(this.requisition); - this.requisition = new JsonPlaceholderReplacer() - .addVariableMap(Store.getData()) - .replace(fileReplaced) as input.RequisitionModel; - componentParentBackupper.putParentsBack(this.requisition); - } - - private async startRequisitionReporter(): Promise { - this.requisitionReporter = new RequisitionReporter(this.requisition); - const report = await Promise.race([ - this.timeoutPath(), - this.happyPath()]); - - const iterationCounter: string = (+report.totalIterations! > 1) ? ` [${report.iteration}]` : ''; - Logger.info(`Requisition '${report.name + iterationCounter}' is over (${report.valid}) - ${report.time ? report.time.totalTime : 0}ms`); - Logger.trace(`Store keys: ${Object.keys(Store.getData()).join('; ')}`); - - return report; - } - - private async timeoutPath(): Promise { - const report = await this.requisitionReporter!.startTimeout(); - report.requisitions = await Promise.all(this.childrenRequisitionRunner.map(childRunner => childRunner.interrupt())); - Logger.debug(`Requisition '${this.requisition.name}' timed out`); - - return report; - } - - private async happyPath(): Promise { - await this.requisitionReporter!.delay(); - Logger.debug(`Handling requisitions children of '${this.requisition.name}'`); - let childrenReport: output.RequisitionModel[] = await this.executeChildren(); - const report = await this.requisitionReporter!.execute(); - report.requisitions = childrenReport; - report.valid = report.valid && - report.requisitions.every((requisition) => testModelIsNotFailing(requisition)) && - Object.keys(report.hooks || {}).every((key: string) => report.hooks ? report.hooks[key].valid : true); - Logger.debug(`Requisition ${this.requisition.name} went through the happy path`); - return report; - } - - private async executeChildren(): Promise { - if (this.requisition.parallel) { - const models = await Promise.all(this.requisition.requisitions - .map(async (child: input.RequisitionModel, index: number) => { - return await this.executeChild(child, index); - })); - return models.reduce((acc, child) => acc.concat(child), []); - } else { - let childrenReport: output.RequisitionModel[] = []; - let index = 0; - for (const child of this.requisition.requisitions) { - childrenReport = childrenReport.concat(await this.executeChild(child, index)); - ++index; - } - return childrenReport; - } - } - - private async executeChild(child: input.RequisitionModel, index: number): Promise { - child.parent = this.requisition; - const childRunner = new RequisitionRunner(child); - this.childrenRequisitionRunner.push(childRunner); - const requisitionModels = await childRunner.run(); - this.requisition.requisitions[index] = childRunner.requisition; - return requisitionModels; - } - -} diff --git a/src/requisition-runners/requisition-validator.test.ts b/src/requisition-runners/requisition-validator.test.ts deleted file mode 100644 index 7c7d618c..00000000 --- a/src/requisition-runners/requisition-validator.test.ts +++ /dev/null @@ -1,54 +0,0 @@ -import {RequisitionValidator} from './requisition-validator'; - -describe('RequisitionValidator', () => { - it('Should return error message', () => { - expect(new RequisitionValidator().getErrorMessage()) - .toBe(`Unable to find: 'onInit', 'onFinish', 'delay', 'requisitions', 'publishers', 'subscriptions' nor 'import'.`); - }); - - it('Should reject empty', () => { - expect(new RequisitionValidator().validate()).toBeFalsy(); - }); - - it('Should accept import', () => { - expect(new RequisitionValidator().validate({import: {}})).toBeTruthy(); - }); - - it('Should accept onInit', () => { - expect(new RequisitionValidator().validate({onInit: {}})).toBeTruthy(); - }); - - it('Should accept delay', () => { - expect(new RequisitionValidator().validate({delay: {}})).toBeTruthy(); - }); - - it('Should accept onFinish', () => { - expect(new RequisitionValidator().validate({onFinish: {}})).toBeTruthy(); - }); - - it('Should accept publishers', () => { - expect(new RequisitionValidator().validate({publishers: [{type: ''}]})).toBeTruthy(); - }); - - it('Should reject empty publishers', () => { - expect(new RequisitionValidator().validate({publishers: []})).toBeFalsy(); - }); - - it('Should accept subscriptions', () => { - expect(new RequisitionValidator().validate({subscriptions: [{type: ''}]})).toBeTruthy(); - }); - - it('Should reject empty subscriptions', () => { - expect(new RequisitionValidator().validate({subscriptions: []})).toBeFalsy(); - }); - - it('Should go recursive', () => { - expect(new RequisitionValidator().validate({ - requisitions: [{ - requisitions: [{ - onInit: {} - }] - }] - })).toBeTruthy(); - }); -}); diff --git a/src/requisition-runners/requisition-validator.ts b/src/requisition-runners/requisition-validator.ts deleted file mode 100644 index 204ccf0a..00000000 --- a/src/requisition-runners/requisition-validator.ts +++ /dev/null @@ -1,30 +0,0 @@ -import {RequisitionModel} from '../models/inputs/requisition-model'; - -export class RequisitionValidator { - - public validate(requisition: RequisitionModel): boolean { - if (typeof requisition !== 'object' || !requisition) { - return false; - } - if (requisition.onInit !== undefined || - requisition.onFinish !== undefined || - requisition.import !== undefined || - requisition.delay !== undefined) { - return true; - } - if (Array.isArray(requisition.publishers) && requisition.publishers.length > 0) { - return true; - } - if (Array.isArray(requisition.subscriptions) && requisition.subscriptions.length > 0) { - return true; - } - if (Array.isArray(requisition.requisitions) && requisition.requisitions.length > 0) { - return requisition.requisitions.every(child => this.validate(child)); - } - return false; - } - - public getErrorMessage(): string { - return 'Unable to find: \'onInit\', \'onFinish\', \'delay\', \'requisitions\', \'publishers\', \'subscriptions\' nor \'import\'.'; - } -} diff --git a/src/run-as-child/assert-describer.test.ts b/src/run-as-child/assert-describer.test.ts index 090f8572..f9048f57 100644 --- a/src/run-as-child/assert-describer.test.ts +++ b/src/run-as-child/assert-describer.test.ts @@ -1,5 +1,5 @@ -import {DynamicModulesManager} from '../plugins/dynamic-modules-manager'; -import {AssertDescriber} from './assert-describer'; +import { DynamicModulesManager } from '../plugins/dynamic-modules-manager'; +import { AssertDescriber } from './assert-describer'; jest.mock('../plugins/dynamic-modules-manager'); @@ -8,19 +8,21 @@ process.send = processSendMock; // @ts-ignore DynamicModulesManager.getInstance.mockImplementation(() => { - return { - getAsserterManager: () => ({getMatchingAsserters: () => 'mockedAsserter'}) - }; + return { + getAsserterManager: () => ({ + getMatchingAsserters: () => 'mockedAsserter' + }) + }; }); describe('AssertDescriber', () => { + it('should describe asserters when a message arrives', async () => { + const message = { value: 'value' }; + await new AssertDescriber().process(message); - it('should describe asserters when a message arrives', async () => { - const message = {value: 'value'}; - await new AssertDescriber().process(message); - - expect(processSendMock).toHaveBeenCalledWith({event: 'ASSERTERS_LIST', value: 'mockedAsserter'}); + expect(processSendMock).toHaveBeenCalledWith({ + event: 'ASSERTERS_LIST', + value: 'mockedAsserter' }); - - + }); }); diff --git a/src/run-as-child/assert-describer.ts b/src/run-as-child/assert-describer.ts index d0ccc4aa..f8627cbf 100644 --- a/src/run-as-child/assert-describer.ts +++ b/src/run-as-child/assert-describer.ts @@ -1,15 +1,14 @@ -import {DynamicModulesManager} from '../plugins/dynamic-modules-manager'; -import {ChildSendingEvents} from './child-sending-events'; -import {ParentReplier} from './parent-replier'; +import { DynamicModulesManager } from '../plugins/dynamic-modules-manager'; +import { ChildSendingEvents } from './child-sending-events'; +import { ParentReplier } from './parent-replier'; export class AssertDescriber implements ParentReplier { - public async process(message: any): Promise { - const asserters = DynamicModulesManager.getInstance().getAsserterManager().getMatchingAsserters(''); - process.send!( - { - event: ChildSendingEvents.ASSERTERS_LIST, - value: asserters - }); - return true; - } + public async process(message: any): Promise { + const asserters = DynamicModulesManager.getInstance().getAsserterManager().getMatchingAsserters(''); + process.send!({ + event: ChildSendingEvents.ASSERTERS_LIST, + value: asserters + }); + return true; + } } diff --git a/src/run-as-child/child-receiving-events.ts b/src/run-as-child/child-receiving-events.ts index 9e323aed..ec80dfe9 100644 --- a/src/run-as-child/child-receiving-events.ts +++ b/src/run-as-child/child-receiving-events.ts @@ -1,7 +1,8 @@ export enum ChildReceivingEvents { - RUN_REQUISITION = 'RUN_REQUISITION', - ADD_MODULE = 'ADD_MODULE', - GET_PROTOCOLS = 'GET_PROTOCOLS', - GET_ASSERTERS = 'GET_ASSERTERS', - CLEAN_STORE = 'CLEAN_STORE', + SET_STORE = 'SET_STORE', + ADD_MODULE = 'ADD_MODULE', + CLEAN_STORE = 'CLEAN_STORE', + GET_PROTOCOLS = 'GET_PROTOCOLS', + GET_ASSERTERS = 'GET_ASSERTERS', + RUN_REQUISITION = 'RUN_REQUISITION' } diff --git a/src/run-as-child/child-requisition-runner.test.ts b/src/run-as-child/child-requisition-runner.test.ts deleted file mode 100644 index 62169e9a..00000000 --- a/src/run-as-child/child-requisition-runner.test.ts +++ /dev/null @@ -1,31 +0,0 @@ -import {ChildRequisitionRunner} from './child-requisition-runner'; -import {RequisitionRunner} from '../requisition-runners/requisition-runner'; - -jest.mock('../requisition-runners/requisition-runner'); - -const processSendMock = jest.fn(); -process.send = processSendMock; - -const runMock = jest.fn(); -const requisitionRunnerConstructorMock = jest.fn(() => ({run: runMock})); -// @ts-ignore -RequisitionRunner.mockImplementation(requisitionRunnerConstructorMock); - -describe('ChildRequisitionRunner', () => { - beforeEach(() => { - processSendMock.mockClear(); - requisitionRunnerConstructorMock.mockClear(); - runMock.mockClear(); - }); - - it('should run enqueuer runner when a message arrives', async () => { - const requisition = 'value'; - const message = {value: 'value'}; - await new ChildRequisitionRunner().process(message); - - expect(requisitionRunnerConstructorMock).toHaveBeenCalledWith('value'); - expect(runMock).toHaveBeenCalled(); - }); - - -}); diff --git a/src/run-as-child/child-requisition-runner.ts b/src/run-as-child/child-requisition-runner.ts deleted file mode 100644 index 8e05cdb2..00000000 --- a/src/run-as-child/child-requisition-runner.ts +++ /dev/null @@ -1,9 +0,0 @@ -import {RequisitionRunner} from '../requisition-runners/requisition-runner'; -import {ParentReplier} from './parent-replier'; - -export class ChildRequisitionRunner implements ParentReplier { - public async process(message: any): Promise { - await new RequisitionRunner(message.value).run(); - return true; - } -} diff --git a/src/run-as-child/child-sending-events.ts b/src/run-as-child/child-sending-events.ts index a11bebd0..6e1ad3f3 100644 --- a/src/run-as-child/child-sending-events.ts +++ b/src/run-as-child/child-sending-events.ts @@ -1,5 +1,5 @@ export enum ChildSendingEvents { - PROTOCOLS_LIST = 'PROTOCOLS_LIST', - ASSERTERS_LIST = 'ASSERTERS_LIST', - PROCESS_EXIT = 'PROCESS_EXIT' + PROTOCOLS_LIST = 'PROTOCOLS_LIST', + ASSERTERS_LIST = 'ASSERTERS_LIST', + PROCESS_EXIT = 'PROCESS_EXIT' } diff --git a/src/run-as-child/child-task-runner.test.ts b/src/run-as-child/child-task-runner.test.ts new file mode 100644 index 00000000..7f999167 --- /dev/null +++ b/src/run-as-child/child-task-runner.test.ts @@ -0,0 +1,29 @@ +import { ChildTaskRunner } from './child-task-runner'; +import { TaskRunner } from '../task-runners/task-runner'; + +jest.mock('../task-runners/task-runner'); + +const processSendMock = jest.fn(); +process.send = processSendMock; + +const runMock = jest.fn(); +const taskRunnerConstructorMock = jest.fn(() => ({ run: runMock })); +// @ts-ignore +TaskRunner.mockImplementation(taskRunnerConstructorMock); + +describe('ChildTaskRunner', () => { + beforeEach(() => { + processSendMock.mockClear(); + taskRunnerConstructorMock.mockClear(); + runMock.mockClear(); + }); + + it('should run enqueuer runner when a message arrives', async () => { + const task = 'value'; + const message = { value: 'value' }; + await new ChildTaskRunner().process(message); + + expect(taskRunnerConstructorMock).toHaveBeenCalledWith('value'); + expect(runMock).toHaveBeenCalled(); + }); +}); diff --git a/src/run-as-child/child-task-runner.ts b/src/run-as-child/child-task-runner.ts new file mode 100644 index 00000000..811ab8c9 --- /dev/null +++ b/src/run-as-child/child-task-runner.ts @@ -0,0 +1,9 @@ +import { TaskRunner } from '../task-runners/task-runner'; +import { ParentReplier } from './parent-replier'; + +export class ChildTaskRunner implements ParentReplier { + public async process(message: any): Promise { + await new TaskRunner(message.value).run(); + return true; + } +} diff --git a/src/run-as-child/module-adder.test.ts b/src/run-as-child/module-adder.test.ts index edb707e4..23377f44 100644 --- a/src/run-as-child/module-adder.test.ts +++ b/src/run-as-child/module-adder.test.ts @@ -1,23 +1,20 @@ -import {Configuration} from '../configurations/configuration'; -import {ModuleAdder} from './module-adder'; +import { Configuration } from '../configurations/configuration'; +import { ModuleAdder } from './module-adder'; jest.mock('../configurations/configuration'); const addPluginMock = jest.fn(); // @ts-ignore Configuration.getInstance.mockImplementation(() => { - return { - addPlugin: addPluginMock - }; + return { + addPlugin: addPluginMock + }; }); describe('ModuleAdder', () => { + it('should add module when a message arrives', async () => { + const message = { value: 'value' }; + await new ModuleAdder().process(message); - it('should add module when a message arrives', async () => { - const message = {value: 'value'}; - await new ModuleAdder().process(message); - - expect(addPluginMock).toHaveBeenCalledWith('value'); - }); - - + expect(addPluginMock).toHaveBeenCalledWith('value'); + }); }); diff --git a/src/run-as-child/module-adder.ts b/src/run-as-child/module-adder.ts index 647e94db..e76874f2 100644 --- a/src/run-as-child/module-adder.ts +++ b/src/run-as-child/module-adder.ts @@ -1,10 +1,9 @@ -import {Configuration} from '../configurations/configuration'; -import {ParentReplier} from './parent-replier'; +import { Configuration } from '../configurations/configuration'; +import { ParentReplier } from './parent-replier'; export class ModuleAdder implements ParentReplier { - public async process(message: any): Promise { - Configuration.getInstance().addPlugin(message.value); - return true; - } - + public async process(message: any): Promise { + Configuration.getInstance().addPlugin(message.value); + return true; + } } diff --git a/src/run-as-child/parent-replier.ts b/src/run-as-child/parent-replier.ts index 411ab08b..73db28dd 100644 --- a/src/run-as-child/parent-replier.ts +++ b/src/run-as-child/parent-replier.ts @@ -1,3 +1,3 @@ export interface ParentReplier { - process(message: any): Promise; + process(message: any): Promise; } diff --git a/src/run-as-child/protocol-describer.test.ts b/src/run-as-child/protocol-describer.test.ts index bb9bb3cb..18c89693 100644 --- a/src/run-as-child/protocol-describer.test.ts +++ b/src/run-as-child/protocol-describer.test.ts @@ -1,5 +1,5 @@ -import {DynamicModulesManager} from '../plugins/dynamic-modules-manager'; -import {ProtocolDescriber} from './protocol-describer'; +import { DynamicModulesManager } from '../plugins/dynamic-modules-manager'; +import { ProtocolDescriber } from './protocol-describer'; jest.mock('../plugins/dynamic-modules-manager'); @@ -8,18 +8,20 @@ process.send = processSendMock; // @ts-ignore DynamicModulesManager.getInstance.mockImplementation(() => { - return { - getProtocolManager: () => ({getProtocolsDescription: () => 'mockedProtocol'}) - }; + return { + getProtocolManager: () => ({ + getProtocolsDescription: () => 'mockedProtocol' + }) + }; }); describe('ProtocolDescriber', () => { + it('should describe protocols when a message arrives', async () => { + const message = { value: 'value' }; + await new ProtocolDescriber().process(message); - it('should describe protocols when a message arrives', async () => { - const message = {value: 'value'}; - await new ProtocolDescriber().process(message); - - expect(processSendMock).toHaveBeenCalledWith({event: 'PROTOCOLS_LIST', value: 'mockedProtocol'}); + expect(processSendMock).toHaveBeenCalledWith({ + event: 'PROTOCOLS_LIST', + value: 'mockedProtocol' }); - - + }); }); diff --git a/src/run-as-child/protocol-describer.ts b/src/run-as-child/protocol-describer.ts index 77948c5c..236e5c96 100644 --- a/src/run-as-child/protocol-describer.ts +++ b/src/run-as-child/protocol-describer.ts @@ -1,15 +1,14 @@ -import {DynamicModulesManager} from '../plugins/dynamic-modules-manager'; -import {ChildSendingEvents} from './child-sending-events'; -import {ParentReplier} from './parent-replier'; +import { DynamicModulesManager } from '../plugins/dynamic-modules-manager'; +import { ChildSendingEvents } from './child-sending-events'; +import { ParentReplier } from './parent-replier'; export class ProtocolDescriber implements ParentReplier { - public async process(message: any): Promise { - const protocols = DynamicModulesManager.getInstance().getProtocolManager().getProtocolsDescription(); - process.send!( - { - event: ChildSendingEvents.PROTOCOLS_LIST, - value: protocols - }); - return true; - } + public async process(message: any): Promise { + const protocols = DynamicModulesManager.getInstance().getProtocolManager().getProtocolsDescription(); + process.send!({ + event: ChildSendingEvents.PROTOCOLS_LIST, + value: protocols + }); + return true; + } } diff --git a/src/run-as-child/store-cleaner.test.ts b/src/run-as-child/store-cleaner.test.ts index b64b7717..e31430d7 100644 --- a/src/run-as-child/store-cleaner.test.ts +++ b/src/run-as-child/store-cleaner.test.ts @@ -1,15 +1,13 @@ -import {StoreCleaner} from './store-cleaner'; -import {Store} from '../configurations/store'; +import { StoreCleaner } from './store-cleaner'; +import { Store } from '../configurations/store'; describe('StoreCleaner', () => { + it('should clean store when a message arrives', async () => { + Store.getData().test = true; - it('should clean store when a message arrives', async () => { - Store.getData().test = true; - - expect(Store.getData().test).toBeTruthy(); - await new StoreCleaner().process(); - - expect(Store.getData().test).toBeUndefined(); - }); + expect(Store.getData().test).toBeTruthy(); + await new StoreCleaner().process(); + expect(Store.getData().test).toBeUndefined(); + }); }); diff --git a/src/run-as-child/store-cleaner.ts b/src/run-as-child/store-cleaner.ts index 6fc19114..b4467d48 100644 --- a/src/run-as-child/store-cleaner.ts +++ b/src/run-as-child/store-cleaner.ts @@ -1,9 +1,9 @@ -import {ParentReplier} from './parent-replier'; -import {Store} from '../configurations/store'; +import { ParentReplier } from './parent-replier'; +import { Store } from '../configurations/store'; export class StoreCleaner implements ParentReplier { - public async process(): Promise { - Store.refreshData(); - return true; - } + public async process(): Promise { + Store.refreshData(); + return true; + } } diff --git a/src/run-as-child/store-setter.test.ts b/src/run-as-child/store-setter.test.ts new file mode 100644 index 00000000..42409a93 --- /dev/null +++ b/src/run-as-child/store-setter.test.ts @@ -0,0 +1,19 @@ +import { StoreSetter } from './store-setter'; +import { Store } from '../configurations/store'; + +describe('StoreSetter', () => { + it('should set store when a message arrives', async () => { + expect(Store.getData().a).toBeUndefined(); + expect(Store.getData().b).toBeUndefined(); + + await new StoreSetter().process({ + value: { + a: true, + b: 4 + } + }); + + expect(Store.getData().a).toBeTruthy(); + expect(Store.getData().b).toBe(4); + }); +}); diff --git a/src/run-as-child/store-setter.ts b/src/run-as-child/store-setter.ts new file mode 100644 index 00000000..051e3ee0 --- /dev/null +++ b/src/run-as-child/store-setter.ts @@ -0,0 +1,11 @@ +import { Store } from '../configurations/store'; +import { ParentReplier } from './parent-replier'; + +export class StoreSetter implements ParentReplier { + public async process(message: any): Promise { + Object.keys(message.value || {}).forEach(key => { + Store.getData()[key] = message.value[key]; + }); + return true; + } +} diff --git a/src/sensors/custom-sensor.ts b/src/sensors/custom-sensor.ts new file mode 100644 index 00000000..7a8ca5d1 --- /dev/null +++ b/src/sensors/custom-sensor.ts @@ -0,0 +1,55 @@ +import { Sensor } from './sensor'; +import { SensorModel } from '../models/inputs/sensor-model'; +import { Store } from '../configurations/store'; +import { Logger } from '../loggers/logger'; +import * as fs from 'fs'; +import requireFromString from 'require-from-string'; +import { MainInstance } from '../plugins/main-instance'; +import { SensorProtocol } from '../protocols/sensor-protocol'; + +class CustomSensor extends Sensor { + constructor(sensorModel: SensorModel) { + super(sensorModel); + try { + const moduleString: string = fs.readFileSync(this.module).toString(); + const module = requireFromString(moduleString); + this['custom'] = new module.Sensor(this); + } catch (err) { + Logger.error(`Error loading module '${this.module}': ${err}`); + } + } + + public async mount(): Promise { + return this.custom.mount({ store: Store.getData(), logger: Logger }); + } + + public async receiveMessage(): Promise { + return this.custom.receiveMessage({ + store: Store.getData(), + logger: Logger + }); + } + + public async unmount(): Promise { + if (this.custom.close) { + return this.custom.close({ + store: Store.getData(), + logger: Logger + }); + } + } + + public async respond(): Promise { + if (this.custom.respond) { + return this.custom.respond({ + store: Store.getData(), + logger: Logger + }); + } + } +} + +export function entryPoint(mainInstance: MainInstance): void { + const protocol = new SensorProtocol('custom', (sensorModel: SensorModel) => new CustomSensor(sensorModel)); + mainInstance.protocolManager.addProtocol(protocol); +} diff --git a/src/sensors/filename-watcher-sensor.ts b/src/sensors/filename-watcher-sensor.ts new file mode 100644 index 00000000..660bd0ac --- /dev/null +++ b/src/sensors/filename-watcher-sensor.ts @@ -0,0 +1,82 @@ +import { Sensor } from './sensor'; +import { Logger } from '../loggers/logger'; +import { SensorModel } from '../models/inputs/sensor-model'; +import * as fs from 'fs'; +import * as glob from 'glob'; +import { MainInstance } from '../plugins/main-instance'; +import { SensorProtocol } from '../protocols/sensor-protocol'; + +class FileSystemWatcherSensor extends Sensor { + constructor(sensorAttributes: SensorModel) { + super(sensorAttributes); + this.options = sensorAttributes.options || { nodir: true }; + } + + public mount(): Promise { + return Promise.resolve(); + } + + public async receiveMessage(): Promise { + return new Promise((resolve, reject) => { + let interval = setInterval(() => { + const files = glob.sync(this.fileNamePattern, this.options); + if (files.length > 0) { + const filename = files[0]; + try { + this.executeHookEvent('onMessageReceived', this.extractFileInformation(filename)); + resolve(); + } catch (error) { + Logger.warning(`Error reading file ${filename}: ${error}`); + reject(error); + } + clearInterval(interval); + } + }, 50); + }); + } + + private extractFileInformation(filename: string) { + const stat = fs.lstatSync(filename); + return { + content: fs.readFileSync(filename).toString(), + name: filename, + size: stat.size, + modified: stat.mtime, + created: stat.ctime + }; + } +} + +export function entryPoint(mainInstance: MainInstance): void { + const protocol = new SensorProtocol('file', (sensorModel: SensorModel) => new FileSystemWatcherSensor(sensorModel), { + description: 'The file sensor provides an implementation of filesystem readers', + libraryHomepage: 'https://github.com/isaacs/node-glob', + schema: { + attributes: { + fileNamePattern: { + description: 'Glob pattern to identify files to be watched', + required: true, + type: 'string' + }, + options: { + description: 'https://github.com/isaacs/node-glob#options', + type: 'object', + required: false + } + }, + hooks: { + onMessageReceived: { + arguments: { + name: {}, + content: {}, + size: {}, + modified: {}, + created: {} + } + } + } + } + }).addAlternativeName('file-system-watcher', 'file-watcher'); + + mainInstance.protocolManager.addProtocol(protocol); +} diff --git a/src/sensors/http-sensor.ts b/src/sensors/http-sensor.ts new file mode 100644 index 00000000..7afe9e49 --- /dev/null +++ b/src/sensors/http-sensor.ts @@ -0,0 +1,291 @@ +import { Sensor } from './sensor'; +import { Logger } from '../loggers/logger'; +import { SensorModel } from '../models/inputs/sensor-model'; +import { HttpContainerPool } from '../pools/http-container-pool'; +import { TestModel } from '../models/outputs/test-model'; +import { HttpActuatorFetcher } from '../pools/http-actuator-fetcher'; +import { MainInstance } from '../plugins/main-instance'; +import { SensorProtocol } from '../protocols/sensor-protocol'; +import { HttpAuthenticationFactory } from '../http-authentications/http-authentication-factory'; +import * as core from 'express-serve-static-core'; + +const DEFAULT_TIMEOUT = 5000; + +class HttpSensor extends Sensor { + private readonly proxy: boolean; + private responseToClientHandler?: any; + private expressApp?: core.Express | any; + + constructor(sensorAttributes: SensorModel) { + super(sensorAttributes); + + this.type = this.type.toLowerCase(); + this.proxy = this.isProxyServer(); + this['method'] = sensorAttributes.method || 'get'; + this['method'] = this.method.toLowerCase(); + } + + public async mount(): Promise { + try { + const app = await HttpContainerPool.getApp(this.port, this.credentials); + this.expressApp = app; + this.expressApp.keepAliveTimeout = 60 * 1000 + 1000; + this.expressApp.headersTimeout = 60 * 1000 + 2000; + } catch (err) { + const message = `Error in [${this.type}] ${this.name} sensor: ${err}`; + Logger.error(message); + throw err; + } + } + + public unmount(): Promise { + return HttpContainerPool.releaseApp(this.port); + } + + public respond(): Promise { + Logger.trace(`${this.type} sending response: ${JSON.stringify(this.response)}`); + try { + Object.keys(this.response.headers || {}).forEach(key => { + this.responseToClientHandler.header(key, this.response.headers[key]); + }); + const body = typeof this.response.payload === 'number' ? '' + this.response.payload : this.response.payload; + this.responseToClientHandler.status(this.response.status).send(body); + Logger.debug(`${this.type} response sent`); + return Promise.resolve(); + } catch (err) { + return Promise.reject(`${this.type} response back sending error: ${err}`); + } + } + + public onMessageReceivedTests(messageReceived: any): TestModel[] { + if (this.authentication && messageReceived) { + Logger.debug( + `${this.type} authenticating message with ${JSON.stringify(Object.keys(this.authentication), null, 2)}` + ); + const verifier = new HttpAuthenticationFactory().create(this.authentication); + return verifier.verify(messageReceived.headers.authorization); + } + return []; + } + + public receiveMessage(): Promise { + if (this.proxy) { + return this.proxyServerMessageReceiving(); + } else { + return this.realServerMessageReceiving(); + } + } + + private realServerMessageReceiving(): Promise { + return new Promise(resolve => { + Logger.debug(`Listening to (${this.method.toUpperCase()}) ${this.port}:${this.endpoint}`); + const realServerFunction = (request: any, responseHandler: any, next: any) => { + Logger.debug( + `${this.type.toUpperCase()}:${this.port} got hit (${this.method.toUpperCase()}) ${this.endpoint}: ${request.rawBody}` + ); + if (this.responseToClientHandler) { + next(); + } + this.responseToClientHandler = responseHandler; + this.onMessageReceivedTests(request); + const receivedStructure = this.createMessageReceivedStructure(request); + this.executeHookEvent('onMessageReceived', receivedStructure); + resolve(this.processResponseToBePrinted(receivedStructure)); + }; + this.expressApp[this.method](this.endpoint, (request: any, responseHandler: any, next: any) => + realServerFunction(request, responseHandler, next) + ); + }); + } + + private processResponseToBePrinted(response: any) { + try { + if (response.body) { + response.body = JSON.parse(response.body); + } + } catch (e) {} + return response; + } + + private proxyServerMessageReceiving(): Promise { + return new Promise((resolve, reject) => { + Logger.debug(`Listening to (${this.method})${this.port}${this.endpoint}/*`); + const proxyNamedFunction = (originalRequest: any, responseHandler: any, next: any) => { + this.responseToClientHandler = responseHandler; + Logger.debug(`${this.type}:${this.port} got hit (${this.method}) ${this.endpoint}: ${originalRequest.rawBody}`); + this.redirect.headers = originalRequest.headers; + this.redirect.payload = originalRequest.rawBody; + this.onMessageReceivedTests(originalRequest); + this.executeHookEvent('onOriginalMessageReceived', this.createMessageReceivedStructure(originalRequest)); + + this.redirectCall() + .then((redirectionResponse: any) => { + Logger.trace( + `${this.type}:${this.port} got redirection response: ` + `${JSON.stringify(redirectionResponse, null, 2)}` + ); + this.response = { + status: redirectionResponse.statusCode, + payload: redirectionResponse.body, + headers: redirectionResponse.headers + }; + this.executeHookEvent('onMessageReceived', redirectionResponse); + resolve(); + next(); + }) + .catch(err => { + reject(err); + next(); + }); + }; + this.expressApp[this.method](this.endpoint, (request: any, responseHandler: any, next: any) => + proxyNamedFunction(request, responseHandler, next) + ); + }); + } + + private createMessageReceivedStructure(message: any): any { + return { + headers: message.headers, + params: message.params, + query: message.query, + url: message.url, + body: message.rawBody + }; + } + + private redirectCall(): Promise { + Logger.info(`Redirecting call from ${this.endpoint} (${this.port}) to ${this.url}`); + return new HttpActuatorFetcher( + this.redirect.url, + this.redirect.method || 'get', + this.redirect.headers, + this.redirect.payload, + this.redirect.timeout || DEFAULT_TIMEOUT + ).request(); + } + + private isProxyServer(): boolean { + if (this.type) { + return this.type.indexOf('proxy') != -1; + } + throw `Http server type is not known: ${this.type}`; + } +} + +export function entryPoint(mainInstance: MainInstance): void { + const protocol = new SensorProtocol('http', (sensorModel: SensorModel) => new HttpSensor(sensorModel), { + description: 'The HTTP sensor provides implementations of http servers and proxies', + libraryHomepage: 'https://expressjs.com/', + schema: { + attributes: { + endpoint: { + required: true, + type: 'string', + example: '/almighty/enqueuer' + }, + method: { + required: false, + type: 'string', + defaultValue: 'GET', + listValues: ['GET', 'POST', 'PATCH', 'PUT', 'OPTIONS', 'HEAD', 'DELETE'] + }, + port: { + required: true, + type: 'int' + }, + timeout: { + required: false, + type: 'int', + defaultValue: DEFAULT_TIMEOUT, + suffix: 'ms' + }, + credentials: { + required: false, + description: 'Values used when being used as a secure server', + type: { + key: { + required: true, + type: 'string' + }, + cert: { + required: true, + type: 'string' + } + } + }, + redirect: { + description: 'Values used when being used as a proxy sensor', + type: { + url: { + required: true, + type: 'string', + example: 'https://github.com/enqueuer-land/enqueuer' + }, + method: { + required: false, + type: 'string', + defaultValue: 'GET', + listValues: ['GET', 'POST', 'PATCH', 'PUT', 'OPTIONS', 'HEAD', 'DELETE'] + }, + timeout: { + required: false, + type: 'int', + defaultValue: DEFAULT_TIMEOUT, + suffix: 'ms' + }, + headers: { + description: '', + type: 'object', + defaultValue: {} + } + } + }, + headers: { + description: '', + type: 'object', + defaultValue: {} + }, + response: { + description: 'Response to be given when not being used as proxy', + type: { + status: { + required: true, + type: 'int' + }, + payload: { + required: true, + type: 'any' + } + }, + defaultValue: {} + } + }, + hooks: { + onMessageReceived: { + description: '', + arguments: { + params: {}, + query: {}, + body: {}, + url: {} + } + }, + onOriginalMessageReceived: { + description: + 'Useful when using a proxy sensor. ' + + 'Gets called when the proxy is hit, before actually proxying the message', + arguments: { + params: {}, + query: {}, + body: {}, + url: {} + } + } + } + } + }) + .addAlternativeName('https', 'http-proxy', 'https-proxy', 'http-server', 'https-server') + .setLibrary('fetch'); + + mainInstance.protocolManager.addProtocol(protocol); +} diff --git a/src/sensors/null-sensor.ts b/src/sensors/null-sensor.ts new file mode 100644 index 00000000..69143c06 --- /dev/null +++ b/src/sensors/null-sensor.ts @@ -0,0 +1,16 @@ +import { Sensor } from './sensor'; +import { SensorModel } from '../models/inputs/sensor-model'; + +export class NullSensor extends Sensor { + constructor(sensorAttributes: SensorModel) { + super(sensorAttributes); + } + + public mount(): Promise { + return Promise.reject(`Undefined sensor: '${this.type}'`); + } + + public async receiveMessage(): Promise { + return Promise.reject(`Undefined sensor: '${this.type}'`); + } +} diff --git a/src/sensors/sensor.ts b/src/sensors/sensor.ts new file mode 100644 index 00000000..673e2274 --- /dev/null +++ b/src/sensors/sensor.ts @@ -0,0 +1,47 @@ +import { SensorModel } from '../models/inputs/sensor-model'; +import { Logger } from '../loggers/logger'; +import { Event } from '../models/events/event'; + +export abstract class Sensor { + public name: string; + public messageReceived?: any; + public timeout?: number; + public onMessageReceived?: Event; + public onFinish?: Event; + public response?: any; + public type: string; + public avoid: boolean = false; + public ignore: boolean = false; + + [propName: string]: any; + + protected constructor(sensorAttributes: SensorModel) { + Object.keys(sensorAttributes).forEach(key => { + this[key] = sensorAttributes[key]; + }); + this.type = sensorAttributes.type; + this.name = sensorAttributes.name; + } + + public abstract mount(): Promise; + + public abstract receiveMessage(): Promise; + + public async unmount(): Promise { + //do nothing + } + + public async respond(): Promise {} + + public registerHookEventExecutor(hookEventExecutor: (eventName: string, args: any) => void) { + this.hookEventExecutor = hookEventExecutor; + } + + protected executeHookEvent(hookName: string, args: any = {}) { + if (this.hookEventExecutor) { + this.hookEventExecutor(hookName, args); + } else { + Logger.warning(`Hook event executor not registered in sensor`); + } + } +} diff --git a/src/sensors/standard-input-sensor.ts b/src/sensors/standard-input-sensor.ts new file mode 100644 index 00000000..8380e393 --- /dev/null +++ b/src/sensors/standard-input-sensor.ts @@ -0,0 +1,56 @@ +import { Sensor } from './sensor'; +import { SensorModel } from '../models/inputs/sensor-model'; +import { MainInstance } from '../plugins/main-instance'; +import { SensorProtocol } from '../protocols/sensor-protocol'; + +class StandardInputSensor extends Sensor { + private value?: string; + + constructor(sensorModel: SensorModel) { + super(sensorModel); + } + + public receiveMessage(): Promise { + return new Promise(resolve => { + process.stdin.on('end', () => { + if (this.value) { + resolve(); + this.executeHookEvent('onMessageReceived', { message: this.value }); + } + }); + }); + } + + public mount(): Promise { + process.stdin.setEncoding('utf8'); + process.stdin.resume(); + process.stdin.on('data', chunk => { + if (!this.value) { + this.value = chunk.toString(); + } else { + this.value += chunk; + } + }); + return Promise.resolve(); + } + + public async unmount(): Promise { + process.stdin.pause(); + } +} + +export function entryPoint(mainInstance: MainInstance): void { + const protocol = new SensorProtocol('stdin', (sensorModel: SensorModel) => new StandardInputSensor(sensorModel), { + schema: { + hooks: { + onMessageReceived: { + arguments: { + message: {} + } + } + } + } + }).addAlternativeName('standard-input'); + + mainInstance.protocolManager.addProtocol(protocol); +} diff --git a/src/sensors/stream-sensor.ts b/src/sensors/stream-sensor.ts new file mode 100644 index 00000000..5327e1d1 --- /dev/null +++ b/src/sensors/stream-sensor.ts @@ -0,0 +1,300 @@ +import { Sensor } from './sensor'; +import { SensorModel } from '../models/inputs/sensor-model'; +import * as net from 'net'; +import * as tls from 'tls'; +import { Logger } from '../loggers/logger'; +import { Store } from '../configurations/store'; +import { HandlerListener } from '../handlers/handler-listener'; +import * as fs from 'fs'; +import { Timeout } from '../timers/timeout'; +import { MainInstance } from '../plugins/main-instance'; +import { SensorProtocol } from '../protocols/sensor-protocol'; +import { ProtocolDocumentation } from '../protocols/protocol-documentation'; + +export class StreamSensor extends Sensor { + private server: any; + private stream: any; + private sslServerGotConnection?: Promise; + + constructor(sensorAttributes: SensorModel) { + super(sensorAttributes); + this.type = this.type.toLowerCase(); + if (this.response && typeof sensorAttributes.response != 'string') { + this.response = JSON.stringify(sensorAttributes.response, null, 2); + } + } + + public async receiveMessage(): Promise { + if (this.loadStream) { + return await this.waitForData(); + } else if ('ssl' === (this.type || '').toLowerCase()) { + await this.sslServerGotConnection; + return await this.gotConnection(this.stream); + } else { + return new Promise((resolve, reject) => { + this.server.once('connection', (stream: any) => { + this.gotConnection(stream) + .then(() => resolve()) + .catch((err: any) => reject(err)); + }); + }); + } + } + + private async gotConnection(stream: any): Promise { + this.stream = stream; + Logger.debug(`${this.type} readableStream got a connection ${this.stream}`); + this.sendGreeting(); + await this.waitForData(); + } + + public override async mount(): Promise { + if (this.loadStream) { + return this.reuseServer(); + } else { + return this.createServer(); + } + } + + public override async unmount(): Promise { + this.persistStream(); + if ('uds' === (this.type || '').toLowerCase() && fs.existsSync(this.path)) { + fs.unlinkSync(this.path); + } + if (this.server) { + this.server.close(); + this.server = null; + } + Logger.debug(`${this.type} closed`); + } + + public respond(): Promise { + if (!this.response) { + return Promise.resolve(); + } + return new Promise(resolve => { + if (this.stream) { + if (this.stream.write) { + Logger.debug(`${this.type} readableStream (${this.stream.localPort}) sending response`); + this.stream.write(this.response, () => { + Logger.debug(`${this.type} readableStream response sent`); + resolve(); + }); + } else { + resolve(); + } + } + }); + } + + private reuseServer(): Promise { + return new Promise((resolve, reject) => { + try { + this.tryToLoadStream(); + Logger.debug(`${this.type} readableStream is reusing ${this.type} stream running on ${this.stream.localPort}`); + resolve(); + } catch (err) { + Logger.error(`Stream sensor errored: ` + err); + this.createServer() + .then(() => resolve()) + .catch(err => reject(err)); + } + }); + } + + private createServer(): Promise { + return new Promise((resolve, reject) => { + this.createStream() + .then(() => { + Logger.debug(`${this.type} readableStream is ready ${this.port || this.path}`); + resolve(); + }) + .catch(err => { + const message = `${this.type} readableStream errored ${this.port || this.path}: ${err}`; + Logger.error(message); + reject(message); + }); + }); + } + + private createStream(): Promise { + if ('tcp' === (this.type || '').toLowerCase()) { + this.server = net.createServer(); + return new HandlerListener(this.server).listen(this.port); + } else if ('ssl' === (this.type || '').toLowerCase()) { + this.createSslConnection(); + return new HandlerListener(this.server).listen(this.port); + } else { + this.server = net.createServer(); + return new HandlerListener(this.server).listen(this.path); + } + } + + private async createSslConnection() { + this.sslServerGotConnection = new Promise((resolve, reject) => { + try { + this.server = tls.createServer(this.options, stream => { + this.stream = stream; + resolve(); + }); + } catch (err) { + reject(err); + } + }); + } + + private sendGreeting() { + if (this.greeting && this.stream.write) { + Logger.debug(`${this.type} readableStream (${this.stream.localPort}) sending greeting message`); + this.stream.write(this.greeting); + } + } + + private tryToLoadStream() { + Logger.debug(`readableStream is loading ${this.type} stream: ${this.loadStream}`); + this.stream = Store.getData()[this.loadStream]; + if (this.stream) { + Logger.debug(`readableStream loaded ${this.type} stream: ${this.loadStream} (${this.stream.localPort})`); + } else { + throw `Impossible to load ${this.type} stream: ${this.loadStream}`; + } + } + + private waitForData(): Promise { + return new Promise((resolve, reject) => { + let message: any = { + payload: undefined, + stream: this.stream.address ? this.stream.address() : undefined, + path: this.path + }; + Logger.trace(`${this.type} readableStream is waiting on data`); + if (this.streamTimeout) { + new Timeout(() => { + Logger.trace(`Readable 'stream timeout' emitted`); + this.onEnd(message, resolve, reject); + }).start(this.streamTimeout); + } else { + this.stream.once('end', () => { + Logger.trace(`'End' timeout emitted`); + this.onEnd(message, resolve, reject); + }); + } + this.stream.on('data', (msg: any) => { + this.onData(msg, message, resolve); + }); + }); + } + + // ssl 8 - 8 + + private onEnd(message: any, resolve: any, reject: any) { + if (message.payload !== undefined) { + this['finished'] = true; + if (!this.finished) { + this.executeHookEvent('onMessageReceived', message); + } + resolve(); + } else { + reject(); + } + } + + private onData(msg: any, message: any, resolve: any) { + if (!this.finished) { + Logger.debug(`'${this.type}' readableStream got data '${msg}'`); + if (message.payload === undefined) { + message.payload = ''; + } + message.payload += msg; + if (!this.streamTimeout) { + this['finished'] = true; + this.executeHookEvent('onMessageReceived', message); + resolve(); + } + } + } + + private persistStream() { + if (this.stream) { + if (this.saveStream) { + Logger.debug(`Persisting sensor ${this.type} stream ${this.saveStream}`); + Store.getData()[this.saveStream] = this.stream; + this['saveStream'] = undefined; + } else if (typeof this.stream.end === 'function') { + Logger.trace(`Ending ${this.type} stream`); + this.stream.end(); + } + } + } +} + +export function entryPoint(mainInstance: MainInstance): void { + const createFunction = (sensorModel: SensorModel) => new StreamSensor(sensorModel); + const docs: ProtocolDocumentation = { + description: 'The stream sensor provides implementations of TCP/UDS servers', + libraryHomepage: 'https://nodejs.org/api/net.html', + schema: { + attributes: { + response: { + required: true, + type: 'text' + }, + greeting: { + required: false, + type: 'text' + }, + port: { + description: 'Defined when using TCP', + required: false, + type: 'int' + }, + path: { + description: 'Defined when using UDS', + required: false, + type: 'string' + }, + saveStream: { + description: 'Set it when you want to reuse this stream', + required: false, + type: 'string' + }, + loadStream: { + description: 'Set it when you want to reuse an opened stream', + required: false, + type: 'string' + }, + streamTimeout: { + description: 'Timeout to stop listening after the first byte is read', + required: false, + type: 'int' + }, + options: { + description: + 'Defined when using SSL. https://nodejs.org/api/net.html#net_net_createserver_options_connectionlistener', + required: false, + type: 'object' + } + }, + hooks: { + onMessageReceived: { + arguments: { + payload: {}, + stream: {}, + path: { + description: 'Defined only when it is a UDS server' + } + } + } + } + } + }; + const tcp = new SensorProtocol('tcp', createFunction, docs); + + const uds = new SensorProtocol('uds', createFunction, docs); + + const ssl = new SensorProtocol('ssl', createFunction, docs); + + mainInstance.protocolManager.addProtocol(tcp); + mainInstance.protocolManager.addProtocol(uds); + mainInstance.protocolManager.addProtocol(ssl); +} diff --git a/src/sensors/udp-sensor.ts b/src/sensors/udp-sensor.ts new file mode 100644 index 00000000..150bfff9 --- /dev/null +++ b/src/sensors/udp-sensor.ts @@ -0,0 +1,81 @@ +import { Sensor } from './sensor'; +import { SensorModel } from '../models/inputs/sensor-model'; +import * as dgram from 'dgram'; +import { Logger } from '../loggers/logger'; +import { MainInstance } from '../plugins/main-instance'; +import { SensorProtocol } from '../protocols/sensor-protocol'; + +class UdpSensor extends Sensor { + private server: any; + + constructor(sensorAttributes: SensorModel) { + super(sensorAttributes); + + if (typeof sensorAttributes.response != 'string') { + this.response = JSON.stringify(sensorAttributes.response, null, 2); + } + } + + public receiveMessage(): Promise { + return new Promise((resolve, reject) => { + this.server.on('error', (err: any) => { + this.server.close(); + reject(err); + }); + + this.server.on('message', (msg: Buffer, remoteInfo: any) => { + this.server.close(); + this.executeHookEvent('onMessageReceived', { + payload: msg, + remoteInfo: remoteInfo + }); + resolve(); + }); + }); + } + + public mount(): Promise { + return new Promise((resolve, reject) => { + this.server = dgram.createSocket('udp4'); + try { + this.server.bind(this.port); + resolve(); + } catch (err) { + const message = `Udp server could not listen to ${this.port}`; + Logger.error(message); + reject(message); + } + }); + } +} + +export function entryPoint(mainInstance: MainInstance): void { + const protocol = new SensorProtocol('udp', (sensorModel: SensorModel) => new UdpSensor(sensorModel), { + description: 'The udp sensor provides an implementation of UDP Datagram sockets servers', + libraryHomepage: 'https://nodejs.org/api/dgram.html', + schema: { + attributes: { + port: { + required: true, + type: 'int' + }, + response: { + required: true, + type: 'string' + } + }, + hooks: { + onMessageReceived: { + arguments: { + payload: {}, + remoteInfo: { + description: 'Remote address information' + } + } + } + } + } + }).addAlternativeName('udp-server'); + + mainInstance.protocolManager.addProtocol(protocol); +} diff --git a/src/strings/id-generator.test.ts b/src/strings/id-generator.test.ts index a39b2bad..37f21f74 100644 --- a/src/strings/id-generator.test.ts +++ b/src/strings/id-generator.test.ts @@ -1,51 +1,49 @@ -import {IdGenerator} from './id-generator'; -import {createHash} from 'crypto'; -import {DateController} from '../timers/date-controller'; +import { IdGenerator } from './id-generator'; +import { createHash } from 'crypto'; +import { DateController } from '../timers/date-controller'; jest.mock('../timers/date-controller'); // @ts-ignore DateController.mockImplementation(() => { - return { - getStringOnlyNumbers: () => { - return '20180409113740057612'; - } - }; + return { + getStringOnlyNumbers: () => { + return '20180409113740057612'; + } + }; }); describe('IdGenerator', () => { + it('generateId', () => { + const text: string = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.'; + const hash = createHash('sha256'); + hash.update(text, 'utf8'); - it('generateId', () => { - const text: string = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.'; - const hash = createHash('sha256'); - hash.update(text, 'utf8'); - - const idGenerator: IdGenerator = new IdGenerator(text); - const expected = '1137400576_a58dd8680'; + const idGenerator: IdGenerator = new IdGenerator(text); + const expected = '1137400576_a58dd8680'; - let generatedId = idGenerator.generateId(); + let generatedId = idGenerator.generateId(); - expect(generatedId.substring(0, 20)).toBe(expected); - expect(generatedId.length).toBeGreaterThan(20); - }); + expect(generatedId.substring(0, 20)).toBe(expected); + expect(generatedId.length).toBeGreaterThan(20); + }); - it('generateId of objects', () => { - const value = { - cycle: { - deeper: { - deepest: true - } - } - }; - const hash = createHash('sha256'); - hash.update(JSON.stringify(value), 'utf8'); - - const idGenerator: IdGenerator = new IdGenerator(value); - const expected = '1137400576_d569243bb'; + it('generateId of objects', () => { + const value = { + cycle: { + deeper: { + deepest: true + } + } + }; + const hash = createHash('sha256'); + hash.update(JSON.stringify(value), 'utf8'); - let generatedId = idGenerator.generateId(); + const idGenerator: IdGenerator = new IdGenerator(value); + const expected = '1137400576_d569243bb'; - expect(generatedId.substring(0, 20)).toBe(expected); - expect(generatedId.length).toBeGreaterThan(20); - }); + let generatedId = idGenerator.generateId(); + expect(generatedId.substring(0, 20)).toBe(expected); + expect(generatedId.length).toBeGreaterThan(20); + }); }); diff --git a/src/strings/id-generator.ts b/src/strings/id-generator.ts index 0f3b6fa1..f05f47f0 100644 --- a/src/strings/id-generator.ts +++ b/src/strings/id-generator.ts @@ -1,27 +1,28 @@ -import {DateController} from '../timers/date-controller'; -import {createHash} from 'crypto'; -import {ObjectDecycler} from '../object-parser/object-decycler'; +import { DateController } from '../timers/date-controller'; +import { createHash } from 'crypto'; +import { ObjectDecycler } from '../object-parser/object-decycler'; export class IdGenerator { + private readonly value: string; - private readonly value: string; - - public constructor(value: any) { - if (value && typeof (value) !== 'string') { - this.value = JSON.stringify(new ObjectDecycler().decycle(value)); - } else { - this.value = value as string; - } + public constructor(value: any) { + if (value && typeof value !== 'string') { + this.value = JSON.stringify(new ObjectDecycler().decycle(value)); + } else { + this.value = value as string; } + } - public generateId(): string { - const hash = createHash('sha256'); - hash.update(this.value, 'utf8'); - const coded = hash.digest('hex'); - return new DateController().getStringOnlyNumbers().substr(8, 10) + - '_' + - coded.substr(0, 10) + - '_' + - Math.trunc(Math.random() * 1000000 + 0x123); - } + public generateId(): string { + const hash = createHash('sha256'); + hash.update(this.value, 'utf8'); + const coded = hash.digest('hex'); + return ( + new DateController().getStringOnlyNumbers().substr(8, 10) + + '_' + + coded.substr(0, 10) + + '_' + + Math.trunc(Math.random() * 1000000 + 0x123) + ); + } } diff --git a/src/strings/string-random-creator.test.ts b/src/strings/string-random-creator.test.ts index df8b732c..43a666b2 100644 --- a/src/strings/string-random-creator.test.ts +++ b/src/strings/string-random-creator.test.ts @@ -1,45 +1,44 @@ -import {DateController} from "../timers/date-controller"; -import {StringRandomCreator} from "./string-random-creator"; +import { DateController } from '../timers/date-controller'; +import { StringRandomCreator } from './string-random-creator'; -jest.mock("../timers/date-controller"); +jest.mock('../timers/date-controller'); +// @ts-expect-error DateController.mockImplementation(() => { - return { - getStringOnlyNumbers: () => { - return "20180409113740057612" - } - }; + return { + getStringOnlyNumbers: () => { + return '20180409113740057612'; + } + }; }); describe('StringRandomCreator', () => { + it('length', () => { + const length = 10; + const creator: StringRandomCreator = new StringRandomCreator(); - it('length', () => { - const length = 10; - const creator: StringRandomCreator = new StringRandomCreator(); + let created = creator.create(length); - let created = creator.create(length); + expect(created.length).toBe(length); + }); - expect(created.length).toBe(length); - }); + it('possibles', () => { + const length = 10; + const creator: StringRandomCreator = new StringRandomCreator('A'); - it('possibles', () => { - const length = 10; - const creator: StringRandomCreator = new StringRandomCreator("A"); + let created = creator.create(length); - let created = creator.create(length); + expect(created.length).toBe(length); + expect(created).toBe('AAAAAAAAAA'); + }); - expect(created.length).toBe(length); - expect(created).toBe("AAAAAAAAAA"); - }); + it('empty possibles', () => { + const length = 10; + const possibles = ''; + const creator: StringRandomCreator = new StringRandomCreator(possibles); - it('empty possibles', () => { - const length = 10; - const possibles = ""; - const creator: StringRandomCreator = new StringRandomCreator(possibles); + let created = creator.create(length); - let created = creator.create(length); - - expect(created.length).toBe(possibles.length); - expect(created).toBe(possibles); - }); - -}); \ No newline at end of file + expect(created.length).toBe(possibles.length); + expect(created).toBe(possibles); + }); +}); diff --git a/src/strings/string-random-creator.ts b/src/strings/string-random-creator.ts index 3b6f4ede..1ef153ff 100644 --- a/src/strings/string-random-creator.ts +++ b/src/strings/string-random-creator.ts @@ -1,19 +1,17 @@ export class StringRandomCreator { + private possible: string; - private possible: string; + public constructor(possible: string = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789') { + this.possible = possible; + } - public constructor(possible: string = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789') { - this.possible = possible; - } - - public create = (length: number): string => { - let text = ''; - - for (let i = length; i > 0; --i) { - text += this.possible.charAt(Math.floor(Math.random() * this.possible.length)); - } + public create = (length: number): string => { + let text = ''; - return text; + for (let i = length; i > 0; --i) { + text += this.possible.charAt(Math.floor(Math.random() * this.possible.length)); } -} \ No newline at end of file + return text; + }; +} diff --git a/src/subscriptions/custom-subscription.ts b/src/subscriptions/custom-subscription.ts deleted file mode 100644 index f287ff7e..00000000 --- a/src/subscriptions/custom-subscription.ts +++ /dev/null @@ -1,48 +0,0 @@ -import {Subscription} from './subscription'; -import {SubscriptionModel} from '../models/inputs/subscription-model'; -import {Store} from '../configurations/store'; -import {Logger} from '../loggers/logger'; -import * as fs from 'fs'; -import requireFromString from 'require-from-string'; -import {MainInstance} from '../plugins/main-instance'; -import {SubscriptionProtocol} from '../protocols/subscription-protocol'; - -class CustomSubscription extends Subscription { - - constructor(subscriptionModel: SubscriptionModel) { - super(subscriptionModel); - try { - const moduleString: string = fs.readFileSync(this.module).toString(); - const module = requireFromString(moduleString); - this['custom'] = new module.Subscription(this); - } catch (err) { - Logger.error(`Error loading module '${this.module}': ${err}`); - } - } - - public async subscribe(): Promise { - return this.custom.subscribe({store: Store.getData(), logger: Logger}); - } - - public async receiveMessage(): Promise { - return this.custom.receiveMessage({store: Store.getData(), logger: Logger}); - } - - public async unsubscribe(): Promise { - if (this.custom.unsubscribe) { - return this.custom.unsubscribe({store: Store.getData(), logger: Logger}); - } - } - - public async sendResponse(): Promise { - if (this.custom.sendResponse) { - return this.custom.sendResponse({store: Store.getData(), logger: Logger}); - } - } -} - -export function entryPoint(mainInstance: MainInstance): void { - const protocol = new SubscriptionProtocol('custom', - (subscriptionModel: SubscriptionModel) => new CustomSubscription(subscriptionModel)); - mainInstance.protocolManager.addProtocol(protocol); -} diff --git a/src/subscriptions/filename-watcher-subscription.ts b/src/subscriptions/filename-watcher-subscription.ts deleted file mode 100644 index 15e2158b..00000000 --- a/src/subscriptions/filename-watcher-subscription.ts +++ /dev/null @@ -1,86 +0,0 @@ -import {Subscription} from './subscription'; -import {Logger} from '../loggers/logger'; -import {SubscriptionModel} from '../models/inputs/subscription-model'; -import * as fs from 'fs'; -import * as glob from 'glob'; -import {MainInstance} from '../plugins/main-instance'; -import {SubscriptionProtocol} from '../protocols/subscription-protocol'; - -class FileSystemWatcherSubscription extends Subscription { - - constructor(subscriptionAttributes: SubscriptionModel) { - super(subscriptionAttributes); - this['options'] = subscriptionAttributes.options || {nodir: true}; - } - - public subscribe(): Promise { - return Promise.resolve(); - } - - public async receiveMessage(): Promise { - return new Promise((resolve, reject) => { - let interval = setInterval(() => { - const files = glob.sync(this.fileNamePattern, this.options); - if (files.length > 0) { - const filename = files[0]; - try { - this.executeHookEvent('onMessageReceived', this.extractFileInformation(filename)); - resolve(); - } catch (error) { - Logger.warning(`Error reading file ${filename}: ${error}`); - reject(error); - } - clearInterval(interval); - } - }, 50); - }); - } - - private extractFileInformation(filename: string) { - const stat = fs.lstatSync(filename); - return { - content: fs.readFileSync(filename).toString(), - name: filename, - size: stat.size, - modified: stat.mtime, - created: stat.ctime - }; - } -} - -export function entryPoint(mainInstance: MainInstance): void { - const protocol = new SubscriptionProtocol('file', - (subscriptionModel: SubscriptionModel) => new FileSystemWatcherSubscription(subscriptionModel), - { - description: 'The file subscription provides an implementation of filesystem readers', - libraryHomepage: 'https://github.com/isaacs/node-glob', - schema: { - attributes: { - fileNamePattern: { - description: 'Glob pattern to identify files to be watched', - required: true, - type: 'string' - }, - options: { - description: 'https://github.com/isaacs/node-glob#options', - type: 'object', - required: false - }, - }, - hooks: { - onMessageReceived: { - arguments: { - name: {}, - content: {}, - size: {}, - modified: {}, - created: {} - } - } - } - } - }) - .addAlternativeName('file-system-watcher', 'file-watcher'); - - mainInstance.protocolManager.addProtocol(protocol); -} diff --git a/src/subscriptions/http-subscription.ts b/src/subscriptions/http-subscription.ts deleted file mode 100644 index 31d99215..00000000 --- a/src/subscriptions/http-subscription.ts +++ /dev/null @@ -1,297 +0,0 @@ -import {Subscription} from './subscription'; -import {Logger} from '../loggers/logger'; -import {SubscriptionModel} from '../models/inputs/subscription-model'; -import {HttpContainerPool} from '../pools/http-container-pool'; -import {TestModel} from '../models/outputs/test-model'; -import {HttpRequester} from '../pools/http-requester'; -import {MainInstance} from '../plugins/main-instance'; -import {SubscriptionProtocol} from '../protocols/subscription-protocol'; -import {HttpAuthenticationFactory} from '../http-authentications/http-authentication-factory'; - -class HttpSubscription extends Subscription { - - private readonly proxy: boolean; - private readonly secureServer: boolean; - private responseToClientHandler?: any; - private expressApp: any; - - constructor(subscriptionAttributes: SubscriptionModel) { - super(subscriptionAttributes); - - this.type = this.type.toLowerCase(); - this.secureServer = this.isSecureServer(); - this.proxy = this.isProxyServer(); - this['method'] = subscriptionAttributes.method || 'get'; - this['method'] = this.method.toLowerCase(); - } - - public async subscribe(): Promise { - try { - this.expressApp = await HttpContainerPool.getApp(this.port, this.secureServer, this.credentials); - } catch (err) { - const message = `Error in ${this.type} subscription: ${err}`; - Logger.error(message); - throw err; - } - } - - public unsubscribe(): Promise { - return HttpContainerPool.releaseApp(this.port); - } - - public sendResponse(): Promise { - Logger.trace(`${this.type} sending response: ${JSON.stringify(this.response)}`); - try { - Object.keys(this.response.headers || {}).forEach(key => { - this.responseToClientHandler.header(key, this.response.headers[key]); - }); - const body = typeof this.response.payload === 'number' ? '' + this.response.payload : this.response.payload; - this.responseToClientHandler.status(this.response.status).send(body); - Logger.debug(`${this.type} response sent`); - return Promise.resolve(); - } catch (err) { - return Promise.reject(`${this.type} response back sending error: ${err}`); - } - } - - public onMessageReceivedTests(messageReceived: any): TestModel[] { - if (this.authentication && messageReceived) { - Logger.debug(`${this.type} authenticating message with ${JSON.stringify(Object.keys(this.authentication), null, 2)}`); - const verifier = new HttpAuthenticationFactory().create(this.authentication); - return verifier.verify(messageReceived.headers.authorization); - } - return []; - } - - public receiveMessage(): Promise { - if (this.proxy) { - return this.proxyServerMessageReceiving(); - } else { - return this.realServerMessageReceiving(); - } - } - - private realServerMessageReceiving(): Promise { - return new Promise((resolve) => { - Logger.debug(`Listening to (${this.method.toUpperCase()}) ${this.port}:${this.endpoint}`); - const realServerFunction = (request: any, responseHandler: any, next: any) => { - Logger.debug(`${this.type.toUpperCase()}:${this.port} got hit (${this.method.toUpperCase()}) ${this.endpoint}: ${request.rawBody}`); - if (this.responseToClientHandler) { - next(); - } - this.responseToClientHandler = responseHandler; - this.onMessageReceivedTests(request); - const receivedStructure = this.createMessageReceivedStructure(request); - this.executeHookEvent('onMessageReceived', receivedStructure); - resolve(this.processResponseToBePrinted(receivedStructure)); - }; - this.expressApp[this.method](this.endpoint, - (request: any, responseHandler: any, next: any) => - realServerFunction(request, responseHandler, next)); - }); - } - - private processResponseToBePrinted(response: any) { - try { - if (response.body) { - response.body = JSON.parse(response.body); - } - } catch (e) { - } - return response; - } - - private proxyServerMessageReceiving(): Promise { - return new Promise((resolve, reject) => { - Logger.debug(`Listening to (${this.method})${this.port}${this.endpoint}/*`); - const proxyNamedFunction = (originalRequest: any, responseHandler: any, next: any) => { - this.responseToClientHandler = responseHandler; - Logger.debug(`${this.type}:${this.port} got hit (${this.method}) ${this.endpoint}: ${originalRequest.rawBody}`); - this.redirect['url'] = this.redirect.url + originalRequest.url.replace(this.endpoint, ''); - this.redirect['headers'] = originalRequest.headers; - this.redirect['payload'] = originalRequest.rawBody; - this.onMessageReceivedTests(originalRequest); - this.executeHookEvent('onOriginalMessageReceived', this.createMessageReceivedStructure(originalRequest)); - - this.redirectCall() - .then((redirectionResponse: any) => { - Logger.trace(`${this.type}:${this.port} got redirection response: ` + - `${JSON.stringify(redirectionResponse, null, 2)}`); - this.response = { - status: redirectionResponse.statusCode, - payload: redirectionResponse.body, - headers: redirectionResponse.headers - }; - this.executeHookEvent('onMessageReceived', redirectionResponse); - resolve(); - next(); - }) - .catch(err => { - reject(err); - next(); - }); - - }; - this.expressApp[this.method](this.endpoint + '/*', proxyNamedFunction); - }); - } - - private createMessageReceivedStructure(message: any): any { - return { - headers: message.headers, - params: message.params, - query: message.query, - url: message.url, - body: message.rawBody - }; - } - - private redirectCall(): Promise { - Logger.info(`Redirecting call from ${this.endpoint} (${this.port}) to ${this.url}`); - return new HttpRequester(this.redirect.url, - this.redirect.method || 'get', - this.redirect.headers, - this.redirect.payload, - this.redirect.timeout || 3000) - .request(); - } - - private isSecureServer(): boolean { - if (this.type) { - if (this.type.indexOf('https') != -1) { - return true; - } else if (this.type.indexOf('http') != -1) { - return false; - } - } - throw `Http server type is not known: ${this.type}`; - } - - private isProxyServer(): boolean { - if (this.type) { - return this.type.indexOf('proxy') != -1; - } - throw `Http server type is not known: ${this.type}`; - } -} - -export function entryPoint(mainInstance: MainInstance): void { - const protocol = new SubscriptionProtocol('http', - (subscriptionModel: SubscriptionModel) => new HttpSubscription(subscriptionModel), - { - description: 'The HTTP subscription provides implementations of http servers and proxies', - libraryHomepage: 'https://expressjs.com/', - schema: { - attributes: { - endpoint: { - required: true, - type: 'string', - example: '/almighty/enqueuer' - }, - method: { - required: false, - type: 'string', - defaultValue: 'GET', - listValues: ['GET', 'POST', 'PATCH', 'PUT', 'OPTIONS', 'HEAD', 'DELETE'] - }, - port: { - required: true, - type: 'int' - }, - timeout: { - required: false, - type: 'int', - defaultValue: 3000, - suffix: 'ms' - }, - credentials: { - required: false, - description: 'Values used when being used as a secure server', - type: { - key: { - required: true, - type: 'string' - }, - cert: { - required: true, - type: 'string' - } - } - }, - redirect: { - description: 'Values used when being used as a proxy subscription', - type: { - url: { - required: true, - type: 'string', - example: 'https://github.com/enqueuer-land/enqueuer' - }, - method: { - required: false, - type: 'string', - defaultValue: 'GET', - listValues: ['GET', 'POST', 'PATCH', 'PUT', 'OPTIONS', 'HEAD', 'DELETE'] - }, - timeout: { - required: false, - type: 'int', - defaultValue: 3000, - suffix: 'ms' - }, - headers: { - description: '', - type: 'object', - defaultValue: {} - }, - - } - }, - headers: { - description: '', - type: 'object', - defaultValue: {} - }, - response: { - description: 'Response to be given when not being used as proxy', - type: { - status: { - required: true, - type: 'int' - }, - payload: { - required: true, - type: 'any' - } - }, - defaultValue: {} - }, - }, - hooks: { - onMessageReceived: { - description: '', - arguments: { - params: {}, - query: {}, - body: {}, - url: {}, - } - }, - onOriginalMessageReceived: { - description: 'Useful when using a proxy subscription. ' + - 'Gets called when the proxy is hit, before actually proxying the message', - arguments: { - params: {}, - query: {}, - body: {}, - url: {}, - } - } - } - } - } - ) - .addAlternativeName('https', 'http-proxy', 'https-proxy', 'http-server', 'https-server') - .setLibrary('express'); - - mainInstance.protocolManager.addProtocol(protocol); -} diff --git a/src/subscriptions/null-subscription.ts b/src/subscriptions/null-subscription.ts deleted file mode 100644 index 305dc172..00000000 --- a/src/subscriptions/null-subscription.ts +++ /dev/null @@ -1,17 +0,0 @@ -import {Subscription} from './subscription'; -import {SubscriptionModel} from '../models/inputs/subscription-model'; - -export class NullSubscription extends Subscription { - constructor(subscriptionAttributes: SubscriptionModel) { - super(subscriptionAttributes); - } - - public subscribe(): Promise { - return Promise.reject(`Undefined subscription: '${this.type}'`); - } - - public async receiveMessage(): Promise { - return Promise.reject(`Undefined subscription: '${this.type}'`); - } - -} diff --git a/src/subscriptions/standard-input-subscription.ts b/src/subscriptions/standard-input-subscription.ts deleted file mode 100644 index d2ab209a..00000000 --- a/src/subscriptions/standard-input-subscription.ts +++ /dev/null @@ -1,60 +0,0 @@ -import {Subscription} from './subscription'; -import {SubscriptionModel} from '../models/inputs/subscription-model'; -import {MainInstance} from '../plugins/main-instance'; -import {SubscriptionProtocol} from '../protocols/subscription-protocol'; - -class StandardInputSubscription extends Subscription { - private value?: string; - - constructor(subscriptionModel: SubscriptionModel) { - super(subscriptionModel); - } - - public receiveMessage(): Promise { - return new Promise((resolve) => { - process.stdin.on('end', () => { - if (this.value) { - resolve(); - this.executeHookEvent('onMessageReceived', {message: this.value}); - } - }); - }); - } - - public subscribe(): Promise { - process.stdin.setEncoding('utf8'); - process.stdin.resume(); - process.stdin.on('data', (chunk) => { - if (!this.value) { - this.value = chunk; - } else { - this.value += chunk; - } - }); - return Promise.resolve(); - } - - public async unsubscribe(): Promise { - process.stdin.pause(); - } - -} - -export function entryPoint(mainInstance: MainInstance): void { - const protocol = new SubscriptionProtocol('stdin', - (subscriptionModel: SubscriptionModel) => new StandardInputSubscription(subscriptionModel), - { - schema: { - hooks: { - onMessageReceived: { - arguments: { - message: {}, - } - } - } - } - }) - .addAlternativeName('standard-input'); - - mainInstance.protocolManager.addProtocol(protocol); -} diff --git a/src/subscriptions/stream-subscription.ts b/src/subscriptions/stream-subscription.ts deleted file mode 100644 index 1126201a..00000000 --- a/src/subscriptions/stream-subscription.ts +++ /dev/null @@ -1,301 +0,0 @@ -import {Subscription} from './subscription'; -import {SubscriptionModel} from '../models/inputs/subscription-model'; -import * as net from 'net'; -import * as tls from 'tls'; -import {Logger} from '../loggers/logger'; -import {Store} from '../configurations/store'; -import {HandlerListener} from '../handlers/handler-listener'; -import * as fs from 'fs'; -import {Timeout} from '../timers/timeout'; -import {MainInstance} from '../plugins/main-instance'; -import {SubscriptionProtocol} from '../protocols/subscription-protocol'; -import {ProtocolDocumentation} from '../protocols/protocol-documentation'; - -export class StreamSubscription extends Subscription { - - private server: any; - private stream: any; - - constructor(subscriptionAttributes: SubscriptionModel) { - super(subscriptionAttributes); - this.type = this.type.toLowerCase(); - if (this.response && typeof subscriptionAttributes.response != 'string') { - this.response = JSON.stringify(subscriptionAttributes.response, null, 2); - } - } - - public async receiveMessage(): Promise { - if (this.loadStream) { - return await this.waitForData(); - } else if ('ssl' === (this.type || '').toLowerCase()) { - await this.sslServerGotConnection; - return await this.gotConnection(this.stream); - } else { - return new Promise((resolve, reject) => { - this.server.once('connection', (stream: any) => { - this.gotConnection(stream) - .then(() => resolve()) - .catch((err: any) => reject(err)); - }); - }); - } - } - - private async gotConnection(stream: any): Promise { - this.stream = stream; - Logger.debug(`${this.type} readableStream got a connection ${this.stream}`); - this.sendGreeting(); - await this.waitForData(); - } - - public subscribe(): Promise { - if (this.loadStream) { - return this.reuseServer(); - } else { - return this.createServer(); - } - } - - public async unsubscribe(): Promise { - this.persistStream(); - if ('uds' === (this.type || '').toLowerCase() && fs.existsSync(this.path)) { - fs.unlinkSync(this.path); - } - if (this.server) { - this.server.close(); - this.server = null; - } - Logger.debug(`${this.type} unsubscribed`); - } - - public sendResponse(): Promise { - if (!this.response) { - return Promise.resolve(); - } - return new Promise((resolve) => { - if (this.stream) { - if (this.stream.write) { - Logger.debug(`${this.type} readableStream (${this.stream.localPort}) sending response`); - this.stream.write(this.response, () => { - Logger.debug(`${this.type} readableStream response sent`); - resolve(); - }); - } else { - resolve(); - } - } - }); - } - - private reuseServer(): Promise { - return new Promise((resolve, reject) => { - try { - this.tryToLoadStream(); - Logger.debug(`${this.type} readableStream is reusing ${this.type} stream running on ${this.stream.localPort}`); - resolve(); - } catch (err) { - Logger.error(err); - this.createServer() - .then(() => resolve()) - .catch((err) => reject(err)); - } - - }); - } - - private createServer(): Promise { - return new Promise((resolve, reject) => { - this.createStream() - .then(() => { - Logger.debug(`${this.type} readableStream is ready ${this.port || this.path}`); - resolve(); - }) - .catch(err => { - const message = `${this.type} readableStream errored ${this.port || this.path}: ${err}`; - Logger.error(message); - reject(message); - }); - }); - } - - private createStream(): Promise { - if ('tcp' === (this.type || '').toLowerCase()) { - this.server = net.createServer(); - return new HandlerListener(this.server).listen(this.port); - } else if ('ssl' === (this.type || '').toLowerCase()) { - this.createSslConnection(); - return new HandlerListener(this.server).listen(this.port); - } else { - this.server = net.createServer(); - return new HandlerListener(this.server).listen(this.path); - } - } - - private createSslConnection() { - this['sslServerGotConnection'] = new Promise((resolve, reject) => { - try { - this.server = tls.createServer(this.options, (stream) => { - this.stream = stream; - resolve(); - }); - } catch (err) { - reject(err); - } - }); - } - - private sendGreeting() { - if (this.greeting && this.stream.write) { - Logger.debug(`${this.type} readableStream (${this.stream.localPort}) sending greeting message`); - this.stream.write(this.greeting); - } - } - - private tryToLoadStream() { - Logger.debug(`readableStream is loading ${this.type} stream: ${this.loadStream}`); - this.stream = Store.getData()[this.loadStream]; - if (this.stream) { - Logger.debug(`readableStream loaded ${this.type} stream: ${this.loadStream} (${this.stream.localPort})`); - } else { - throw `Impossible to load ${this.type} stream: ${this.loadStream}`; - } - } - - private waitForData(): Promise { - return new Promise((resolve, reject) => { - let message: any = { - payload: undefined, - stream: this.stream.address ? this.stream.address() : undefined, - path: this.path - }; - Logger.trace(`${this.type} readableStream is waiting on data`); - if (this.streamTimeout) { - new Timeout(() => { - Logger.trace(`Readable 'stream timeout' emitted`); - this.onEnd(message, resolve, reject); - }).start(this.streamTimeout); - } else { - this.stream.once('end', () => { - Logger.trace(`'End' timeout emitted`); - this.onEnd(message, resolve, reject); - }); - } - this.stream.on('data', (msg: any) => { - this.onData(msg, message, resolve); - }); - }); - } - - // ssl 8 - 8 - - private onEnd(message: any, resolve: any, reject: any) { - if (message.payload !== undefined) { - this['finished'] = true; - if (!this.finished) { - this.executeHookEvent('onMessageReceived', message); - } - resolve(); - } else { - reject(); - } - } - - private onData(msg: any, message: any, resolve: any) { - if (!this.finished) { - Logger.debug(`'${this.type}' readableStream got data '${msg}'`); - if (message.payload === undefined) { - message.payload = ''; - } - message.payload += msg; - if (!this.streamTimeout) { - this['finished'] = true; - this.executeHookEvent('onMessageReceived', message); - resolve(); - } - } - } - - private persistStream() { - if (this.stream) { - if (this.saveStream) { - Logger.debug(`Persisting subscription ${this.type} stream ${this.saveStream}`); - Store.getData()[this.saveStream] = this.stream; - this['saveStream'] = undefined; - } else if (typeof (this.stream.end) === 'function') { - Logger.trace(`Ending ${this.type} stream`); - this.stream.end(); - } - } - } - -} - -export function entryPoint(mainInstance: MainInstance): void { - const createFunction = (subscriptionModel: SubscriptionModel) => new StreamSubscription(subscriptionModel); - const docs: ProtocolDocumentation = { - description: 'The stream subscription provides implementations of TCP/UDS servers', - libraryHomepage: 'https://nodejs.org/api/net.html', - schema: { - attributes: { - response: { - required: true, - type: 'text' - }, - greeting: { - required: false, - type: 'text' - }, - port: { - description: 'Defined when using TCP', - required: false, - type: 'int' - }, - path: { - description: 'Defined when using UDS', - required: false, - type: 'string' - }, - saveStream: { - description: 'Set it when you want to reuse this stream', - required: false, - type: 'string' - }, - loadStream: { - description: 'Set it when you want to reuse an opened stream', - required: false, - type: 'string' - }, - streamTimeout: { - description: 'Timeout to stop listening after the first byte is read', - required: false, - type: 'int' - }, - options: { - description: 'Defined when using SSL. https://nodejs.org/api/net.html#net_net_createserver_options_connectionlistener', - required: false, - type: 'object' - }, - }, - hooks: { - onMessageReceived: { - arguments: { - payload: {}, - stream: {}, - path: { - description: 'Defined only when it is a UDS server', - } - } - } - } - } - }; - const tcp = new SubscriptionProtocol('tcp', createFunction, docs); - - const uds = new SubscriptionProtocol('uds', createFunction, docs); - - const ssl = new SubscriptionProtocol('ssl', createFunction, docs); - - mainInstance.protocolManager.addProtocol(tcp); - mainInstance.protocolManager.addProtocol(uds); - mainInstance.protocolManager.addProtocol(ssl); -} diff --git a/src/subscriptions/subscription.ts b/src/subscriptions/subscription.ts deleted file mode 100644 index ac63de23..00000000 --- a/src/subscriptions/subscription.ts +++ /dev/null @@ -1,49 +0,0 @@ -import {SubscriptionModel} from '../models/inputs/subscription-model'; -import {Logger} from '../loggers/logger'; -import {Event} from '../models/events/event'; - -export abstract class Subscription { - public name: string; - public messageReceived?: any; - public timeout?: number; - public onMessageReceived?: Event; - public onFinish?: Event; - public response?: any; - public type: string; - public avoid: boolean = false; - public ignore: boolean = false; - - [propName: string]: any; - - protected constructor(subscriptionAttributes: SubscriptionModel) { - Object.keys(subscriptionAttributes).forEach(key => { - this[key] = subscriptionAttributes[key]; - }); - this.type = subscriptionAttributes.type; - this.name = subscriptionAttributes.name; - } - - public abstract subscribe(): Promise; - - public abstract receiveMessage(): Promise; - - public async unsubscribe(): Promise { - //do nothing - } - - public async sendResponse(): Promise { - } - - public registerHookEventExecutor(hookEventExecutor: (eventName: string, args: any) => void) { - this['hookEventExecutor'] = hookEventExecutor; - } - - protected executeHookEvent(hookName: string, args: any = {}) { - if (this['hookEventExecutor']) { - this['hookEventExecutor'](hookName, args); - } else { - Logger.warning(`Hook event executor not registered in subscription`); - } - } - -} diff --git a/src/subscriptions/udp-subscription.ts b/src/subscriptions/udp-subscription.ts deleted file mode 100644 index de9a490c..00000000 --- a/src/subscriptions/udp-subscription.ts +++ /dev/null @@ -1,83 +0,0 @@ -import {Subscription} from './subscription'; -import {SubscriptionModel} from '../models/inputs/subscription-model'; -import * as dgram from 'dgram'; -import {Logger} from '../loggers/logger'; -import {MainInstance} from '../plugins/main-instance'; -import {SubscriptionProtocol} from '../protocols/subscription-protocol'; - -class UdpSubscription extends Subscription { - private server: any; - - constructor(subscriptionAttributes: SubscriptionModel) { - super(subscriptionAttributes); - - if (typeof subscriptionAttributes.response != 'string') { - this.response = JSON.stringify(subscriptionAttributes.response, null, 2); - } - } - - public receiveMessage(): Promise { - return new Promise((resolve, reject) => { - this.server.on('error', (err: any) => { - this.server.close(); - reject(err); - }); - - this.server.on('message', (msg: Buffer, remoteInfo: any) => { - this.server.close(); - this.executeHookEvent('onMessageReceived', {payload: msg, remoteInfo: remoteInfo}); - resolve(); - }); - } - ); - } - - public subscribe(): Promise { - return new Promise((resolve, reject) => { - this.server = dgram.createSocket('udp4'); - try { - this.server.bind(this.port); - resolve(); - } catch (err) { - const message = `Udp server could not listen to ${this.port}`; - Logger.error(message); - reject(message); - } - }); - } - -} - -export function entryPoint(mainInstance: MainInstance): void { - const protocol = new SubscriptionProtocol('udp', - (subscriptionModel: SubscriptionModel) => new UdpSubscription(subscriptionModel), - { - description: 'The udp subscription provides an implementation of UDP Datagram sockets servers', - libraryHomepage: 'https://nodejs.org/api/dgram.html', - schema: { - attributes: { - port: { - required: true, - type: 'int' - }, - response: { - required: true, - type: 'string' - }, - }, - hooks: { - onMessageReceived: { - arguments: { - payload: {}, - remoteInfo: { - description: 'Remote address information', - } - } - } - } - }, - }) - .addAlternativeName('udp-server'); - - mainInstance.protocolManager.addProtocol(protocol); -} diff --git a/src/task-runners/component-importer.test.ts b/src/task-runners/component-importer.test.ts new file mode 100644 index 00000000..01589a9e --- /dev/null +++ b/src/task-runners/component-importer.test.ts @@ -0,0 +1,98 @@ +import { ComponentImporter } from './component-importer'; + +describe('ComponentImporter', () => { + it('should return the same if there is no importTask', () => { + const task = { name: 'name', value: 1234 }; + + // @ts-ignore + const imported = new ComponentImporter().importTask(task); + + expect(imported).toEqual(task); + }); + + it('should throw on not valid', () => { + const task = { import: 'throw file' }; + + // @ts-ignore + expect(() => new ComponentImporter().importTask(task)).toThrow(); + }); + + it('should merge tasks (imported has higher priority)', () => { + const imported = { + onInit: { + script: 'imported' + }, + name: 'imported', + importedValue: 1234 + }; + const original = { + import: imported, + onInit: { + script: 'original' + }, + name: 'original', + iterations: 1 + }; + + // @ts-ignore + const merged = new ComponentImporter().importTask(original); + + expect(merged.name).toBe(imported.name); + expect(merged.import).toBeDefined(); + expect(merged.onInit!.script).toBe(imported.onInit.script); + expect(merged.importedValue).toBe(1234); + expect(merged.iterations).toBe(1); + }); + + it('should merge sensor (imported has higher priority)', () => { + const imported = { + onInit: { + script: 'imported' + }, + name: 'imported', + importedValue: 1234 + }; + const original = { + import: imported, + onInit: { + script: 'original' + }, + name: 'original', + type: 'http' + }; + + const merged = new ComponentImporter().importSensor(original); + + expect(merged.name).toBe(imported.name); + expect(merged.import).toBeDefined(); + expect(merged.onInit!.script).toBe(imported.onInit.script); + expect(merged.importedValue).toBe(1234); + expect(merged.type).toBe(original.type); + }); + + it('should merge actuator (imported has higher priority)', () => { + const imported = { + onInit: { + script: 'imported' + }, + name: 'imported', + importedValue: 1234 + }; + const original = { + import: imported, + onInit: { + script: 'original' + }, + name: 'original', + type: 'http' + }; + + const merged = new ComponentImporter().importActuator(original); + + expect(merged.name).toBe(imported.name); + expect(merged.import).toBeDefined(); + expect(merged.onInit!.script).toBe(imported.onInit.script); + expect(merged.importedValue).toBe(1234); + expect(merged.type).toBe(original.type); + }); +}); diff --git a/src/task-runners/component-importer.ts b/src/task-runners/component-importer.ts new file mode 100644 index 00000000..82fd6917 --- /dev/null +++ b/src/task-runners/component-importer.ts @@ -0,0 +1,40 @@ +import { TaskModel } from '../models/inputs/task-model'; +import { TaskAdopter } from '../components/task-adopter'; +import { TaskValidator } from './task-validator'; +import { Logger } from '../loggers/logger'; +import { SensorModel } from '../models/inputs/sensor-model'; +import { ActuatorModel } from '../models/inputs/actuator-model'; + +export class ComponentImporter { + public importTask(task: TaskModel): TaskModel { + const importValue = task.import; + if (importValue) { + const imported: any = Array.isArray(importValue) ? { tasks: importValue } : importValue; + const taskValidator = new TaskValidator(); + if (!taskValidator.validate(imported)) { + const message = `Error importing ${JSON.stringify(importValue)}: ${taskValidator.getErrorMessage()}`; + Logger.error(message); + throw message; + } + const merged = Object.assign({}, task, imported); + return new TaskAdopter(merged).getTask(); + } + return task; + } + + public importSensor(sensor: SensorModel): SensorModel { + const importValue = sensor.import; + if (importValue) { + return Object.assign({}, sensor, importValue); + } + return sensor; + } + + public importActuator(actuatorModel: ActuatorModel): ActuatorModel { + const importValue = actuatorModel.import; + if (importValue) { + return Object.assign({}, actuatorModel, importValue); + } + return actuatorModel; + } +} diff --git a/src/task-runners/iterations-evaluator.test.ts b/src/task-runners/iterations-evaluator.test.ts new file mode 100644 index 00000000..fc201384 --- /dev/null +++ b/src/task-runners/iterations-evaluator.test.ts @@ -0,0 +1,32 @@ +import { IterationsEvaluator } from './iterations-evaluator'; + +describe('IterationsEvaluator', () => { + it('Should return 1 when undefined', () => { + // @ts-ignore + expect(new IterationsEvaluator().iterations()).toBe(1); + }); + + it('Should return 1 when true', () => { + // @ts-ignore + expect(new IterationsEvaluator().iterations('1 === 1')).toBe(1); + }); + + it('Should return 0 when false', () => { + // @ts-ignore + expect(new IterationsEvaluator().iterations('1 === 0')).toBe(0); + }); + + it('Should parse string', () => { + // @ts-ignore + expect(new IterationsEvaluator().iterations('10')).toBe(10); + }); + + it('Should catch exceptions and return 0', () => { + // @ts-ignore + expect(new IterationsEvaluator().iterations('throw "gui"')).toBe(0); + }); + + it('Should accept number', () => { + expect(new IterationsEvaluator().iterations(100)).toBe(100); + }); +}); diff --git a/src/task-runners/iterations-evaluator.ts b/src/task-runners/iterations-evaluator.ts new file mode 100644 index 00000000..db348951 --- /dev/null +++ b/src/task-runners/iterations-evaluator.ts @@ -0,0 +1,24 @@ +import { Logger } from '../loggers/logger'; + +export class IterationsEvaluator { + public iterations(iterations: number): number { + if (iterations !== undefined) { + try { + //TODO be able to use 'task' as argument + const evaluated = eval(iterations.toString()); + switch (typeof evaluated) { + case 'boolean': + return evaluated ? 1 : 0; + case 'number': + default: + return evaluated; + } + } catch (err) { + Logger.warning(`Error evaluating iterations: $${iterations}: ${err}`); + return 0; + } + } + + return 1; + } +} diff --git a/src/task-runners/task-file-parser.test.ts b/src/task-runners/task-file-parser.test.ts new file mode 100644 index 00000000..4470a7a8 --- /dev/null +++ b/src/task-runners/task-file-parser.test.ts @@ -0,0 +1,49 @@ +import { DynamicModulesManager } from '../plugins/dynamic-modules-manager'; +import * as fs from 'fs'; +import { TaskFileParser } from './task-file-parser'; +import * as glob from 'glob'; + +jest.mock('fs'); +jest.mock('glob'); +// @ts-ignore +glob.sync.mockImplementation((pattern: string) => [pattern]); + +describe('TaskFileParser', () => { + beforeEach(() => { + // @ts-ignore + delete DynamicModulesManager.instance; + }); + + it('Should parse array as just one', () => { + const taskInput = { + onInit: {}, + id: 0 + }; + DynamicModulesManager.getInstance() + .getObjectParserManager() + .addObjectParser(() => { + return { + parse: () => taskInput + }; + }, 'yml'); + + const fileContent = JSON.stringify(taskInput); + // @ts-ignore + fs.readFileSync.mockImplementationOnce(() => Buffer.from(fileContent)); + const filename = 'anyStuff'; + + const task = new TaskFileParser().parseFile(filename); + + expect(task.name).toBe(filename); + expect(task.id).toBe(taskInput.id); + expect(task.onInit).toEqual(taskInput.onInit); + }); + + it('Should throw', () => { + // @ts-ignore + fs.readFileSync.mockImplementationOnce(() => { + throw 'error'; + }); + expect(() => new TaskFileParser().parseFile('anyStuff')).toThrow(); + }); +}); diff --git a/src/task-runners/task-file-parser.ts b/src/task-runners/task-file-parser.ts new file mode 100644 index 00000000..02ee38a4 --- /dev/null +++ b/src/task-runners/task-file-parser.ts @@ -0,0 +1,12 @@ +import { TaskModel } from '../models/inputs/task-model'; +import { TaskParser } from './task-parser'; +import * as fs from 'fs'; + +export class TaskFileParser { + public parseFile(filename: string): TaskModel { + const fileBufferContent = fs.readFileSync(filename).toString(); + const task = new TaskParser().parse(fileBufferContent); + task.name = task.name || filename; + return task; + } +} diff --git a/src/task-runners/task-file-pattern-parser.test.ts b/src/task-runners/task-file-pattern-parser.test.ts new file mode 100644 index 00000000..31e4e5dd --- /dev/null +++ b/src/task-runners/task-file-pattern-parser.test.ts @@ -0,0 +1,139 @@ +import { TaskFilePatternParser } from './task-file-pattern-parser'; +import { DynamicModulesManager } from '../plugins/dynamic-modules-manager'; +import { YmlObjectParser } from '../object-parser/yml-object-parser'; +import * as fs from 'fs'; +import * as glob from 'glob'; + +jest.mock('fs'); +jest.mock('glob'); +// @ts-ignore +glob.sync.mockImplementation((pattern: string) => [pattern]); + +describe('TaskFilePatternParser', () => { + beforeEach(() => { + // @ts-ignore + delete DynamicModulesManager.instance; + }); + + it('Should parse array as just one', () => { + const tasksInput = [ + { + onInit: {}, + id: 0 + }, + { + actuators: [{ type: true }], + name: 'named', + id: 1 + } + ]; + DynamicModulesManager.getInstance() + .getObjectParserManager() + .addObjectParser(() => { + return { + parse: () => tasksInput + }; + }, 'yml'); + + const fileContent = JSON.stringify(tasksInput); + // @ts-ignore + fs.readFileSync.mockImplementationOnce(() => Buffer.from(fileContent)); + const filename = 'anyStuff'; + + const task = new TaskFilePatternParser([filename]).parse(); + + expect(task[0].name).toBe(filename); + + expect(task[0].tasks[0].id).toBe(tasksInput[0].id); + expect(task[0].tasks[0].onInit).toEqual(tasksInput[0].onInit); + + expect(task[0].tasks[1].name).toBe(tasksInput[1].name); + expect(task[0].tasks[1].id).toBe(tasksInput[1].id); + expect(task[0].tasks[1].actuators).toEqual(tasksInput[1].actuators); + }); + + it('Should add invalid file error', () => { + // @ts-ignore + fs.readFileSync.mockImplementationOnce(() => { + throw 'error'; + }); + const parser: TaskFilePatternParser = new TaskFilePatternParser(['anyStuff']); + + parser.parse(); + + expect(parser.getFilesErrors()[0]).toEqual({ + description: 'error', + name: "Error parsing file 'anyStuff'", + valid: false + }); + }); + + it('Should no test found error', () => { + const parser: TaskFilePatternParser = new TaskFilePatternParser([]); + + parser.parse(); + + expect(parser.getFilesErrors()[0]).toEqual({ + description: 'No test file was found', + name: 'No test file was found', + valid: false + }); + }); + + it('Should add if file is not yml nor json', () => { + const notYml = 'foo bar\nfoo: bar'; + + DynamicModulesManager.getInstance() + .getObjectParserManager() + .addObjectParser(() => { + return { + parse: value => new YmlObjectParser().parse(value) + }; + }, 'yml'); + + // @ts-ignore + fs.readFileSync.mockImplementationOnce(() => Buffer.from(notYml)); + + const parser = new TaskFilePatternParser(['anyStuff']); + parser.parse(); + + const parsedErrorDescription: any = parser.getFilesErrors()[0].description; + expect(parsedErrorDescription).toBeDefined(); + }); + + it('Should add error if it is not a valid task', () => { + const notYml = 'hey: bar'; + + DynamicModulesManager.getInstance() + .getObjectParserManager() + .addObjectParser(() => { + return { + parse: value => new YmlObjectParser().parse(value) + }; + }, 'yml'); + + // @ts-ignore + fs.readFileSync.mockImplementationOnce(() => Buffer.from(notYml)); + + const parser = new TaskFilePatternParser(['anyStuff']); + parser.parse(); + + expect(parser.getFilesErrors()[0].name).toBe("Error parsing file 'anyStuff'"); + }); + + it('should add every not matching file to error', () => { + // @ts-ignore + glob.sync.mockReset(); + // @ts-ignore + glob.sync.mockImplementationOnce(() => []); + + const parser = new TaskFilePatternParser(['not-matching-pattern']); + parser.parse(); + + expect(parser.getFilesErrors()[0]).toEqual({ + description: "No file was found with: 'not-matching-pattern'", + name: "No file was found with: 'not-matching-pattern'", + valid: false + }); + }); +}); diff --git a/src/task-runners/task-file-pattern-parser.ts b/src/task-runners/task-file-pattern-parser.ts new file mode 100644 index 00000000..d791cfc3 --- /dev/null +++ b/src/task-runners/task-file-pattern-parser.ts @@ -0,0 +1,56 @@ +import { Logger } from '../loggers/logger'; +import { TestModel } from '../models/outputs/test-model'; +import * as input from '../models/inputs/task-model'; +import { TaskModel } from '../models/inputs/task-model'; +import * as glob from 'glob'; +import { TaskFileParser } from './task-file-parser'; + +export class TaskFilePatternParser { + private filesErrors: TestModel[] = []; + + constructor(private readonly patterns: string[]) {} + + public getFilesErrors(): TestModel[] { + return this.filesErrors; + } + + public parse(): TaskModel[] { + this.filesErrors = []; + const tasks: input.TaskModel[] = []; + const matchingFiles = this.getMatchingFiles(); + matchingFiles.forEach((file: string) => { + try { + tasks.push(new TaskFileParser().parseFile(file)); + } catch (err) { + this.addError(`Error parsing file '${file}'`, '' + (err as Error)); + } + }); + if (matchingFiles.length === 0) { + const title = `No test file was found`; + this.addError(title, title); + } + return tasks; + } + + private getMatchingFiles(): string[] { + let result: string[] = []; + this.patterns.map((pattern: string) => { + const items = glob.sync(pattern, { nodir: true }); + if (items.length > 0) { + result = result.concat(items.sort()); + } else { + const message = `No file was found with: '${pattern}'`; + this.addError(message, message); + } + }); + result = [...new Set(result)]; + + Logger.info(`Files list: ${JSON.stringify(result, null, 2)}`); + return result; + } + + private addError(title: string, message: string) { + Logger.error(message); + this.filesErrors.push({ name: title, valid: false, description: message }); + } +} diff --git a/src/task-runners/task-parser.test.ts b/src/task-runners/task-parser.test.ts new file mode 100644 index 00000000..2c193cd8 --- /dev/null +++ b/src/task-runners/task-parser.test.ts @@ -0,0 +1,86 @@ +import { DynamicModulesManager } from '../plugins/dynamic-modules-manager'; +import { YmlObjectParser } from '../object-parser/yml-object-parser'; +import { TaskParser } from './task-parser'; + +describe('TaskParser', () => { + beforeEach(() => { + // @ts-ignore + delete DynamicModulesManager.instance; + }); + + it('Should parse array as just one', () => { + const tasksInput = [ + { + onInit: {}, + id: 0 + }, + { + actuators: [{ type: true }], + name: 'named', + id: 1 + } + ]; + DynamicModulesManager.getInstance() + .getObjectParserManager() + .addObjectParser(() => { + return { + parse: () => tasksInput + }; + }, 'yml'); + + const fileContent = JSON.stringify(tasksInput); + const task = new TaskParser().parse(fileContent); + + expect(task.tasks[0].id).toBe(tasksInput[0].id); + expect(task.tasks[0].onInit).toEqual(tasksInput[0].onInit); + + expect(task.tasks[1].name).toBe(tasksInput[1].name); + expect(task.tasks[1].id).toBe(tasksInput[1].id); + expect(task.tasks[1].actuators).toEqual(tasksInput[1].actuators); + }); + + it('Should throw', () => { + expect(() => new TaskParser().parse('anyStuff')).toThrow(); + }); + + it('Should add errors if file is not yml nor json', () => { + const notYml = 'foo bar\nfoo: bar'; + + DynamicModulesManager.getInstance() + .getObjectParserManager() + .addObjectParser(() => { + return { + parse: value => new YmlObjectParser().parse(value) + }; + }, 'yml'); + + try { + new TaskParser().parse('any:1\nany:1'); + expect(true).toBeFalsy(); + } catch (err) { + expect(err).toBeDefined(); + } + }); + + it('Should add error if it is not a valid task', () => { + const notYml = 'hey: bar'; + + DynamicModulesManager.getInstance() + .getObjectParserManager() + .addObjectParser(() => { + return { + parse: value => new YmlObjectParser().parse(value) + }; + }, 'yml'); + + try { + new TaskParser().parse(notYml); + expect(true).toBeFalsy(); + } catch (err) { + expect(err).toBe( + `'hey: bar' is not a valid task:` + + ` Unable to find: 'onInit', 'onFinish', 'delay', 'tasks', 'actuators', 'sensors' nor 'import'.` + ); + } + }); +}); diff --git a/src/task-runners/task-parser.ts b/src/task-runners/task-parser.ts new file mode 100644 index 00000000..1179a1a7 --- /dev/null +++ b/src/task-runners/task-parser.ts @@ -0,0 +1,18 @@ +import { DynamicModulesManager } from '../plugins/dynamic-modules-manager'; +import { TaskModel } from '../models/inputs/task-model'; +import { TaskValidator } from './task-validator'; + +export class TaskParser { + public parse(value: string): TaskModel { + const parsed: any = DynamicModulesManager.getInstance() + .getObjectParserManager() + .tryToParseWithParsers(value, ['yml', 'json']); + + const task = Array.isArray(parsed) ? { tasks: parsed } : parsed; + const taskValidator = new TaskValidator(); + if (!taskValidator.validate(task)) { + throw "'" + value + "' is not a valid task: " + taskValidator.getErrorMessage(); + } + return task; + } +} diff --git a/src/task-runners/task-runner.test.ts b/src/task-runners/task-runner.test.ts new file mode 100644 index 00000000..27449749 --- /dev/null +++ b/src/task-runners/task-runner.test.ts @@ -0,0 +1,81 @@ +import { TaskRunner } from './task-runner'; +import { TaskModel } from '../models/inputs/task-model'; +import { Store } from '../configurations/store'; + +describe('TaskRunner', () => { + it('Should return task reporter skipped', async () => { + // @ts-ignore + const task: TaskModel = { + iterations: 0, + name: 'skipped', + id: 'id' + }; + + const reports = await new TaskRunner(task).run(); + const actual = reports[0]; + + expect(reports.length).toBe(1); + expect(actual.name).toBe(task.name); + expect(actual.id).toBe(task.id); + expect(actual.valid).toBeTruthy(); + expect(actual.hooks!.onInit.valid).toBeTruthy(); + expect(actual.hooks!.onFinish.valid).toBeTruthy(); + }); + + it('Should return task report collection', async () => { + const task: TaskModel = { + name: 'super cool', + iterations: 5, + tasks: [], + actuators: [], + sensors: [] + } as any; + + const reports = await new TaskRunner(task).run(); + const report = reports[0]; + + expect(reports.length).toBe(task.iterations); + expect(report.time).toBeDefined(); + expect(report.name).toContain(task.name); + expect(report.valid).toBeTruthy(); + }); + + it('Should run children task report collection', async () => { + const task: TaskModel = { + name: 'super cool', + tasks: [ + // @ts-expect-error + { + name: 'child', + actuators: [], + sensors: [], + tasks: [] + } + ], + actuators: [], + sensors: [] + }; + + const reports = await new TaskRunner(task).run(); + const report = reports[0].tasks[0]; + + expect(report.time).toBeDefined(); + expect(report.name).toContain('child'); + expect(report.valid).toBeTruthy(); + }); + + it('Should replace stuff', async () => { + const keyName = 'value'; + Store.getData().keyName = keyName; + // @ts-ignore + const task: TaskModel = { + name: '<>' + }; + + const reports = await new TaskRunner(task).run(); + const report = reports[0]; + + expect(reports.length).toBe(1); + expect(report.name).toBe(keyName); + }); +}); diff --git a/src/task-runners/task-runner.ts b/src/task-runners/task-runner.ts new file mode 100644 index 00000000..6496e16b --- /dev/null +++ b/src/task-runners/task-runner.ts @@ -0,0 +1,168 @@ +import { Logger } from '../loggers/logger'; +import { TaskReporter } from '../reporters/task-reporter'; +import * as input from '../models/inputs/task-model'; +import * as output from '../models/outputs/task-model'; +import { JsonPlaceholderReplacer } from 'json-placeholder-replacer'; +import { Store } from '../configurations/store'; +import { TaskDefaultReports } from '../models-defaults/outputs/task-default-reports'; +import { FileContentMapCreator } from '../configurations/file-content-map-creator'; +import { IterationsEvaluator } from './iterations-evaluator'; +import { ComponentParentBackupper } from '../components/component-parent-backupper'; +import { ComponentImporter } from './component-importer'; +import { TaskAdopter } from '../components/task-adopter'; +import { NotificationEmitter } from '../notifications/notification-emitter'; +import { testModelIsNotFailing } from '../models/outputs/test-model'; +import { Notifications } from '../notifications/notifications'; + +export class TaskRunner { + private task: input.TaskModel; + private childrenTaskRunner: TaskRunner[] = []; + private taskReporter?: TaskReporter; + + public constructor(task: input.TaskModel) { + this.task = new TaskAdopter(task).getTask(); + } + + public async run(): Promise { + NotificationEmitter.emit(Notifications.REQUISITION_STARTED, { + task: this.task + }); + Logger.info(`Running task '${this.task.name}'`); + try { + this.importTask(); + } catch (err) { + Logger.error(`Error importing task`); + const report = TaskDefaultReports.createRunningError(this.task, err); + this.emitOnFinishNotification(report); + return [report]; + } + this.replaceVariables(); + const evaluatedIterations: number = new IterationsEvaluator().iterations(this.task.iterations); + if (evaluatedIterations <= 0) { + Logger.info(`Task will be skipped duo to no iterations`); + const report = TaskDefaultReports.createSkippedReport(this.task); + this.emitOnFinishNotification(report); + return [report]; + } else if (this.task.ignore) { + Logger.info(`Task will be ignored`); + const report = TaskDefaultReports.createIgnoredReport(this.task); + this.emitOnFinishNotification(report); + return [report]; + } + return await this.iterateTask(evaluatedIterations); + } + + private async iterateTask(iterations: number): Promise { + const reports = []; + for (let iterationCounter = 0; iterationCounter < iterations; ++iterationCounter) { + try { + this.replaceVariables(); + this.task.iteration = iterationCounter; + this.task.totalIterations = iterations; + const iterationSuffix: string = iterations > 1 ? ` [${iterationCounter}]` : ''; + Logger.trace(`Task runner starting task reporter for '${this.task.name + iterationSuffix}'`); + const report = await this.startTaskReporter(); + reports.push(report); + this.emitOnFinishNotification(report); + } catch (err) { + reports.push(TaskDefaultReports.createRunningError(this.task, '' + err)); + Logger.error('' + err); + } + } + return reports; + } + + private importTask() { + if (this.task.import) { + this.task = new ComponentImporter().importTask(this.task); + } + } + + private async interrupt(): Promise { + const report: output.TaskModel = await this.taskReporter!.interrupt(); + this.emitOnFinishNotification(report); + return report; + } + + private emitOnFinishNotification(report: output.TaskModel) { + NotificationEmitter.emit(Notifications.REQUISITION_FINISHED, { + task: report + }); + } + + private replaceVariables(): void { + Logger.debug(`Evaluating variables of task '${this.task.name}'`); + const componentParentBackupper = new ComponentParentBackupper(); + componentParentBackupper.removeParents(this.task); + const fileMapCreator = new FileContentMapCreator(this.task); + const fileReplaced = new JsonPlaceholderReplacer({ defaultValueSeparator: '->' }) + .addVariableMap(fileMapCreator.getMap()) + .replace(this.task); + this.task = new JsonPlaceholderReplacer({ defaultValueSeparator: '->' }) + .addVariableMap(Store.getData()) + .replace(fileReplaced) as input.TaskModel; + componentParentBackupper.putParentsBack(this.task); + } + + private async startTaskReporter(): Promise { + this.taskReporter = new TaskReporter(this.task); + const report = await Promise.race([this.timeoutPath(), this.happyPath()]); + + const iterationCounter: string = +report.totalIterations! > 1 ? ` [${report.iteration}]` : ''; + Logger.info( + `Task '${report.name + iterationCounter}' is over (status: ${report.valid ? 'OK' : 'Errored'}) - ${report.time ? report.time.totalTime : 0}ms` + ); + Logger.trace(`Store keys: ${Object.keys(Store.getData()).join('; ')}`); + + return report; + } + + private async timeoutPath(): Promise { + const report = await this.taskReporter!.startTimeout(); + report.tasks = await Promise.all(this.childrenTaskRunner.map(childRunner => childRunner.interrupt())); + Logger.debug(`Task '${this.task.name}' timed out (${this.task.timeout}ms)`); + return report; + } + + private async happyPath(): Promise { + await this.taskReporter!.delay(); + Logger.debug(`Handling tasks children of '${this.task.name}'`); + let childrenReport: output.TaskModel[] = await this.executeChildren(); + const report = await this.taskReporter!.execute(); + report.tasks = childrenReport; + report.valid = + report.valid && + report.tasks.every(task => testModelIsNotFailing(task)) && + Object.keys(report.hooks || {}).every((key: string) => (report.hooks ? report.hooks[key].valid : true)); + Logger.debug(`Task ${this.task.name} didn't time out`); + return report; + } + + private async executeChildren(): Promise { + if (this.task.parallel) { + const models = await Promise.all( + this.task.tasks.map(async (child: input.TaskModel, index: number) => { + return await this.executeChild(child, index); + }) + ); + return models.reduce((acc, child) => acc.concat(child), []); + } else { + let childrenReport: output.TaskModel[] = []; + let index = 0; + for (const child of this.task.tasks) { + childrenReport = childrenReport.concat(await this.executeChild(child, index)); + ++index; + } + return childrenReport; + } + } + + private async executeChild(child: input.TaskModel, index: number): Promise { + child.parent = this.task; + const childRunner = new TaskRunner(child); + this.childrenTaskRunner.push(childRunner); + const taskModels = await childRunner.run(); + this.task.tasks[index] = childRunner.task; + return taskModels; + } +} diff --git a/src/task-runners/task-validator.test.ts b/src/task-runners/task-validator.test.ts new file mode 100644 index 00000000..6b123422 --- /dev/null +++ b/src/task-runners/task-validator.test.ts @@ -0,0 +1,71 @@ +import { TaskValidator } from './task-validator'; + +describe('TaskValidator', () => { + it('Should return error message', () => { + expect(new TaskValidator().getErrorMessage()).toBe( + `Unable to find: 'onInit', 'onFinish', 'delay', 'tasks', 'actuators', 'sensors' nor 'import'.` + ); + }); + + it('Should reject empty', () => { + // @ts-expect-error + expect(new TaskValidator().validate()).toBeFalsy(); + }); + + it('Should accept import', () => { + // @ts-expect-error + expect(new TaskValidator().validate({ import: {} })).toBeTruthy(); + }); + + it('Should accept onInit', () => { + // @ts-expect-error + expect(new TaskValidator().validate({ onInit: {} })).toBeTruthy(); + }); + + it('Should accept delay', () => { + // @ts-expect-error + expect(new TaskValidator().validate({ delay: {} })).toBeTruthy(); + }); + + it('Should accept onFinish', () => { + // @ts-expect-error + expect(new TaskValidator().validate({ onFinish: {} })).toBeTruthy(); + }); + + it('Should accept actuators', () => { + // @ts-expect-error + expect(new TaskValidator().validate({ actuators: [{ type: '' }] })).toBeTruthy(); + }); + + it('Should reject empty actuators', () => { + // @ts-expect-error + expect(new TaskValidator().validate({ actuators: [] })).toBeFalsy(); + }); + + it('Should accept sensors', () => { + // @ts-expect-error + expect(new TaskValidator().validate({ sensors: [{ type: '' }] })).toBeTruthy(); + }); + + it('Should reject empty sensors', () => { + // @ts-expect-error + expect(new TaskValidator().validate({ sensors: [] })).toBeFalsy(); + }); + + it('Should go recursive', () => { + expect( + new TaskValidator().validate({ + tasks: [ + { + tasks: [ + // @ts-expect-error + { + onInit: {} + } + ] + } + ] + }) + ).toBeTruthy(); + }); +}); diff --git a/src/task-runners/task-validator.ts b/src/task-runners/task-validator.ts new file mode 100644 index 00000000..9a100959 --- /dev/null +++ b/src/task-runners/task-validator.ts @@ -0,0 +1,31 @@ +import { TaskModel } from '../models/inputs/task-model'; + +export class TaskValidator { + public validate(task: TaskModel): boolean { + if (typeof task !== 'object' || !task) { + return false; + } + if ( + task.onInit !== undefined || + task.onFinish !== undefined || + task.import !== undefined || + task.delay !== undefined + ) { + return true; + } + if (Array.isArray(task.actuators) && task.actuators.length > 0) { + return true; + } + if (Array.isArray(task.sensors) && task.sensors.length > 0) { + return true; + } + if (Array.isArray(task.tasks) && task.tasks.length > 0) { + return task.tasks.every(child => this.validate(child)); + } + return false; + } + + public getErrorMessage(): string { + return "Unable to find: 'onInit', 'onFinish', 'delay', 'tasks', 'actuators', 'sensors' nor 'import'."; + } +} diff --git a/src/timers/date-controller.test.ts b/src/timers/date-controller.test.ts index d2e45780..4f1dfce1 100644 --- a/src/timers/date-controller.test.ts +++ b/src/timers/date-controller.test.ts @@ -1,53 +1,52 @@ -import {DateController} from './date-controller'; +import { DateController } from './date-controller'; let leftPad = (number: number, targetLength: number): string => { - let output = number + ''; - while (output.length < targetLength) { - output = '0' + output; - } - return output; + let output = number + ''; + while (output.length < targetLength) { + output = '0' + output; + } + return output; }; -describe('DateController', function() { +describe('DateController', function () { + it('should be protected against wrong initialization', function () { + const date = new DateController(); - it('should be protected against wrong initialization', function() { - const date = new DateController(); + expect(date).not.toBeNull(); + }); - expect(date).not.toBeNull(); - }); + it('should parse to string properly', function () { + const currentDate = new Date(); - it('should parse to string properly', function() { - const currentDate = new Date(); + const expectedToString = currentDate.toISOString(); + const actualToString = new DateController(currentDate).toString(); - const expectedToString = currentDate.toISOString(); - const actualToString = new DateController(currentDate).toString(); + expect(actualToString).toBe(expectedToString); + }); - expect(actualToString).toBe(expectedToString); - }); + it('should parse to string with only number properly', function () { + const currentDate = new Date(); - it('should parse to string with only number properly', function() { - const currentDate = new Date(); + const expectedToString = + leftPad(currentDate.getFullYear(), 4) + + leftPad(currentDate.getMonth() + 1, 2) + + leftPad(currentDate.getDate(), 2) + + leftPad(currentDate.getHours(), 2) + + leftPad(currentDate.getMinutes(), 2) + + leftPad(currentDate.getSeconds(), 2) + + leftPad(currentDate.getMilliseconds(), 4); - const expectedToString = leftPad(currentDate.getFullYear(), 4) + - leftPad(currentDate.getMonth() + 1, 2) + - leftPad(currentDate.getDate(), 2) + - leftPad(currentDate.getHours(), 2) + - leftPad(currentDate.getMinutes(), 2) + - leftPad(currentDate.getSeconds(), 2) + - leftPad(currentDate.getMilliseconds(), 4); + const actualToString = new DateController(currentDate).getStringOnlyNumbers(); - const actualToString = new DateController(currentDate).getStringOnlyNumbers(); + expect(actualToString.substr(0, actualToString.length - 2)).toBe(expectedToString); + }); - expect(actualToString.substr(0, actualToString.length - 2)).toBe(expectedToString); - }); + it('should get time', function () { + const currentDate = new Date(); - it('should get time', function() { - const currentDate = new Date(); - - const expectedTime = currentDate.getTime(); - const actualTime = new DateController(currentDate).getTime(); - - expect(actualTime).toBe(expectedTime); - }); + const expectedTime = currentDate.getTime(); + const actualTime = new DateController(currentDate).getTime(); + expect(actualTime).toBe(expectedTime); + }); }); diff --git a/src/timers/date-controller.ts b/src/timers/date-controller.ts index 5dfd4151..1c5f2839 100644 --- a/src/timers/date-controller.ts +++ b/src/timers/date-controller.ts @@ -1,35 +1,36 @@ export class DateController { + private date: Date; - private date: Date; + public constructor(date: Date = new Date()) { + this.date = date || new Date(); + } - public constructor(date: Date = new Date()) { - this.date = date || new Date(); - } + public toString(): string { + return this.date.toISOString(); + } - public toString(): string { - return this.date.toISOString(); + private leftPad(number: number, targetLength: number): string { + let output = number + ''; + while (output.length < targetLength) { + output = '0' + output; } + return output; + } - private leftPad(number: number, targetLength: number): string { - let output = number + ''; - while (output.length < targetLength) { - output = '0' + output; - } - return output; - } + public getStringOnlyNumbers(): string { + return ( + this.leftPad(this.date.getFullYear(), 4) + + this.leftPad(this.date.getMonth() + 1, 2) + + this.leftPad(this.date.getDate(), 2) + + this.leftPad(this.date.getHours(), 2) + + this.leftPad(this.date.getMinutes(), 2) + + this.leftPad(this.date.getSeconds(), 2) + + this.leftPad(this.date.getMilliseconds(), 4) + + this.leftPad(Math.trunc(Math.random() * 100), 2) + ); + } - public getStringOnlyNumbers(): string { - return this.leftPad(this.date.getFullYear(), 4) + - this.leftPad(this.date.getMonth() + 1, 2) + - this.leftPad(this.date.getDate(), 2) + - this.leftPad(this.date.getHours(), 2) + - this.leftPad(this.date.getMinutes(), 2) + - this.leftPad(this.date.getSeconds(), 2) + - this.leftPad(this.date.getMilliseconds(), 4) + - this.leftPad(Math.trunc(Math.random() * 100), 2); - } - - public getTime(): number { - return this.date.getTime(); - } + public getTime(): number { + return this.date.getTime(); + } } diff --git a/src/timers/timeout.test.ts b/src/timers/timeout.test.ts index 8998d4ec..4df819af 100644 --- a/src/timers/timeout.test.ts +++ b/src/timers/timeout.test.ts @@ -1,66 +1,62 @@ -import {Timeout} from "./timeout"; -import {DateController} from "./date-controller"; +import { Timeout } from './timeout'; +import { DateController } from './date-controller'; jest.useFakeTimers(); +jest.spyOn(global, 'setTimeout'); -describe('Timeout', function() { +describe('Timeout', function () { + test('should call callback after given time', done => { + const startTime = new DateController().getTime(); + const toleranceInMilliseconds = 100; + const period: number = 2; - test('should call callback after given time', done => { - const startTime = new DateController().getTime(); - const toleranceInMilliseconds = 100; - const period: number = 2; + let timeoutCallback = jest.fn(() => { + const callbackCalledTime = new DateController().getTime(); - let timeoutCallback = jest.fn(() => { - const callbackCalledTime = new DateController().getTime(); - - expect(callbackCalledTime - startTime).toBeLessThan(toleranceInMilliseconds + period); - done() - }); - - const timeout: Timeout = new Timeout(timeoutCallback); - - expect(timeoutCallback).not.toBeCalled(); - - timeout.start(period); - - // Fast-forward until all timers have been executed - jest.runAllTimers(); + expect(callbackCalledTime - startTime).toBeLessThan(toleranceInMilliseconds + period); + done(); + }); - expect(setTimeout).toHaveBeenLastCalledWith(expect.any(Function), period); + const timeout: Timeout = new Timeout(timeoutCallback); - }); + expect(timeoutCallback).not.toBeCalled(); - test('should not call callback if clean in time', () => { - const period: number = 2; + timeout.start(period); - let timeoutCallback = jest.fn(); - const timeout: Timeout = new Timeout(timeoutCallback); + // Fast-forward until all timers have been executed + jest.runAllTimers(); - expect(timeoutCallback).not.toBeCalled(); + expect(setTimeout).toHaveBeenLastCalledWith(expect.any(Function), period); + }); - timeout.start(period); - timeout.clear(); + test('should not call callback if clean in time', () => { + const period: number = 2; - // Fast-forward until all timers have been executed - jest.runAllTimers(); + let timeoutCallback = jest.fn(); + const timeout: Timeout = new Timeout(timeoutCallback); - expect(timeoutCallback).not.toBeCalled(); + expect(timeoutCallback).not.toBeCalled(); - }); + timeout.start(period); + timeout.clear(); - test('should not clear timeout if it\'s not started', () => { - let timeoutCallback = jest.fn(); - const timeout: Timeout = new Timeout(timeoutCallback); + // Fast-forward until all timers have been executed + jest.runAllTimers(); - expect(timeoutCallback).not.toBeCalled(); + expect(timeoutCallback).not.toBeCalled(); + }); - timeout.clear(); + test("should not clear timeout if it's not started", () => { + let timeoutCallback = jest.fn(); + const timeout: Timeout = new Timeout(timeoutCallback); - // Fast-forward until all timers have been executed - jest.runAllTimers(); + expect(timeoutCallback).not.toBeCalled(); - expect(timeoutCallback).not.toBeCalled(); + timeout.clear(); - }); + // Fast-forward until all timers have been executed + jest.runAllTimers(); -}); \ No newline at end of file + expect(timeoutCallback).not.toBeCalled(); + }); +}); diff --git a/src/timers/timeout.ts b/src/timers/timeout.ts index dd8c39f5..4968f8bb 100644 --- a/src/timers/timeout.ts +++ b/src/timers/timeout.ts @@ -1,23 +1,22 @@ export class Timeout { + private timeoutId?: ReturnType; + private callback: Function; - private timer?: NodeJS.Timer; - private callback: Function; + public constructor(callBack: Function) { + this.callback = callBack; + } - public constructor(callBack: Function) { - this.callback = callBack; - } - - public start(period: number) { - this.timer = global.setTimeout(() => { - this.clear(); - this.callback(); - }, period); - } + public start(period: number) { + this.timeoutId = global.setTimeout(() => { + this.clear(); + this.callback(); + }, period); + } - public clear() { - if (this.timer) { - global.clearTimeout(this.timer); - delete this.timer; - } + public clear() { + if (this.timeoutId) { + global.clearTimeout(this.timeoutId); + delete this.timeoutId; } -} \ No newline at end of file + } +} diff --git a/tsconfig.json b/tsconfig.json index 0e563d39..e65bb589 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,65 +2,54 @@ "compileOnSave": true, "compilerOptions": { /* Basic Options */ - "target": "es6", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */ - "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ + "target": "es6" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */, + "module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */, // "lib": [], /* Specify library files to be included in the compilation. */ // "allowJs": true, /* Allow javascript files to be compiled. */ // "checkJs": true, /* Report errors in .js files. */ // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ - "declaration": true, /* Generates corresponding '.d.ts' file. */ - // "sourceMap": true, /* Generates corresponding '.map' file. */ + "declaration": true /* Generates corresponding '.d.ts' file. */, + "sourceMap": true /* Generates corresponding '.map' file. */, // "outFile": "./", /* Concatenate and emit output to single file. */ - "outDir": "./js", /* Redirect output structure to the directory. */ + "outDir": "./dist" /* Redirect output structure to the directory. */, // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ // "removeComments": true, /* Do not emit comments to output. */ // "noEmit": true, /* Do not emit outputs. */ // "importHelpers": true, /* Import emit helpers from 'tslib'. */ // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ - /* Strict Type-Checking Options */ - "strict": true, /* Enable all strict type-checking options. */ + "strict": true /* Enable all strict type-checking options. */, // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ // "strictNullChecks": true, /* Enable strict null checks. */ // "strictFunctionTypes": true, /* Enable strict checking of function types. */ // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ - /* Additional Checks */ // "noUnusedLocals": true, /* Report errors on unused locals. */ // "noUnusedParameters": true, /* Report errors on unused parameters. */ // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ - /* Module Resolution Options */ - "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ + "moduleResolution": "node" /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */, // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ // "typeRoots": [], /* List of folders to include type definitions from. */ // "types": [], /* Type declaration files to be included in compilation. */ - "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ - "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ + "allowSyntheticDefaultImports": true /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */, + "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */, // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ - /* Source Map Options */ // "sourceRoot": "./", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ // "mapRoot": "./", /* Specify the location where debugger should locate map files instead of generated locations. */ // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ - /* Experimental Options */ - "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ - "emitDecoratorMetadata": true /* Enables experimental support for emitting type metadata for decorators. */ - + "experimentalDecorators": true /* Enables experimental support for ES7 decorators. */, + "emitDecoratorMetadata": true /* Enables experimental support for emitting type metadata for decorators. */ }, - "include": [ - "src/**/*" - ], - "exclude": [ - "node_modules", - "**/*.test.ts" - ] + "include": ["src/**/*"], + "exclude": ["node_modules", "**/*.test.ts"] } diff --git a/tslint.json b/tslint.json index bcd5c02e..a9ce0131 100644 --- a/tslint.json +++ b/tslint.json @@ -17,7 +17,8 @@ "quotemark": [true, "single", "avoid-escape"], "semicolon": [true, "always"], "typedef-whitespace": [ - true, { + true, + { "call-signature": "nospace", "index-signature": "nospace", "parameter": "nospace", @@ -25,13 +26,6 @@ "variable-declaration": "nospace" } ], - "whitespace": [ - true, - "check-branch", - "check-decl", - "check-operator", - "check-separator", - "check-type" - ] + "whitespace": [true, "check-branch", "check-decl", "check-operator", "check-separator", "check-type"] } }