diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..25a10f9 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,9 @@ +root = true + +[**.{ts,json,js}] +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true +indent_style = space +indent_size = 4 \ No newline at end of file diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..e562b25 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,94 @@ +module.exports = { + root: true, + extends: ["eslint:recommended", "standard", "prettier"], + plugins: ["unused-imports", "workspaces", "notice"], + env: { + es6: true, + node: true, + }, + ignorePatterns: [".eslintrc.js", "dist", "node_modules"], + rules: { + "notice/notice": [ + "error", + { + mustMatch: + "Copyright \\(c\\) [0-9]{0,4} Contributors to the Eclipse Foundation", + templateFile: __dirname + "/license.template.txt", + onNonMatchingHeader: "replace", + }, + ], + "no-use-before-define": "off", + "unused-imports/no-unused-imports": "error", + "guard-for-in": "error", + "unused-imports/no-unused-vars": [ + "warn", + { + args: "none", + varsIgnorePattern: "Test", // Ignore test suites from unused-imports + }, + ], + }, + overrides: [ + { + files: ["**/*.ts", "**/*.tsx"], + parser: "@typescript-eslint/parser", + parserOptions: { + tsconfigRootDir: __dirname, + project: ["./tsconfig.eslint.json"], + }, + extends: [ + "eslint:recommended", + "standard", + "prettier", + "plugin:@typescript-eslint/recommended", + "plugin:workspaces/recommended", + ], + plugins: [ + "@typescript-eslint", + "unused-imports", + "workspaces", + "notice", + ], + env: { + es6: true, + node: true, + }, + // ignorePatterns: [".eslintrc.js", "dist", "node_modules", "/examples", "bin", "*.js"], + rules: { + "notice/notice": [ + "error", + { + mustMatch: + "Copyright \\(c\\) [0-9]{0,4} Contributors to the Eclipse Foundation", + templateFile: __dirname + "/license.template.txt", + onNonMatchingHeader: "replace", + }, + ], + "@typescript-eslint/no-unused-vars": "off", // or "@typescript-eslint/no-unused-vars": "off", + "no-use-before-define": "off", + "@typescript-eslint/no-use-before-define": ["error"], + "@typescript-eslint/prefer-nullish-coalescing": "error", + "unused-imports/no-unused-imports": "error", + "@typescript-eslint/strict-boolean-expressions": "error", + "guard-for-in": "error", + "unused-imports/no-unused-vars": [ + "warn", + { + args: "none", + varsIgnorePattern: "Test", // Ignore test suites from unused-imports + }, + ], + }, + }, + { + files: ["**/*.test.js", "**/*.test.ts"], + env: { + mocha: true, + }, + rules: { + "no-unused-expressions": "off", + "@typescript-eslint/no-unused-expressions": "off", + }, + }, + ], +}; diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..23b4d1d --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,73 @@ +name: CI Test Things + +on: + push: + paths-ignore: + - "**.md" + - "**.png" + - "**.svg" + pull_request: + paths-ignore: + - "**.md" + - "**.png" + - "**.svg" + +jobs: + test: + runs-on: ubuntu-latest + + strategy: + matrix: + node-version: [18.x] + # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ + + steps: + - name: Checkout the branch + uses: actions/checkout@v3 + + - name: Install Python + uses: actions/setup-python@v4 + with: + python-version: "3.11" + + - name: Install Poetry + uses: snok/install-poetry@v1 + + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v3 + with: + node-version: ${{ matrix.node-version }} + cache: "npm" + + - name: Install dependencies + run: npm run setup + + - name: Test Things + run: npm test + + eslint: + name: eslint + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Use Node.js 18 + uses: actions/setup-node@v1 + with: + node-version: 18 + + - name: Install + run: npm ci + + - name: Lint + run: npm run lint + + prettier: + name: Check coding style + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actionsx/prettier@v2 + with: + args: --check . diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml deleted file mode 100644 index 0348605..0000000 --- a/.github/workflows/run-tests.yml +++ /dev/null @@ -1,47 +0,0 @@ -name: Run tests - -on: - push: - paths-ignore: - - "**.md" - - "**.png" - - "**.svg" - pull_request: - paths-ignore: - - "**.md" - - "**.png" - - "**.svg" - -jobs: - test: - runs-on: ubuntu-latest - - strategy: - matrix: - node-version: [18.x] - # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ - - steps: - - name: Checkout the branch - uses: actions/checkout@v3 - - - name: Install Python - uses: actions/setup-python@v4 - with: - python-version: '3.11' - - - name: Install Poetry - uses: snok/install-poetry@v1 - - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v3 - with: - node-version: ${{ matrix.node-version }} - cache: "npm" - - - name: Install dependencies - run: npm run setup - - - name: Test Things - run: npm test - \ No newline at end of file diff --git a/.gitignore b/.gitignore index 845b0a2..459bdce 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,4 @@ __pycache__/ # TS files dist/ -.tsbuildinfo \ No newline at end of file +*.tsbuildinfo \ No newline at end of file diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..36d0dc5 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,3 @@ +package-lock.json +**/dist +coverage diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 0000000..1d4ebc0 --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,7 @@ +{ + "trailingComma": "es5", + "printWidth": 120, + "singleQuote": false, + "tabWidth": 4, + "endOfLine": "lf" +} \ No newline at end of file diff --git a/README.md b/README.md index b1381a7..2a8c1d7 100644 --- a/README.md +++ b/README.md @@ -101,29 +101,30 @@ See the mashup's [readme](./mashups/smart-home/README.md). You can start the devices inside a container, for that running `docker-compose -f docker-compose-infra.yml -f docker-compose-things.yml up` at the root directory builds and runs the containers. For custom configuration, take a look at the `Dockerfile` of each device or [docker-compose-things.yml](./docker-compose-things.yml). [docker-compose-things.yml](./docker-compose-things.yml) consists of the docker configuration of the things. -[docker-compose-infra.yml](./docker-compose-infra.yml) consists of the docker configuration of additional tools such as traefik, prometheus, grafana, cadvisor and portainer. +[docker-compose-infra.yml](./docker-compose-infra.yml) consists of the docker configuration of additional tools such as traefik, prometheus, grafana, cadvisor and portainer. After the run, as default, the devices are accessible at: -| Thing Title | Access URL | -| ----------- | ---------- | -| http-advanced-coffee-machine | `http://localhost/http-advanced-coffee-machine` | -| coap-calculator-simple | `coap://localhost:5683/coap-calculator-simple` | -| coap-calculator-content-negotiation | `coap://localhost:5684/coap-calculator-content-negotiation` | -| http-express-calculator-simple | `http://localhost/http-express-calculator-simple` | +| Thing Title | Access URL | +| ------------------------------------------- | -------------------------------------------------------------- | +| http-advanced-coffee-machine | `http://localhost/http-advanced-coffee-machine` | +| coap-calculator-simple | `coap://localhost:5683/coap-calculator-simple` | +| coap-calculator-content-negotiation | `coap://localhost:5684/coap-calculator-content-negotiation` | +| http-express-calculator-simple | `http://localhost/http-express-calculator-simple` | | http-express-calculator-content-negotiation | `http://localhost/http-express-calculator-content-negotiation` | -| http-flask-calculator | `http://localhost/http-flask-calculator` | -| mqtt-calculator | `mqtt://test.mosquitto.org:1883/mqtt-calculator` | -| modbus-elevator | `modbus+tcp://localhost:3179/1` | -| http-data-schema-thing | `http://localhost/http-data-schema-thing` | +| http-flask-calculator | `http://localhost/http-flask-calculator` | +| mqtt-calculator | `mqtt://test.mosquitto.org:1883/mqtt-calculator` | +| modbus-elevator | `modbus+tcp://localhost:3179/1` | +| http-data-schema-thing | `http://localhost/http-data-schema-thing` | To be able to access additional tools, the user must login through GitHub. For GitHub authentication to work, environment variables `OAUTH_SECRET` and, `GITHUB_CLIENT_ID` and `GITHUB_CLIENT_SECRET` must be set with the configuration of GitHub OAuth application. Only whitelisted emails can access some of the services. Whitelisted emails can be set using the environment variable `WHITELISTED_EMAILS`. These services are accessible at: -- Traefik dashboard -> dashboard.localhost +- Traefik dashboard -> dashboard.localhost - Prometheus -> prometheus.localhost - cAdvisor -> cadvisor.localhost Grafana and Portainer UIs are public access but they run their own authentication and authorization. These services are accessible at: + - Grafana -> grafana.localhost - Portainer -> portainer.localhost @@ -139,8 +140,9 @@ For Node.js-based devices, we use npm workspaces and running `npm install` at th Grafana dashboard json files are stored in [./conf/grafana/dashboards](./conf/grafana/dashboards/). To save your newly created dashboard locally and push it into the remote repository: - - Export the dashboard as JSON file using Share > Export. - - Save the exported JSON file to [./conf/grafana/dashboards](./conf/grafana/dashboards/). + +- Export the dashboard as JSON file using Share > Export. +- Save the exported JSON file to [./conf/grafana/dashboards](./conf/grafana/dashboards/). If your dashboard uses another datasource than our default `prometheus-datasource`, new datasource also must be provisioned in [./conf/grafana/datasources](./conf/grafana/provisioning/datasources/). -For more information check Grafana's provisioning [documentation](https://grafana.com/docs/grafana/latest/administration/provisioning/). \ No newline at end of file +For more information check Grafana's provisioning [documentation](https://grafana.com/docs/grafana/latest/administration/provisioning/). diff --git a/conf/grafana/dashboards/http-data-schema-thing-dashboard.json b/conf/grafana/dashboards/http-data-schema-thing-dashboard.json index ba1189c..1885e28 100644 --- a/conf/grafana/dashboards/http-data-schema-thing-dashboard.json +++ b/conf/grafana/dashboards/http-data-schema-thing-dashboard.json @@ -1,19 +1,19 @@ { "annotations": { - "list": [ - { - "builtIn": 1, - "datasource": { - "type": "grafana", - "uid": "-- Grafana --" - }, - "enable": true, - "hide": true, - "iconColor": "rgba(0, 211, 255, 1)", - "name": "Annotations & Alerts", - "type": "dashboard" - } - ] + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] }, "editable": true, "fiscalYearStartMonth": 0, @@ -21,2014 +21,1972 @@ "id": 7, "links": [], "panels": [ - { - "datasource": { - "default": true, - "type": "prometheus", - "uid": "adwznwwq8yupse" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisBorderShow": true, - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "barWidthFactor": 0.5, - "drawStyle": "line", - "fillOpacity": 50, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": false, - "lineInterpolation": "smooth", - "lineWidth": 3, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 0 - }, - "id": 1, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "right", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { + { "datasource": { - "type": "prometheus", - "uid": "adwznwwq8yupse" - }, - "disableTextWrap": false, - "editorMode": "builder", - "expr": "changes(traefik_service_requests_total{protocol=\"http\", service=\"http-data-schema-thing-test-things@docker\"}[1m])", - "fullMetaSearch": false, - "includeNullMetadata": true, - "instant": false, - "legendFormat": "Status Code:{{code}}", - "range": true, - "refId": "A", - "useBackend": false - } - ], - "title": "Requests", - "type": "timeseries" - }, - { - "datasource": { - "default": false, - "type": "loki", - "uid": "cdxkxyq1xjmyoc" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "fieldMinMax": false, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null + "default": true, + "type": "prometheus", + "uid": "adwznwwq8yupse" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": true, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.5, + "drawStyle": "line", + "fillOpacity": 50, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "smooth", + "lineWidth": 3, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 0 + }, + "id": 1, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" } - ] }, - "unit": "none" - }, - "overrides": [] - }, - "gridPos": { - "h": 4, - "w": 4, - "x": 12, - "y": 0 - }, - "id": 2, - "options": { - "colorMode": "value", - "graphMode": "none", - "justifyMode": "auto", - "orientation": "auto", - "percentChangeColorMode": "standard", - "reduceOptions": { - "calcs": [ - "last" + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "adwznwwq8yupse" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "changes(traefik_service_requests_total{protocol=\"http\", service=\"http-data-schema-thing-test-things@docker\"}[1m])", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "Status Code:{{code}}", + "range": true, + "refId": "A", + "useBackend": false + } ], - "fields": "/.*/", - "limit": 5, - "values": false - }, - "showPercentChange": false, - "text": {}, - "textMode": "auto", - "wideLayout": true + "title": "Requests", + "type": "timeseries" }, - "pluginVersion": "11.2.0", - "targets": [ - { + { "datasource": { - "type": "loki", - "uid": "cdxkxyq1xjmyoc" - }, - "direction": "forward", - "editorMode": "code", - "expr": "{affordance=\"property\",affordanceName=\"bool\",thing=\"http-data-schema-thing\"}", - "queryType": "range", - "refId": "A" - } - ], - "title": "property \"bool\"", - "transformations": [ - { - "id": "extractFields", + "default": false, + "type": "loki", + "uid": "cdxkxyq1xjmyoc" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "fieldMinMax": false, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 4, + "x": 12, + "y": 0 + }, + "id": 2, "options": { - "format": "json", - "jsonPaths": [ + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": ["last"], + "fields": "/.*/", + "limit": 5, + "values": false + }, + "showPercentChange": false, + "text": {}, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.2.0", + "targets": [ { - "alias": "result", - "path": "message" + "datasource": { + "type": "loki", + "uid": "cdxkxyq1xjmyoc" + }, + "direction": "forward", + "editorMode": "code", + "expr": "{affordance=\"property\",affordanceName=\"bool\",thing=\"http-data-schema-thing\"}", + "queryType": "range", + "refId": "A" } - ], - "keepTime": true, - "replace": true, - "source": "Line" - } - }, - { - "id": "convertFieldType", - "options": { - "conversions": [ + ], + "title": "property \"bool\"", + "transformations": [ { - "destinationType": "boolean", - "targetField": "result" - } - ], - "fields": {} - } - } - ], - "type": "stat" - }, - { - "datasource": { - "default": false, - "type": "loki", - "uid": "cdxkxyq1xjmyoc" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "fieldMinMax": false, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ + "id": "extractFields", + "options": { + "format": "json", + "jsonPaths": [ + { + "alias": "result", + "path": "message" + } + ], + "keepTime": true, + "replace": true, + "source": "Line" + } + }, { - "color": "green", - "value": null + "id": "convertFieldType", + "options": { + "conversions": [ + { + "destinationType": "boolean", + "targetField": "result" + } + ], + "fields": {} + } } - ] - }, - "unit": "none" - }, - "overrides": [] - }, - "gridPos": { - "h": 4, - "w": 4, - "x": 16, - "y": 0 - }, - "id": 4, - "options": { - "colorMode": "value", - "graphMode": "none", - "justifyMode": "auto", - "orientation": "auto", - "percentChangeColorMode": "standard", - "reduceOptions": { - "calcs": [ - "last" ], - "fields": "/.*/", - "limit": 5, - "values": false - }, - "showPercentChange": false, - "text": {}, - "textMode": "auto", - "wideLayout": true + "type": "stat" }, - "pluginVersion": "11.2.0", - "targets": [ - { + { "datasource": { - "type": "loki", - "uid": "cdxkxyq1xjmyoc" - }, - "direction": "forward", - "editorMode": "code", - "expr": "{affordance=\"property\",affordanceName=\"num\",thing=\"http-data-schema-thing\"}", - "queryType": "range", - "refId": "A" - } - ], - "title": "property \"num\"", - "transformations": [ - { - "id": "extractFields", + "default": false, + "type": "loki", + "uid": "cdxkxyq1xjmyoc" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "fieldMinMax": false, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 4, + "x": 16, + "y": 0 + }, + "id": 4, "options": { - "format": "json", - "jsonPaths": [ + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": ["last"], + "fields": "/.*/", + "limit": 5, + "values": false + }, + "showPercentChange": false, + "text": {}, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.2.0", + "targets": [ { - "alias": "result", - "path": "message" + "datasource": { + "type": "loki", + "uid": "cdxkxyq1xjmyoc" + }, + "direction": "forward", + "editorMode": "code", + "expr": "{affordance=\"property\",affordanceName=\"num\",thing=\"http-data-schema-thing\"}", + "queryType": "range", + "refId": "A" } - ], - "keepTime": true, - "replace": true, - "source": "Line" - } - }, - { - "id": "convertFieldType", - "options": { - "conversions": [ + ], + "title": "property \"num\"", + "transformations": [ { - "destinationType": "number", - "targetField": "result" - } - ], - "fields": {} - } - } - ], - "type": "stat" - }, - { - "datasource": { - "default": false, - "type": "loki", - "uid": "cdxkxyq1xjmyoc" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "fieldMinMax": false, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ + "id": "extractFields", + "options": { + "format": "json", + "jsonPaths": [ + { + "alias": "result", + "path": "message" + } + ], + "keepTime": true, + "replace": true, + "source": "Line" + } + }, { - "color": "green", - "value": null + "id": "convertFieldType", + "options": { + "conversions": [ + { + "destinationType": "number", + "targetField": "result" + } + ], + "fields": {} + } } - ] - }, - "unit": "none" - }, - "overrides": [] - }, - "gridPos": { - "h": 4, - "w": 4, - "x": 20, - "y": 0 - }, - "id": 6, - "options": { - "colorMode": "value", - "graphMode": "none", - "justifyMode": "auto", - "orientation": "auto", - "percentChangeColorMode": "standard", - "reduceOptions": { - "calcs": [ - "last" ], - "fields": "/.*/", - "limit": 5, - "values": false - }, - "showPercentChange": false, - "text": {}, - "textMode": "auto", - "wideLayout": true + "type": "stat" }, - "pluginVersion": "11.2.0", - "targets": [ - { + { "datasource": { - "type": "loki", - "uid": "cdxkxyq1xjmyoc" - }, - "direction": "forward", - "editorMode": "code", - "expr": "{affordance=\"property\",affordanceName=\"array\",thing=\"http-data-schema-thing\"}", - "queryType": "range", - "refId": "A" - } - ], - "title": "property \"array\"", - "transformations": [ - { - "id": "extractFields", + "default": false, + "type": "loki", + "uid": "cdxkxyq1xjmyoc" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "fieldMinMax": false, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 4, + "x": 20, + "y": 0 + }, + "id": 6, "options": { - "format": "json", - "jsonPaths": [ + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": ["last"], + "fields": "/.*/", + "limit": 5, + "values": false + }, + "showPercentChange": false, + "text": {}, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.2.0", + "targets": [ { - "alias": "result", - "path": "message" + "datasource": { + "type": "loki", + "uid": "cdxkxyq1xjmyoc" + }, + "direction": "forward", + "editorMode": "code", + "expr": "{affordance=\"property\",affordanceName=\"array\",thing=\"http-data-schema-thing\"}", + "queryType": "range", + "refId": "A" } - ], - "keepTime": true, - "replace": true, - "source": "Line" - } - }, - { - "id": "convertFieldType", - "options": { - "conversions": [ + ], + "title": "property \"array\"", + "transformations": [ { - "destinationType": "string", - "targetField": "result" - } - ], - "fields": {} - } - } - ], - "type": "stat" - }, - { - "datasource": { - "default": false, - "type": "loki", - "uid": "cdxkxyq1xjmyoc" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "fieldMinMax": false, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ + "id": "extractFields", + "options": { + "format": "json", + "jsonPaths": [ + { + "alias": "result", + "path": "message" + } + ], + "keepTime": true, + "replace": true, + "source": "Line" + } + }, { - "color": "green", - "value": null + "id": "convertFieldType", + "options": { + "conversions": [ + { + "destinationType": "string", + "targetField": "result" + } + ], + "fields": {} + } } - ] - }, - "unit": "none" - }, - "overrides": [] - }, - "gridPos": { - "h": 4, - "w": 4, - "x": 12, - "y": 4 - }, - "id": 3, - "options": { - "colorMode": "value", - "graphMode": "none", - "justifyMode": "auto", - "orientation": "auto", - "percentChangeColorMode": "standard", - "reduceOptions": { - "calcs": [ - "last" ], - "fields": "/.*/", - "limit": 5, - "values": false - }, - "showPercentChange": false, - "text": {}, - "textMode": "auto", - "wideLayout": true + "type": "stat" }, - "pluginVersion": "11.2.0", - "targets": [ - { + { "datasource": { - "type": "loki", - "uid": "cdxkxyq1xjmyoc" - }, - "direction": "forward", - "editorMode": "code", - "expr": "{affordance=\"property\",affordanceName=\"int\",thing=\"http-data-schema-thing\"}", - "queryType": "range", - "refId": "A" - } - ], - "title": "property \"int\"", - "transformations": [ - { - "id": "extractFields", + "default": false, + "type": "loki", + "uid": "cdxkxyq1xjmyoc" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "fieldMinMax": false, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 4, + "x": 12, + "y": 4 + }, + "id": 3, "options": { - "format": "json", - "jsonPaths": [ + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": ["last"], + "fields": "/.*/", + "limit": 5, + "values": false + }, + "showPercentChange": false, + "text": {}, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.2.0", + "targets": [ { - "alias": "result", - "path": "message" + "datasource": { + "type": "loki", + "uid": "cdxkxyq1xjmyoc" + }, + "direction": "forward", + "editorMode": "code", + "expr": "{affordance=\"property\",affordanceName=\"int\",thing=\"http-data-schema-thing\"}", + "queryType": "range", + "refId": "A" } - ], - "keepTime": true, - "replace": true, - "source": "Line" - } - }, - { - "id": "convertFieldType", - "options": { - "conversions": [ + ], + "title": "property \"int\"", + "transformations": [ { - "destinationType": "number", - "targetField": "result" - } - ], - "fields": {} - } - } - ], - "type": "stat" - }, - { - "datasource": { - "default": false, - "type": "loki", - "uid": "cdxkxyq1xjmyoc" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "fieldMinMax": false, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ + "id": "extractFields", + "options": { + "format": "json", + "jsonPaths": [ + { + "alias": "result", + "path": "message" + } + ], + "keepTime": true, + "replace": true, + "source": "Line" + } + }, { - "color": "green", - "value": null + "id": "convertFieldType", + "options": { + "conversions": [ + { + "destinationType": "number", + "targetField": "result" + } + ], + "fields": {} + } } - ] - }, - "unit": "none" - }, - "overrides": [] - }, - "gridPos": { - "h": 4, - "w": 4, - "x": 16, - "y": 4 - }, - "id": 5, - "options": { - "colorMode": "value", - "graphMode": "none", - "justifyMode": "auto", - "orientation": "auto", - "percentChangeColorMode": "standard", - "reduceOptions": { - "calcs": [ - "last" ], - "fields": "/.*/", - "limit": 5, - "values": false - }, - "showPercentChange": false, - "text": {}, - "textMode": "auto", - "wideLayout": true + "type": "stat" }, - "pluginVersion": "11.2.0", - "targets": [ - { + { "datasource": { - "type": "loki", - "uid": "cdxkxyq1xjmyoc" - }, - "direction": "forward", - "editorMode": "code", - "expr": "{affordance=\"property\",affordanceName=\"string\",thing=\"http-data-schema-thing\"}", - "queryType": "range", - "refId": "A" - } - ], - "title": "property \"string\"", - "transformations": [ - { - "id": "extractFields", + "default": false, + "type": "loki", + "uid": "cdxkxyq1xjmyoc" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "fieldMinMax": false, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 4, + "x": 16, + "y": 4 + }, + "id": 5, "options": { - "format": "json", - "jsonPaths": [ + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": ["last"], + "fields": "/.*/", + "limit": 5, + "values": false + }, + "showPercentChange": false, + "text": {}, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.2.0", + "targets": [ { - "alias": "result", - "path": "message" + "datasource": { + "type": "loki", + "uid": "cdxkxyq1xjmyoc" + }, + "direction": "forward", + "editorMode": "code", + "expr": "{affordance=\"property\",affordanceName=\"string\",thing=\"http-data-schema-thing\"}", + "queryType": "range", + "refId": "A" } - ], - "keepTime": true, - "replace": true, - "source": "Line" - } - }, - { - "id": "convertFieldType", - "options": { - "conversions": [ + ], + "title": "property \"string\"", + "transformations": [ { - "destinationType": "string", - "targetField": "result" - } - ], - "fields": {} - } - } - ], - "type": "stat" - }, - { - "datasource": { - "default": false, - "type": "loki", - "uid": "cdxkxyq1xjmyoc" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "fieldMinMax": false, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ + "id": "extractFields", + "options": { + "format": "json", + "jsonPaths": [ + { + "alias": "result", + "path": "message" + } + ], + "keepTime": true, + "replace": true, + "source": "Line" + } + }, { - "color": "green", - "value": null + "id": "convertFieldType", + "options": { + "conversions": [ + { + "destinationType": "string", + "targetField": "result" + } + ], + "fields": {} + } } - ] - }, - "unit": "none" - }, - "overrides": [] - }, - "gridPos": { - "h": 4, - "w": 4, - "x": 20, - "y": 4 - }, - "id": 7, - "options": { - "colorMode": "value", - "graphMode": "none", - "justifyMode": "auto", - "orientation": "auto", - "percentChangeColorMode": "standard", - "reduceOptions": { - "calcs": [ - "last" ], - "fields": "/.*/", - "limit": 5, - "values": false - }, - "showPercentChange": false, - "text": {}, - "textMode": "auto", - "wideLayout": true + "type": "stat" }, - "pluginVersion": "11.2.0", - "targets": [ - { + { "datasource": { - "type": "loki", - "uid": "cdxkxyq1xjmyoc" - }, - "direction": "forward", - "editorMode": "code", - "expr": "{affordance=\"property\",affordanceName=\"object\",thing=\"http-data-schema-thing\"}", - "queryType": "range", - "refId": "A" - } - ], - "title": "property \"object\"", - "transformations": [ - { - "id": "extractFields", - "options": { - "format": "json", - "jsonPaths": [ - { - "alias": "result", - "path": "message" - } - ], - "keepTime": true, - "replace": true, - "source": "Line" - } - }, - { - "id": "convertFieldType", + "default": false, + "type": "loki", + "uid": "cdxkxyq1xjmyoc" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "fieldMinMax": false, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 4, + "x": 20, + "y": 4 + }, + "id": 7, "options": { - "conversions": [ + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": ["last"], + "fields": "/.*/", + "limit": 5, + "values": false + }, + "showPercentChange": false, + "text": {}, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.2.0", + "targets": [ { - "destinationType": "string", - "targetField": "result" + "datasource": { + "type": "loki", + "uid": "cdxkxyq1xjmyoc" + }, + "direction": "forward", + "editorMode": "code", + "expr": "{affordance=\"property\",affordanceName=\"object\",thing=\"http-data-schema-thing\"}", + "queryType": "range", + "refId": "A" } - ], - "fields": {} - } - } - ], - "type": "stat" - }, - { - "datasource": { - "default": false, - "type": "loki", - "uid": "cdxkxyq1xjmyoc" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "align": "auto", - "cellOptions": { - "type": "auto" - }, - "inspect": false - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ + ], + "title": "property \"object\"", + "transformations": [ { - "color": "green", - "value": null + "id": "extractFields", + "options": { + "format": "json", + "jsonPaths": [ + { + "alias": "result", + "path": "message" + } + ], + "keepTime": true, + "replace": true, + "source": "Line" + } }, { - "color": "red", - "value": 80 + "id": "convertFieldType", + "options": { + "conversions": [ + { + "destinationType": "string", + "targetField": "result" + } + ], + "fields": {} + } } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 6, - "x": 0, - "y": 8 - }, - "id": 13, - "options": { - "cellHeight": "sm", - "footer": { - "countRows": false, - "fields": "", - "reducer": [ - "sum" ], - "show": false - }, - "frameIndex": 0, - "showHeader": true + "type": "stat" }, - "pluginVersion": "11.2.0", - "targets": [ - { + { "datasource": { - "type": "loki", - "uid": "cdxkxyq1xjmyoc" - }, - "direction": "backward", - "editorMode": "code", - "expr": "{affordance=\"action\",affordanceName=\"void-void\",messageType=\"actionInput\",op=\"invokeaction\",thing=\"http-data-schema-thing\"}", - "queryType": "range", - "refId": "A" - } - ], - "title": "action \"void-void\" Inputs", - "transformations": [ - { - "id": "extractFields", + "default": false, + "type": "loki", + "uid": "cdxkxyq1xjmyoc" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "cellOptions": { + "type": "auto" + }, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 6, + "x": 0, + "y": 8 + }, + "id": 13, "options": { - "format": "json", - "jsonPaths": [ + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": ["sum"], + "show": false + }, + "frameIndex": 0, + "showHeader": true + }, + "pluginVersion": "11.2.0", + "targets": [ { - "alias": "Value", - "path": "message" + "datasource": { + "type": "loki", + "uid": "cdxkxyq1xjmyoc" + }, + "direction": "backward", + "editorMode": "code", + "expr": "{affordance=\"action\",affordanceName=\"void-void\",messageType=\"actionInput\",op=\"invokeaction\",thing=\"http-data-schema-thing\"}", + "queryType": "range", + "refId": "A" } - ], - "keepTime": true, - "replace": true, - "source": "Line" - } - } - ], - "type": "table" - }, - { - "datasource": { - "default": false, - "type": "loki", - "uid": "cdxkxyq1xjmyoc" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "align": "auto", - "cellOptions": { - "type": "auto" - }, - "inspect": false - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, + ], + "title": "action \"void-void\" Inputs", + "transformations": [ { - "color": "red", - "value": 80 + "id": "extractFields", + "options": { + "format": "json", + "jsonPaths": [ + { + "alias": "Value", + "path": "message" + } + ], + "keepTime": true, + "replace": true, + "source": "Line" + } } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 6, - "x": 6, - "y": 8 - }, - "id": 14, - "options": { - "cellHeight": "sm", - "footer": { - "countRows": false, - "fields": "", - "reducer": [ - "sum" ], - "show": false - }, - "frameIndex": 0, - "showHeader": true + "type": "table" }, - "pluginVersion": "11.2.0", - "targets": [ - { + { "datasource": { - "type": "loki", - "uid": "cdxkxyq1xjmyoc" - }, - "direction": "backward", - "editorMode": "code", - "expr": "{affordance=\"action\",affordanceName=\"void-void\",messageType=\"actionOutput\",op=\"invokeaction\",thing=\"http-data-schema-thing\"}", - "queryType": "range", - "refId": "A" - } - ], - "title": "action \"void-void\" Outputs", - "transformations": [ - { - "id": "extractFields", + "default": false, + "type": "loki", + "uid": "cdxkxyq1xjmyoc" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "cellOptions": { + "type": "auto" + }, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 6, + "x": 6, + "y": 8 + }, + "id": 14, "options": { - "format": "json", - "jsonPaths": [ + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": ["sum"], + "show": false + }, + "frameIndex": 0, + "showHeader": true + }, + "pluginVersion": "11.2.0", + "targets": [ { - "alias": "Value", - "path": "message" + "datasource": { + "type": "loki", + "uid": "cdxkxyq1xjmyoc" + }, + "direction": "backward", + "editorMode": "code", + "expr": "{affordance=\"action\",affordanceName=\"void-void\",messageType=\"actionOutput\",op=\"invokeaction\",thing=\"http-data-schema-thing\"}", + "queryType": "range", + "refId": "A" } - ], - "keepTime": true, - "replace": true, - "source": "Line" - } - } - ], - "type": "table" - }, - { - "datasource": { - "default": false, - "type": "loki", - "uid": "cdxkxyq1xjmyoc" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ + ], + "title": "action \"void-void\" Outputs", + "transformations": [ { - "color": "green", - "value": null + "id": "extractFields", + "options": { + "format": "json", + "jsonPaths": [ + { + "alias": "Value", + "path": "message" + } + ], + "keepTime": true, + "replace": true, + "source": "Line" + } } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 12, - "w": 3, - "x": 12, - "y": 8 - }, - "id": 23, - "options": { - "colorMode": "value", - "graphMode": "none", - "justifyMode": "auto", - "orientation": "horizontal", - "percentChangeColorMode": "standard", - "reduceOptions": { - "calcs": [ - "sum" ], - "fields": "", - "values": false - }, - "showPercentChange": false, - "text": {}, - "textMode": "value_and_name", - "wideLayout": true + "type": "table" }, - "pluginVersion": "11.2.0", - "targets": [ - { + { "datasource": { - "type": "loki", - "uid": "cdxkxyq1xjmyoc" - }, - "editorMode": "code", - "expr": "count_over_time({affordance=\"action\", thing=\"http-data-schema-thing\", op=\"invokeaction\", messageType=\"\"}[$__auto])", - "legendFormat": "{{affordanceName}}", - "queryType": "range", - "refId": "A", - "step": "" - } - ], - "title": "Action Invocation Count", - "type": "stat" - }, - { - "datasource": { - "default": false, - "type": "loki", - "uid": "cdxkxyq1xjmyoc" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "align": "auto", - "cellOptions": { - "type": "auto" - }, - "inspect": false - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null + "default": false, + "type": "loki", + "uid": "cdxkxyq1xjmyoc" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 12, + "w": 3, + "x": 12, + "y": 8 + }, + "id": 23, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": ["sum"], + "fields": "", + "values": false }, + "showPercentChange": false, + "text": {}, + "textMode": "value_and_name", + "wideLayout": true + }, + "pluginVersion": "11.2.0", + "targets": [ { - "color": "red", - "value": 80 + "datasource": { + "type": "loki", + "uid": "cdxkxyq1xjmyoc" + }, + "editorMode": "code", + "expr": "count_over_time({affordance=\"action\", thing=\"http-data-schema-thing\", op=\"invokeaction\", messageType=\"\"}[$__auto])", + "legendFormat": "{{affordanceName}}", + "queryType": "range", + "refId": "A", + "step": "" } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 6, - "x": 0, - "y": 16 - }, - "id": 15, - "options": { - "cellHeight": "sm", - "footer": { - "countRows": false, - "fields": "", - "reducer": [ - "sum" ], - "show": false - }, - "frameIndex": 0, - "showHeader": true + "title": "Action Invocation Count", + "type": "stat" }, - "pluginVersion": "11.2.0", - "targets": [ - { + { "datasource": { - "type": "loki", - "uid": "cdxkxyq1xjmyoc" - }, - "direction": "backward", - "editorMode": "code", - "expr": "{affordance=\"action\",affordanceName=\"void-int\",messageType=\"actionInput\",op=\"invokeaction\",thing=\"http-data-schema-thing\"}", - "queryType": "range", - "refId": "A" - } - ], - "title": "action \"void-int\" Inputs", - "transformations": [ - { - "id": "extractFields", + "default": false, + "type": "loki", + "uid": "cdxkxyq1xjmyoc" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "cellOptions": { + "type": "auto" + }, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 6, + "x": 0, + "y": 16 + }, + "id": 15, "options": { - "format": "json", - "jsonPaths": [ + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": ["sum"], + "show": false + }, + "frameIndex": 0, + "showHeader": true + }, + "pluginVersion": "11.2.0", + "targets": [ { - "alias": "Value", - "path": "message" + "datasource": { + "type": "loki", + "uid": "cdxkxyq1xjmyoc" + }, + "direction": "backward", + "editorMode": "code", + "expr": "{affordance=\"action\",affordanceName=\"void-int\",messageType=\"actionInput\",op=\"invokeaction\",thing=\"http-data-schema-thing\"}", + "queryType": "range", + "refId": "A" } - ], - "keepTime": true, - "replace": true, - "source": "Line" - } - } - ], - "type": "table" - }, - { - "datasource": { - "default": false, - "type": "loki", - "uid": "cdxkxyq1xjmyoc" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "align": "auto", - "cellOptions": { - "type": "auto" - }, - "inspect": false - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, + ], + "title": "action \"void-int\" Inputs", + "transformations": [ { - "color": "red", - "value": 80 + "id": "extractFields", + "options": { + "format": "json", + "jsonPaths": [ + { + "alias": "Value", + "path": "message" + } + ], + "keepTime": true, + "replace": true, + "source": "Line" + } } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 6, - "x": 6, - "y": 16 - }, - "id": 16, - "options": { - "cellHeight": "sm", - "footer": { - "countRows": false, - "fields": "", - "reducer": [ - "sum" ], - "show": false - }, - "frameIndex": 0, - "showHeader": true + "type": "table" }, - "pluginVersion": "11.2.0", - "targets": [ - { + { "datasource": { - "type": "loki", - "uid": "cdxkxyq1xjmyoc" - }, - "direction": "backward", - "editorMode": "code", - "expr": "{affordance=\"action\",affordanceName=\"void-int\",messageType=\"actionOutput\",op=\"invokeaction\",thing=\"http-data-schema-thing\"}", - "queryType": "range", - "refId": "A" - } - ], - "title": "action \"void-int\" Outputs", - "transformations": [ - { - "id": "extractFields", + "default": false, + "type": "loki", + "uid": "cdxkxyq1xjmyoc" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "cellOptions": { + "type": "auto" + }, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 6, + "x": 6, + "y": 16 + }, + "id": 16, "options": { - "format": "json", - "jsonPaths": [ + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": ["sum"], + "show": false + }, + "frameIndex": 0, + "showHeader": true + }, + "pluginVersion": "11.2.0", + "targets": [ { - "alias": "Value", - "path": "message" + "datasource": { + "type": "loki", + "uid": "cdxkxyq1xjmyoc" + }, + "direction": "backward", + "editorMode": "code", + "expr": "{affordance=\"action\",affordanceName=\"void-int\",messageType=\"actionOutput\",op=\"invokeaction\",thing=\"http-data-schema-thing\"}", + "queryType": "range", + "refId": "A" } - ], - "keepTime": true, - "replace": true, - "source": "Line" - } - } - ], - "type": "table" - }, - { - "datasource": { - "default": false, - "type": "loki", - "uid": "cdxkxyq1xjmyoc" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "align": "auto", - "cellOptions": { - "type": "auto" - }, - "inspect": false - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, + ], + "title": "action \"void-int\" Outputs", + "transformations": [ { - "color": "red", - "value": 80 + "id": "extractFields", + "options": { + "format": "json", + "jsonPaths": [ + { + "alias": "Value", + "path": "message" + } + ], + "keepTime": true, + "replace": true, + "source": "Line" + } } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 6, - "x": 0, - "y": 24 - }, - "id": 8, - "options": { - "cellHeight": "sm", - "footer": { - "countRows": false, - "fields": "", - "reducer": [ - "sum" ], - "show": false - }, - "frameIndex": 0, - "showHeader": true + "type": "table" }, - "pluginVersion": "11.2.0", - "targets": [ - { + { "datasource": { - "type": "loki", - "uid": "cdxkxyq1xjmyoc" - }, - "direction": "backward", - "editorMode": "code", - "expr": "{affordance=\"action\",affordanceName=\"int-void\",messageType=\"actionInput\",op=\"invokeaction\",thing=\"http-data-schema-thing\"}", - "queryType": "range", - "refId": "A" - } - ], - "title": "action \"int-void\" Inputs", - "transformations": [ - { - "id": "extractFields", + "default": false, + "type": "loki", + "uid": "cdxkxyq1xjmyoc" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "cellOptions": { + "type": "auto" + }, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 6, + "x": 0, + "y": 24 + }, + "id": 8, "options": { - "format": "json", - "jsonPaths": [ + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": ["sum"], + "show": false + }, + "frameIndex": 0, + "showHeader": true + }, + "pluginVersion": "11.2.0", + "targets": [ { - "alias": "Value", - "path": "message" + "datasource": { + "type": "loki", + "uid": "cdxkxyq1xjmyoc" + }, + "direction": "backward", + "editorMode": "code", + "expr": "{affordance=\"action\",affordanceName=\"int-void\",messageType=\"actionInput\",op=\"invokeaction\",thing=\"http-data-schema-thing\"}", + "queryType": "range", + "refId": "A" } - ], - "keepTime": true, - "replace": true, - "source": "Line" - } - } - ], - "type": "table" - }, - { - "datasource": { - "default": false, - "type": "loki", - "uid": "cdxkxyq1xjmyoc" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "align": "auto", - "cellOptions": { - "type": "auto" - }, - "inspect": false - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, + ], + "title": "action \"int-void\" Inputs", + "transformations": [ { - "color": "red", - "value": 80 + "id": "extractFields", + "options": { + "format": "json", + "jsonPaths": [ + { + "alias": "Value", + "path": "message" + } + ], + "keepTime": true, + "replace": true, + "source": "Line" + } } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 6, - "x": 6, - "y": 24 - }, - "id": 12, - "options": { - "cellHeight": "sm", - "footer": { - "countRows": false, - "fields": "", - "reducer": [ - "sum" ], - "show": false - }, - "frameIndex": 0, - "showHeader": true + "type": "table" }, - "pluginVersion": "11.2.0", - "targets": [ - { + { "datasource": { - "type": "loki", - "uid": "cdxkxyq1xjmyoc" - }, - "direction": "backward", - "editorMode": "code", - "expr": "{affordance=\"action\",affordanceName=\"int-void\",messageType=\"actionOutput\",op=\"invokeaction\",thing=\"http-data-schema-thing\"}", - "queryType": "range", - "refId": "A" - } - ], - "title": "action \"int-void\" Outputs", - "transformations": [ - { - "id": "extractFields", + "default": false, + "type": "loki", + "uid": "cdxkxyq1xjmyoc" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "cellOptions": { + "type": "auto" + }, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 6, + "x": 6, + "y": 24 + }, + "id": 12, "options": { - "format": "json", - "jsonPaths": [ + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": ["sum"], + "show": false + }, + "frameIndex": 0, + "showHeader": true + }, + "pluginVersion": "11.2.0", + "targets": [ { - "alias": "Value", - "path": "message" + "datasource": { + "type": "loki", + "uid": "cdxkxyq1xjmyoc" + }, + "direction": "backward", + "editorMode": "code", + "expr": "{affordance=\"action\",affordanceName=\"int-void\",messageType=\"actionOutput\",op=\"invokeaction\",thing=\"http-data-schema-thing\"}", + "queryType": "range", + "refId": "A" } - ], - "keepTime": true, - "replace": true, - "source": "Line" - } - } - ], - "type": "table" - }, - { - "datasource": { - "default": false, - "type": "loki", - "uid": "cdxkxyq1xjmyoc" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "align": "auto", - "cellOptions": { - "type": "auto" - }, - "inspect": false - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, + ], + "title": "action \"int-void\" Outputs", + "transformations": [ { - "color": "red", - "value": 80 + "id": "extractFields", + "options": { + "format": "json", + "jsonPaths": [ + { + "alias": "Value", + "path": "message" + } + ], + "keepTime": true, + "replace": true, + "source": "Line" + } } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 6, - "x": 0, - "y": 32 - }, - "id": 10, - "options": { - "cellHeight": "sm", - "footer": { - "countRows": false, - "fields": "", - "reducer": [ - "sum" ], - "show": false - }, - "frameIndex": 0, - "showHeader": true + "type": "table" }, - "pluginVersion": "11.2.0", - "targets": [ - { + { "datasource": { - "type": "loki", - "uid": "cdxkxyq1xjmyoc" - }, - "direction": "backward", - "editorMode": "code", - "expr": "{affordance=\"action\",affordanceName=\"int-int\",messageType=\"actionInput\",op=\"invokeaction\",thing=\"http-data-schema-thing\"}", - "queryType": "range", - "refId": "A" - } - ], - "title": "action \"int-int\" Inputs", - "transformations": [ - { - "id": "extractFields", + "default": false, + "type": "loki", + "uid": "cdxkxyq1xjmyoc" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "cellOptions": { + "type": "auto" + }, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 6, + "x": 0, + "y": 32 + }, + "id": 10, "options": { - "format": "json", - "jsonPaths": [ + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": ["sum"], + "show": false + }, + "frameIndex": 0, + "showHeader": true + }, + "pluginVersion": "11.2.0", + "targets": [ { - "alias": "Value", - "path": "message" + "datasource": { + "type": "loki", + "uid": "cdxkxyq1xjmyoc" + }, + "direction": "backward", + "editorMode": "code", + "expr": "{affordance=\"action\",affordanceName=\"int-int\",messageType=\"actionInput\",op=\"invokeaction\",thing=\"http-data-schema-thing\"}", + "queryType": "range", + "refId": "A" } - ], - "keepTime": true, - "replace": true, - "source": "Line" - } - } - ], - "type": "table" - }, - { - "datasource": { - "default": false, - "type": "loki", - "uid": "cdxkxyq1xjmyoc" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "align": "auto", - "cellOptions": { - "type": "auto" - }, - "inspect": false - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, + ], + "title": "action \"int-int\" Inputs", + "transformations": [ { - "color": "red", - "value": 80 + "id": "extractFields", + "options": { + "format": "json", + "jsonPaths": [ + { + "alias": "Value", + "path": "message" + } + ], + "keepTime": true, + "replace": true, + "source": "Line" + } } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 6, - "x": 6, - "y": 32 - }, - "id": 11, - "options": { - "cellHeight": "sm", - "footer": { - "countRows": false, - "fields": "", - "reducer": [ - "sum" ], - "show": false - }, - "frameIndex": 0, - "showHeader": true + "type": "table" }, - "pluginVersion": "11.2.0", - "targets": [ - { + { "datasource": { - "type": "loki", - "uid": "cdxkxyq1xjmyoc" - }, - "direction": "backward", - "editorMode": "code", - "expr": "{affordance=\"action\",affordanceName=\"int-int\",messageType=\"actionOutput\",op=\"invokeaction\",thing=\"http-data-schema-thing\"}", - "queryType": "range", - "refId": "A" - } - ], - "title": "action \"int-int\" Outputs", - "transformations": [ - { - "id": "extractFields", + "default": false, + "type": "loki", + "uid": "cdxkxyq1xjmyoc" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "cellOptions": { + "type": "auto" + }, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 6, + "x": 6, + "y": 32 + }, + "id": 11, "options": { - "format": "json", - "jsonPaths": [ + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": ["sum"], + "show": false + }, + "frameIndex": 0, + "showHeader": true + }, + "pluginVersion": "11.2.0", + "targets": [ { - "alias": "Value", - "path": "message" + "datasource": { + "type": "loki", + "uid": "cdxkxyq1xjmyoc" + }, + "direction": "backward", + "editorMode": "code", + "expr": "{affordance=\"action\",affordanceName=\"int-int\",messageType=\"actionOutput\",op=\"invokeaction\",thing=\"http-data-schema-thing\"}", + "queryType": "range", + "refId": "A" } - ], - "keepTime": true, - "replace": true, - "source": "Line" - } - } - ], - "type": "table" - }, - { - "datasource": { - "default": false, - "type": "loki", - "uid": "cdxkxyq1xjmyoc" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "align": "auto", - "cellOptions": { - "type": "auto" - }, - "inspect": false - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, + ], + "title": "action \"int-int\" Outputs", + "transformations": [ { - "color": "red", - "value": 80 + "id": "extractFields", + "options": { + "format": "json", + "jsonPaths": [ + { + "alias": "Value", + "path": "message" + } + ], + "keepTime": true, + "replace": true, + "source": "Line" + } } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 6, - "x": 0, - "y": 40 - }, - "id": 17, - "options": { - "cellHeight": "sm", - "footer": { - "countRows": false, - "fields": "", - "reducer": [ - "sum" ], - "show": false - }, - "frameIndex": 0, - "showHeader": true + "type": "table" }, - "pluginVersion": "11.2.0", - "targets": [ - { + { "datasource": { - "type": "loki", - "uid": "cdxkxyq1xjmyoc" - }, - "direction": "backward", - "editorMode": "code", - "expr": "{affordance=\"action\",affordanceName=\"int-string\",messageType=\"actionInput\",op=\"invokeaction\",thing=\"http-data-schema-thing\"}", - "queryType": "range", - "refId": "A" - } - ], - "title": "action \"int-string\" Inputs", - "transformations": [ - { - "id": "extractFields", + "default": false, + "type": "loki", + "uid": "cdxkxyq1xjmyoc" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "cellOptions": { + "type": "auto" + }, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 6, + "x": 0, + "y": 40 + }, + "id": 17, "options": { - "format": "json", - "jsonPaths": [ + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": ["sum"], + "show": false + }, + "frameIndex": 0, + "showHeader": true + }, + "pluginVersion": "11.2.0", + "targets": [ { - "alias": "Value", - "path": "message" + "datasource": { + "type": "loki", + "uid": "cdxkxyq1xjmyoc" + }, + "direction": "backward", + "editorMode": "code", + "expr": "{affordance=\"action\",affordanceName=\"int-string\",messageType=\"actionInput\",op=\"invokeaction\",thing=\"http-data-schema-thing\"}", + "queryType": "range", + "refId": "A" } - ], - "keepTime": true, - "replace": true, - "source": "Line" - } - } - ], - "type": "table" - }, - { - "datasource": { - "default": false, - "type": "loki", - "uid": "cdxkxyq1xjmyoc" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "align": "auto", - "cellOptions": { - "type": "auto" - }, - "inspect": false - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, + ], + "title": "action \"int-string\" Inputs", + "transformations": [ { - "color": "red", - "value": 80 + "id": "extractFields", + "options": { + "format": "json", + "jsonPaths": [ + { + "alias": "Value", + "path": "message" + } + ], + "keepTime": true, + "replace": true, + "source": "Line" + } } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 6, - "x": 6, - "y": 40 - }, - "id": 18, - "options": { - "cellHeight": "sm", - "footer": { - "countRows": false, - "fields": "", - "reducer": [ - "sum" ], - "show": false - }, - "frameIndex": 0, - "showHeader": true + "type": "table" }, - "pluginVersion": "11.2.0", - "targets": [ - { + { "datasource": { - "type": "loki", - "uid": "cdxkxyq1xjmyoc" - }, - "direction": "backward", - "editorMode": "code", - "expr": "{affordance=\"action\",affordanceName=\"int-string\",messageType=\"actionOutput\",op=\"invokeaction\",thing=\"http-data-schema-thing\"}", - "queryType": "range", - "refId": "A" - } - ], - "title": "action \"int-string\" Outputs", - "transformations": [ - { - "id": "extractFields", + "default": false, + "type": "loki", + "uid": "cdxkxyq1xjmyoc" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "cellOptions": { + "type": "auto" + }, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 6, + "x": 6, + "y": 40 + }, + "id": 18, "options": { - "format": "json", - "jsonPaths": [ + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": ["sum"], + "show": false + }, + "frameIndex": 0, + "showHeader": true + }, + "pluginVersion": "11.2.0", + "targets": [ { - "alias": "Value", - "path": "message" + "datasource": { + "type": "loki", + "uid": "cdxkxyq1xjmyoc" + }, + "direction": "backward", + "editorMode": "code", + "expr": "{affordance=\"action\",affordanceName=\"int-string\",messageType=\"actionOutput\",op=\"invokeaction\",thing=\"http-data-schema-thing\"}", + "queryType": "range", + "refId": "A" } - ], - "keepTime": true, - "replace": true, - "source": "Line" - } - } - ], - "type": "table" - }, - { - "datasource": { - "default": false, - "type": "loki", - "uid": "cdxkxyq1xjmyoc" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "align": "auto", - "cellOptions": { - "type": "auto" - }, - "inspect": false - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, + ], + "title": "action \"int-string\" Outputs", + "transformations": [ { - "color": "red", - "value": 80 + "id": "extractFields", + "options": { + "format": "json", + "jsonPaths": [ + { + "alias": "Value", + "path": "message" + } + ], + "keepTime": true, + "replace": true, + "source": "Line" + } } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 6, - "x": 0, - "y": 48 - }, - "id": 19, - "options": { - "cellHeight": "sm", - "footer": { - "countRows": false, - "fields": "", - "reducer": [ - "sum" ], - "show": false - }, - "frameIndex": 0, - "showHeader": true + "type": "table" }, - "pluginVersion": "11.2.0", - "targets": [ - { + { "datasource": { - "type": "loki", - "uid": "cdxkxyq1xjmyoc" - }, - "direction": "backward", - "editorMode": "code", - "expr": "{affordance=\"action\",affordanceName=\"void-obj\",messageType=\"actionInput\",op=\"invokeaction\",thing=\"http-data-schema-thing\"}", - "queryType": "range", - "refId": "A" - } - ], - "title": "action \"void-obj\" Inputs", - "transformations": [ - { - "id": "extractFields", + "default": false, + "type": "loki", + "uid": "cdxkxyq1xjmyoc" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "cellOptions": { + "type": "auto" + }, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 6, + "x": 0, + "y": 48 + }, + "id": 19, "options": { - "format": "json", - "jsonPaths": [ + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": ["sum"], + "show": false + }, + "frameIndex": 0, + "showHeader": true + }, + "pluginVersion": "11.2.0", + "targets": [ { - "alias": "Value", - "path": "message" + "datasource": { + "type": "loki", + "uid": "cdxkxyq1xjmyoc" + }, + "direction": "backward", + "editorMode": "code", + "expr": "{affordance=\"action\",affordanceName=\"void-obj\",messageType=\"actionInput\",op=\"invokeaction\",thing=\"http-data-schema-thing\"}", + "queryType": "range", + "refId": "A" } - ], - "keepTime": true, - "replace": true, - "source": "Line" - } - } - ], - "type": "table" - }, - { - "datasource": { - "default": false, - "type": "loki", - "uid": "cdxkxyq1xjmyoc" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "align": "auto", - "cellOptions": { - "type": "auto" - }, - "inspect": false - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, + ], + "title": "action \"void-obj\" Inputs", + "transformations": [ { - "color": "red", - "value": 80 + "id": "extractFields", + "options": { + "format": "json", + "jsonPaths": [ + { + "alias": "Value", + "path": "message" + } + ], + "keepTime": true, + "replace": true, + "source": "Line" + } } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 6, - "x": 6, - "y": 48 - }, - "id": 20, - "options": { - "cellHeight": "sm", - "footer": { - "countRows": false, - "fields": "", - "reducer": [ - "sum" ], - "show": false - }, - "frameIndex": 0, - "showHeader": true + "type": "table" }, - "pluginVersion": "11.2.0", - "targets": [ - { + { "datasource": { - "type": "loki", - "uid": "cdxkxyq1xjmyoc" - }, - "direction": "backward", - "editorMode": "code", - "expr": "{affordance=\"action\",affordanceName=\"void-obj\",messageType=\"actionOutput\",op=\"invokeaction\",thing=\"http-data-schema-thing\"}", - "queryType": "range", - "refId": "A" - } - ], - "title": "action \"void-obj\" Outputs", - "transformations": [ - { - "id": "extractFields", + "default": false, + "type": "loki", + "uid": "cdxkxyq1xjmyoc" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "cellOptions": { + "type": "auto" + }, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 6, + "x": 6, + "y": 48 + }, + "id": 20, "options": { - "format": "json", - "jsonPaths": [ + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": ["sum"], + "show": false + }, + "frameIndex": 0, + "showHeader": true + }, + "pluginVersion": "11.2.0", + "targets": [ { - "alias": "Value", - "path": "message" + "datasource": { + "type": "loki", + "uid": "cdxkxyq1xjmyoc" + }, + "direction": "backward", + "editorMode": "code", + "expr": "{affordance=\"action\",affordanceName=\"void-obj\",messageType=\"actionOutput\",op=\"invokeaction\",thing=\"http-data-schema-thing\"}", + "queryType": "range", + "refId": "A" } - ], - "keepTime": true, - "replace": true, - "source": "Line" - } - } - ], - "type": "table" - }, - { - "datasource": { - "default": false, - "type": "loki", - "uid": "cdxkxyq1xjmyoc" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "align": "auto", - "cellOptions": { - "type": "auto" - }, - "inspect": false - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, + ], + "title": "action \"void-obj\" Outputs", + "transformations": [ { - "color": "red", - "value": 80 + "id": "extractFields", + "options": { + "format": "json", + "jsonPaths": [ + { + "alias": "Value", + "path": "message" + } + ], + "keepTime": true, + "replace": true, + "source": "Line" + } } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 6, - "x": 0, - "y": 56 - }, - "id": 21, - "options": { - "cellHeight": "sm", - "footer": { - "countRows": false, - "fields": "", - "reducer": [ - "sum" ], - "show": false - }, - "frameIndex": 0, - "showHeader": true + "type": "table" }, - "pluginVersion": "11.2.0", - "targets": [ - { + { "datasource": { - "type": "loki", - "uid": "cdxkxyq1xjmyoc" - }, - "direction": "backward", - "editorMode": "code", - "expr": "{affordance=\"action\",affordanceName=\"obj-void\",messageType=\"actionInput\",op=\"invokeaction\",thing=\"http-data-schema-thing\"}", - "queryType": "range", - "refId": "A" - } - ], - "title": "action \"obj-void\" Inputs", - "transformations": [ - { - "id": "extractFields", + "default": false, + "type": "loki", + "uid": "cdxkxyq1xjmyoc" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "cellOptions": { + "type": "auto" + }, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 6, + "x": 0, + "y": 56 + }, + "id": 21, "options": { - "format": "json", - "jsonPaths": [ + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": ["sum"], + "show": false + }, + "frameIndex": 0, + "showHeader": true + }, + "pluginVersion": "11.2.0", + "targets": [ { - "alias": "Value", - "path": "message" + "datasource": { + "type": "loki", + "uid": "cdxkxyq1xjmyoc" + }, + "direction": "backward", + "editorMode": "code", + "expr": "{affordance=\"action\",affordanceName=\"obj-void\",messageType=\"actionInput\",op=\"invokeaction\",thing=\"http-data-schema-thing\"}", + "queryType": "range", + "refId": "A" } - ], - "keepTime": true, - "replace": true, - "source": "Line" - } - } - ], - "type": "table" - }, - { - "datasource": { - "default": false, - "type": "loki", - "uid": "cdxkxyq1xjmyoc" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "align": "auto", - "cellOptions": { - "type": "auto" - }, - "inspect": false - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, + ], + "title": "action \"obj-void\" Inputs", + "transformations": [ { - "color": "red", - "value": 80 + "id": "extractFields", + "options": { + "format": "json", + "jsonPaths": [ + { + "alias": "Value", + "path": "message" + } + ], + "keepTime": true, + "replace": true, + "source": "Line" + } } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 6, - "x": 6, - "y": 56 - }, - "id": 22, - "options": { - "cellHeight": "sm", - "footer": { - "countRows": false, - "fields": "", - "reducer": [ - "sum" ], - "show": false - }, - "frameIndex": 0, - "showHeader": true + "type": "table" }, - "pluginVersion": "11.2.0", - "targets": [ - { + { "datasource": { - "type": "loki", - "uid": "cdxkxyq1xjmyoc" - }, - "direction": "backward", - "editorMode": "code", - "expr": "{affordance=\"action\",affordanceName=\"obj-void\",messageType=\"actionOutput\",op=\"invokeaction\",thing=\"http-data-schema-thing\"}", - "queryType": "range", - "refId": "A" - } - ], - "title": "action \"obj-void\" Outputs", - "transformations": [ - { - "id": "extractFields", + "default": false, + "type": "loki", + "uid": "cdxkxyq1xjmyoc" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "cellOptions": { + "type": "auto" + }, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 6, + "x": 6, + "y": 56 + }, + "id": 22, "options": { - "format": "json", - "jsonPaths": [ + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": ["sum"], + "show": false + }, + "frameIndex": 0, + "showHeader": true + }, + "pluginVersion": "11.2.0", + "targets": [ { - "alias": "Value", - "path": "message" + "datasource": { + "type": "loki", + "uid": "cdxkxyq1xjmyoc" + }, + "direction": "backward", + "editorMode": "code", + "expr": "{affordance=\"action\",affordanceName=\"obj-void\",messageType=\"actionOutput\",op=\"invokeaction\",thing=\"http-data-schema-thing\"}", + "queryType": "range", + "refId": "A" } - ], - "keepTime": true, - "replace": true, - "source": "Line" - } - } - ], - "type": "table" - } + ], + "title": "action \"obj-void\" Outputs", + "transformations": [ + { + "id": "extractFields", + "options": { + "format": "json", + "jsonPaths": [ + { + "alias": "Value", + "path": "message" + } + ], + "keepTime": true, + "replace": true, + "source": "Line" + } + } + ], + "type": "table" + } ], "refresh": "10s", "schemaVersion": 39, "tags": [], "templating": { - "list": [] + "list": [] }, "time": { - "from": "now-3h", - "to": "now" + "from": "now-3h", + "to": "now" }, "timepicker": {}, "timezone": "browser", @@ -2036,4 +1994,4 @@ "uid": "adyul1byyo000b", "version": 1, "weekStart": "" - } \ No newline at end of file +} diff --git a/conf/grafana/dashboards/http-express-calculator-simple-dashboard.json b/conf/grafana/dashboards/http-express-calculator-simple-dashboard.json index 8fcb77e..8d6ec98 100644 --- a/conf/grafana/dashboards/http-express-calculator-simple-dashboard.json +++ b/conf/grafana/dashboards/http-express-calculator-simple-dashboard.json @@ -1,1039 +1,1019 @@ { - "annotations": { - "list": [ - { - "builtIn": 1, - "datasource": { - "type": "grafana", - "uid": "-- Grafana --" - }, - "enable": true, - "hide": true, - "iconColor": "rgba(0, 211, 255, 1)", - "name": "Annotations & Alerts", - "type": "dashboard" - } - ] - }, - "editable": true, - "fiscalYearStartMonth": 0, - "graphTooltip": 0, - "links": [], - "panels": [ - { - "datasource": { - "default": true, - "type": "prometheus", - "uid": "adwznwwq8yupse" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisBorderShow": true, - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "barWidthFactor": 0.5, - "drawStyle": "bars", - "fillOpacity": 100, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": false, - "lineInterpolation": "smooth", - "lineWidth": 2, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 0 - }, - "id": 1, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "right", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "adwznwwq8yupse" - }, - "disableTextWrap": false, - "editorMode": "builder", - "expr": "changes(traefik_service_requests_total{protocol=\"http\", service=\"http-express-calculator-simple-test-things@docker\"}[1m])", - "fullMetaSearch": false, - "includeNullMetadata": true, - "instant": false, - "legendFormat": "Status Code:{{code}}", - "range": true, - "refId": "A", - "useBackend": false - } - ], - "title": "Requests", - "type": "timeseries" + ] }, - { - "datasource": { - "default": false, - "type": "loki", - "uid": "cdxkxyq1xjmyoc" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "fieldMinMax": false, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - } - ] - }, - "unit": "none" - }, - "overrides": [] - }, - "gridPos": { - "h": 4, - "w": 4, - "x": 12, - "y": 0 - }, - "id": 2, - "options": { - "colorMode": "value", - "graphMode": "none", - "justifyMode": "auto", - "orientation": "auto", - "percentChangeColorMode": "standard", - "reduceOptions": { - "calcs": [ - "last" - ], - "fields": "/.*/", - "limit": 5, - "values": false - }, - "showPercentChange": false, - "text": {}, - "textMode": "auto", - "wideLayout": true - }, - "pluginVersion": "11.2.0", - "targets": [ + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "links": [], + "panels": [ { - "datasource": { - "type": "loki", - "uid": "cdxkxyq1xjmyoc" - }, - "direction": "forward", - "editorMode": "code", - "expr": "{affordance=\"property\", affordanceName=\"result\",thing=\"http-express-calculator-simple\", messageType=\"updateProperty\"}", - "queryType": "range", - "refId": "A" - } - ], - "title": "property \"result\"", - "transformations": [ - { - "id": "extractFields", - "options": { - "format": "json", - "jsonPaths": [ - { - "alias": "result", - "path": "message" - } + "datasource": { + "default": true, + "type": "prometheus", + "uid": "adwznwwq8yupse" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": true, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.5, + "drawStyle": "bars", + "fillOpacity": 100, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "smooth", + "lineWidth": 2, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 0 + }, + "id": 1, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "adwznwwq8yupse" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "changes(traefik_service_requests_total{protocol=\"http\", service=\"http-express-calculator-simple-test-things@docker\"}[1m])", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "Status Code:{{code}}", + "range": true, + "refId": "A", + "useBackend": false + } ], - "keepTime": true, - "replace": true, - "source": "Line" - } + "title": "Requests", + "type": "timeseries" }, { - "id": "convertFieldType", - "options": { - "conversions": [ - { - "destinationType": "number", - "targetField": "result" - } + "datasource": { + "default": false, + "type": "loki", + "uid": "cdxkxyq1xjmyoc" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "fieldMinMax": false, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 4, + "x": 12, + "y": 0 + }, + "id": 2, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": ["last"], + "fields": "/.*/", + "limit": 5, + "values": false + }, + "showPercentChange": false, + "text": {}, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.2.0", + "targets": [ + { + "datasource": { + "type": "loki", + "uid": "cdxkxyq1xjmyoc" + }, + "direction": "forward", + "editorMode": "code", + "expr": "{affordance=\"property\", affordanceName=\"result\",thing=\"http-express-calculator-simple\", messageType=\"updateProperty\"}", + "queryType": "range", + "refId": "A" + } ], - "fields": {} - } - } - ], - "type": "stat" - }, - { - "datasource": { - "default": false, - "type": "loki", - "uid": "cdxkxyq1xjmyoc" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "fieldMinMax": false, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - } - ] - }, - "unit": "none" - }, - "overrides": [] - }, - "gridPos": { - "h": 4, - "w": 4, - "x": 16, - "y": 0 - }, - "id": 12, - "options": { - "colorMode": "value", - "graphMode": "none", - "justifyMode": "auto", - "orientation": "auto", - "percentChangeColorMode": "standard", - "reduceOptions": { - "calcs": [ - "last" - ], - "fields": "/.*/", - "limit": 5, - "values": false - }, - "showPercentChange": false, - "text": {}, - "textMode": "auto", - "wideLayout": true - }, - "pluginVersion": "11.2.0", - "targets": [ - { - "datasource": { - "type": "loki", - "uid": "cdxkxyq1xjmyoc" - }, - "direction": "forward", - "editorMode": "code", - "expr": "{affordance=\"property\",affordanceName=\"lastChange\",thing=\"http-express-calculator-simple\", messageType=\"updateProperty\"}", - "queryType": "range", - "refId": "A" - } - ], - "title": "property \"lastChange\"", - "transformations": [ - { - "id": "extractFields", - "options": { - "format": "json", - "jsonPaths": [ - { - "alias": "lastChange", - "path": "message" - } + "title": "property \"result\"", + "transformations": [ + { + "id": "extractFields", + "options": { + "format": "json", + "jsonPaths": [ + { + "alias": "result", + "path": "message" + } + ], + "keepTime": true, + "replace": true, + "source": "Line" + } + }, + { + "id": "convertFieldType", + "options": { + "conversions": [ + { + "destinationType": "number", + "targetField": "result" + } + ], + "fields": {} + } + } ], - "keepTime": true, - "replace": true, - "source": "Line" - } + "type": "stat" }, { - "id": "convertFieldType", - "options": { - "conversions": [ - { - "destinationType": "string", - "targetField": "lastChange" - } + "datasource": { + "default": false, + "type": "loki", + "uid": "cdxkxyq1xjmyoc" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "fieldMinMax": false, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 4, + "x": 16, + "y": 0 + }, + "id": 12, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": ["last"], + "fields": "/.*/", + "limit": 5, + "values": false + }, + "showPercentChange": false, + "text": {}, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.2.0", + "targets": [ + { + "datasource": { + "type": "loki", + "uid": "cdxkxyq1xjmyoc" + }, + "direction": "forward", + "editorMode": "code", + "expr": "{affordance=\"property\",affordanceName=\"lastChange\",thing=\"http-express-calculator-simple\", messageType=\"updateProperty\"}", + "queryType": "range", + "refId": "A" + } ], - "fields": {} - } - } - ], - "type": "stat" - }, - { - "datasource": { - "default": false, - "type": "loki", - "uid": "cdxkxyq1xjmyoc" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 5, - "w": 3, - "x": 12, - "y": 4 - }, - "id": 3, - "options": { - "colorMode": "value", - "graphMode": "none", - "justifyMode": "auto", - "orientation": "horizontal", - "percentChangeColorMode": "standard", - "reduceOptions": { - "calcs": [ - "sum" - ], - "fields": "", - "values": false - }, - "showPercentChange": false, - "textMode": "value_and_name", - "wideLayout": true - }, - "pluginVersion": "11.2.0", - "targets": [ - { - "datasource": { - "type": "loki", - "uid": "cdxkxyq1xjmyoc" - }, - "editorMode": "code", - "expr": "count_over_time({affordance=\"action\", thing=\"http-express-calculator-simple\", op=\"invokeaction\", messageType=\"\"}[$__auto])", - "legendFormat": "{{affordanceName}}", - "queryType": "range", - "refId": "A", - "step": "" - } - ], - "title": "Action Invocation Count", - "type": "stat" - }, - { - "datasource": { - "default": false, - "type": "loki", - "uid": "cdxkxyq1xjmyoc" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 6, - "w": 3, - "x": 15, - "y": 4 - }, - "id": 4, - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "percentChangeColorMode": "standard", - "reduceOptions": { - "calcs": [ - "last" - ], - "fields": "", - "values": false - }, - "showPercentChange": false, - "textMode": "auto", - "wideLayout": true - }, - "pluginVersion": "11.2.0", - "targets": [ - { - "datasource": { - "type": "loki", - "uid": "cdxkxyq1xjmyoc" - }, - "direction": "forward", - "editorMode": "code", - "expr": "{affordance=\"event\",affordanceName=\"update\",messageType=\"subscriptionCount\",thing=\"http-express-calculator-simple\"}", - "legendFormat": "Subscribe {{affordanceName}}", - "queryType": "range", - "refId": "A", - "step": "" - } - ], - "title": "event \"update\" Subscription Count", - "transformations": [ - { - "id": "extractFields", - "options": { - "format": "json", - "jsonPaths": [ - { - "alias": "Value", - "path": "message" - } + "title": "property \"lastChange\"", + "transformations": [ + { + "id": "extractFields", + "options": { + "format": "json", + "jsonPaths": [ + { + "alias": "lastChange", + "path": "message" + } + ], + "keepTime": true, + "replace": true, + "source": "Line" + } + }, + { + "id": "convertFieldType", + "options": { + "conversions": [ + { + "destinationType": "string", + "targetField": "lastChange" + } + ], + "fields": {} + } + } ], - "source": "Line" - } + "type": "stat" }, { - "id": "convertFieldType", - "options": { - "conversions": [ - { - "destinationType": "number", - "targetField": "Value" - } + "datasource": { + "default": false, + "type": "loki", + "uid": "cdxkxyq1xjmyoc" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 3, + "x": 12, + "y": 4 + }, + "id": 3, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": ["sum"], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "value_and_name", + "wideLayout": true + }, + "pluginVersion": "11.2.0", + "targets": [ + { + "datasource": { + "type": "loki", + "uid": "cdxkxyq1xjmyoc" + }, + "editorMode": "code", + "expr": "count_over_time({affordance=\"action\", thing=\"http-express-calculator-simple\", op=\"invokeaction\", messageType=\"\"}[$__auto])", + "legendFormat": "{{affordanceName}}", + "queryType": "range", + "refId": "A", + "step": "" + } ], - "fields": {} - } - } - ], - "type": "stat" - }, - { - "datasource": { - "default": false, - "type": "loki", - "uid": "cdxkxyq1xjmyoc" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 6, - "w": 3, - "x": 18, - "y": 4 - }, - "id": 7, - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "percentChangeColorMode": "standard", - "reduceOptions": { - "calcs": [ - "last" - ], - "fields": "", - "values": false + "title": "Action Invocation Count", + "type": "stat" }, - "showPercentChange": false, - "textMode": "auto", - "wideLayout": true - }, - "pluginVersion": "11.2.0", - "targets": [ - { - "datasource": { - "type": "loki", - "uid": "cdxkxyq1xjmyoc" - }, - "direction": "forward", - "editorMode": "code", - "expr": "{affordance=\"property\",affordanceName=\"result\",thing=\"http-express-calculator-simple\", messageType=\"observeCount\"}", - "legendFormat": "Observe {{affordanceName}}", - "queryType": "range", - "refId": "A", - "step": "" - } - ], - "title": "propert \"result\" Observe Count", - "transformations": [ { - "id": "extractFields", - "options": { - "format": "json", - "jsonPaths": [ - { - "alias": "Value", - "path": "message" - } + "datasource": { + "default": false, + "type": "loki", + "uid": "cdxkxyq1xjmyoc" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 3, + "x": 15, + "y": 4 + }, + "id": 4, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": ["last"], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.2.0", + "targets": [ + { + "datasource": { + "type": "loki", + "uid": "cdxkxyq1xjmyoc" + }, + "direction": "forward", + "editorMode": "code", + "expr": "{affordance=\"event\",affordanceName=\"update\",messageType=\"subscriptionCount\",thing=\"http-express-calculator-simple\"}", + "legendFormat": "Subscribe {{affordanceName}}", + "queryType": "range", + "refId": "A", + "step": "" + } ], - "source": "Line" - } - }, - { - "id": "convertFieldType", - "options": { - "conversions": [ - { - "destinationType": "number", - "targetField": "Value" - } + "title": "event \"update\" Subscription Count", + "transformations": [ + { + "id": "extractFields", + "options": { + "format": "json", + "jsonPaths": [ + { + "alias": "Value", + "path": "message" + } + ], + "source": "Line" + } + }, + { + "id": "convertFieldType", + "options": { + "conversions": [ + { + "destinationType": "number", + "targetField": "Value" + } + ], + "fields": {} + } + } ], - "fields": {} - } - } - ], - "type": "stat" - }, - { - "datasource": { - "default": false, - "type": "loki", - "uid": "cdxkxyq1xjmyoc" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "align": "auto", - "cellOptions": { - "type": "auto" - }, - "inspect": false - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } + "type": "stat" }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 6, - "x": 0, - "y": 8 - }, - "id": 5, - "options": { - "cellHeight": "sm", - "footer": { - "countRows": false, - "fields": "", - "reducer": [ - "sum" - ], - "show": false - }, - "frameIndex": 0, - "showHeader": true - }, - "pluginVersion": "11.2.0", - "targets": [ - { - "datasource": { - "type": "loki", - "uid": "cdxkxyq1xjmyoc" - }, - "direction": "backward", - "editorMode": "code", - "expr": "{affordance=\"action\",affordanceName=\"add\",messageType=\"actionInput\",op=\"invokeaction\",thing=\"http-express-calculator-simple\"}", - "queryType": "range", - "refId": "A" - } - ], - "title": "action \"add\" Inputs", - "transformations": [ { - "id": "extractFields", - "options": { - "format": "json", - "jsonPaths": [ - { - "alias": "Value", - "path": "message" - } - ], - "keepTime": true, - "replace": true, - "source": "Line" - } - } - ], - "type": "table" - }, - { - "datasource": { - "default": false, - "type": "loki", - "uid": "cdxkxyq1xjmyoc" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "align": "auto", - "cellOptions": { - "type": "auto" + "datasource": { + "default": false, + "type": "loki", + "uid": "cdxkxyq1xjmyoc" }, - "inspect": false - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 6, - "x": 6, - "y": 8 - }, - "id": 9, - "options": { - "cellHeight": "sm", - "footer": { - "countRows": false, - "fields": "", - "reducer": [ - "sum" - ], - "show": false + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 3, + "x": 18, + "y": 4 + }, + "id": 7, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": ["last"], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.2.0", + "targets": [ + { + "datasource": { + "type": "loki", + "uid": "cdxkxyq1xjmyoc" + }, + "direction": "forward", + "editorMode": "code", + "expr": "{affordance=\"property\",affordanceName=\"result\",thing=\"http-express-calculator-simple\", messageType=\"observeCount\"}", + "legendFormat": "Observe {{affordanceName}}", + "queryType": "range", + "refId": "A", + "step": "" + } + ], + "title": "propert \"result\" Observe Count", + "transformations": [ + { + "id": "extractFields", + "options": { + "format": "json", + "jsonPaths": [ + { + "alias": "Value", + "path": "message" + } + ], + "source": "Line" + } + }, + { + "id": "convertFieldType", + "options": { + "conversions": [ + { + "destinationType": "number", + "targetField": "Value" + } + ], + "fields": {} + } + } + ], + "type": "stat" }, - "frameIndex": 0, - "showHeader": true - }, - "pluginVersion": "11.2.0", - "targets": [ - { - "datasource": { - "type": "loki", - "uid": "cdxkxyq1xjmyoc" - }, - "direction": "backward", - "editorMode": "code", - "expr": "{affordance=\"action\",affordanceName=\"add\",messageType=\"actionOutput\",op=\"invokeaction\",thing=\"http-express-calculator-simple\"}", - "queryType": "range", - "refId": "A" - } - ], - "title": "action \"add\" Outputs", - "transformations": [ { - "id": "extractFields", - "options": { - "format": "json", - "jsonPaths": [ - { - "alias": "Value", - "path": "message" - } + "datasource": { + "default": false, + "type": "loki", + "uid": "cdxkxyq1xjmyoc" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "cellOptions": { + "type": "auto" + }, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 6, + "x": 0, + "y": 8 + }, + "id": 5, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": ["sum"], + "show": false + }, + "frameIndex": 0, + "showHeader": true + }, + "pluginVersion": "11.2.0", + "targets": [ + { + "datasource": { + "type": "loki", + "uid": "cdxkxyq1xjmyoc" + }, + "direction": "backward", + "editorMode": "code", + "expr": "{affordance=\"action\",affordanceName=\"add\",messageType=\"actionInput\",op=\"invokeaction\",thing=\"http-express-calculator-simple\"}", + "queryType": "range", + "refId": "A" + } ], - "keepTime": true, - "replace": true, - "source": "Line" - } - } - ], - "type": "table" - }, - { - "datasource": { - "default": false, - "type": "loki", - "uid": "cdxkxyq1xjmyoc" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 6, - "w": 3, - "x": 18, - "y": 10 - }, - "id": 8, - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "percentChangeColorMode": "standard", - "reduceOptions": { - "calcs": [ - "last" - ], - "fields": "", - "values": false + "title": "action \"add\" Inputs", + "transformations": [ + { + "id": "extractFields", + "options": { + "format": "json", + "jsonPaths": [ + { + "alias": "Value", + "path": "message" + } + ], + "keepTime": true, + "replace": true, + "source": "Line" + } + } + ], + "type": "table" }, - "showPercentChange": false, - "textMode": "auto", - "wideLayout": true - }, - "pluginVersion": "11.2.0", - "targets": [ - { - "datasource": { - "type": "loki", - "uid": "cdxkxyq1xjmyoc" - }, - "direction": "forward", - "editorMode": "code", - "expr": "{affordance=\"property\",affordanceName=\"lastChange\",thing=\"http-express-calculator-simple\", messageType=\"observeCount\"}", - "legendFormat": "Observe {{affordanceName}}", - "queryType": "range", - "refId": "A", - "step": "" - } - ], - "title": "property \"lastChange\" Observe Count", - "transformations": [ { - "id": "extractFields", - "options": { - "format": "json", - "jsonPaths": [ - { - "alias": "Value", - "path": "message" - } + "datasource": { + "default": false, + "type": "loki", + "uid": "cdxkxyq1xjmyoc" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "cellOptions": { + "type": "auto" + }, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 6, + "x": 6, + "y": 8 + }, + "id": 9, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": ["sum"], + "show": false + }, + "frameIndex": 0, + "showHeader": true + }, + "pluginVersion": "11.2.0", + "targets": [ + { + "datasource": { + "type": "loki", + "uid": "cdxkxyq1xjmyoc" + }, + "direction": "backward", + "editorMode": "code", + "expr": "{affordance=\"action\",affordanceName=\"add\",messageType=\"actionOutput\",op=\"invokeaction\",thing=\"http-express-calculator-simple\"}", + "queryType": "range", + "refId": "A" + } ], - "keepTime": false, - "replace": true, - "source": "Line" - } + "title": "action \"add\" Outputs", + "transformations": [ + { + "id": "extractFields", + "options": { + "format": "json", + "jsonPaths": [ + { + "alias": "Value", + "path": "message" + } + ], + "keepTime": true, + "replace": true, + "source": "Line" + } + } + ], + "type": "table" }, { - "id": "convertFieldType", - "options": {} - } - ], - "type": "stat" - }, - { - "datasource": { - "default": false, - "type": "loki", - "uid": "cdxkxyq1xjmyoc" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "align": "auto", - "cellOptions": { - "type": "auto" + "datasource": { + "default": false, + "type": "loki", + "uid": "cdxkxyq1xjmyoc" }, - "inspect": false - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 6, - "x": 0, - "y": 16 - }, - "id": 10, - "options": { - "cellHeight": "sm", - "footer": { - "countRows": false, - "fields": "", - "reducer": [ - "sum" - ], - "show": false + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 3, + "x": 18, + "y": 10 + }, + "id": 8, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": ["last"], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.2.0", + "targets": [ + { + "datasource": { + "type": "loki", + "uid": "cdxkxyq1xjmyoc" + }, + "direction": "forward", + "editorMode": "code", + "expr": "{affordance=\"property\",affordanceName=\"lastChange\",thing=\"http-express-calculator-simple\", messageType=\"observeCount\"}", + "legendFormat": "Observe {{affordanceName}}", + "queryType": "range", + "refId": "A", + "step": "" + } + ], + "title": "property \"lastChange\" Observe Count", + "transformations": [ + { + "id": "extractFields", + "options": { + "format": "json", + "jsonPaths": [ + { + "alias": "Value", + "path": "message" + } + ], + "keepTime": false, + "replace": true, + "source": "Line" + } + }, + { + "id": "convertFieldType", + "options": {} + } + ], + "type": "stat" }, - "frameIndex": 0, - "showHeader": true - }, - "pluginVersion": "11.2.0", - "targets": [ - { - "datasource": { - "type": "loki", - "uid": "cdxkxyq1xjmyoc" - }, - "direction": "backward", - "editorMode": "code", - "expr": "{affordance=\"action\",affordanceName=\"subtract\",messageType=\"actionInput\",op=\"invokeaction\",thing=\"http-express-calculator-simple\"}", - "queryType": "range", - "refId": "A" - } - ], - "title": "action \"subtract\" Inputs", - "transformations": [ { - "id": "extractFields", - "options": { - "format": "json", - "jsonPaths": [ - { - "alias": "Value", - "path": "message" - } - ], - "keepTime": true, - "replace": true, - "source": "Line" - } - } - ], - "type": "table" - }, - { - "datasource": { - "default": false, - "type": "loki", - "uid": "cdxkxyq1xjmyoc" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "align": "auto", - "cellOptions": { - "type": "auto" + "datasource": { + "default": false, + "type": "loki", + "uid": "cdxkxyq1xjmyoc" }, - "inspect": false - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 6, - "x": 6, - "y": 16 - }, - "id": 11, - "options": { - "cellHeight": "sm", - "footer": { - "countRows": false, - "fields": "", - "reducer": [ - "sum" - ], - "show": false + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "cellOptions": { + "type": "auto" + }, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 6, + "x": 0, + "y": 16 + }, + "id": 10, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": ["sum"], + "show": false + }, + "frameIndex": 0, + "showHeader": true + }, + "pluginVersion": "11.2.0", + "targets": [ + { + "datasource": { + "type": "loki", + "uid": "cdxkxyq1xjmyoc" + }, + "direction": "backward", + "editorMode": "code", + "expr": "{affordance=\"action\",affordanceName=\"subtract\",messageType=\"actionInput\",op=\"invokeaction\",thing=\"http-express-calculator-simple\"}", + "queryType": "range", + "refId": "A" + } + ], + "title": "action \"subtract\" Inputs", + "transformations": [ + { + "id": "extractFields", + "options": { + "format": "json", + "jsonPaths": [ + { + "alias": "Value", + "path": "message" + } + ], + "keepTime": true, + "replace": true, + "source": "Line" + } + } + ], + "type": "table" }, - "frameIndex": 0, - "showHeader": true - }, - "pluginVersion": "11.2.0", - "targets": [ - { - "datasource": { - "type": "loki", - "uid": "cdxkxyq1xjmyoc" - }, - "direction": "backward", - "editorMode": "code", - "expr": "{affordance=\"action\",affordanceName=\"subtract\",messageType=\"actionOutput\",op=\"invokeaction\",thing=\"http-express-calculator-simple\"}", - "queryType": "range", - "refId": "A" - } - ], - "title": "action \"subtract\" Outputs", - "transformations": [ { - "id": "extractFields", - "options": { - "format": "json", - "jsonPaths": [ - { - "alias": "Value", - "path": "message" - } + "datasource": { + "default": false, + "type": "loki", + "uid": "cdxkxyq1xjmyoc" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "cellOptions": { + "type": "auto" + }, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 6, + "x": 6, + "y": 16 + }, + "id": 11, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": ["sum"], + "show": false + }, + "frameIndex": 0, + "showHeader": true + }, + "pluginVersion": "11.2.0", + "targets": [ + { + "datasource": { + "type": "loki", + "uid": "cdxkxyq1xjmyoc" + }, + "direction": "backward", + "editorMode": "code", + "expr": "{affordance=\"action\",affordanceName=\"subtract\",messageType=\"actionOutput\",op=\"invokeaction\",thing=\"http-express-calculator-simple\"}", + "queryType": "range", + "refId": "A" + } ], - "keepTime": true, - "replace": true, - "source": "Line" - } + "title": "action \"subtract\" Outputs", + "transformations": [ + { + "id": "extractFields", + "options": { + "format": "json", + "jsonPaths": [ + { + "alias": "Value", + "path": "message" + } + ], + "keepTime": true, + "replace": true, + "source": "Line" + } + } + ], + "type": "table" } - ], - "type": "table" - } - ], - "refresh": "10s", - "schemaVersion": 39, - "tags": [], - "templating": { - "list": [] - }, - "time": { - "from": "now-30m", - "to": "now" - }, - "timepicker": {}, - "timezone": "browser", - "title": "HTTP Express Calculator Simple", - "uid": "bdwzocnv714hsd", - "version": 7, - "weekStart": "" -} \ No newline at end of file + ], + "refresh": "10s", + "schemaVersion": 39, + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-30m", + "to": "now" + }, + "timepicker": {}, + "timezone": "browser", + "title": "HTTP Express Calculator Simple", + "uid": "bdwzocnv714hsd", + "version": 7, + "weekStart": "" +} diff --git a/conf/grafana/dashboards/mqtt-calculator-dashboard.json b/conf/grafana/dashboards/mqtt-calculator-dashboard.json index e757a04..0d98d95 100644 --- a/conf/grafana/dashboards/mqtt-calculator-dashboard.json +++ b/conf/grafana/dashboards/mqtt-calculator-dashboard.json @@ -1,19 +1,19 @@ { "annotations": { - "list": [ - { - "builtIn": 1, - "datasource": { - "type": "grafana", - "uid": "-- Grafana --" - }, - "enable": true, - "hide": true, - "iconColor": "rgba(0, 211, 255, 1)", - "name": "Annotations & Alerts", - "type": "dashboard" - } - ] + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] }, "editable": true, "fiscalYearStartMonth": 0, @@ -21,634 +21,620 @@ "id": 10, "links": [], "panels": [ - { - "datasource": { - "default": false, - "type": "loki", - "uid": "cdxkxyq1xjmyoc" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" + { + "datasource": { + "default": false, + "type": "loki", + "uid": "cdxkxyq1xjmyoc" }, - "fieldMinMax": false, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - } - ] + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "fieldMinMax": false, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "none" + }, + "overrides": [] }, - "unit": "none" - }, - "overrides": [] - }, - "gridPos": { - "h": 4, - "w": 4, - "x": 0, - "y": 0 - }, - "id": 1, - "options": { - "colorMode": "value", - "graphMode": "none", - "justifyMode": "auto", - "orientation": "auto", - "percentChangeColorMode": "standard", - "reduceOptions": { - "calcs": [ - "last" - ], - "fields": "/.*/", - "limit": 5, - "values": false - }, - "showPercentChange": false, - "text": {}, - "textMode": "auto", - "wideLayout": true - }, - "pluginVersion": "11.2.0", - "targets": [ - { - "datasource": { - "type": "loki", - "uid": "cdxkxyq1xjmyoc" + "gridPos": { + "h": 4, + "w": 4, + "x": 0, + "y": 0 }, - "direction": "forward", - "editorMode": "code", - "expr": "{affordance=\"property\", affordanceName=\"result\",thing=\"mqtt-calculator\", messageType=\"updateProperty\"}", - "queryType": "range", - "refId": "A" - } - ], - "title": "property \"result\"", - "transformations": [ - { - "id": "extractFields", + "id": 1, "options": { - "format": "json", - "jsonPaths": [ + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": ["last"], + "fields": "/.*/", + "limit": 5, + "values": false + }, + "showPercentChange": false, + "text": {}, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.2.0", + "targets": [ { - "alias": "result", - "path": "message" + "datasource": { + "type": "loki", + "uid": "cdxkxyq1xjmyoc" + }, + "direction": "forward", + "editorMode": "code", + "expr": "{affordance=\"property\", affordanceName=\"result\",thing=\"mqtt-calculator\", messageType=\"updateProperty\"}", + "queryType": "range", + "refId": "A" } - ], - "keepTime": true, - "replace": true, - "source": "Line" - } - }, - { - "id": "convertFieldType", - "options": { - "conversions": [ + ], + "title": "property \"result\"", + "transformations": [ { - "destinationType": "number", - "targetField": "result" - } - ], - "fields": {} - } - } - ], - "type": "stat" - }, - { - "datasource": { - "default": false, - "type": "loki", - "uid": "cdxkxyq1xjmyoc" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "fieldMinMax": false, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ + "id": "extractFields", + "options": { + "format": "json", + "jsonPaths": [ + { + "alias": "result", + "path": "message" + } + ], + "keepTime": true, + "replace": true, + "source": "Line" + } + }, { - "color": "green", - "value": null + "id": "convertFieldType", + "options": { + "conversions": [ + { + "destinationType": "number", + "targetField": "result" + } + ], + "fields": {} + } } - ] - }, - "unit": "none" - }, - "overrides": [] - }, - "gridPos": { - "h": 4, - "w": 4, - "x": 4, - "y": 0 - }, - "id": 2, - "options": { - "colorMode": "value", - "graphMode": "none", - "justifyMode": "auto", - "orientation": "auto", - "percentChangeColorMode": "standard", - "reduceOptions": { - "calcs": [ - "last" ], - "fields": "/.*/", - "limit": 5, - "values": false - }, - "showPercentChange": false, - "text": {}, - "textMode": "auto", - "wideLayout": true + "type": "stat" }, - "pluginVersion": "11.2.0", - "targets": [ - { + { "datasource": { - "type": "loki", - "uid": "cdxkxyq1xjmyoc" + "default": false, + "type": "loki", + "uid": "cdxkxyq1xjmyoc" }, - "direction": "forward", - "editorMode": "code", - "expr": "{affordance=\"property\",affordanceName=\"lastChange\",thing=\"mqtt-calculator\", messageType=\"updateProperty\"}", - "queryType": "range", - "refId": "A" - } - ], - "title": "property \"lastChange\"", - "transformations": [ - { - "id": "extractFields", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "fieldMinMax": false, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 4, + "x": 4, + "y": 0 + }, + "id": 2, "options": { - "format": "json", - "jsonPaths": [ + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": ["last"], + "fields": "/.*/", + "limit": 5, + "values": false + }, + "showPercentChange": false, + "text": {}, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.2.0", + "targets": [ { - "alias": "lastChange", - "path": "message" + "datasource": { + "type": "loki", + "uid": "cdxkxyq1xjmyoc" + }, + "direction": "forward", + "editorMode": "code", + "expr": "{affordance=\"property\",affordanceName=\"lastChange\",thing=\"mqtt-calculator\", messageType=\"updateProperty\"}", + "queryType": "range", + "refId": "A" } - ], - "keepTime": true, - "replace": true, - "source": "Line" - } - }, - { - "id": "convertFieldType", - "options": { - "conversions": [ + ], + "title": "property \"lastChange\"", + "transformations": [ { - "destinationType": "string", - "targetField": "lastChange" - } - ], - "fields": {} - } - } - ], - "type": "stat" - }, - { - "datasource": { - "default": false, - "type": "loki", - "uid": "cdxkxyq1xjmyoc" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ + "id": "extractFields", + "options": { + "format": "json", + "jsonPaths": [ + { + "alias": "lastChange", + "path": "message" + } + ], + "keepTime": true, + "replace": true, + "source": "Line" + } + }, { - "color": "green", - "value": null + "id": "convertFieldType", + "options": { + "conversions": [ + { + "destinationType": "string", + "targetField": "lastChange" + } + ], + "fields": {} + } } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 4, - "w": 4, - "x": 8, - "y": 0 - }, - "id": 3, - "options": { - "colorMode": "value", - "graphMode": "none", - "justifyMode": "auto", - "orientation": "horizontal", - "percentChangeColorMode": "standard", - "reduceOptions": { - "calcs": [ - "sum" ], - "fields": "", - "values": false - }, - "showPercentChange": false, - "textMode": "value_and_name", - "wideLayout": true + "type": "stat" }, - "pluginVersion": "11.2.0", - "targets": [ - { + { "datasource": { - "type": "loki", - "uid": "cdxkxyq1xjmyoc" + "default": false, + "type": "loki", + "uid": "cdxkxyq1xjmyoc" }, - "editorMode": "code", - "expr": "count_over_time({affordance=\"action\", thing=\"mqtt-calculator\", op=\"invokeaction\", messageType=\"\"}[$__auto])", - "legendFormat": "{{affordanceName}}", - "queryType": "range", - "refId": "A", - "step": "" - } - ], - "title": "Action Invocation Count", - "type": "stat" - }, - { - "datasource": { - "default": false, - "type": "loki", - "uid": "cdxkxyq1xjmyoc" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [] }, - "custom": { - "align": "auto", - "cellOptions": { - "type": "auto" - }, - "inspect": false + "gridPos": { + "h": 4, + "w": 4, + "x": 8, + "y": 0 }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null + "id": 3, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": ["sum"], + "fields": "", + "values": false }, + "showPercentChange": false, + "textMode": "value_and_name", + "wideLayout": true + }, + "pluginVersion": "11.2.0", + "targets": [ { - "color": "red", - "value": 80 + "datasource": { + "type": "loki", + "uid": "cdxkxyq1xjmyoc" + }, + "editorMode": "code", + "expr": "count_over_time({affordance=\"action\", thing=\"mqtt-calculator\", op=\"invokeaction\", messageType=\"\"}[$__auto])", + "legendFormat": "{{affordanceName}}", + "queryType": "range", + "refId": "A", + "step": "" } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 6, - "x": 0, - "y": 4 - }, - "id": 4, - "options": { - "cellHeight": "sm", - "footer": { - "countRows": false, - "fields": "", - "reducer": [ - "sum" ], - "show": false - }, - "frameIndex": 0, - "showHeader": true + "title": "Action Invocation Count", + "type": "stat" }, - "pluginVersion": "11.2.0", - "targets": [ - { + { "datasource": { - "type": "loki", - "uid": "cdxkxyq1xjmyoc" + "default": false, + "type": "loki", + "uid": "cdxkxyq1xjmyoc" }, - "direction": "backward", - "editorMode": "code", - "expr": "{affordance=\"action\",affordanceName=\"add\",messageType=\"actionInput\",op=\"invokeaction\",thing=\"mqtt-calculator\"}", - "queryType": "range", - "refId": "A" - } - ], - "title": "action \"add\" Inputs", - "transformations": [ - { - "id": "extractFields", - "options": { - "format": "json", - "jsonPaths": [ - { - "alias": "Value", - "path": "message" - } - ], - "keepTime": true, - "replace": true, - "source": "Line" - } - } - ], - "type": "table" - }, - { - "datasource": { - "default": false, - "type": "loki", - "uid": "cdxkxyq1xjmyoc" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "cellOptions": { + "type": "auto" + }, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] }, - "custom": { - "align": "auto", - "cellOptions": { - "type": "auto" - }, - "inspect": false + "gridPos": { + "h": 8, + "w": 6, + "x": 0, + "y": 4 }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null + "id": 4, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": ["sum"], + "show": false }, + "frameIndex": 0, + "showHeader": true + }, + "pluginVersion": "11.2.0", + "targets": [ { - "color": "red", - "value": 80 + "datasource": { + "type": "loki", + "uid": "cdxkxyq1xjmyoc" + }, + "direction": "backward", + "editorMode": "code", + "expr": "{affordance=\"action\",affordanceName=\"add\",messageType=\"actionInput\",op=\"invokeaction\",thing=\"mqtt-calculator\"}", + "queryType": "range", + "refId": "A" } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 6, - "x": 6, - "y": 4 - }, - "id": 5, - "options": { - "cellHeight": "sm", - "footer": { - "countRows": false, - "fields": "", - "reducer": [ - "sum" ], - "show": false - }, - "frameIndex": 0, - "showHeader": true - }, - "pluginVersion": "11.2.0", - "targets": [ - { - "datasource": { - "type": "loki", - "uid": "cdxkxyq1xjmyoc" - }, - "direction": "backward", - "editorMode": "code", - "expr": "{affordance=\"action\",affordanceName=\"add\",messageType=\"actionOutput\",op=\"invokeaction\",thing=\"mqtt-calculator\"}", - "queryType": "range", - "refId": "A" - } - ], - "title": "action \"add\" Outputs", - "transformations": [ - { - "id": "extractFields", - "options": { - "format": "json", - "jsonPaths": [ + "title": "action \"add\" Inputs", + "transformations": [ { - "alias": "Value", - "path": "message" + "id": "extractFields", + "options": { + "format": "json", + "jsonPaths": [ + { + "alias": "Value", + "path": "message" + } + ], + "keepTime": true, + "replace": true, + "source": "Line" + } } - ], - "keepTime": true, - "replace": true, - "source": "Line" - } - } - ], - "type": "table" - }, - { - "datasource": { - "default": false, - "type": "loki", - "uid": "cdxkxyq1xjmyoc" + ], + "type": "table" }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" + { + "datasource": { + "default": false, + "type": "loki", + "uid": "cdxkxyq1xjmyoc" }, - "custom": { - "align": "auto", - "cellOptions": { - "type": "auto" - }, - "inspect": false + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "cellOptions": { + "type": "auto" + }, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null + "gridPos": { + "h": 8, + "w": 6, + "x": 6, + "y": 4 + }, + "id": 5, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": ["sum"], + "show": false }, + "frameIndex": 0, + "showHeader": true + }, + "pluginVersion": "11.2.0", + "targets": [ { - "color": "red", - "value": 80 + "datasource": { + "type": "loki", + "uid": "cdxkxyq1xjmyoc" + }, + "direction": "backward", + "editorMode": "code", + "expr": "{affordance=\"action\",affordanceName=\"add\",messageType=\"actionOutput\",op=\"invokeaction\",thing=\"mqtt-calculator\"}", + "queryType": "range", + "refId": "A" } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 6, - "x": 0, - "y": 12 - }, - "id": 6, - "options": { - "cellHeight": "sm", - "footer": { - "countRows": false, - "fields": "", - "reducer": [ - "sum" ], - "show": false - }, - "frameIndex": 0, - "showHeader": true - }, - "pluginVersion": "11.2.0", - "targets": [ - { - "datasource": { - "type": "loki", - "uid": "cdxkxyq1xjmyoc" - }, - "direction": "backward", - "editorMode": "code", - "expr": "{affordance=\"action\",affordanceName=\"subtract\",messageType=\"actionInput\",op=\"invokeaction\",thing=\"mqtt-calculator\"}", - "queryType": "range", - "refId": "A" - } - ], - "title": "action \"subtract\" Inputs", - "transformations": [ - { - "id": "extractFields", - "options": { - "format": "json", - "jsonPaths": [ + "title": "action \"add\" Outputs", + "transformations": [ { - "alias": "Value", - "path": "message" + "id": "extractFields", + "options": { + "format": "json", + "jsonPaths": [ + { + "alias": "Value", + "path": "message" + } + ], + "keepTime": true, + "replace": true, + "source": "Line" + } } - ], - "keepTime": true, - "replace": true, - "source": "Line" - } - } - ], - "type": "table" - }, - { - "datasource": { - "default": false, - "type": "loki", - "uid": "cdxkxyq1xjmyoc" + ], + "type": "table" }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" + { + "datasource": { + "default": false, + "type": "loki", + "uid": "cdxkxyq1xjmyoc" }, - "custom": { - "align": "auto", - "cellOptions": { - "type": "auto" - }, - "inspect": false + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "cellOptions": { + "type": "auto" + }, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null + "gridPos": { + "h": 8, + "w": 6, + "x": 0, + "y": 12 + }, + "id": 6, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": ["sum"], + "show": false }, + "frameIndex": 0, + "showHeader": true + }, + "pluginVersion": "11.2.0", + "targets": [ { - "color": "red", - "value": 80 + "datasource": { + "type": "loki", + "uid": "cdxkxyq1xjmyoc" + }, + "direction": "backward", + "editorMode": "code", + "expr": "{affordance=\"action\",affordanceName=\"subtract\",messageType=\"actionInput\",op=\"invokeaction\",thing=\"mqtt-calculator\"}", + "queryType": "range", + "refId": "A" + } + ], + "title": "action \"subtract\" Inputs", + "transformations": [ + { + "id": "extractFields", + "options": { + "format": "json", + "jsonPaths": [ + { + "alias": "Value", + "path": "message" + } + ], + "keepTime": true, + "replace": true, + "source": "Line" + } } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 6, - "x": 6, - "y": 12 - }, - "id": 7, - "options": { - "cellHeight": "sm", - "footer": { - "countRows": false, - "fields": "", - "reducer": [ - "sum" ], - "show": false - }, - "frameIndex": 0, - "showHeader": true + "type": "table" }, - "pluginVersion": "11.2.0", - "targets": [ - { + { "datasource": { - "type": "loki", - "uid": "cdxkxyq1xjmyoc" + "default": false, + "type": "loki", + "uid": "cdxkxyq1xjmyoc" }, - "direction": "backward", - "editorMode": "code", - "expr": "{affordance=\"action\",affordanceName=\"subtract\",messageType=\"actionOutput\",op=\"invokeaction\",thing=\"mqtt-calculator\"}", - "queryType": "range", - "refId": "A" - } - ], - "title": "action \"subtract\" Outputs", - "transformations": [ - { - "id": "extractFields", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "cellOptions": { + "type": "auto" + }, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 6, + "x": 6, + "y": 12 + }, + "id": 7, "options": { - "format": "json", - "jsonPaths": [ + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": ["sum"], + "show": false + }, + "frameIndex": 0, + "showHeader": true + }, + "pluginVersion": "11.2.0", + "targets": [ { - "alias": "Value", - "path": "message" + "datasource": { + "type": "loki", + "uid": "cdxkxyq1xjmyoc" + }, + "direction": "backward", + "editorMode": "code", + "expr": "{affordance=\"action\",affordanceName=\"subtract\",messageType=\"actionOutput\",op=\"invokeaction\",thing=\"mqtt-calculator\"}", + "queryType": "range", + "refId": "A" } - ], - "keepTime": true, - "replace": true, - "source": "Line" - } - } - ], - "type": "table" - } + ], + "title": "action \"subtract\" Outputs", + "transformations": [ + { + "id": "extractFields", + "options": { + "format": "json", + "jsonPaths": [ + { + "alias": "Value", + "path": "message" + } + ], + "keepTime": true, + "replace": true, + "source": "Line" + } + } + ], + "type": "table" + } ], "refresh": "10s", "schemaVersion": 39, "tags": [], "templating": { - "list": [] + "list": [] }, "time": { - "from": "now-6h", - "to": "now" + "from": "now-6h", + "to": "now" }, "timepicker": {}, "timezone": "browser", @@ -656,4 +642,4 @@ "uid": "adyy7x2gkc6iof", "version": 1, "weekStart": "" - } \ No newline at end of file +} diff --git a/conf/grafana/provisioning/dashboards/dashboard.yml b/conf/grafana/provisioning/dashboards/dashboard.yml index 2d2266d..57bc72e 100644 --- a/conf/grafana/provisioning/dashboards/dashboard.yml +++ b/conf/grafana/provisioning/dashboards/dashboard.yml @@ -8,4 +8,4 @@ providers: disableDeletion: false updateIntervalSeconds: 10 options: - path: /var/lib/grafana/dashboards \ No newline at end of file + path: /var/lib/grafana/dashboards diff --git a/conf/loki/loki-config.yaml b/conf/loki/loki-config.yaml index 1602d0c..6f3ae60 100644 --- a/conf/loki/loki-config.yaml +++ b/conf/loki/loki-config.yaml @@ -32,7 +32,6 @@ schema_config: index: prefix: index_ period: 24h - # ruler: # alertmanager_url: http://localhost:9093 diff --git a/conf/prometheus/prometheus.yml b/conf/prometheus/prometheus.yml index 967fc58..8068873 100644 --- a/conf/prometheus/prometheus.yml +++ b/conf/prometheus/prometheus.yml @@ -1,14 +1,14 @@ global: scrape_interval: 10s scrape_configs: - - job_name: 'traefik' + - job_name: "traefik" static_configs: - - targets: ['reverse-proxy:8080'] + - targets: ["reverse-proxy:8080"] - - job_name: 'prometheus' + - job_name: "prometheus" static_configs: - - targets: ['prometheus:9090'] + - targets: ["prometheus:9090"] - job_name: cadvisor static_configs: - - targets: ['cadvisor:8080'] \ No newline at end of file + - targets: ["cadvisor:8080"] diff --git a/conf/promtail/promtail-config.yaml b/conf/promtail/promtail-config.yaml index 06427ff..9915e09 100644 --- a/conf/promtail/promtail-config.yaml +++ b/conf/promtail/promtail-config.yaml @@ -7,7 +7,6 @@ positions: clients: - url: http://loki:3100/loki/api/v1/push - # scrape_configs: # - job_name: system # static_configs: diff --git a/docker-compose-infra.yml b/docker-compose-infra.yml index 4203efc..68b0ec7 100644 --- a/docker-compose-infra.yml +++ b/docker-compose-infra.yml @@ -32,10 +32,10 @@ services: deploy: resources: limits: - cpus: '0.08' + cpus: "0.08" memory: 100M reservations: - cpus: '0.05' + cpus: "0.05" memory: 50M test-things-auth: image: thomseddon/traefik-forward-auth:2 @@ -68,10 +68,10 @@ services: deploy: resources: limits: - cpus: '0.08' + cpus: "0.08" memory: 200M reservations: - cpus: '0.05' + cpus: "0.05" memory: 100M depends_on: - cadvisor @@ -93,10 +93,10 @@ services: deploy: resources: limits: - cpus: '0.08' + cpus: "0.08" memory: 100M reservations: - cpus: '0.05' + cpus: "0.05" memory: 50M depends_on: - prometheus @@ -109,28 +109,28 @@ services: - "traefik.http.routers.cadvisor.middlewares=test-things-auth" - "traefik.http.services.cadvisor.loadbalancer.server.port=${CADVISOR_PORT}" volumes: - - /:/rootfs:ro - - /var/run/docker.sock:/var/run/docker.sock - - /sys:/sys:ro - - /var/lib/docker/:/var/lib/docker:ro + - /:/rootfs:ro + - /var/run/docker.sock:/var/run/docker.sock + - /sys:/sys:ro + - /var/lib/docker/:/var/lib/docker:ro deploy: resources: limits: - cpus: '0.8' + cpus: "0.8" memory: 200M reservations: - cpus: '0.05' + cpus: "0.05" memory: 100M depends_on: - - coap-calculator-simple - - coap-calculator-content-negotiation - - http-express-calculator-simple - - http-express-calculator-content-negotiation - - http-flask-calculator - - mqtt-calculator - - modbus-elevator - - http-advanced-coffee-machine - - http-data-schema-thing + - coap-calculator-simple + - coap-calculator-content-negotiation + - http-express-calculator-simple + - http-express-calculator-content-negotiation + - http-flask-calculator + - mqtt-calculator + - modbus-elevator + - http-advanced-coffee-machine + - http-data-schema-thing portainer: image: portainer/portainer-ce:latest volumes: @@ -142,10 +142,10 @@ services: deploy: resources: limits: - cpus: '0.08' + cpus: "0.08" memory: 50M reservations: - cpus: '0.05' + cpus: "0.05" memory: 50M loki: image: grafana/loki:2.9.2 diff --git a/docker-compose-things.yml b/docker-compose-things.yml index 691264d..4bf184f 100644 --- a/docker-compose-things.yml +++ b/docker-compose-things.yml @@ -13,10 +13,10 @@ services: deploy: resources: limits: - cpus: '0.05' + cpus: "0.05" memory: 50M reservations: - cpus: '0.01' + cpus: "0.01" memory: 25M coap-calculator-content-negotiation: labels: @@ -31,10 +31,10 @@ services: deploy: resources: limits: - cpus: '0.05' + cpus: "0.05" memory: 50M reservations: - cpus: '0.01' + cpus: "0.01" memory: 25M http-express-calculator-simple: labels: @@ -51,19 +51,19 @@ services: deploy: resources: limits: - cpus: '0.05' + cpus: "0.05" memory: 50M reservations: - cpus: '0.01' + cpus: "0.01" memory: 25M depends_on: - loki healthcheck: - test: wget --no-verbose --tries=1 --spider http://${HOSTNAME}:${WEB_PORT_OUT}/http-express-calculator-simple || exit 1 - interval: ${HC_INTERVAL} - timeout: ${HC_TIMEOUT} - retries: ${HC_RETRIES} - start_period: ${HC_START_PERIOD} + test: wget --no-verbose --tries=1 --spider http://${HOSTNAME}:${WEB_PORT_OUT}/http-express-calculator-simple || exit 1 + interval: ${HC_INTERVAL} + timeout: ${HC_TIMEOUT} + retries: ${HC_RETRIES} + start_period: ${HC_START_PERIOD} http-express-calculator-content-negotiation: labels: - traefik.http.routers.http-express-calculator-content-negotiation.rule=PathPrefix(`/http-express-calculator-content-negotiation`) @@ -77,10 +77,10 @@ services: deploy: resources: limits: - cpus: '0.05' + cpus: "0.05" memory: 50M reservations: - cpus: '0.01' + cpus: "0.01" memory: 25M healthcheck: test: wget --no-verbose --tries=1 --spider http://${HOSTNAME}:${WEB_PORT_OUT}/http-express-calculator-content-negotiation || exit 1 @@ -101,10 +101,10 @@ services: deploy: resources: limits: - cpus: '0.05' + cpus: "0.05" memory: 50M reservations: - cpus: '0.01' + cpus: "0.01" memory: 25M healthcheck: test: wget --no-verbose --tries=1 --spider http://${HOSTNAME}:${WEB_PORT_OUT}/http-flask-calculator || exit 1 @@ -122,10 +122,10 @@ services: deploy: resources: limits: - cpus: '0.05' + cpus: "0.05" memory: 50M reservations: - cpus: '0.01' + cpus: "0.01" memory: 25M modbus-elevator: labels: @@ -141,10 +141,10 @@ services: deploy: resources: limits: - cpus: '0.05' + cpus: "0.05" memory: 50M reservations: - cpus: '0.01' + cpus: "0.01" memory: 25M http-advanced-coffee-machine: labels: @@ -159,10 +159,10 @@ services: deploy: resources: limits: - cpus: '0.05' + cpus: "0.05" memory: 50M reservations: - cpus: '0.01' + cpus: "0.01" memory: 25M http-data-schema-thing: labels: @@ -179,10 +179,10 @@ services: deploy: resources: limits: - cpus: '0.05' + cpus: "0.05" memory: 50M reservations: - cpus: '0.01' + cpus: "0.01" memory: 25M smart-home-presence-sensor: build: @@ -191,13 +191,13 @@ services: deploy: resources: limits: - cpus: '0.05' + cpus: "0.05" memory: 50M reservations: - cpus: '0.01' + cpus: "0.01" memory: 25M smart-home-simple-coffee-machine: - labels: + labels: # TD is not exposed since it contains hostname that cannot be accessed outside the docker network - traefik.http.routers.smart-home-simple-coffee-machine.rule=PathRegexp(`/smart-home-simple-coffee-machine/.+`) build: @@ -206,17 +206,17 @@ services: args: - PORT_ARG=${WEB_PORT_OUT} environment: - # Since mashup communicates inside the Docker network use of localhost in a TD result as + # Since mashup communicates inside the Docker network use of localhost in a TD result as # the mashup container's its own localhost. Therefore hostname given as their docker service name. # In production we will have a dedicated address and this issue won't be a problem. - SIMPLE_COFFEE_MACHINE_HOSTNAME=smart-home-simple-coffee-machine deploy: resources: limits: - cpus: '0.05' + cpus: "0.05" memory: 50M reservations: - cpus: '0.01' + cpus: "0.01" memory: 25M smart-home-smart-clock: labels: @@ -227,19 +227,19 @@ services: args: - PORT_ARG=${SMART_HOME_SMART_CLOCK_PORT_OUT} environment: - # Since mashup communicates inside the Docker network use of localhost in a TD result as + # Since mashup communicates inside the Docker network use of localhost in a TD result as # the mashup container's its own localhost. Therefore hostname given as their docker service name. # In production we will have a dedicated address and this issue won't be a problem. - SMART_CLOCK_HOSTNAME=smart-home-smart-clock deploy: resources: limits: - cpus: '0.05' + cpus: "0.05" memory: 200M reservations: - cpus: '0.01' + cpus: "0.01" memory: 50M - smart-home-mashup: + smart-home-mashup: build: context: ./mashups/smart-home dockerfile: ./dockerfiles/mashup.Dockerfile @@ -256,8 +256,8 @@ services: deploy: resources: limits: - cpus: '0.05' + cpus: "0.05" memory: 50M reservations: - cpus: '0.01' - memory: 25M \ No newline at end of file + cpus: "0.01" + memory: 25M diff --git a/license.template.txt b/license.template.txt new file mode 100644 index 0000000..fc0cd0a --- /dev/null +++ b/license.template.txt @@ -0,0 +1,15 @@ +/******************************************************************************** + * Copyright (c) <%= YEAR %> Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the W3C Software Notice and + * Document License (2015-05-13) which is available at + * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document. + * + * SPDX-License-Identifier: EPL-2.0 OR W3C-20150513 + ********************************************************************************/ + \ No newline at end of file diff --git a/mashups/smart-home/README.md b/mashups/smart-home/README.md index 89c7313..f5b692d 100644 --- a/mashups/smart-home/README.md +++ b/mashups/smart-home/README.md @@ -1,4 +1,5 @@ # Smart Home Mashup + You want to automate a small part of your home. The room setup can be seen in the illustration below. ![Quickstart Setup](./assets/quickstart-setup.svg) @@ -6,6 +7,7 @@ Each morning, when you wake up and go towards the kitchen area, you want the cof Your task is to write a script that brews the coffee of your choice when you pass in front the presence sensor in the kitchen if it is earlier than 13:00 but later than 5:00. This mashup consist of three Things: + - Presence Sensor (MQTT) - Simple Coffee Machine (HTTP) - Smart Clock (CoAP) @@ -15,9 +17,11 @@ Their .ts files can be found under [things](./things/) folder. Mashup's main code is located at [index.ts](./index.ts). ## Running the Mashup + After `npm run build` is executed, .ts files be compiled and executable JavaScript files will be available under [dist](./dist/). One can run all Things manually or using the [runMashupThings](./runMashupThings.sh) script. After Things start running, the main mashup logic can be run using the command `node ./dist/index.js`. ## Running with Docker -Every Thing and the main logic have separate docker files. \ No newline at end of file + +Every Thing and the main logic have separate docker files. diff --git a/mashups/smart-home/mashup-logic.ts b/mashups/smart-home/mashup-logic.ts index cafbfe0..51469f0 100644 --- a/mashups/smart-home/mashup-logic.ts +++ b/mashups/smart-home/mashup-logic.ts @@ -1,77 +1,106 @@ -// importing dependencies -import { Servient, Helpers } from '@node-wot/core' -import { HttpClientFactory, HttpsClientFactory } from '@node-wot/binding-http' -import { CoapClientFactory } from '@node-wot/binding-coap' -import { MqttClientFactory } from '@node-wot/binding-mqtt' -import dotenv from 'dotenv' -dotenv.config() +/******************************************************************************** + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the W3C Software Notice and + * Document License (2015-05-13) which is available at + * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document. + * + * SPDX-License-Identifier: EPL-2.0 OR W3C-20150513 + ********************************************************************************/ + +import { Servient, Helpers } from "@node-wot/core"; +import { HttpClientFactory, HttpsClientFactory } from "@node-wot/binding-http"; +import { CoapClientFactory } from "@node-wot/binding-coap"; +import { MqttClientFactory } from "@node-wot/binding-mqtt"; +import dotenv from "dotenv"; +dotenv.config(); // create a Servient and add HTTP, CoAP and MQTT bindings -let servient = new Servient(); +const servient = new Servient(); servient.addClientFactory(new HttpClientFactory()); servient.addClientFactory(new HttpsClientFactory()); servient.addClientFactory(new CoapClientFactory()); servient.addClientFactory(new MqttClientFactory()); -let wotHelper = new Helpers(servient); +const wotHelper = new Helpers(servient); (async () => { - const WoT = await servient.start(); + const WoT = await servient.start(); - const coffeeMachineURL = process.env.SIMPLE_COFFEE_MACHINE_HOSTNAME === 'smart-home-simple-coffee-machine' ? - `http://${process.env.SIMPLE_COFFEE_MACHINE_HOSTNAME}/smart-home-simple-coffee-machine` : - `http://${process.env.SIMPLE_COFFEE_MACHINE_HOSTNAME}:${process.env.SIMPLE_COFFEE_MACHINE_PORT}/smart-home-simple-coffee-machine` + const coffeeMachineURL = + process.env.SIMPLE_COFFEE_MACHINE_HOSTNAME === + "smart-home-simple-coffee-machine" + ? `http://${process.env.SIMPLE_COFFEE_MACHINE_HOSTNAME}/smart-home-simple-coffee-machine` + : `http://${process.env.SIMPLE_COFFEE_MACHINE_HOSTNAME}:${process.env.SIMPLE_COFFEE_MACHINE_PORT}/smart-home-simple-coffee-machine`; - // we will fetch the TDs of the devices - const coffeeMachineTD = await wotHelper.fetch(coffeeMachineURL) as WoT.ThingDescription - // Alternatively, this Thing self-hosts its TD at http://plugfest.thingweb.io:8081/coffee-machine that you can fetch - const presenceSensorTD = await wotHelper.fetch( - `mqtt://${process.env.PRESENCE_SENSOR_BROKER_URI}/smart-home-presence-sensor` - ) as WoT.ThingDescription - const smartClockTD = await wotHelper.fetch( - `coap://${process.env.SMART_CLOCK_HOSTNAME}:${process.env.SMART_CLOCK_PORT}/smart-home-smart-clock` - ) as WoT.ThingDescription + // we will fetch the TDs of the devices + const coffeeMachineTD = (await wotHelper.fetch( + coffeeMachineURL + )) as WoT.ThingDescription; + // Alternatively, this Thing self-hosts its TD at http://plugfest.thingweb.io:8081/coffee-machine that you can fetch + const presenceSensorTD = (await wotHelper.fetch( + `mqtt://${process.env.PRESENCE_SENSOR_BROKER_URI}/smart-home-presence-sensor` + )) as WoT.ThingDescription; + const smartClockTD = (await wotHelper.fetch( + `coap://${process.env.SMART_CLOCK_HOSTNAME}:${process.env.SMART_CLOCK_PORT}/smart-home-smart-clock` + )) as WoT.ThingDescription; - // consuming TDs allows creates a software object, which allows us to execute functions on them - const coffeeMachineThing = await WoT.consume(coffeeMachineTD); - const presenceSensorThing = await WoT.consume(presenceSensorTD); - const smartClockThing = await WoT.consume(smartClockTD); + // consuming TDs allows creates a software object, which allows us to execute functions on them + const coffeeMachineThing = await WoT.consume(coffeeMachineTD); + const presenceSensorThing = await WoT.consume(presenceSensorTD); + const smartClockThing = await WoT.consume(smartClockTD); - let morningCoffeeFlag = false + let morningCoffeeFlag = false; - // We subscribe to the presence detection events - presenceSensorThing.subscribeEvent("presenceDetected", async (eventData) => { - // We can log the distance of the detection but this is not necessary. - // The emission of the event implies that a detection happened anyways - console.log("Detected presence at,", await eventData.value(), "mm"); + // We subscribe to the presence detection events + presenceSensorThing.subscribeEvent( + "presenceDetected", + async (eventData) => { + // We can log the distance of the detection but this is not necessary. + // The emission of the event implies that a detection happened anyways + console.log("Detected presence at,", await eventData.value(), "mm"); - // We read the time property from the smart clock - const currentTimeData = await smartClockThing.readProperty("time"); - const currentTime: any = await currentTimeData.value(); // You need to always call the .value function + type Time = { + hour: number; + minute: number; + }; - // Optionally, we can log the current time - console.log( - "Current time is " + currentTime.hour.toString().padStart(2, '0') + ":" + currentTime.minute.toString().padStart(2, '0') - ); + // We read the time property from the smart clock + const currentTimeData = await smartClockThing.readProperty("time"); + const currentTime: Time = (await currentTimeData.value()) as Time; // You need to always call the .value function - // To avoid accidental brews, a flag is used to check whether a coffee was brewed before - if (!morningCoffeeFlag) { - // As the task indicates, we brew only between 5:00 and 13:00 - if (currentTime.hour <= 13 && currentTime.hour >= 5) { - // To brew a coffee, we invoke the brew action in the coffee machine - await coffeeMachineThing.invokeAction("brew", "espresso"); - // We log to indicate to the user that brewing has finished - console.log("brewed espresso"); - // for today we should not brew any more coffee - morningCoffeeFlag = true; - } - } + // Optionally, we can log the current time + console.log( + "Current time is " + + currentTime.hour.toString().padStart(2, "0") + + ":" + + currentTime.minute.toString().padStart(2, "0") + ); - // we reset the morningCoffeeFlag every day at 1am - setInterval(() => { - if (currentTime.hour == 1) { - morningCoffeeFlag = false; - } - }, 1000); - }); -})(); \ No newline at end of file + // To avoid accidental brews, a flag is used to check whether a coffee was brewed before + if (!morningCoffeeFlag) { + // As the task indicates, we brew only between 5:00 and 13:00 + if (currentTime.hour <= 13 && currentTime.hour >= 5) { + // To brew a coffee, we invoke the brew action in the coffee machine + await coffeeMachineThing.invokeAction("brew", "espresso"); + // We log to indicate to the user that brewing has finished + console.log("brewed espresso"); + // for today we should not brew any more coffee + morningCoffeeFlag = true; + } + } + + // we reset the morningCoffeeFlag every day at 1am + setInterval(() => { + if (currentTime.hour === 1) { + morningCoffeeFlag = false; + } + }, 1000); + } + ); +})(); diff --git a/mashups/smart-home/package.json b/mashups/smart-home/package.json index a91f3a6..9802cd9 100644 --- a/mashups/smart-home/package.json +++ b/mashups/smart-home/package.json @@ -1,28 +1,30 @@ { - "name": "smart-home", - "version": "1.0.0", - "description": "Smart Home Mashup", - "main": "index.ts", - "scripts": { - "build": "tsc -b", - "start": "node ./dist/mashup-logic.js", - "start:presence-sensor": "node ./dist/things/presence-sensor.js", - "start:simple-coffee-machine": "node ./dist/things/simple-coffee-machine.js", - "start:smart-clock": "node ./dist/things/smart-clock.js", - "start:all": "./runMashupThings.sh" - }, - "author": "Eclipse Thingweb (https://thingweb.io/)", - "license": "EPL-2.0 OR W3C-20150513", - "dependencies": { - "@node-wot/binding-coap": "~0.8.16", - "@node-wot/binding-http": "~0.8.16", - "@node-wot/binding-mqtt": "~0.8.16", - "@node-wot/core": "~0.8.16" - }, - "devDependencies": { - "@types/debug": "^4.1.12", - "@types/node-fetch": "^2.6.11", - "dotenv": "^16.4.5", - "typescript": "^4.7.4" - } + "name": "smart-home", + "version": "1.0.0", + "description": "Smart Home Mashup", + "scripts": { + "build": "tsc -b", + "start": "node ./dist/mashup-logic.js", + "start:presence-sensor": "node ./dist/things/presence-sensor.js", + "start:simple-coffee-machine": "node ./dist/things/simple-coffee-machine.js", + "start:smart-clock": "node ./dist/things/smart-clock.js", + "start:all": "./runMashupThings.sh", + "lint": "eslint .", + "lint:fix": "eslint . --fix", + "format": "prettier --write \"**/*.ts\" \"**/*.json\"" + }, + "author": "Eclipse Thingweb (https://thingweb.io/)", + "license": "EPL-2.0 OR W3C-20150513", + "dependencies": { + "@node-wot/binding-coap": "~0.8.16", + "@node-wot/binding-http": "~0.8.16", + "@node-wot/binding-mqtt": "~0.8.16", + "@node-wot/core": "~0.8.16" + }, + "devDependencies": { + "@types/debug": "^4.1.12", + "@types/node-fetch": "^2.6.11", + "dotenv": "^16.4.5", + "typescript": "^4.7.4" + } } diff --git a/mashups/smart-home/things/presence-sensor.ts b/mashups/smart-home/things/presence-sensor.ts index 197942f..6818904 100644 --- a/mashups/smart-home/things/presence-sensor.ts +++ b/mashups/smart-home/things/presence-sensor.ts @@ -18,12 +18,13 @@ import { Servient } from "@node-wot/core"; import { MqttBrokerServer } from "@node-wot/binding-mqtt"; -import dotenv from 'dotenv' -dotenv.config() +import dotenv from "dotenv"; +dotenv.config(); // create Servient add MQTT binding with port configuration const servient = new Servient(); -const brokerUri = process.env.PRESENCE_SENSOR_BROKER_URI ?? "mqtt://test.mosquitto.org"; +const brokerUri = + process.env.PRESENCE_SENSOR_BROKER_URI ?? "mqtt://test.mosquitto.org"; servient.addServer(new MqttBrokerServer({ uri: brokerUri })); servient.start().then((WoT) => { @@ -64,4 +65,4 @@ servient.start().then((WoT) => { .catch((e) => { console.log(e); }); -}); \ No newline at end of file +}); diff --git a/mashups/smart-home/things/simple-coffee-machine.ts b/mashups/smart-home/things/simple-coffee-machine.ts index fa34be8..18a36d8 100644 --- a/mashups/smart-home/things/simple-coffee-machine.ts +++ b/mashups/smart-home/things/simple-coffee-machine.ts @@ -18,13 +18,13 @@ import { Servient } from "@node-wot/core"; import { HttpServer } from "@node-wot/binding-http"; -import dotenv from 'dotenv' -dotenv.config() +import dotenv from "dotenv"; +dotenv.config(); // create Servient add HTTP binding with port configuration const servient = new Servient(); -const hostname = process.env.SIMPLE_COFFEE_MACHINE_HOSTNAME ?? 'localhost'; -const httpPort = process.env.SIMPLE_COFFEE_MACHINE_PORT ?? '8081'; +const hostname = process.env.SIMPLE_COFFEE_MACHINE_HOSTNAME ?? "localhost"; +const httpPort = process.env.SIMPLE_COFFEE_MACHINE_PORT ?? "8081"; servient.addServer( new HttpServer({ baseUri: `http://${hostname}:${httpPort}`, @@ -44,7 +44,8 @@ function timeout(ms: number) { servient.start().then((WoT) => { WoT.produce({ title: "smart-home-simple-coffee-machine", - description: "A simple coffee machine that can be interacted over the Internet", + description: + "A simple coffee machine that can be interacted over the Internet", support: "https://github.com/eclipse-thingweb/node-wot/", "@context": "https://www.w3.org/2022/wot/td/v1.1", properties: { @@ -137,7 +138,11 @@ servient.start().then((WoT) => { return undefined; } } else if (coffeeType === "cappuccino") { - if (waterAmount <= 20 || beansAmount <= 25 || milkAmount <= 15) { + if ( + waterAmount <= 20 || + beansAmount <= 25 || + milkAmount <= 15 + ) { throw new Error("Not enough water or beans"); } else { await timeout(2000); @@ -186,7 +191,9 @@ servient.start().then((WoT) => { }); thing.setActionHandler("refill", async (params, options) => { - const selectedResource = (await params.value()) as Array<"water" | "beans" | "milk">; + const selectedResource = (await params.value()) as Array< + "water" | "beans" | "milk" + >; console.info("received refill order of ", selectedResource); if (selectedResource!.indexOf("water") !== -1) { waterAmount = 1000; @@ -204,10 +211,12 @@ servient.start().then((WoT) => { // expose the thing thing.expose().then(() => { console.info(thing.getThingDescription().title + " ready"); - console.info("TD available at http://" + hostname + ":" + httpPort); + console.info( + "TD available at http://" + hostname + ":" + httpPort + ); }); }) .catch((e) => { console.log(e); }); -}); \ No newline at end of file +}); diff --git a/mashups/smart-home/things/smart-clock.ts b/mashups/smart-home/things/smart-clock.ts index be31ae1..9688d83 100644 --- a/mashups/smart-home/things/smart-clock.ts +++ b/mashups/smart-home/things/smart-clock.ts @@ -17,19 +17,19 @@ import { Servient } from "@node-wot/core"; import { CoapServer } from "@node-wot/binding-coap"; -import dotenv from 'dotenv' -dotenv.config() +import dotenv from "dotenv"; +dotenv.config(); // create Servient add CoAP binding with port configuration const servient = new Servient(); -const hostname = process.env.SMART_CLOCK_HOSTNAME ?? 'localhost'; +const hostname = process.env.SMART_CLOCK_HOSTNAME ?? "localhost"; const port = process.env.SMART_CLOCK_PORT ?? "5686"; servient.addServer( - new CoapServer({ + new CoapServer({ address: hostname, - port: parseInt(port) - } -)); + port: parseInt(port), + }) +); let minuteCounter = 0; let hourCounter = 5; @@ -54,7 +54,8 @@ async function timeCount(thing: WoT.ExposedThing) { servient.start().then((WoT) => { WoT.produce({ title: "smart-home-smart-clock", - description: "a smart clock that runs 60 times faster than real time, where 1 hour happens in 1 minute.", + description: + "a smart clock that runs 60 times faster than real time, where 1 hour happens in 1 minute.", support: "https://github.com/eclipse-thingweb/node-wot/", "@context": "https://www.w3.org/2022/wot/td/v1.1", properties: { @@ -101,4 +102,4 @@ servient.start().then((WoT) => { .catch((e) => { console.log(e); }); -}); \ No newline at end of file +}); diff --git a/mashups/smart-home/tsconfig.json b/mashups/smart-home/tsconfig.json index c90f523..4150c64 100644 --- a/mashups/smart-home/tsconfig.json +++ b/mashups/smart-home/tsconfig.json @@ -10,5 +10,5 @@ "esModuleInterop": true, "removeComments": false }, - "include": ["./mashup-logic.ts", "./things/**.ts"], -} \ No newline at end of file + "include": ["./mashup-logic.ts", "./things/**.ts"] +} diff --git a/package-lock.json b/package-lock.json index 82862f7..0f3d29f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,5862 +1,7432 @@ { - "name": "test-things", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "test-things", - "license": "EPL-2.0 OR W3C-20150513", - "workspaces": [ - "./mashups/*", - "./things/*/*/*" - ], - "devDependencies": { - "@node-wot/binding-http": "~0.8.16", - "@node-wot/core": "~0.8.16", - "@types/chai": "^4.3.17", - "@types/chai-as-promised": "^7.1.8", - "@types/mocha": "^10.0.7", - "@types/node": "^22.4.1", - "ajv": "^8.12.0", - "chai": "^4.5.0", - "chai-as-promised": "^7.1.1", - "mocha": "^10.7.3" - } - }, - "mashups/smart-home": { - "version": "1.0.0", - "license": "EPL-2.0 OR W3C-20150513", - "dependencies": { - "@node-wot/binding-coap": "~0.8.16", - "@node-wot/binding-http": "~0.8.16", - "@node-wot/binding-mqtt": "~0.8.16", - "@node-wot/core": "~0.8.16" - }, - "devDependencies": { - "@types/debug": "^4.1.12", - "@types/node-fetch": "^2.6.11", - "dotenv": "^16.4.5", - "typescript": "^4.7.4" - } - }, - "node_modules/@babel/runtime": { - "version": "7.25.6", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.6.tgz", - "integrity": "sha512-VBj9MYyDb9tuLq7yzqjgzt6Q+IBQLrGZfdjOekyEirZPHxXWoTSGUTMrpsfi58Up73d13NfYLv8HT9vmznjzhQ==", - "dependencies": { - "regenerator-runtime": "^0.14.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@colors/colors": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", - "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==", - "engines": { - "node": ">=0.1.90" - } - }, - "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==", - "dev": true, - "dependencies": { - "@jridgewell/trace-mapping": "0.3.9" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@dabh/diagnostics": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz", - "integrity": "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==", - "dependencies": { - "colorspace": "1.1.x", - "enabled": "2.0.x", - "kuler": "^2.0.0" - } - }, - "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==", - "dev": true, - "engines": { - "node": ">=6.0.0" - } - }, - "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==", - "dev": true - }, - "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==", - "dev": true, - "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - }, - "node_modules/@leichtgewicht/ip-codec": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz", - "integrity": "sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==" - }, - "node_modules/@napi-rs/snappy-android-arm-eabi": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/@napi-rs/snappy-android-arm-eabi/-/snappy-android-arm-eabi-7.2.2.tgz", - "integrity": "sha512-H7DuVkPCK5BlAr1NfSU8bDEN7gYs+R78pSHhDng83QxRnCLmVIZk33ymmIwurmoA1HrdTxbkbuNl+lMvNqnytw==", - "cpu": [ - "arm" - ], - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/snappy-android-arm64": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/@napi-rs/snappy-android-arm64/-/snappy-android-arm64-7.2.2.tgz", - "integrity": "sha512-2R/A3qok+nGtpVK8oUMcrIi5OMDckGYNoBLFyli3zp8w6IArPRfg1yOfVUcHvpUDTo9T7LOS1fXgMOoC796eQw==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/snappy-darwin-arm64": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/@napi-rs/snappy-darwin-arm64/-/snappy-darwin-arm64-7.2.2.tgz", - "integrity": "sha512-USgArHbfrmdbuq33bD5ssbkPIoT7YCXCRLmZpDS6dMDrx+iM7eD2BecNbOOo7/v1eu6TRmQ0xOzeQ6I/9FIi5g==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/snappy-darwin-x64": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/@napi-rs/snappy-darwin-x64/-/snappy-darwin-x64-7.2.2.tgz", - "integrity": "sha512-0APDu8iO5iT0IJKblk2lH0VpWSl9zOZndZKnBYIc+ei1npw2L5QvuErFOTeTdHBtzvUHASB+9bvgaWnQo4PvTQ==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/snappy-freebsd-x64": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/@napi-rs/snappy-freebsd-x64/-/snappy-freebsd-x64-7.2.2.tgz", - "integrity": "sha512-mRTCJsuzy0o/B0Hnp9CwNB5V6cOJ4wedDTWEthsdKHSsQlO7WU9W1yP7H3Qv3Ccp/ZfMyrmG98Ad7u7lG58WXA==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/snappy-linux-arm-gnueabihf": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/@napi-rs/snappy-linux-arm-gnueabihf/-/snappy-linux-arm-gnueabihf-7.2.2.tgz", - "integrity": "sha512-v1uzm8+6uYjasBPcFkv90VLZ+WhLzr/tnfkZ/iD9mHYiULqkqpRuC8zvc3FZaJy5wLQE9zTDkTJN1IvUcZ+Vcg==", - "cpu": [ - "arm" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/snappy-linux-arm64-gnu": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/@napi-rs/snappy-linux-arm64-gnu/-/snappy-linux-arm64-gnu-7.2.2.tgz", - "integrity": "sha512-LrEMa5pBScs4GXWOn6ZYXfQ72IzoolZw5txqUHVGs8eK4g1HR9HTHhb2oY5ySNaKakG5sOgMsb1rwaEnjhChmQ==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/snappy-linux-arm64-musl": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/@napi-rs/snappy-linux-arm64-musl/-/snappy-linux-arm64-musl-7.2.2.tgz", - "integrity": "sha512-3orWZo9hUpGQcB+3aTLW7UFDqNCQfbr0+MvV67x8nMNYj5eAeUtMmUE/HxLznHO4eZ1qSqiTwLbVx05/Socdlw==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/snappy-linux-x64-gnu": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/@napi-rs/snappy-linux-x64-gnu/-/snappy-linux-x64-gnu-7.2.2.tgz", - "integrity": "sha512-jZt8Jit/HHDcavt80zxEkDpH+R1Ic0ssiVCoueASzMXa7vwPJeF4ZxZyqUw4qeSy7n8UUExomu8G8ZbP6VKhgw==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/snappy-linux-x64-musl": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/@napi-rs/snappy-linux-x64-musl/-/snappy-linux-x64-musl-7.2.2.tgz", - "integrity": "sha512-Dh96IXgcZrV39a+Tej/owcd9vr5ihiZ3KRix11rr1v0MWtVb61+H1GXXlz6+Zcx9y8jM1NmOuiIuJwkV4vZ4WA==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/snappy-win32-arm64-msvc": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/@napi-rs/snappy-win32-arm64-msvc/-/snappy-win32-arm64-msvc-7.2.2.tgz", - "integrity": "sha512-9No0b3xGbHSWv2wtLEn3MO76Yopn1U2TdemZpCaEgOGccz1V+a/1d16Piz3ofSmnA13HGFz3h9NwZH9EOaIgYA==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/snappy-win32-ia32-msvc": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/@napi-rs/snappy-win32-ia32-msvc/-/snappy-win32-ia32-msvc-7.2.2.tgz", - "integrity": "sha512-QiGe+0G86J74Qz1JcHtBwM3OYdTni1hX1PFyLRo3HhQUSpmi13Bzc1En7APn+6Pvo7gkrcy81dObGLDSxFAkQQ==", - "cpu": [ - "ia32" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/snappy-win32-x64-msvc": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/@napi-rs/snappy-win32-x64-msvc/-/snappy-win32-x64-msvc-7.2.2.tgz", - "integrity": "sha512-a43cyx1nK0daw6BZxVcvDEXxKMFLSBSDTAhsFD0VqSKcC7MGUBMaqyoWUcMiI7LBSz4bxUmxDWKfCYzpEmeb3w==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@node-wot/binding-coap": { - "version": "0.8.16", - "resolved": "https://registry.npmjs.org/@node-wot/binding-coap/-/binding-coap-0.8.16.tgz", - "integrity": "sha512-WJRLZSRd+YugQ+biSSsH5DM9Jr4H72HVTHqRaFWUg2lxSiGJGC28EQK/KSyfvxLepZwsNf/VqaHWvgQOCj1bGA==", - "dependencies": { - "@node-wot/core": "0.8.16", - "@types/node": "16.18.35", - "coap": "^1.4.0", - "multicast-dns": "^7.2.5", - "node-coap-client": "1.0.8", - "rxjs": "5.5.11", - "slugify": "^1.4.5", - "wot-typescript-definitions": "0.8.0-SNAPSHOT.29" - } - }, - "node_modules/@node-wot/binding-coap/node_modules/@types/node": { - "version": "16.18.35", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.35.tgz", - "integrity": "sha512-yqU2Rf94HFZqgHf6Tuyc/IqVD0l3U91KjvypSr1GtJKyrnl6L/kfnxVqN4QOwcF5Zx9tO/HKK+fozGr5AtqA+g==" - }, - "node_modules/@node-wot/binding-http": { - "version": "0.8.16", - "resolved": "https://registry.npmjs.org/@node-wot/binding-http/-/binding-http-0.8.16.tgz", - "integrity": "sha512-PqpjyEcJt1apjWpObtctPfQ+iK2FAf5IHrwESiMHL0fdGtbA1/tw0KZdC8/krZK7DtI+WvY+fnajdvS0MAqhvw==", - "dependencies": { - "@node-wot/core": "0.8.16", - "@types/eventsource": "1.1.10", - "accept-language-parser": "1.5.0", - "basic-auth": "2.0.1", - "client-oauth2": "^4.2.5", - "eventsource": "^2.0.2", - "find-my-way": "^7.6.2", - "node-fetch": "^2.6.7", - "query-string": "^7.1.1", - "rxjs": "5.5.11", - "slugify": "^1.4.5" - } - }, - "node_modules/@node-wot/binding-modbus": { - "version": "0.8.16", - "resolved": "https://registry.npmjs.org/@node-wot/binding-modbus/-/binding-modbus-0.8.16.tgz", - "integrity": "sha512-7OAX4SduuE8Sm/XrXdv7kaSyykJ/xtRpmg5+EcXl9YrAORHT3SdFZPsvoGz/UGsAE7R95VW55r19Yx9h3rOOOw==", - "dependencies": { - "@node-wot/core": "0.8.16", - "modbus-serial": "^8.0.17", - "rxjs": "5.5.11", - "wot-typescript-definitions": "0.8.0-SNAPSHOT.29" - } - }, - "node_modules/@node-wot/binding-mqtt": { - "version": "0.8.16", - "resolved": "https://registry.npmjs.org/@node-wot/binding-mqtt/-/binding-mqtt-0.8.16.tgz", - "integrity": "sha512-GgEl5nbLjY96YaZClL/mCq0JL3ROH9cKE41UKM0ymKs8dvNlKmLeC7YLN1NCMKHcYVaxvJSrIaDVgEtadF8qEw==", - "dependencies": { - "@node-wot/core": "0.8.16", - "aedes": "^0.46.2", - "mqtt": "^5.3.2", - "rxjs": "5.5.11" - } - }, - "node_modules/@node-wot/binding-mqtt/node_modules/commist": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/commist/-/commist-3.2.0.tgz", - "integrity": "sha512-4PIMoPniho+LqXmpS5d3NuGYncG6XWlkBSVGiWycL22dd42OYdUGil2CWuzklaJoNxyxUSpO4MKIBU94viWNAw==" - }, - "node_modules/@node-wot/binding-mqtt/node_modules/help-me": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/help-me/-/help-me-5.0.0.tgz", - "integrity": "sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==" - }, - "node_modules/@node-wot/binding-mqtt/node_modules/mqtt": { - "version": "5.10.1", - "resolved": "https://registry.npmjs.org/mqtt/-/mqtt-5.10.1.tgz", - "integrity": "sha512-hXCOki8sANoQ7w+2OzJzg6qMBxTtrH9RlnVNV8panLZgnl+Gh0J/t4k6r8Az8+C7y3KAcyXtn0mmLixyUom8Sw==", - "dependencies": { - "@types/readable-stream": "^4.0.5", - "@types/ws": "^8.5.9", - "commist": "^3.2.0", - "concat-stream": "^2.0.0", - "debug": "^4.3.4", - "help-me": "^5.0.0", - "lru-cache": "^10.0.1", - "minimist": "^1.2.8", - "mqtt-packet": "^9.0.0", - "number-allocator": "^1.0.14", - "readable-stream": "^4.4.2", - "reinterval": "^1.1.0", - "rfdc": "^1.3.0", - "split2": "^4.2.0", - "worker-timers": "^7.1.4", - "ws": "^8.17.1" - }, - "bin": { - "mqtt": "build/bin/mqtt.js", - "mqtt_pub": "build/bin/pub.js", - "mqtt_sub": "build/bin/sub.js" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@node-wot/binding-mqtt/node_modules/mqtt-packet": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/mqtt-packet/-/mqtt-packet-9.0.0.tgz", - "integrity": "sha512-8v+HkX+fwbodsWAZIZTI074XIoxVBOmPeggQuDFCGg1SqNcC+uoRMWu7J6QlJPqIUIJXmjNYYHxBBLr1Y/Df4w==", - "dependencies": { - "bl": "^6.0.8", - "debug": "^4.3.4", - "process-nextick-args": "^2.0.1" - } - }, - "node_modules/@node-wot/binding-mqtt/node_modules/split2": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", - "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", - "engines": { - "node": ">= 10.x" - } - }, - "node_modules/@node-wot/binding-mqtt/node_modules/ws": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", - "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/@node-wot/core": { - "version": "0.8.16", - "resolved": "https://registry.npmjs.org/@node-wot/core/-/core-0.8.16.tgz", - "integrity": "sha512-viHaXIeIryx+sm0H8oBnR5mUSTFj9raWaJYCNa/BGphfOUhcOB5pajS5NOJcQUzSy76Z9XY3JMw3taRAuPTvsw==", - "dependencies": { - "@petamoriken/float16": "^3.1.1", - "@thingweb/thing-model": "^1.0.3", - "ajv": "^8.11.0", - "ajv-formats": "^2.1.1", - "cbor": "^8.1.0", - "content-type": "^1.0.5", - "debug": "^4.3.4", - "is-absolute-url": "3.0.3", - "uritemplate": "0.3.4", - "url-toolkit": "2.1.6", - "uuid": "^7.0.3", - "web-streams-polyfill": "^3.0.1" - } - }, - "node_modules/@petamoriken/float16": { - "version": "3.8.7", - "resolved": "https://registry.npmjs.org/@petamoriken/float16/-/float16-3.8.7.tgz", - "integrity": "sha512-/Ri4xDDpe12NT6Ex/DRgHzLlobiQXEW/hmG08w1wj/YU7hLemk97c+zHQFp0iZQ9r7YqgLEXZR2sls4HxBf9NA==" - }, - "node_modules/@protobufjs/aspromise": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", - "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==" - }, - "node_modules/@protobufjs/base64": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", - "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" - }, - "node_modules/@protobufjs/codegen": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", - "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" - }, - "node_modules/@protobufjs/eventemitter": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", - "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==" - }, - "node_modules/@protobufjs/fetch": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", - "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", - "dependencies": { - "@protobufjs/aspromise": "^1.1.1", - "@protobufjs/inquire": "^1.1.0" - } - }, - "node_modules/@protobufjs/float": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", - "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==" - }, - "node_modules/@protobufjs/inquire": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", - "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==" - }, - "node_modules/@protobufjs/path": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", - "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==" - }, - "node_modules/@protobufjs/pool": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", - "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==" - }, - "node_modules/@protobufjs/utf8": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", - "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" - }, - "node_modules/@serialport/binding-mock": { - "version": "10.2.2", - "resolved": "https://registry.npmjs.org/@serialport/binding-mock/-/binding-mock-10.2.2.tgz", - "integrity": "sha512-HAFzGhk9OuFMpuor7aT5G1ChPgn5qSsklTFOTUX72Rl6p0xwcSVsRtG/xaGp6bxpN7fI9D/S8THLBWbBgS6ldw==", - "dependencies": { - "@serialport/bindings-interface": "^1.2.1", - "debug": "^4.3.3" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/@serialport/bindings-cpp": { - "version": "11.0.3", - "resolved": "https://registry.npmjs.org/@serialport/bindings-cpp/-/bindings-cpp-11.0.3.tgz", - "integrity": "sha512-xgNDJ7pHHZCJMoDsEH+D8q5CV+V3RGN4/jLEG9SQ7q6kh+o03axV0l/upPHZ0HW4tTXpGgqPIGbXOTrD4RGQQA==", - "hasInstallScript": true, - "dependencies": { - "@serialport/bindings-interface": "1.2.2", - "@serialport/parser-readline": "11.0.0", - "debug": "4.3.4", - "node-addon-api": "6.1.0", - "node-gyp-build": "4.6.0" - }, - "engines": { - "node": ">=14.0.0" - }, - "funding": { - "url": "https://opencollective.com/serialport/donate" - } - }, - "node_modules/@serialport/bindings-cpp/node_modules/@serialport/parser-delimiter": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/@serialport/parser-delimiter/-/parser-delimiter-11.0.0.tgz", - "integrity": "sha512-aZLJhlRTjSmEwllLG7S4J8s8ctRAS0cbvCpO87smLvl3e4BgzbVgF6Z6zaJd3Aji2uSiYgfedCdNc4L6W+1E2g==", - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://opencollective.com/serialport/donate" - } - }, - "node_modules/@serialport/bindings-cpp/node_modules/@serialport/parser-readline": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/@serialport/parser-readline/-/parser-readline-11.0.0.tgz", - "integrity": "sha512-rRAivhRkT3YO28WjmmG4FQX6L+KMb5/ikhyylRfzWPw0nSXy97+u07peS9CbHqaNvJkMhH1locp2H36aGMOEIA==", - "dependencies": { - "@serialport/parser-delimiter": "11.0.0" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://opencollective.com/serialport/donate" - } - }, - "node_modules/@serialport/bindings-cpp/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/@serialport/bindings-cpp/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "node_modules/@serialport/bindings-interface": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@serialport/bindings-interface/-/bindings-interface-1.2.2.tgz", - "integrity": "sha512-CJaUd5bLvtM9c5dmO9rPBHPXTa9R2UwpkJ0wdh9JCYcbrPWsKz+ErvR0hBLeo7NPeiFdjFO4sonRljiw4d2XiA==", - "engines": { - "node": "^12.22 || ^14.13 || >=16" - } - }, - "node_modules/@serialport/parser-byte-length": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/@serialport/parser-byte-length/-/parser-byte-length-11.0.1.tgz", - "integrity": "sha512-UsffR5b3NHwhjJzsWv5fZMkoq3wGNyUcRTA9jlu02w+2kMlBRJPzlPVB5szVX0VWUEqkCg+3VaU2XWuYr+uAUA==", - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://opencollective.com/serialport/donate" - } - }, - "node_modules/@serialport/parser-cctalk": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/@serialport/parser-cctalk/-/parser-cctalk-11.0.1.tgz", - "integrity": "sha512-klzVQfRcC1m0SVDV2Dy9hHfwweO2/mUMUyuXK04FRkKHy5/AdETmk9KTVVVVfpDCSysvHoyQPwiDFq8ddwX3cQ==", - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://opencollective.com/serialport/donate" - } - }, - "node_modules/@serialport/parser-delimiter": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/@serialport/parser-delimiter/-/parser-delimiter-11.0.1.tgz", - "integrity": "sha512-NAsYa3OFt2xEnj/+0BRkQP2qkRNbXBPEq6uFJEdNdzcTSF+BTRXkoIRrWBq3N6koovPqW6lnbxc/iJYe5AX/2Q==", - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://opencollective.com/serialport/donate" - } - }, - "node_modules/@serialport/parser-inter-byte-timeout": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/@serialport/parser-inter-byte-timeout/-/parser-inter-byte-timeout-11.0.1.tgz", - "integrity": "sha512-PEFV9dSpW+ptH1rLhdB9KgE+rbJ/FvQiZz0mx+4jkv/Po4g3PNsEEMXfMW0aQVSFVsmitvmE0jHlhGjLv8GQEg==", - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://opencollective.com/serialport/donate" - } - }, - "node_modules/@serialport/parser-packet-length": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/@serialport/parser-packet-length/-/parser-packet-length-11.0.1.tgz", - "integrity": "sha512-KwPu8dsAI+eN4fnUS1vVmrOpUtBK4p9L9cHhwn5ZmfcvwvZMHp/J+IEu7xH0g5aM1/8QEoaql26BQP+sZ71NQQ==", - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/@serialport/parser-readline": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/@serialport/parser-readline/-/parser-readline-11.0.1.tgz", - "integrity": "sha512-wkJ3EI733+yhbi7eBWzs/qn8+cfIBcYQjfrILPNqslAy6VlgdKw+pHoblDFmg78GN0TqGUDSWlTJ65oLEPVp5Q==", - "dependencies": { - "@serialport/parser-delimiter": "11.0.1" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://opencollective.com/serialport/donate" - } - }, - "node_modules/@serialport/parser-ready": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/@serialport/parser-ready/-/parser-ready-11.0.1.tgz", - "integrity": "sha512-v/bvlgKhrNt+SVLSqlfXCO1HEinfRRMGnzqbpdVCgu2SiWIEenCLjs51JisKVYQoQFcXdP/EHZnzm7NPXHDlAg==", - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://opencollective.com/serialport/donate" - } - }, - "node_modules/@serialport/parser-regex": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/@serialport/parser-regex/-/parser-regex-11.0.1.tgz", - "integrity": "sha512-Lf3k7qibYqZ0+/wX3UA8fRng3WtQ+UyLpjQhG1COs6OBSq5/I5tYXczfhlrbA0gHo1qzgzr2V2t7m6FoBSc81Q==", - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://opencollective.com/serialport/donate" - } - }, - "node_modules/@serialport/parser-slip-encoder": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/@serialport/parser-slip-encoder/-/parser-slip-encoder-11.0.1.tgz", - "integrity": "sha512-l4mXsAGzpmPO7+uqKJqtPDW643irfnGEWbiy34FoYvuOs8n0SmiMtgQZFAtvlTNQCRWE2tykF/WG6K/McJthDw==", - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://opencollective.com/serialport/donate" - } - }, - "node_modules/@serialport/parser-spacepacket": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/@serialport/parser-spacepacket/-/parser-spacepacket-11.0.1.tgz", - "integrity": "sha512-Lq7fXoOsLOMo4XEt9HB31zV5LhrteXlsOy2o6r39TfRwU6x8Nou9jQMA9vW0a6yPra5zwsHIaNrA6tDOGj2Ozg==", - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://opencollective.com/serialport/donate" - } - }, - "node_modules/@serialport/stream": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/@serialport/stream/-/stream-11.0.1.tgz", - "integrity": "sha512-6pjyKRg8MQuvhGfg36+PF7K5eGNQcEswCSiAg1UPilqqFS8X1QnaiSCn5UFp/hCN+pAtlFjkOi0ztvtmSI7n4g==", - "dependencies": { - "@serialport/bindings-interface": "1.2.2", - "debug": "4.3.4" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://opencollective.com/serialport/donate" - } - }, - "node_modules/@serialport/stream/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/@serialport/stream/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "node_modules/@servie/events": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@servie/events/-/events-1.0.0.tgz", - "integrity": "sha512-sBSO19KzdrJCM3gdx6eIxV8M9Gxfgg6iDQmH5TIAGaUu+X9VDdsINXJOnoiZ1Kx3TrHdH4bt5UVglkjsEGBcvw==" - }, - "node_modules/@thingweb/thing-model": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@thingweb/thing-model/-/thing-model-1.0.3.tgz", - "integrity": "sha512-KAm2ux3A7B2lq0LkQw1Eze4g1w1c1zvhlaCA/TQFtX5LQoMsm6uqMuW6X+PcOzSsyKIekxYQjcgwC4rFZpfDbg==", - "dependencies": { - "ajv": "^8.11.0", - "ajv-formats": "^3.0.1", - "debug": "^4.3.4", - "json-placeholder-replacer": "^2.0.4", - "wot-thing-description-types": "1.1.0-09-November-2023", - "wot-thing-model-types": "^1.1.0-09-November-2023", - "wot-typescript-definitions": "0.8.0-SNAPSHOT.29" - } - }, - "node_modules/@thingweb/thing-model/node_modules/ajv-formats": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", - "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", - "dependencies": { - "ajv": "^8.0.0" - }, - "peerDependencies": { - "ajv": "^8.0.0" - }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } - } - }, - "node_modules/@thingweb/thing-model/node_modules/json-placeholder-replacer": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/json-placeholder-replacer/-/json-placeholder-replacer-2.0.5.tgz", - "integrity": "sha512-pK/MgeylpZ1VAMO7s/tp5H6VVsbCIc9i+3cUILYnLTw2MT7xqKLowBQGQmFTtcz/O1414oK+f9C8jT79IADdag==", - "bin": { - "jpr": "dist/index.js", - "json-placeholder-replacer": "dist/index.js" - } - }, - "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==", - "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==", - "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==", - "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==", - "dev": true - }, - "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, - "dependencies": { - "@types/connect": "*", - "@types/node": "*" - } - }, - "node_modules/@types/chai": { - "version": "4.3.19", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.19.tgz", - "integrity": "sha512-2hHHvQBVE2FiSK4eN0Br6snX9MtolHaTo/batnLjlGRhoQzlCL61iVpxoqO7SfFyOw+P/pwv+0zNHzKoGWz9Cw==", - "dev": true - }, - "node_modules/@types/chai-as-promised": { - "version": "7.1.8", - "resolved": "https://registry.npmjs.org/@types/chai-as-promised/-/chai-as-promised-7.1.8.tgz", - "integrity": "sha512-ThlRVIJhr69FLlh6IctTXFkmhtP3NpMZ2QGq69StYLyKZFp/HOp1VdKZj7RvfNWYYcJ1xlbLGLLWj1UvP5u/Gw==", - "dev": true, - "dependencies": { - "@types/chai": "*" - } - }, - "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, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/debug": { - "version": "4.1.12", - "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", - "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", - "dev": true, - "dependencies": { - "@types/ms": "*" - } - }, - "node_modules/@types/eventsource": { - "version": "1.1.10", - "resolved": "https://registry.npmjs.org/@types/eventsource/-/eventsource-1.1.10.tgz", - "integrity": "sha512-rYzRmJSnm44Xb7FICRXEjwe/26ZiiS+VMGmuD17PevMP56cGgLEsaM955sYQW0S+K7h+mPOL70vGf1hi4WDjVA==" - }, - "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, - "dependencies": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "^4.17.33", - "@types/qs": "*", - "@types/serve-static": "*" - } - }, - "node_modules/@types/express-serve-static-core": { - "version": "4.19.5", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.5.tgz", - "integrity": "sha512-y6W03tvrACO72aijJ5uF02FRq5cgDR9lUxddQ8vyF+GvmjJQqbzDcJngEjURc+ZsG31VI3hODNZJ2URj86pzmg==", - "dev": true, - "dependencies": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*", - "@types/send": "*" - } - }, - "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 - }, - "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 - }, - "node_modules/@types/mocha": { - "version": "10.0.8", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.8.tgz", - "integrity": "sha512-HfMcUmy9hTMJh66VNcmeC9iVErIZJli2bszuXc6julh5YGuRb/W5OnkHjwLNYdFlMis0sY3If5SEAp+PktdJjw==", - "dev": true - }, - "node_modules/@types/ms": { - "version": "0.7.34", - "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.34.tgz", - "integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==", - "dev": true - }, - "node_modules/@types/node": { - "version": "22.5.5", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.5.5.tgz", - "integrity": "sha512-Xjs4y5UPO/CLdzpgR6GirZJx36yScjh73+2NlLlkFRSoQN8B0DpfXPdZGnvVmLRLOsqDpOfTNv7D9trgGhmOIA==", - "dependencies": { - "undici-types": "~6.19.2" - } - }, - "node_modules/@types/node-fetch": { - "version": "2.6.11", - "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.11.tgz", - "integrity": "sha512-24xFj9R5+rfQJLRyM56qh+wnVSYhyXC2tkoBndtY0U+vubqNsYXGjufB2nn8Q6gt0LrARwL6UBtMCSVCwl4B1g==", - "dev": true, - "dependencies": { - "@types/node": "*", - "form-data": "^4.0.0" - } - }, - "node_modules/@types/qs": { - "version": "6.9.16", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.16.tgz", - "integrity": "sha512-7i+zxXdPD0T4cKDuxCUXJ4wHcsJLwENa6Z3dCu8cfCK743OGy5Nu1RmAGqDPsoTDINVEcdXKRvR/zre+P2Ku1A==", - "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 - }, - "node_modules/@types/readable-stream": { - "version": "4.0.15", - "resolved": "https://registry.npmjs.org/@types/readable-stream/-/readable-stream-4.0.15.tgz", - "integrity": "sha512-oAZ3kw+kJFkEqyh7xORZOku1YAKvsFTogRY8kVl4vHpEKiDkfnSA/My8haRE7fvmix5Zyy+1pwzOi7yycGLBJw==", - "dependencies": { - "@types/node": "*", - "safe-buffer": "~5.1.1" - } - }, - "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, - "dependencies": { - "@types/mime": "^1", - "@types/node": "*" - } - }, - "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, - "dependencies": { - "@types/http-errors": "*", - "@types/node": "*", - "@types/send": "*" - } - }, - "node_modules/@types/tough-cookie": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", - "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==" - }, - "node_modules/@types/triple-beam": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz", - "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==" - }, - "node_modules/@types/ws": { - "version": "8.5.12", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.12.tgz", - "integrity": "sha512-3tPRkv1EtkDpzlgyKyI8pGsGZAGPEaXeu0DOj5DI25Ja91bdAYddYHbADRYVrZMRbfW+1l5YwXVDKohDJNQxkQ==", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/abort-controller": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", - "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", - "dependencies": { - "event-target-shim": "^5.0.0" - }, - "engines": { - "node": ">=6.5" - } - }, - "node_modules/accept-language-parser": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/accept-language-parser/-/accept-language-parser-1.5.0.tgz", - "integrity": "sha512-QhyTbMLYo0BBGg1aWbeMG4ekWtds/31BrEU+DONOg/7ax23vxpL03Pb7/zBmha2v7vdD3AyzZVWBVGEZxKOXWw==" - }, - "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==", - "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/acorn": { - "version": "8.12.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", - "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", - "dev": true, - "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==", - "dev": true, - "dependencies": { - "acorn": "^8.11.0" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/advanced-coffee-machine": { - "resolved": "things/advanced-coffee-machine/http/ts", - "link": true - }, - "node_modules/aedes": { - "version": "0.46.3", - "resolved": "https://registry.npmjs.org/aedes/-/aedes-0.46.3.tgz", - "integrity": "sha512-i3B+H74uNRhlqcs/JdrMp7e3daz4Cwls0x4yLcfjGXz2tIwnxhF6od4m86O6yyNdz/Gg3jfY3q0sc/Cz8qzg6g==", - "dependencies": { - "aedes-packet": "^2.3.1", - "aedes-persistence": "^8.1.3", - "bulk-write-stream": "^2.0.1", - "end-of-stream": "^1.4.4", - "fastfall": "^1.5.1", - "fastparallel": "^2.4.1", - "fastseries": "^2.0.0", - "hyperid": "^3.0.0", - "mqemitter": "^4.5.0", - "mqtt-packet": "^7.1.2", - "readable-stream": "^3.6.0", - "retimer": "^3.0.0", - "reusify": "^1.0.4", - "uuid": "^8.3.2" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/aedes-packet": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/aedes-packet/-/aedes-packet-2.3.1.tgz", - "integrity": "sha512-LqBd57uc2rui2RbjycW17dylglejG26mM4ewVXGNDnVp/SUHFVEgm7d1HTmYrnSkSCNoHti042qgcTwv/F+BtQ==", - "dependencies": { - "mqtt-packet": "^6.3.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/aedes-packet/node_modules/bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "dependencies": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, - "node_modules/aedes-packet/node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, - "node_modules/aedes-packet/node_modules/mqtt-packet": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/mqtt-packet/-/mqtt-packet-6.10.0.tgz", - "integrity": "sha512-ja8+mFKIHdB1Tpl6vac+sktqy3gA8t9Mduom1BA75cI+R9AHnZOiaBQwpGiWnaVJLDGRdNhQmFaAqd7tkKSMGA==", - "dependencies": { - "bl": "^4.0.2", - "debug": "^4.1.1", - "process-nextick-args": "^2.0.1" - } - }, - "node_modules/aedes-packet/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/aedes-persistence": { - "version": "8.1.3", - "resolved": "https://registry.npmjs.org/aedes-persistence/-/aedes-persistence-8.1.3.tgz", - "integrity": "sha512-VMCjEV+2g1TNJb/IlDEUy6SP9crT+QUhe2xc6UjyqrFNBNgTvHmOefXY7FxWrwmR2QA02vwg3+5p/JXkyg/Dkw==", - "dependencies": { - "aedes-packet": "^2.3.1", - "from2": "^2.3.0", - "qlobber": "^5.0.3" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/aedes/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/aedes/node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ajv-formats": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", - "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", - "dependencies": { - "ajv": "^8.0.0" - }, - "peerDependencies": { - "ajv": "^8.0.0" - }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } - } - }, - "node_modules/ansi-colors": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", - "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "devOptional": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/aproba": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", - "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", - "optional": true - }, - "node_modules/are-we-there-yet": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.7.tgz", - "integrity": "sha512-nxwy40TuMiUGqMyRHgCSWZ9FM4VAoRP4xUYSTv5ImRog+h9yISPbVH7H8fASCIzYn9wlEv4zvFL7uKDMCFQm3g==", - "deprecated": "This package is no longer supported.", - "optional": true, - "dependencies": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" - } - }, - "node_modules/are-we-there-yet/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==", - "optional": true, - "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/are-we-there-yet/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==", - "optional": true, - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "node_modules/array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" - }, - "node_modules/assertion-error": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", - "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/async": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", - "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==" - }, - "node_modules/async-exit-hook": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/async-exit-hook/-/async-exit-hook-2.0.1.tgz", - "integrity": "sha512-NW2cX8m1Q7KPA7a5M2ULQeZ2wR5qI5PAbw5L0UOMxdioVk9PMZ0h1TmyZEkPYrCvYjDlFICusOu1dlEKAAeXBw==", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "dev": true - }, - "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==" - }, - "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==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/basic-auth": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", - "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", - "dependencies": { - "safe-buffer": "5.1.2" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/binary-extensions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", - "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/bindings": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", - "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", - "optional": true, - "dependencies": { - "file-uri-to-path": "1.0.0" - } - }, - "node_modules/bl": { - "version": "6.0.15", - "resolved": "https://registry.npmjs.org/bl/-/bl-6.0.15.tgz", - "integrity": "sha512-RGhjD1XCPS7ZdAH6cEJVaR3gLV4KJP2hvkQ49AH5kwScjiyd0jBM8RsP4oHKzcx+kNCON9752zPeRnuv0HHwzw==", - "dependencies": { - "@types/readable-stream": "^4.0.0", - "buffer": "^6.0.3", - "inherits": "^2.0.4", - "readable-stream": "^4.2.0" - } - }, - "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==", - "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" - } - }, - "node_modules/body-parser/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/body-parser/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browser-stdout": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", - "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", - "dev": true - }, - "node_modules/btoa": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/btoa/-/btoa-1.2.1.tgz", - "integrity": "sha512-SB4/MIGlsiVkMcHmT+pSmIPoNDoHg+7cMzmt3Uxt628MTz2487DKSqK/fuhFBrkuqrYv5UCEnACpF4dTFNKc/g==", - "bin": { - "btoa": "bin/btoa.js" - }, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } - }, - "node_modules/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==" - }, - "node_modules/bulk-write-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/bulk-write-stream/-/bulk-write-stream-2.0.1.tgz", - "integrity": "sha512-XWOLjgHtpDasHfwM8oO4df1JoZwa7/OwTsXDzh4rUTo+9CowzeOFBZz43w+H14h1fyq+xl28tVIBrdjcjj4Gug==", - "dependencies": { - "inherits": "^2.0.3", - "readable-stream": "^3.1.1" - } - }, - "node_modules/bulk-write-stream/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/byte-length": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/byte-length/-/byte-length-1.0.2.tgz", - "integrity": "sha512-ovBpjmsgd/teRmgcPh23d4gJvxDoXtAzEL9xTfMU8Yc2kqCDb7L9jAG0XHl1nzuGl+h3ebCIF1i62UFyA9V/2Q==" - }, - "node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/call-bind": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", - "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", - "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/capitalize": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/capitalize/-/capitalize-2.0.4.tgz", - "integrity": "sha512-wcSyiFqXRYyCoqu0o0ekXzJAKCLMkqWS5QWGlgTJFJKwRmI6pzcN2hBl5VPq9RzLW5Uf4FF/V/lcFfjCtVak2w==" - }, - "node_modules/cbor": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/cbor/-/cbor-8.1.0.tgz", - "integrity": "sha512-DwGjNW9omn6EwP70aXsn7FQJx5kO12tX0bZkaTjzdVFM6/7nhA4t0EENocKGx6D2Bch9PE2KzCUf5SceBdeijg==", - "dependencies": { - "nofilter": "^3.1.0" - }, - "engines": { - "node": ">=12.19" - } - }, - "node_modules/chai": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.5.0.tgz", - "integrity": "sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw==", - "dev": true, - "dependencies": { - "assertion-error": "^1.1.0", - "check-error": "^1.0.3", - "deep-eql": "^4.1.3", - "get-func-name": "^2.0.2", - "loupe": "^2.3.6", - "pathval": "^1.1.1", - "type-detect": "^4.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/chai-as-promised": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/chai-as-promised/-/chai-as-promised-7.1.2.tgz", - "integrity": "sha512-aBDHZxRzYnUYuIAIPBH2s511DjlKPzXNlXSGFC8CwmroWQLfrW0LtE1nK3MAwwNhJPa9raEjNCmRoFpG0Hurdw==", - "dev": true, - "dependencies": { - "check-error": "^1.0.2" - }, - "peerDependencies": { - "chai": ">= 2.1.2 < 6" - } - }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/chalk/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/check-error": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", - "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==", - "dev": true, - "dependencies": { - "get-func-name": "^2.0.2" - }, - "engines": { - "node": "*" - } - }, - "node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "dev": true, - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", - "optional": true - }, - "node_modules/client-oauth2": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/client-oauth2/-/client-oauth2-4.3.3.tgz", - "integrity": "sha512-k8AvUYJon0vv75ufoVo4nALYb/qwFFicO3I0+39C6xEdflqVtr+f9cy+0ZxAduoVSTfhP5DX2tY2XICAd5hy6Q==", - "dependencies": { - "popsicle": "^12.0.5", - "safe-buffer": "^5.2.0" - }, - "engines": { - "node": ">=4.2.0" - } - }, - "node_modules/client-oauth2/node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "node_modules/coap": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/coap/-/coap-1.4.0.tgz", - "integrity": "sha512-guVtoWEr48XcauWlebQThMWKrZU8iUURwghh2+CtOPicvL5TXo6h9qbvTwiBQQTgnBUS/jtfzMBoqX8iNWtGyQ==", - "dependencies": { - "@types/readable-stream": "^4.0.14", - "bl": "^6.0.12", - "capitalize": "^2.0.4", - "coap-packet": "^1.1.1", - "debug": "^4.3.5", - "fastseries": "^2.0.0", - "lru-cache": "^10.2.2", - "readable-stream": "^4.5.2" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/coap-calculator": { - "resolved": "things/calculator/coap/js", - "link": true - }, - "node_modules/coap-packet": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/coap-packet/-/coap-packet-1.1.1.tgz", - "integrity": "sha512-Bkz2ZKI/7hU2gm6nUuo5l+MBSkdFJx7My1ZgNEhKUC7K2yYfQYVbBPRa64BBYLcEcYgaSlau4A1Uw5xfM2I0zw==", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA==", - "optional": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/color": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", - "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", - "dependencies": { - "color-convert": "^1.9.3", - "color-string": "^1.6.0" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/color-string": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", - "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", - "dependencies": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" - } - }, - "node_modules/color/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==", - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/color/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==" - }, - "node_modules/colorspace": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.4.tgz", - "integrity": "sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==", - "dependencies": { - "color": "^3.1.3", - "text-hex": "1.0.x" - } - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/commist": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/commist/-/commist-1.1.0.tgz", - "integrity": "sha512-rraC8NXWOEjhADbZe9QBNzLAN5Q3fsTPQtBV+fEVj6xKIgDgNiEVE6ZNfHpZOqfQ21YUzfVNUXLOEZquYvQPPg==", - "dependencies": { - "leven": "^2.1.0", - "minimist": "^1.1.0" - } - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" - }, - "node_modules/concat-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", - "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", - "engines": [ - "node >= 6.0" - ], - "dependencies": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.0.2", - "typedarray": "^0.0.6" - } - }, - "node_modules/concat-stream/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", - "optional": true - }, - "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==", - "dependencies": { - "safe-buffer": "5.2.1" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/content-disposition/node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", - "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" - }, - "node_modules/core-util-is": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" - }, - "node_modules/crc": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/crc/-/crc-4.3.2.tgz", - "integrity": "sha512-uGDHf4KLLh2zsHa8D8hIQ1H/HtFQhyHrc0uhHBcoKGol/Xnb+MPYfUMw7cvON6ze/GUESTudKayDcJC5HnJv1A==", - "engines": { - "node": ">=12" - }, - "peerDependencies": { - "buffer": ">=6.0.3" - }, - "peerDependenciesMeta": { - "buffer": { - "optional": true - } - } - }, - "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==", - "dev": true - }, - "node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/decamelize": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", - "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/decode-uri-component": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", - "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", - "engines": { - "node": ">=0.10" - } - }, - "node_modules/decompress-response": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz", - "integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==", - "optional": true, - "dependencies": { - "mimic-response": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/deep-eql": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.4.tgz", - "integrity": "sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==", - "dev": true, - "dependencies": { - "type-detect": "^4.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/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==", - "optional": true, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/define-data-property": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", - "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", - "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "gopd": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", - "optional": true - }, - "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==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/detect-libc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", - "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", - "optional": true, - "bin": { - "detect-libc": "bin/detect-libc.js" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/diff": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", - "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", - "dev": true, - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/dns-packet": { - "version": "5.6.1", - "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz", - "integrity": "sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==", - "dependencies": { - "@leichtgewicht/ip-codec": "^2.0.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/dotenv": { - "version": "16.4.5", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", - "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://dotenvx.com" - } - }, - "node_modules/duplexify": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.3.tgz", - "integrity": "sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA==", - "dependencies": { - "end-of-stream": "^1.4.1", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1", - "stream-shift": "^1.0.2" - } - }, - "node_modules/duplexify/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/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==" - }, - "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==", - "devOptional": true - }, - "node_modules/enabled": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", - "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==" - }, - "node_modules/encodeurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", - "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "dependencies": { - "once": "^1.4.0" - } - }, - "node_modules/es-define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", - "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", - "dependencies": { - "get-intrinsic": "^1.2.4" - }, - "engines": { - "node": ">= 0.4" - } - }, - "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==", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" - }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/event-target-shim": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", - "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", - "engines": { - "node": ">=6" - } - }, - "node_modules/events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "engines": { - "node": ">=0.8.x" - } - }, - "node_modules/eventsource": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-2.0.2.tgz", - "integrity": "sha512-IzUmBGPR3+oUG9dUeXynyNmf91/3zUSJg1lCktzKw47OXuhco54U3r9B7O4XX+Rb1Itm9OZ2b0RkTs10bICOxA==", - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/expand-template": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", - "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", - "optional": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/express": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.0.tgz", - "integrity": "sha512-VqcNGcj/Id5ZT1LZ/cfihi3ttTn+NJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng==", - "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.6.0", - "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.10", - "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" - } - }, - "node_modules/express/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/express/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "node_modules/express/node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/fast-decode-uri-component": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/fast-decode-uri-component/-/fast-decode-uri-component-1.0.1.tgz", - "integrity": "sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==" - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" - }, - "node_modules/fast-querystring": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/fast-querystring/-/fast-querystring-1.1.2.tgz", - "integrity": "sha512-g6KuKWmFXc0fID8WWH0jit4g0AGBoJhCkJMb1RmbsSEUNvQ+ZC8D6CUZ+GtF8nMzSPXnhiePyyqqipzNNEnHjg==", - "dependencies": { - "fast-decode-uri-component": "^1.0.1" - } - }, - "node_modules/fast-unique-numbers": { - "version": "8.0.13", - "resolved": "https://registry.npmjs.org/fast-unique-numbers/-/fast-unique-numbers-8.0.13.tgz", - "integrity": "sha512-7OnTFAVPefgw2eBJ1xj2PGGR9FwYzSUso9decayHgCDX4sJkHLdcsYTytTg+tYv+wKF3U8gJuSBz2jJpQV4u/g==", - "dependencies": { - "@babel/runtime": "^7.23.8", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.1.0" - } - }, - "node_modules/fast-uri": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.1.tgz", - "integrity": "sha512-MWipKbbYiYI0UC7cl8m/i/IWTqfC8YXsqjzybjddLsFjStroQzsHXkc73JutMvBiXmOvapk+axIl79ig5t55Bw==" - }, - "node_modules/fastfall": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/fastfall/-/fastfall-1.5.1.tgz", - "integrity": "sha512-KH6p+Z8AKPXnmA7+Iz2Lh8ARCMr+8WNPVludm1LGkZoD2MjY6LVnRMtTKhkdzI+jr0RzQWXKzKyBJm1zoHEL4Q==", - "dependencies": { - "reusify": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/fastparallel": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/fastparallel/-/fastparallel-2.4.1.tgz", - "integrity": "sha512-qUmhxPgNHmvRjZKBFUNI0oZuuH9OlSIOXmJ98lhKPxMZZ7zS/Fi0wRHOihDSz0R1YiIOjxzOY4bq65YTcdBi2Q==", - "dependencies": { - "reusify": "^1.0.4", - "xtend": "^4.0.2" - } - }, - "node_modules/fastseries": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fastseries/-/fastseries-2.0.0.tgz", - "integrity": "sha512-XBU9RXeoYc2/VnvMhplAxEmZLfIk7cvTBu+xwoBuTI8pL19E03cmca17QQycKIdxgwCeFA/a4u27gv1h3ya5LQ==" - }, - "node_modules/fecha": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", - "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==" - }, - "node_modules/file-uri-to-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", - "optional": true - }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/filter-obj": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz", - "integrity": "sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/finalhandler": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", - "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", - "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/finalhandler/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/finalhandler/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "node_modules/find-my-way": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/find-my-way/-/find-my-way-7.7.0.tgz", - "integrity": "sha512-+SrHpvQ52Q6W9f3wJoJBbAQULJuNEEQwBvlvYwACDhBTLOTMiQ0HYWh4+vC3OivGP2ENcTI1oKlFA2OepJNjhQ==", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-querystring": "^1.0.0", - "safe-regex2": "^2.0.0" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/flat": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", - "dev": true, - "bin": { - "flat": "cli.js" - } - }, - "node_modules/fn.name": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", - "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" - }, - "node_modules/form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "dev": true, - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", - "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==", - "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==", - "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==", - "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/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/fs-constants": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", - "optional": true - }, - "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==" - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/gauge": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", - "integrity": "sha512-14x4kjc6lkD3ltw589k0NrPD6cCNTD6CWoVUNpB85+DrtONoZn+Rug6xZU5RvSC4+TZPxA5AnBibQYAvZn41Hg==", - "deprecated": "This package is no longer supported.", - "optional": true, - "dependencies": { - "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" - } - }, - "node_modules/gauge/node_modules/ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", - "optional": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/gauge/node_modules/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": "sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw==", - "optional": true, - "dependencies": { - "number-is-nan": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/gauge/node_modules/string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha512-0XsVpQLnVCXHJfyEs8tC0zpTVIr5PKKsQtkT29IwupnPTjtPmQ3xT/4yCREF9hYkV/3M3kzcUTSAZT6a6h81tw==", - "optional": true, - "dependencies": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/gauge/node_modules/strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", - "optional": true, - "dependencies": { - "ansi-regex": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-func-name": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", - "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/get-intrinsic": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", - "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/github-from-package": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", - "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", - "optional": true - }, - "node_modules/glob": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", - "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^5.0.1", - "once": "^1.3.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "dependencies": { - "get-intrinsic": "^1.1.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/has-property-descriptors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", - "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", - "dependencies": { - "es-define-property": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-proto": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", - "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", - "optional": true - }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "dev": true, - "bin": { - "he": "bin/he" - } - }, - "node_modules/help-me": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/help-me/-/help-me-3.0.0.tgz", - "integrity": "sha512-hx73jClhyk910sidBB7ERlnhMlFsJJIBqSVMFDwPN8o2v9nmp5KgLq1Xz1Bf1fCMMZ6mPrX159iG0VLy/fPMtQ==", - "dependencies": { - "glob": "^7.1.6", - "readable-stream": "^3.6.0" - } - }, - "node_modules/help-me/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==", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/help-me/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", - "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/help-me/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/help-me/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/http-calculator": { - "resolved": "things/calculator/http/express", - "link": true - }, - "node_modules/http-data-schema-thing": { - "resolved": "things/data-schema-thing/http/ts", - "link": true - }, - "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==", - "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/hyperid": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/hyperid/-/hyperid-3.3.0.tgz", - "integrity": "sha512-7qhCVT4MJIoEsNcbhglhdmBKb09QtcmJNiIQGq7js/Khf5FtQQ9bzcAuloeqBeee7XD7JqDeve9KNlQya5tSGQ==", - "dependencies": { - "buffer": "^5.2.1", - "uuid": "^8.3.2", - "uuid-parse": "^1.1.0" - } - }, - "node_modules/hyperid/node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, - "node_modules/hyperid/node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "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==", - "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==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/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.", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "node_modules/ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "optional": true - }, - "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==", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/is-absolute-url": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-3.0.3.tgz", - "integrity": "sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q==", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-arrayish": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" - }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "devOptional": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-plain-obj": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", - "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "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, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" - }, - "node_modules/js-sdsl": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.3.0.tgz", - "integrity": "sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ==", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/js-sdsl" - } - }, - "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/json-placeholder-replacer": { - "version": "1.0.37", - "resolved": "https://registry.npmjs.org/json-placeholder-replacer/-/json-placeholder-replacer-1.0.37.tgz", - "integrity": "sha512-Ix9Rpcp3UvkCULHrS2Wu58Op+oDLD0ubjlmXDMIKQwvvztvEV6diyaB+Duuuvb6lDHav9PISyRaO9dzzt8tOAQ==", - "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==" - }, - "node_modules/kuler": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", - "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" - }, - "node_modules/leven": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-2.1.0.tgz", - "integrity": "sha512-nvVPLpIHUxCUoRLrFqTgSxXJ614d8AgQoWl7zPe/2VadE8+1dpU3LBhowRuBAcuwruWtOdD8oYC9jDNJjXDPyA==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "dev": true, - "dependencies": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/logform": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/logform/-/logform-2.6.1.tgz", - "integrity": "sha512-CdaO738xRapbKIMVn2m4F6KTj4j7ooJ8POVnebSgKo3KBz5axNXRAL7ZdRjIV6NOr2Uf4vjtRkxrFETOioCqSA==", - "dependencies": { - "@colors/colors": "1.6.0", - "@types/triple-beam": "^1.3.2", - "fecha": "^4.2.0", - "ms": "^2.1.1", - "safe-stable-stringify": "^2.3.1", - "triple-beam": "^1.3.0" - }, - "engines": { - "node": ">= 12.0.0" - } - }, - "node_modules/long": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", - "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==" - }, - "node_modules/loupe": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", - "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==", - "dev": true, - "dependencies": { - "get-func-name": "^2.0.1" - } - }, - "node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==" - }, - "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==" - }, - "node_modules/make-error-cause": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/make-error-cause/-/make-error-cause-2.3.0.tgz", - "integrity": "sha512-etgt+n4LlOkGSJbBTV9VROHA5R7ekIPS4vfh+bCAoJgRrJWdqJCBbpS3osRJ/HrT7R68MzMiY3L3sDJ/Fd8aBg==", - "dependencies": { - "make-error": "^1.3.5" - } - }, - "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==", - "engines": { - "node": ">= 0.6" - } - }, - "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==", - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4" - } - }, - "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==", - "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==", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mimic-response": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz", - "integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==", - "optional": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/mkdirp-classic": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", - "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", - "optional": true - }, - "node_modules/mocha": { - "version": "10.7.3", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.7.3.tgz", - "integrity": "sha512-uQWxAu44wwiACGqjbPYmjo7Lg8sFrS3dQe7PP2FQI+woptP4vZXSMcfMyFL/e1yFEeEpV4RtyTpZROOKmxis+A==", - "dev": true, - "dependencies": { - "ansi-colors": "^4.1.3", - "browser-stdout": "^1.3.1", - "chokidar": "^3.5.3", - "debug": "^4.3.5", - "diff": "^5.2.0", - "escape-string-regexp": "^4.0.0", - "find-up": "^5.0.0", - "glob": "^8.1.0", - "he": "^1.2.0", - "js-yaml": "^4.1.0", - "log-symbols": "^4.1.0", - "minimatch": "^5.1.6", - "ms": "^2.1.3", - "serialize-javascript": "^6.0.2", - "strip-json-comments": "^3.1.1", - "supports-color": "^8.1.1", - "workerpool": "^6.5.1", - "yargs": "^16.2.0", - "yargs-parser": "^20.2.9", - "yargs-unparser": "^2.0.0" - }, - "bin": { - "_mocha": "bin/_mocha", - "mocha": "bin/mocha.js" - }, - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/modbus": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/modbus/-/modbus-1.1.3.tgz", - "integrity": "sha512-Y4NvKFNxOvp4KgsL/MXDEauvP3VtA7bAHlXXwv21/d+/o3ppIToweshm3sVhy5ivbSnA3nZ2hQZnlgvlpE4b1A==", - "dependencies": { - "crc": "^4.3.2", - "serialport": "^12.0.0" - } - }, - "node_modules/modbus-elevator": { - "resolved": "things/elevator/modbus/js", - "link": true - }, - "node_modules/modbus-serial": { - "version": "8.0.17", - "resolved": "https://registry.npmjs.org/modbus-serial/-/modbus-serial-8.0.17.tgz", - "integrity": "sha512-1lCNpCY72wTgGnnBzv9O3ZfbKeHl9zZTgVuMgp2mEqFErEZC7p3I0CGpzfQ4XPHT0Aqz+qXhpkGew8WK57SwvQ==", - "dependencies": { - "debug": "^4.3.1", - "serialport": "^12.0.0" - } - }, - "node_modules/modbus-serial/node_modules/@serialport/bindings-cpp": { - "version": "12.0.1", - "resolved": "https://registry.npmjs.org/@serialport/bindings-cpp/-/bindings-cpp-12.0.1.tgz", - "integrity": "sha512-r2XOwY2dDvbW7dKqSPIk2gzsr6M6Qpe9+/Ngs94fNaNlcTRCV02PfaoDmRgcubpNVVcLATlxSxPTIDw12dbKOg==", - "hasInstallScript": true, - "dependencies": { - "@serialport/bindings-interface": "1.2.2", - "@serialport/parser-readline": "11.0.0", - "debug": "4.3.4", - "node-addon-api": "7.0.0", - "node-gyp-build": "4.6.0" - }, - "engines": { - "node": ">=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/serialport/donate" - } - }, - "node_modules/modbus-serial/node_modules/@serialport/bindings-cpp/node_modules/@serialport/parser-delimiter": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/@serialport/parser-delimiter/-/parser-delimiter-11.0.0.tgz", - "integrity": "sha512-aZLJhlRTjSmEwllLG7S4J8s8ctRAS0cbvCpO87smLvl3e4BgzbVgF6Z6zaJd3Aji2uSiYgfedCdNc4L6W+1E2g==", - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://opencollective.com/serialport/donate" - } - }, - "node_modules/modbus-serial/node_modules/@serialport/bindings-cpp/node_modules/@serialport/parser-readline": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/@serialport/parser-readline/-/parser-readline-11.0.0.tgz", - "integrity": "sha512-rRAivhRkT3YO28WjmmG4FQX6L+KMb5/ikhyylRfzWPw0nSXy97+u07peS9CbHqaNvJkMhH1locp2H36aGMOEIA==", - "dependencies": { - "@serialport/parser-delimiter": "11.0.0" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://opencollective.com/serialport/donate" - } - }, - "node_modules/modbus-serial/node_modules/@serialport/parser-byte-length": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/@serialport/parser-byte-length/-/parser-byte-length-12.0.0.tgz", - "integrity": "sha512-0ei0txFAj+s6FTiCJFBJ1T2hpKkX8Md0Pu6dqMrYoirjPskDLJRgZGLqoy3/lnU1bkvHpnJO+9oJ3PB9v8rNlg==", - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://opencollective.com/serialport/donate" - } - }, - "node_modules/modbus-serial/node_modules/@serialport/parser-cctalk": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/@serialport/parser-cctalk/-/parser-cctalk-12.0.0.tgz", - "integrity": "sha512-0PfLzO9t2X5ufKuBO34DQKLXrCCqS9xz2D0pfuaLNeTkyGUBv426zxoMf3rsMRodDOZNbFblu3Ae84MOQXjnZw==", - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://opencollective.com/serialport/donate" - } - }, - "node_modules/modbus-serial/node_modules/@serialport/parser-delimiter": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/@serialport/parser-delimiter/-/parser-delimiter-12.0.0.tgz", - "integrity": "sha512-gu26tVt5lQoybhorLTPsH2j2LnX3AOP2x/34+DUSTNaUTzu2fBXw+isVjQJpUBFWu6aeQRZw5bJol5X9Gxjblw==", - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://opencollective.com/serialport/donate" - } - }, - "node_modules/modbus-serial/node_modules/@serialport/parser-inter-byte-timeout": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/@serialport/parser-inter-byte-timeout/-/parser-inter-byte-timeout-12.0.0.tgz", - "integrity": "sha512-GnCh8K0NAESfhCuXAt+FfBRz1Cf9CzIgXfp7SdMgXwrtuUnCC/yuRTUFWRvuzhYKoAo1TL0hhUo77SFHUH1T/w==", - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://opencollective.com/serialport/donate" - } - }, - "node_modules/modbus-serial/node_modules/@serialport/parser-packet-length": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/@serialport/parser-packet-length/-/parser-packet-length-12.0.0.tgz", - "integrity": "sha512-p1hiCRqvGHHLCN/8ZiPUY/G0zrxd7gtZs251n+cfNTn+87rwcdUeu9Dps3Aadx30/sOGGFL6brIRGK4l/t7MuQ==", - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/modbus-serial/node_modules/@serialport/parser-readline": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/@serialport/parser-readline/-/parser-readline-12.0.0.tgz", - "integrity": "sha512-O7cywCWC8PiOMvo/gglEBfAkLjp/SENEML46BXDykfKP5mTPM46XMaX1L0waWU6DXJpBgjaL7+yX6VriVPbN4w==", - "dependencies": { - "@serialport/parser-delimiter": "12.0.0" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://opencollective.com/serialport/donate" - } - }, - "node_modules/modbus-serial/node_modules/@serialport/parser-ready": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/@serialport/parser-ready/-/parser-ready-12.0.0.tgz", - "integrity": "sha512-ygDwj3O4SDpZlbrRUraoXIoIqb8sM7aMKryGjYTIF0JRnKeB1ys8+wIp0RFMdFbO62YriUDextHB5Um5cKFSWg==", - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://opencollective.com/serialport/donate" - } - }, - "node_modules/modbus-serial/node_modules/@serialport/parser-regex": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/@serialport/parser-regex/-/parser-regex-12.0.0.tgz", - "integrity": "sha512-dCAVh4P/pZrLcPv9NJ2mvPRBg64L5jXuiRxIlyxxdZGH4WubwXVXY/kBTihQmiAMPxbT3yshSX8f2+feqWsxqA==", - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://opencollective.com/serialport/donate" - } - }, - "node_modules/modbus-serial/node_modules/@serialport/parser-slip-encoder": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/@serialport/parser-slip-encoder/-/parser-slip-encoder-12.0.0.tgz", - "integrity": "sha512-0APxDGR9YvJXTRfY+uRGhzOhTpU5akSH183RUcwzN7QXh8/1jwFsFLCu0grmAUfi+fItCkR+Xr1TcNJLR13VNA==", - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://opencollective.com/serialport/donate" - } - }, - "node_modules/modbus-serial/node_modules/@serialport/parser-spacepacket": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/@serialport/parser-spacepacket/-/parser-spacepacket-12.0.0.tgz", - "integrity": "sha512-dozONxhPC/78pntuxpz/NOtVps8qIc/UZzdc/LuPvVsqCoJXiRxOg6ZtCP/W58iibJDKPZPAWPGYeZt9DJxI+Q==", - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://opencollective.com/serialport/donate" - } - }, - "node_modules/modbus-serial/node_modules/@serialport/stream": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/@serialport/stream/-/stream-12.0.0.tgz", - "integrity": "sha512-9On64rhzuqKdOQyiYLYv2lQOh3TZU/D3+IWCR5gk0alPel2nwpp4YwDEGiUBfrQZEdQ6xww0PWkzqth4wqwX3Q==", - "dependencies": { - "@serialport/bindings-interface": "1.2.2", - "debug": "4.3.4" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://opencollective.com/serialport/donate" - } - }, - "node_modules/modbus-serial/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/modbus-serial/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "node_modules/modbus-serial/node_modules/node-addon-api": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.0.0.tgz", - "integrity": "sha512-vgbBJTS4m5/KkE16t5Ly0WW9hz46swAstv0hYYwMtbG7AznRhNyfLRe8HZAiWIpcHzoO7HxhLuBQj9rJ/Ho0ZA==" - }, - "node_modules/modbus-serial/node_modules/serialport": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/serialport/-/serialport-12.0.0.tgz", - "integrity": "sha512-AmH3D9hHPFmnF/oq/rvigfiAouAKyK/TjnrkwZRYSFZxNggJxwvbAbfYrLeuvq7ktUdhuHdVdSjj852Z55R+uA==", - "dependencies": { - "@serialport/binding-mock": "10.2.2", - "@serialport/bindings-cpp": "12.0.1", - "@serialport/parser-byte-length": "12.0.0", - "@serialport/parser-cctalk": "12.0.0", - "@serialport/parser-delimiter": "12.0.0", - "@serialport/parser-inter-byte-timeout": "12.0.0", - "@serialport/parser-packet-length": "12.0.0", - "@serialport/parser-readline": "12.0.0", - "@serialport/parser-ready": "12.0.0", - "@serialport/parser-regex": "12.0.0", - "@serialport/parser-slip-encoder": "12.0.0", - "@serialport/parser-spacepacket": "12.0.0", - "@serialport/stream": "12.0.0", - "debug": "4.3.4" - }, - "engines": { - "node": ">=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/serialport/donate" - } - }, - "node_modules/modbus/node_modules/@serialport/bindings-cpp": { - "version": "12.0.1", - "resolved": "https://registry.npmjs.org/@serialport/bindings-cpp/-/bindings-cpp-12.0.1.tgz", - "integrity": "sha512-r2XOwY2dDvbW7dKqSPIk2gzsr6M6Qpe9+/Ngs94fNaNlcTRCV02PfaoDmRgcubpNVVcLATlxSxPTIDw12dbKOg==", - "hasInstallScript": true, - "dependencies": { - "@serialport/bindings-interface": "1.2.2", - "@serialport/parser-readline": "11.0.0", - "debug": "4.3.4", - "node-addon-api": "7.0.0", - "node-gyp-build": "4.6.0" - }, - "engines": { - "node": ">=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/serialport/donate" - } - }, - "node_modules/modbus/node_modules/@serialport/bindings-cpp/node_modules/@serialport/parser-delimiter": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/@serialport/parser-delimiter/-/parser-delimiter-11.0.0.tgz", - "integrity": "sha512-aZLJhlRTjSmEwllLG7S4J8s8ctRAS0cbvCpO87smLvl3e4BgzbVgF6Z6zaJd3Aji2uSiYgfedCdNc4L6W+1E2g==", - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://opencollective.com/serialport/donate" - } - }, - "node_modules/modbus/node_modules/@serialport/bindings-cpp/node_modules/@serialport/parser-readline": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/@serialport/parser-readline/-/parser-readline-11.0.0.tgz", - "integrity": "sha512-rRAivhRkT3YO28WjmmG4FQX6L+KMb5/ikhyylRfzWPw0nSXy97+u07peS9CbHqaNvJkMhH1locp2H36aGMOEIA==", - "dependencies": { - "@serialport/parser-delimiter": "11.0.0" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://opencollective.com/serialport/donate" - } - }, - "node_modules/modbus/node_modules/@serialport/parser-byte-length": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/@serialport/parser-byte-length/-/parser-byte-length-12.0.0.tgz", - "integrity": "sha512-0ei0txFAj+s6FTiCJFBJ1T2hpKkX8Md0Pu6dqMrYoirjPskDLJRgZGLqoy3/lnU1bkvHpnJO+9oJ3PB9v8rNlg==", - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://opencollective.com/serialport/donate" - } - }, - "node_modules/modbus/node_modules/@serialport/parser-cctalk": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/@serialport/parser-cctalk/-/parser-cctalk-12.0.0.tgz", - "integrity": "sha512-0PfLzO9t2X5ufKuBO34DQKLXrCCqS9xz2D0pfuaLNeTkyGUBv426zxoMf3rsMRodDOZNbFblu3Ae84MOQXjnZw==", - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://opencollective.com/serialport/donate" - } - }, - "node_modules/modbus/node_modules/@serialport/parser-delimiter": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/@serialport/parser-delimiter/-/parser-delimiter-12.0.0.tgz", - "integrity": "sha512-gu26tVt5lQoybhorLTPsH2j2LnX3AOP2x/34+DUSTNaUTzu2fBXw+isVjQJpUBFWu6aeQRZw5bJol5X9Gxjblw==", - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://opencollective.com/serialport/donate" - } - }, - "node_modules/modbus/node_modules/@serialport/parser-inter-byte-timeout": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/@serialport/parser-inter-byte-timeout/-/parser-inter-byte-timeout-12.0.0.tgz", - "integrity": "sha512-GnCh8K0NAESfhCuXAt+FfBRz1Cf9CzIgXfp7SdMgXwrtuUnCC/yuRTUFWRvuzhYKoAo1TL0hhUo77SFHUH1T/w==", - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://opencollective.com/serialport/donate" - } - }, - "node_modules/modbus/node_modules/@serialport/parser-packet-length": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/@serialport/parser-packet-length/-/parser-packet-length-12.0.0.tgz", - "integrity": "sha512-p1hiCRqvGHHLCN/8ZiPUY/G0zrxd7gtZs251n+cfNTn+87rwcdUeu9Dps3Aadx30/sOGGFL6brIRGK4l/t7MuQ==", - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/modbus/node_modules/@serialport/parser-readline": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/@serialport/parser-readline/-/parser-readline-12.0.0.tgz", - "integrity": "sha512-O7cywCWC8PiOMvo/gglEBfAkLjp/SENEML46BXDykfKP5mTPM46XMaX1L0waWU6DXJpBgjaL7+yX6VriVPbN4w==", - "dependencies": { - "@serialport/parser-delimiter": "12.0.0" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://opencollective.com/serialport/donate" - } - }, - "node_modules/modbus/node_modules/@serialport/parser-ready": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/@serialport/parser-ready/-/parser-ready-12.0.0.tgz", - "integrity": "sha512-ygDwj3O4SDpZlbrRUraoXIoIqb8sM7aMKryGjYTIF0JRnKeB1ys8+wIp0RFMdFbO62YriUDextHB5Um5cKFSWg==", - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://opencollective.com/serialport/donate" - } - }, - "node_modules/modbus/node_modules/@serialport/parser-regex": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/@serialport/parser-regex/-/parser-regex-12.0.0.tgz", - "integrity": "sha512-dCAVh4P/pZrLcPv9NJ2mvPRBg64L5jXuiRxIlyxxdZGH4WubwXVXY/kBTihQmiAMPxbT3yshSX8f2+feqWsxqA==", - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://opencollective.com/serialport/donate" - } - }, - "node_modules/modbus/node_modules/@serialport/parser-slip-encoder": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/@serialport/parser-slip-encoder/-/parser-slip-encoder-12.0.0.tgz", - "integrity": "sha512-0APxDGR9YvJXTRfY+uRGhzOhTpU5akSH183RUcwzN7QXh8/1jwFsFLCu0grmAUfi+fItCkR+Xr1TcNJLR13VNA==", - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://opencollective.com/serialport/donate" - } - }, - "node_modules/modbus/node_modules/@serialport/parser-spacepacket": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/@serialport/parser-spacepacket/-/parser-spacepacket-12.0.0.tgz", - "integrity": "sha512-dozONxhPC/78pntuxpz/NOtVps8qIc/UZzdc/LuPvVsqCoJXiRxOg6ZtCP/W58iibJDKPZPAWPGYeZt9DJxI+Q==", - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://opencollective.com/serialport/donate" - } - }, - "node_modules/modbus/node_modules/@serialport/stream": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/@serialport/stream/-/stream-12.0.0.tgz", - "integrity": "sha512-9On64rhzuqKdOQyiYLYv2lQOh3TZU/D3+IWCR5gk0alPel2nwpp4YwDEGiUBfrQZEdQ6xww0PWkzqth4wqwX3Q==", - "dependencies": { - "@serialport/bindings-interface": "1.2.2", - "debug": "4.3.4" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://opencollective.com/serialport/donate" - } - }, - "node_modules/modbus/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/modbus/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "node_modules/modbus/node_modules/node-addon-api": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.0.0.tgz", - "integrity": "sha512-vgbBJTS4m5/KkE16t5Ly0WW9hz46swAstv0hYYwMtbG7AznRhNyfLRe8HZAiWIpcHzoO7HxhLuBQj9rJ/Ho0ZA==" - }, - "node_modules/modbus/node_modules/serialport": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/serialport/-/serialport-12.0.0.tgz", - "integrity": "sha512-AmH3D9hHPFmnF/oq/rvigfiAouAKyK/TjnrkwZRYSFZxNggJxwvbAbfYrLeuvq7ktUdhuHdVdSjj852Z55R+uA==", - "dependencies": { - "@serialport/binding-mock": "10.2.2", - "@serialport/bindings-cpp": "12.0.1", - "@serialport/parser-byte-length": "12.0.0", - "@serialport/parser-cctalk": "12.0.0", - "@serialport/parser-delimiter": "12.0.0", - "@serialport/parser-inter-byte-timeout": "12.0.0", - "@serialport/parser-packet-length": "12.0.0", - "@serialport/parser-readline": "12.0.0", - "@serialport/parser-ready": "12.0.0", - "@serialport/parser-regex": "12.0.0", - "@serialport/parser-slip-encoder": "12.0.0", - "@serialport/parser-spacepacket": "12.0.0", - "@serialport/stream": "12.0.0", - "debug": "4.3.4" - }, - "engines": { - "node": ">=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/serialport/donate" - } - }, - "node_modules/mqemitter": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/mqemitter/-/mqemitter-4.5.0.tgz", - "integrity": "sha512-Mp/zytFeIv6piJQkEKnncHcP4R/ErJc5C7dfonkhkNUT2LA/nTayrfNxbipp3M5iCJUTQSUtzfQAQA3XVcKz6w==", - "dependencies": { - "fastparallel": "^2.3.0", - "qlobber": "^5.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/mqtt": { - "version": "4.3.8", - "resolved": "https://registry.npmjs.org/mqtt/-/mqtt-4.3.8.tgz", - "integrity": "sha512-2xT75uYa0kiPEF/PE0VPdavmEkoBzMT/UL9moid0rAvlCtV48qBwxD62m7Ld/4j8tSkIO1E/iqRl/S72SEOhOw==", - "dependencies": { - "commist": "^1.0.0", - "concat-stream": "^2.0.0", - "debug": "^4.1.1", - "duplexify": "^4.1.1", - "help-me": "^3.0.0", - "inherits": "^2.0.3", - "lru-cache": "^6.0.0", - "minimist": "^1.2.5", - "mqtt-packet": "^6.8.0", - "number-allocator": "^1.0.9", - "pump": "^3.0.0", - "readable-stream": "^3.6.0", - "reinterval": "^1.1.0", - "rfdc": "^1.3.0", - "split2": "^3.1.0", - "ws": "^7.5.5", - "xtend": "^4.0.2" - }, - "bin": { - "mqtt": "bin/mqtt.js", - "mqtt_pub": "bin/pub.js", - "mqtt_sub": "bin/sub.js" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/mqtt-calculator": { - "resolved": "things/calculator/mqtt/js", - "link": true - }, - "node_modules/mqtt-packet": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/mqtt-packet/-/mqtt-packet-7.1.2.tgz", - "integrity": "sha512-FFZbcZ2omsf4c5TxEQfcX9hI+JzDpDKPT46OmeIBpVA7+t32ey25UNqlqNXTmeZOr5BLsSIERpQQLsFWJS94SQ==", - "dependencies": { - "bl": "^4.0.2", - "debug": "^4.1.1", - "process-nextick-args": "^2.0.1" - } - }, - "node_modules/mqtt-packet/node_modules/bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "dependencies": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, - "node_modules/mqtt-packet/node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, - "node_modules/mqtt-packet/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/mqtt/node_modules/bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "dependencies": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, - "node_modules/mqtt/node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, - "node_modules/mqtt/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/mqtt/node_modules/mqtt-packet": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/mqtt-packet/-/mqtt-packet-6.10.0.tgz", - "integrity": "sha512-ja8+mFKIHdB1Tpl6vac+sktqy3gA8t9Mduom1BA75cI+R9AHnZOiaBQwpGiWnaVJLDGRdNhQmFaAqd7tkKSMGA==", - "dependencies": { - "bl": "^4.0.2", - "debug": "^4.1.1", - "process-nextick-args": "^2.0.1" - } - }, - "node_modules/mqtt/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - }, - "node_modules/multicast-dns": { - "version": "7.2.5", - "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz", - "integrity": "sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==", - "dependencies": { - "dns-packet": "^5.2.2", - "thunky": "^1.0.2" - }, - "bin": { - "multicast-dns": "cli.js" - } - }, - "node_modules/nan": { - "version": "2.14.2", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz", - "integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==", - "optional": true - }, - "node_modules/napi-build-utils": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", - "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==", - "optional": true - }, - "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==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/node-abi": { - "version": "2.30.1", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.30.1.tgz", - "integrity": "sha512-/2D0wOQPgaUWzVSVgRMx+trKJRC2UG4SUc4oCJoXx9Uxjtp0Vy3/kt7zcbxHF8+Z/pK3UloLWzBISg72brfy1w==", - "optional": true, - "dependencies": { - "semver": "^5.4.1" - } - }, - "node_modules/node-abi/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "optional": true, - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/node-addon-api": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-6.1.0.tgz", - "integrity": "sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==" - }, - "node_modules/node-aead-crypto": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/node-aead-crypto/-/node-aead-crypto-2.2.2.tgz", - "integrity": "sha512-EtCLL1FmVjj2GlBNcLRn75ea+y6yGuEdoTpqc9zIiRkIKk1ucTsOPoKaHYYxHfMAXWqHu2Dw8a44VSSKz45UTg==", - "hasInstallScript": true, - "optional": true, - "dependencies": { - "bindings": "^1.5.0", - "nan": "2.14.x", - "prebuild-install": "^6.1.3" - }, - "engines": { - "node": ">4" - } - }, - "node_modules/node-coap-client": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/node-coap-client/-/node-coap-client-1.0.8.tgz", - "integrity": "sha512-ADki3zyiITRkKVKKa33OsE6Kkr8lMRdURvNgz+ozAuBFPY867BAq8AAsKL5MqlIaFMfW3b2Dq27gaDiRFH4T6w==", - "dependencies": { - "debug": "^4.1.1", - "node-dtls-client": "^0.6.0" - }, - "engines": { - "node": ">= 6.13.0" - } - }, - "node_modules/node-dtls-client": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/node-dtls-client/-/node-dtls-client-0.6.0.tgz", - "integrity": "sha512-TApzfBpbTkJOVouhgy9qJJB1Oui6sZz/1yNs1VNXTBWkF2ZgO594k58Y1moabAuTHzTUEc0XfwWmoz1yl8sx9Q==", - "dependencies": { - "debug": "^4.1.1", - "semver": "^7.0.0" - }, - "engines": { - "node": ">=6" - }, - "optionalDependencies": { - "node-aead-crypto": "^2.0.0" - } - }, - "node_modules/node-fetch": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, - "node_modules/node-gyp-build": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.6.0.tgz", - "integrity": "sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ==", - "bin": { - "node-gyp-build": "bin.js", - "node-gyp-build-optional": "optional.js", - "node-gyp-build-test": "build-test.js" - } - }, - "node_modules/nofilter": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/nofilter/-/nofilter-3.1.0.tgz", - "integrity": "sha512-l2NNj07e9afPnhAhvgVrCD/oy2Ai1yfLpuo3EpiO1jFTsB4sFz6oIfAfSZyQzVpkZQ9xS8ZS5g1jCBgq4Hwo0g==", - "engines": { - "node": ">=12.19" - } - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/npmlog": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", - "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", - "deprecated": "This package is no longer supported.", - "optional": true, - "dependencies": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.3", - "set-blocking": "~2.0.0" - } - }, - "node_modules/number-allocator": { - "version": "1.0.14", - "resolved": "https://registry.npmjs.org/number-allocator/-/number-allocator-1.0.14.tgz", - "integrity": "sha512-OrL44UTVAvkKdOdRQZIJpLkAdjXGTRda052sN4sO77bKEzYYqWKMBjQvrJFzqygI99gL6Z4u2xctPW1tB8ErvA==", - "dependencies": { - "debug": "^4.3.1", - "js-sdsl": "4.3.0" - } - }, - "node_modules/number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ==", - "optional": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "optional": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-inspect": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", - "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==", - "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==", - "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==", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/one-time": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", - "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", - "dependencies": { - "fn.name": "1.x.x" - } - }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-to-regexp": { - "version": "0.1.10", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz", - "integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==" - }, - "node_modules/pathval": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", - "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/popsicle": { - "version": "12.1.2", - "resolved": "https://registry.npmjs.org/popsicle/-/popsicle-12.1.2.tgz", - "integrity": "sha512-xE2vEUa15TiHvFhGmKTtdKk9aSLL5CHX8Vw5kHfVM3R0YHiaTon6Ybsamw0XYqMR+Ng2RijX88iYUKPBMpLBww==", - "dependencies": { - "popsicle-content-encoding": "^1.0.0", - "popsicle-cookie-jar": "^1.0.1", - "popsicle-redirects": "^1.1.0", - "popsicle-transport-http": "^1.1.0", - "popsicle-transport-xhr": "^2.0.0", - "popsicle-user-agent": "^1.0.0", - "servie": "^4.3.3", - "throwback": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/popsicle-content-encoding": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/popsicle-content-encoding/-/popsicle-content-encoding-1.0.0.tgz", - "integrity": "sha512-4Df+vTfM8wCCJVTzPujiI6eOl3SiWQkcZg0AMrOkD1enMXsF3glIkFUZGvour1Sj7jOWCsNSEhBxpbbhclHhzw==", - "peerDependencies": { - "servie": "^4.0.0" - } - }, - "node_modules/popsicle-cookie-jar": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/popsicle-cookie-jar/-/popsicle-cookie-jar-1.0.1.tgz", - "integrity": "sha512-QVIZhADP8nDbXIQW6wq8GU9IOSE8INUACO/9KD9TFKQ7qq8r/y3qUDz59xIi6p6TH19lCJJyBAPSXP1liIoySw==", - "dependencies": { - "@types/tough-cookie": "^4.0.2", - "tough-cookie": "^4.1.3" - }, - "engines": { - "node": ">=8" - }, - "peerDependencies": { - "servie": "^4.0.0" - } - }, - "node_modules/popsicle-redirects": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/popsicle-redirects/-/popsicle-redirects-1.1.1.tgz", - "integrity": "sha512-mC2HrKjdTAWDalOjGxlXw9j6Qxrz/Yd2ui6bPxpi2IQDYWpF4gUAMxbA8EpSWJhLi0PuWKDwTHHPrUPGutAoIA==", - "peerDependencies": { - "servie": "^4.1.0" - } - }, - "node_modules/popsicle-transport-http": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/popsicle-transport-http/-/popsicle-transport-http-1.2.1.tgz", - "integrity": "sha512-i5r3IGHkGiBDm1oPFvOfEeSGWR0lQJcsdTqwvvDjXqcTHYJJi4iSi3ecXIttDiTBoBtRAFAE9nF91fspQr63FQ==", - "dependencies": { - "make-error-cause": "^2.2.0" - }, - "peerDependencies": { - "servie": "^4.2.0" - } - }, - "node_modules/popsicle-transport-xhr": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/popsicle-transport-xhr/-/popsicle-transport-xhr-2.0.0.tgz", - "integrity": "sha512-5Sbud4Widngf1dodJE5cjEYXkzEUIl8CzyYRYR57t6vpy9a9KPGQX6KBKdPjmBZlR5A06pOBXuJnVr23l27rtA==", - "peerDependencies": { - "servie": "^4.2.0" - } - }, - "node_modules/popsicle-user-agent": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/popsicle-user-agent/-/popsicle-user-agent-1.0.0.tgz", - "integrity": "sha512-epKaq3TTfTzXcxBxjpoKYMcTTcAX8Rykus6QZu77XNhJuRHSRxMd+JJrbX/3PFI0opFGSN0BabbAYCbGxbu0mA==", - "peerDependencies": { - "servie": "^4.0.0" - } - }, - "node_modules/prebuild-install": { - "version": "6.1.4", - "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-6.1.4.tgz", - "integrity": "sha512-Z4vpywnK1lBg+zdPCVCsKq0xO66eEV9rWo2zrROGGiRS4JtueBOdlB1FnY8lcy7JsUud/Q3ijUxyWN26Ika0vQ==", - "optional": true, - "dependencies": { - "detect-libc": "^1.0.3", - "expand-template": "^2.0.3", - "github-from-package": "0.0.0", - "minimist": "^1.2.3", - "mkdirp-classic": "^0.5.3", - "napi-build-utils": "^1.0.1", - "node-abi": "^2.21.0", - "npmlog": "^4.0.1", - "pump": "^3.0.0", - "rc": "^1.2.7", - "simple-get": "^3.0.3", - "tar-fs": "^2.0.0", - "tunnel-agent": "^0.6.0" - }, - "bin": { - "prebuild-install": "bin.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/process": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", - "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", - "engines": { - "node": ">= 0.6.0" - } - }, - "node_modules/process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" - }, - "node_modules/protobufjs": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.4.0.tgz", - "integrity": "sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw==", - "hasInstallScript": true, - "dependencies": { - "@protobufjs/aspromise": "^1.1.2", - "@protobufjs/base64": "^1.1.2", - "@protobufjs/codegen": "^2.0.4", - "@protobufjs/eventemitter": "^1.1.0", - "@protobufjs/fetch": "^1.1.0", - "@protobufjs/float": "^1.0.2", - "@protobufjs/inquire": "^1.1.0", - "@protobufjs/path": "^1.1.2", - "@protobufjs/pool": "^1.1.0", - "@protobufjs/utf8": "^1.1.0", - "@types/node": ">=13.7.0", - "long": "^5.0.0" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "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==", - "dependencies": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/psl": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", - "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==" - }, - "node_modules/pump": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz", - "integrity": "sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==", - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "engines": { - "node": ">=6" - } - }, - "node_modules/qlobber": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/qlobber/-/qlobber-5.0.3.tgz", - "integrity": "sha512-wW4GTZPePyh0RgOsM18oDyOUlXfurVRgoNyJfS+y7VWPyd0GYhQp5T2tycZFZjonH+hngxIfklGJhTP/ghidgQ==", - "engines": { - "node": ">= 8" - } - }, - "node_modules/qs": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", - "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", - "dependencies": { - "side-channel": "^1.0.6" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/query-string": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/query-string/-/query-string-7.1.3.tgz", - "integrity": "sha512-hh2WYhq4fi8+b+/2Kg9CEge4fDPvHS534aOOvOZeQ3+Vf2mCFsaFBYj0i+iXcAq6I9Vzp5fjMFBlONvayDC1qg==", - "dependencies": { - "decode-uri-component": "^0.2.2", - "filter-obj": "^1.1.0", - "split-on-first": "^1.0.0", - "strict-uri-encode": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/querystringify": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", - "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" - }, - "node_modules/randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, - "dependencies": { - "safe-buffer": "^5.1.0" - } - }, - "node_modules/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==", - "engines": { - "node": ">= 0.6" - } - }, - "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==", - "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==", - "optional": true, - "dependencies": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "bin": { - "rc": "cli.js" - } - }, - "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==", - "optional": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/readable-stream": { - "version": "4.5.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.2.tgz", - "integrity": "sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==", - "dependencies": { - "abort-controller": "^3.0.0", - "buffer": "^6.0.3", - "events": "^3.3.0", - "process": "^0.11.10", - "string_decoder": "^1.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/regenerator-runtime": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", - "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" - }, - "node_modules/reinterval": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/reinterval/-/reinterval-1.1.0.tgz", - "integrity": "sha512-QIRet3SYrGp0HUHO88jVskiG6seqUGC5iAG7AwI/BV4ypGcuqk9Du6YQBUOUqm9c8pw1eyLoIaONifRua1lsEQ==" - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" - }, - "node_modules/ret": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/ret/-/ret-0.2.2.tgz", - "integrity": "sha512-M0b3YWQs7R3Z917WRQy1HHA7Ba7D8hvZg6UE5mLykJxQVE2ju0IXbGlaHPPlkY+WN7wFP+wUMXmBFA0aV6vYGQ==", - "engines": { - "node": ">=4" - } - }, - "node_modules/retimer": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/retimer/-/retimer-3.0.0.tgz", - "integrity": "sha512-WKE0j11Pa0ZJI5YIk0nflGI7SQsfl2ljihVy7ogh7DeQSeYAUi0ubZ/yEueGtDfUPk6GH5LRw1hBdLq4IwUBWA==" - }, - "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/rfdc": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", - "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==" - }, - "node_modules/rxjs": { - "version": "5.5.11", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-5.5.11.tgz", - "integrity": "sha512-3bjO7UwWfA2CV7lmwYMBzj4fQ6Cq+ftHc2MvUe+WMS7wcdJ1LosDWmdjPQanYp2dBRj572p7PeU81JUxHKOcBA==", - "dependencies": { - "symbol-observable": "1.0.1" - }, - "engines": { - "npm": ">=2.0.0" - } - }, - "node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "node_modules/safe-regex2": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/safe-regex2/-/safe-regex2-2.0.0.tgz", - "integrity": "sha512-PaUSFsUaNNuKwkBijoAPHAK6/eM6VirvyPWlZ7BAQy4D+hCvh4B6lIG+nPdhbFfIbP+gTGBcrdsOaUs0F+ZBOQ==", - "dependencies": { - "ret": "~0.2.0" - } - }, - "node_modules/safe-stable-stringify": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", - "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", - "engines": { - "node": ">=10" - } - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/send": { - "version": "0.19.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", - "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", - "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" - } - }, - "node_modules/send/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/send/node_modules/debug/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "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==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/serialize-javascript": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", - "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", - "dev": true, - "dependencies": { - "randombytes": "^2.1.0" - } - }, - "node_modules/serialport": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/serialport/-/serialport-11.0.1.tgz", - "integrity": "sha512-j/ntDuewAkqL6g5wKjwV2RTyLBL9cpob8aRd3yLAViYApTsJoYqRleyuzst0OboNTBjBsoxQ4YKYhuYHi1XViQ==", - "dependencies": { - "@serialport/binding-mock": "10.2.2", - "@serialport/bindings-cpp": "11.0.3", - "@serialport/parser-byte-length": "11.0.1", - "@serialport/parser-cctalk": "11.0.1", - "@serialport/parser-delimiter": "11.0.1", - "@serialport/parser-inter-byte-timeout": "11.0.1", - "@serialport/parser-packet-length": "11.0.1", - "@serialport/parser-readline": "11.0.1", - "@serialport/parser-ready": "11.0.1", - "@serialport/parser-regex": "11.0.1", - "@serialport/parser-slip-encoder": "11.0.1", - "@serialport/parser-spacepacket": "11.0.1", - "@serialport/stream": "11.0.1", - "debug": "4.3.4" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://opencollective.com/serialport/donate" - } - }, - "node_modules/serialport/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/serialport/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "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==", - "dependencies": { - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.19.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/servie": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/servie/-/servie-4.3.3.tgz", - "integrity": "sha512-b0IrY3b1gVMsWvJppCf19g1p3JSnS0hQi6xu4Hi40CIhf0Lx8pQHcvBL+xunShpmOiQzg1NOia812NAWdSaShw==", - "dependencies": { - "@servie/events": "^1.0.0", - "byte-length": "^1.0.2", - "ts-expect": "^1.1.0" - } - }, - "node_modules/set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", - "optional": true - }, - "node_modules/set-function-length": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", - "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", - "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" - }, - "node_modules/side-channel": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", - "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", - "dependencies": { - "call-bind": "^1.0.7", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4", - "object-inspect": "^1.13.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "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==", - "optional": true - }, - "node_modules/simple-concat": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", - "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "optional": true - }, - "node_modules/simple-get": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.1.tgz", - "integrity": "sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==", - "optional": true, - "dependencies": { - "decompress-response": "^4.2.0", - "once": "^1.3.1", - "simple-concat": "^1.0.0" - } - }, - "node_modules/simple-swizzle": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", - "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", - "dependencies": { - "is-arrayish": "^0.3.1" - } - }, - "node_modules/slugify": { - "version": "1.6.6", - "resolved": "https://registry.npmjs.org/slugify/-/slugify-1.6.6.tgz", - "integrity": "sha512-h+z7HKHYXj6wJU+AnS/+IH8Uh9fdcX1Lrhg1/VMdf9PwoBQXFcXiAdsy2tSK0P6gKwJLXp02r90ahUCqHk9rrw==", - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/smart-home": { - "resolved": "mashups/smart-home", - "link": true - }, - "node_modules/snappy": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/snappy/-/snappy-7.2.2.tgz", - "integrity": "sha512-iADMq1kY0v3vJmGTuKcFWSXt15qYUz7wFkArOrsSg0IFfI3nJqIJvK2/ZbEIndg7erIJLtAVX2nSOqPz7DcwbA==", - "optional": true, - "engines": { - "node": ">= 10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" - }, - "optionalDependencies": { - "@napi-rs/snappy-android-arm-eabi": "7.2.2", - "@napi-rs/snappy-android-arm64": "7.2.2", - "@napi-rs/snappy-darwin-arm64": "7.2.2", - "@napi-rs/snappy-darwin-x64": "7.2.2", - "@napi-rs/snappy-freebsd-x64": "7.2.2", - "@napi-rs/snappy-linux-arm-gnueabihf": "7.2.2", - "@napi-rs/snappy-linux-arm64-gnu": "7.2.2", - "@napi-rs/snappy-linux-arm64-musl": "7.2.2", - "@napi-rs/snappy-linux-x64-gnu": "7.2.2", - "@napi-rs/snappy-linux-x64-musl": "7.2.2", - "@napi-rs/snappy-win32-arm64-msvc": "7.2.2", - "@napi-rs/snappy-win32-ia32-msvc": "7.2.2", - "@napi-rs/snappy-win32-x64-msvc": "7.2.2" - } - }, - "node_modules/split-on-first": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz", - "integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==", - "engines": { - "node": ">=6" - } - }, - "node_modules/split2": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz", - "integrity": "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==", - "dependencies": { - "readable-stream": "^3.0.0" - } - }, - "node_modules/split2/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/stack-trace": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", - "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", - "engines": { - "node": "*" - } - }, - "node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/stream-shift": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.3.tgz", - "integrity": "sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==" - }, - "node_modules/strict-uri-encode": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz", - "integrity": "sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==", - "engines": { - "node": ">=4" - } - }, - "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, - "node_modules/string_decoder/node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/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==", - "devOptional": true, - "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==", - "devOptional": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/symbol-observable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.0.1.tgz", - "integrity": "sha512-Kb3PrPYz4HanVF1LVGuAdW6LoVgIwjUYJGzFe7NDrBLCN4lsV/5J0MFurV+ygS4bRVwrCEt2c7MQ1R2a72oJDw==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/tar-fs": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", - "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", - "optional": true, - "dependencies": { - "chownr": "^1.1.1", - "mkdirp-classic": "^0.5.2", - "pump": "^3.0.0", - "tar-stream": "^2.1.4" - } - }, - "node_modules/tar-stream": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", - "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", - "optional": true, - "dependencies": { - "bl": "^4.0.3", - "end-of-stream": "^1.4.1", - "fs-constants": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/tar-stream/node_modules/bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "optional": true, - "dependencies": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, - "node_modules/tar-stream/node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "optional": true, - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, - "node_modules/tar-stream/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==", - "optional": true, - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/text-hex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", - "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==" - }, - "node_modules/throwback": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/throwback/-/throwback-4.1.0.tgz", - "integrity": "sha512-dLFe8bU8SeH0xeqeKL7BNo8XoPC/o91nz9/ooeplZPiso+DZukhoyZcSz9TFnUNScm+cA9qjU1m1853M6sPOng==" - }, - "node_modules/thunky": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", - "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==" - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/tough-cookie": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz", - "integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==", - "dependencies": { - "psl": "^1.1.33", - "punycode": "^2.1.1", - "universalify": "^0.2.0", - "url-parse": "^1.5.3" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" - }, - "node_modules/triple-beam": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz", - "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==", - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/ts-expect": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/ts-expect/-/ts-expect-1.3.0.tgz", - "integrity": "sha512-e4g0EJtAjk64xgnFPD6kTBUtpnMVzDrMb12N1YZV0VvSlhnVT3SGxiYTLdGy8Q5cYHOIC/FAHmZ10eGrAguicQ==" - }, - "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==", - "dev": true, - "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 - } - } - }, - "node_modules/ts-node/node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true, - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - }, - "node_modules/tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", - "optional": true, - "dependencies": { - "safe-buffer": "^5.0.1" - }, - "engines": { - "node": "*" - } - }, - "node_modules/type-detect": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.1.0.tgz", - "integrity": "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "dependencies": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/typedarray": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==" - }, - "node_modules/typescript": { - "version": "4.9.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", - "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", - "dev": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=4.2.0" - } - }, - "node_modules/undici-types": { - "version": "6.19.8", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", - "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==" - }, - "node_modules/universalify": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", - "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/uritemplate": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/uritemplate/-/uritemplate-0.3.4.tgz", - "integrity": "sha512-enADBvHfhjrwxFMTVWeIIYz51SZ91uC6o2MR/NQTVljJB6HTZ8eQL3Q7JBj3RxNISA14MOwJaU3vpf5R6dyxHA==" - }, - "node_modules/url-parse": { - "version": "1.5.10", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", - "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", - "dependencies": { - "querystringify": "^2.1.1", - "requires-port": "^1.0.0" - } - }, - "node_modules/url-polyfill": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/url-polyfill/-/url-polyfill-1.1.12.tgz", - "integrity": "sha512-mYFmBHCapZjtcNHW0MDq9967t+z4Dmg5CJ0KqysK3+ZbyoNOWQHksGCTWwDhxGXllkWlOc10Xfko6v4a3ucM6A==" - }, - "node_modules/url-toolkit": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/url-toolkit/-/url-toolkit-2.1.6.tgz", - "integrity": "sha512-UaZ2+50am4HwrV2crR/JAf63Q4VvPYphe63WGeoJxeu8gmOm0qxPt+KsukfakPNrX9aymGNEkkaoICwn+OuvBw==" - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" - }, - "node_modules/utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/uuid": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-7.0.3.tgz", - "integrity": "sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg==", - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/uuid-parse": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/uuid-parse/-/uuid-parse-1.1.0.tgz", - "integrity": "sha512-OdmXxA8rDsQ7YpNVbKSJkNzTw2I+S5WsbMDnCtIWSQaosNAcWtFuI/YK1TjzUI6nbkgiqEyh8gWngfcv8Asd9A==" - }, - "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==", - "dev": true - }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/web-streams-polyfill": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", - "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", - "engines": { - "node": ">= 8" - } - }, - "node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" - }, - "node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, - "node_modules/wide-align": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", - "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", - "optional": true, - "dependencies": { - "string-width": "^1.0.2 || 2 || 3 || 4" - } - }, - "node_modules/winston": { - "version": "3.14.2", - "resolved": "https://registry.npmjs.org/winston/-/winston-3.14.2.tgz", - "integrity": "sha512-CO8cdpBB2yqzEf8v895L+GNKYJiEq8eKlHU38af3snQBQ+sdAIUepjMSguOIJC7ICbzm0ZI+Af2If4vIJrtmOg==", - "dependencies": { - "@colors/colors": "^1.6.0", - "@dabh/diagnostics": "^2.0.2", - "async": "^3.2.3", - "is-stream": "^2.0.0", - "logform": "^2.6.0", - "one-time": "^1.0.0", - "readable-stream": "^3.4.0", - "safe-stable-stringify": "^2.3.1", - "stack-trace": "0.0.x", - "triple-beam": "^1.3.0", - "winston-transport": "^4.7.0" - }, - "engines": { - "node": ">= 12.0.0" - } - }, - "node_modules/winston-loki": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/winston-loki/-/winston-loki-6.1.2.tgz", - "integrity": "sha512-l1iqDDaEUt63Q8arDsiVCXIrK3jLjPOEc5UTs+WMVnWf8D+A8ZRErAAXKDOduT240aNGzpTbCwe5zfdqqLlzMg==", - "dependencies": { - "async-exit-hook": "2.0.1", - "btoa": "^1.2.1", - "protobufjs": "^7.2.4", - "url-polyfill": "^1.1.12", - "winston-transport": "^4.3.0" - }, - "optionalDependencies": { - "snappy": "^7.2.2" - } - }, - "node_modules/winston-transport": { - "version": "4.7.1", - "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.7.1.tgz", - "integrity": "sha512-wQCXXVgfv/wUPOfb2x0ruxzwkcZfxcktz6JIMUaPLmcNhO4bZTwA/WtDWK74xV3F2dKu8YadrFv0qhwYjVEwhA==", - "dependencies": { - "logform": "^2.6.1", - "readable-stream": "^3.6.2", - "triple-beam": "^1.3.0" - }, - "engines": { - "node": ">= 12.0.0" - } - }, - "node_modules/winston-transport/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/winston/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/worker-timers": { - "version": "7.1.8", - "resolved": "https://registry.npmjs.org/worker-timers/-/worker-timers-7.1.8.tgz", - "integrity": "sha512-R54psRKYVLuzff7c1OTFcq/4Hue5Vlz4bFtNEIarpSiCYhpifHU3aIQI29S84o1j87ePCYqbmEJPqwBTf+3sfw==", - "dependencies": { - "@babel/runtime": "^7.24.5", - "tslib": "^2.6.2", - "worker-timers-broker": "^6.1.8", - "worker-timers-worker": "^7.0.71" - } - }, - "node_modules/worker-timers-broker": { - "version": "6.1.8", - "resolved": "https://registry.npmjs.org/worker-timers-broker/-/worker-timers-broker-6.1.8.tgz", - "integrity": "sha512-FUCJu9jlK3A8WqLTKXM9E6kAmI/dR1vAJ8dHYLMisLNB/n3GuaFIjJ7pn16ZcD1zCOf7P6H62lWIEBi+yz/zQQ==", - "dependencies": { - "@babel/runtime": "^7.24.5", - "fast-unique-numbers": "^8.0.13", - "tslib": "^2.6.2", - "worker-timers-worker": "^7.0.71" - } - }, - "node_modules/worker-timers-worker": { - "version": "7.0.71", - "resolved": "https://registry.npmjs.org/worker-timers-worker/-/worker-timers-worker-7.0.71.tgz", - "integrity": "sha512-ks/5YKwZsto1c2vmljroppOKCivB/ma97g9y77MAAz2TBBjPPgpoOiS1qYQKIgvGTr2QYPT3XhJWIB6Rj2MVPQ==", - "dependencies": { - "@babel/runtime": "^7.24.5", - "tslib": "^2.6.2" - } - }, - "node_modules/workerpool": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.5.1.tgz", - "integrity": "sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==", - "dev": true - }, - "node_modules/wot-thing-description-types": { - "version": "1.1.0-09-November-2023", - "resolved": "https://registry.npmjs.org/wot-thing-description-types/-/wot-thing-description-types-1.1.0-09-November-2023.tgz", - "integrity": "sha512-qgZ1Khg/L3SkIRSm4POVoj0P5MU4GIwxWdyQz2ckZzhnXesgPqe+AUzGxK37ArlxvaMytyjo0Cx2yfKoDHtlkA==" - }, - "node_modules/wot-thing-model-types": { - "version": "1.1.0-09-November-2023", - "resolved": "https://registry.npmjs.org/wot-thing-model-types/-/wot-thing-model-types-1.1.0-09-November-2023.tgz", - "integrity": "sha512-BwmZGEG5VCth34RwXbQutFre7dcSanm1OhYve6lTxuGyqeRAQsj33CUrN5PjU5LSs68Xubm0FbKJZPvWGFa6LA==" - }, - "node_modules/wot-typescript-definitions": { - "version": "0.8.0-SNAPSHOT.29", - "resolved": "https://registry.npmjs.org/wot-typescript-definitions/-/wot-typescript-definitions-0.8.0-SNAPSHOT.29.tgz", - "integrity": "sha512-V5r/JSbFs/eaWgQavEEvaldjYoMuucJ9Ae0BDfUbSm6d9rJJYLEwfrwFKxBCD25Kdroea+kNlQMrYKk783OREQ==", - "dependencies": { - "wot-thing-description-types": "1.1.0-09-November-2023" - } - }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" - }, - "node_modules/ws": { - "version": "7.5.10", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", - "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", - "engines": { - "node": ">=8.3.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true + "name": "test-things", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "test-things", + "license": "EPL-2.0 OR W3C-20150513", + "workspaces": [ + "./mashups/*", + "./things/*/*/*" + ], + "devDependencies": { + "@node-wot/binding-http": "~0.8.16", + "@node-wot/core": "~0.8.16", + "@types/chai": "^4.3.17", + "@types/chai-as-promised": "^7.1.8", + "@types/mocha": "^10.0.7", + "@types/node": "^22.4.1", + "@typescript-eslint/eslint-plugin": "^8.6.0", + "@typescript-eslint/parser": "^8.6.0", + "ajv": "^8.12.0", + "chai": "^4.5.0", + "chai-as-promised": "^7.1.1", + "eslint": "^8.57.1", + "eslint-config-prettier": "^9.1.0", + "eslint-config-standard": "^17.1.0", + "eslint-plugin-import": "^2.30.0", + "eslint-plugin-node": "^11.1.0", + "eslint-plugin-notice": "^1.0.0", + "eslint-plugin-unused-imports": "^4.1.4", + "eslint-plugin-workspaces": "^0.10.1", + "mocha": "^10.7.3", + "prettier": "^2.3.2" + } + }, + "mashups/smart-home": { + "version": "1.0.0", + "license": "EPL-2.0 OR W3C-20150513", + "dependencies": { + "@node-wot/binding-coap": "~0.8.16", + "@node-wot/binding-http": "~0.8.16", + "@node-wot/binding-mqtt": "~0.8.16", + "@node-wot/core": "~0.8.16" + }, + "devDependencies": { + "@types/debug": "^4.1.12", + "@types/node-fetch": "^2.6.11", + "dotenv": "^16.4.5", + "typescript": "^4.7.4" + } + }, + "node_modules/@babel/runtime": { + "version": "7.25.6", + "license": "MIT", + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@colors/colors": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", + "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==", + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@dabh/diagnostics": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz", + "integrity": "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==", + "dependencies": { + "colorspace": "1.1.x", + "enabled": "2.0.x", + "kuler": "^2.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.11.1", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.4", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/ajv": { + "version": "6.12.6", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { + "version": "1.1.11", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": { + "version": "0.4.1", + "dev": true, + "license": "MIT" + }, + "node_modules/@eslint/eslintrc/node_modules/minimatch": { + "version": "3.1.2", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@eslint/js": { + "version": "8.57.1", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.13.0", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.3", + "debug": "^4.3.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": { + "version": "1.1.11", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@humanwhocodes/config-array/node_modules/minimatch": { + "version": "3.1.2", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.3", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@leichtgewicht/ip-codec": { + "version": "2.0.5", + "license": "MIT" + }, + "node_modules/@napi-rs/snappy-android-arm-eabi": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/@napi-rs/snappy-android-arm-eabi/-/snappy-android-arm-eabi-7.2.2.tgz", + "integrity": "sha512-H7DuVkPCK5BlAr1NfSU8bDEN7gYs+R78pSHhDng83QxRnCLmVIZk33ymmIwurmoA1HrdTxbkbuNl+lMvNqnytw==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/snappy-android-arm64": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/@napi-rs/snappy-android-arm64/-/snappy-android-arm64-7.2.2.tgz", + "integrity": "sha512-2R/A3qok+nGtpVK8oUMcrIi5OMDckGYNoBLFyli3zp8w6IArPRfg1yOfVUcHvpUDTo9T7LOS1fXgMOoC796eQw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/snappy-darwin-arm64": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/@napi-rs/snappy-darwin-arm64/-/snappy-darwin-arm64-7.2.2.tgz", + "integrity": "sha512-USgArHbfrmdbuq33bD5ssbkPIoT7YCXCRLmZpDS6dMDrx+iM7eD2BecNbOOo7/v1eu6TRmQ0xOzeQ6I/9FIi5g==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/snappy-darwin-x64": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/@napi-rs/snappy-darwin-x64/-/snappy-darwin-x64-7.2.2.tgz", + "integrity": "sha512-0APDu8iO5iT0IJKblk2lH0VpWSl9zOZndZKnBYIc+ei1npw2L5QvuErFOTeTdHBtzvUHASB+9bvgaWnQo4PvTQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/snappy-freebsd-x64": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/@napi-rs/snappy-freebsd-x64/-/snappy-freebsd-x64-7.2.2.tgz", + "integrity": "sha512-mRTCJsuzy0o/B0Hnp9CwNB5V6cOJ4wedDTWEthsdKHSsQlO7WU9W1yP7H3Qv3Ccp/ZfMyrmG98Ad7u7lG58WXA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/snappy-linux-arm-gnueabihf": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/@napi-rs/snappy-linux-arm-gnueabihf/-/snappy-linux-arm-gnueabihf-7.2.2.tgz", + "integrity": "sha512-v1uzm8+6uYjasBPcFkv90VLZ+WhLzr/tnfkZ/iD9mHYiULqkqpRuC8zvc3FZaJy5wLQE9zTDkTJN1IvUcZ+Vcg==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/snappy-linux-arm64-gnu": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/@napi-rs/snappy-linux-arm64-gnu/-/snappy-linux-arm64-gnu-7.2.2.tgz", + "integrity": "sha512-LrEMa5pBScs4GXWOn6ZYXfQ72IzoolZw5txqUHVGs8eK4g1HR9HTHhb2oY5ySNaKakG5sOgMsb1rwaEnjhChmQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/snappy-linux-arm64-musl": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/@napi-rs/snappy-linux-arm64-musl/-/snappy-linux-arm64-musl-7.2.2.tgz", + "integrity": "sha512-3orWZo9hUpGQcB+3aTLW7UFDqNCQfbr0+MvV67x8nMNYj5eAeUtMmUE/HxLznHO4eZ1qSqiTwLbVx05/Socdlw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/snappy-linux-x64-gnu": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/@napi-rs/snappy-linux-x64-gnu/-/snappy-linux-x64-gnu-7.2.2.tgz", + "integrity": "sha512-jZt8Jit/HHDcavt80zxEkDpH+R1Ic0ssiVCoueASzMXa7vwPJeF4ZxZyqUw4qeSy7n8UUExomu8G8ZbP6VKhgw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/snappy-linux-x64-musl": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/@napi-rs/snappy-linux-x64-musl/-/snappy-linux-x64-musl-7.2.2.tgz", + "integrity": "sha512-Dh96IXgcZrV39a+Tej/owcd9vr5ihiZ3KRix11rr1v0MWtVb61+H1GXXlz6+Zcx9y8jM1NmOuiIuJwkV4vZ4WA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/snappy-win32-arm64-msvc": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/@napi-rs/snappy-win32-arm64-msvc/-/snappy-win32-arm64-msvc-7.2.2.tgz", + "integrity": "sha512-9No0b3xGbHSWv2wtLEn3MO76Yopn1U2TdemZpCaEgOGccz1V+a/1d16Piz3ofSmnA13HGFz3h9NwZH9EOaIgYA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/snappy-win32-ia32-msvc": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/@napi-rs/snappy-win32-ia32-msvc/-/snappy-win32-ia32-msvc-7.2.2.tgz", + "integrity": "sha512-QiGe+0G86J74Qz1JcHtBwM3OYdTni1hX1PFyLRo3HhQUSpmi13Bzc1En7APn+6Pvo7gkrcy81dObGLDSxFAkQQ==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/snappy-win32-x64-msvc": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/@napi-rs/snappy-win32-x64-msvc/-/snappy-win32-x64-msvc-7.2.2.tgz", + "integrity": "sha512-a43cyx1nK0daw6BZxVcvDEXxKMFLSBSDTAhsFD0VqSKcC7MGUBMaqyoWUcMiI7LBSz4bxUmxDWKfCYzpEmeb3w==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-wot/binding-coap": { + "version": "0.8.16", + "license": "EPL-2.0 OR W3C-20150513", + "dependencies": { + "@node-wot/core": "0.8.16", + "@types/node": "16.18.35", + "coap": "^1.4.0", + "multicast-dns": "^7.2.5", + "node-coap-client": "1.0.8", + "rxjs": "5.5.11", + "slugify": "^1.4.5", + "wot-typescript-definitions": "0.8.0-SNAPSHOT.29" + } + }, + "node_modules/@node-wot/binding-coap/node_modules/@types/node": { + "version": "16.18.35", + "license": "MIT" + }, + "node_modules/@node-wot/binding-http": { + "version": "0.8.16", + "license": "EPL-2.0 OR W3C-20150513", + "dependencies": { + "@node-wot/core": "0.8.16", + "@types/eventsource": "1.1.10", + "accept-language-parser": "1.5.0", + "basic-auth": "2.0.1", + "client-oauth2": "^4.2.5", + "eventsource": "^2.0.2", + "find-my-way": "^7.6.2", + "node-fetch": "^2.6.7", + "query-string": "^7.1.1", + "rxjs": "5.5.11", + "slugify": "^1.4.5" + } + }, + "node_modules/@node-wot/binding-modbus": { + "version": "0.8.16", + "resolved": "https://registry.npmjs.org/@node-wot/binding-modbus/-/binding-modbus-0.8.16.tgz", + "integrity": "sha512-7OAX4SduuE8Sm/XrXdv7kaSyykJ/xtRpmg5+EcXl9YrAORHT3SdFZPsvoGz/UGsAE7R95VW55r19Yx9h3rOOOw==", + "dependencies": { + "@node-wot/core": "0.8.16", + "modbus-serial": "^8.0.17", + "rxjs": "5.5.11", + "wot-typescript-definitions": "0.8.0-SNAPSHOT.29" + } + }, + "node_modules/@node-wot/binding-mqtt": { + "version": "0.8.16", + "license": "EPL-2.0 OR W3C-20150513", + "dependencies": { + "@node-wot/core": "0.8.16", + "aedes": "^0.46.2", + "mqtt": "^5.3.2", + "rxjs": "5.5.11" + } + }, + "node_modules/@node-wot/core": { + "version": "0.8.16", + "license": "EPL-2.0 OR W3C-20150513", + "dependencies": { + "@petamoriken/float16": "^3.1.1", + "@thingweb/thing-model": "^1.0.3", + "ajv": "^8.11.0", + "ajv-formats": "^2.1.1", + "cbor": "^8.1.0", + "content-type": "^1.0.5", + "debug": "^4.3.4", + "is-absolute-url": "3.0.3", + "uritemplate": "0.3.4", + "url-toolkit": "2.1.6", + "uuid": "^7.0.3", + "web-streams-polyfill": "^3.0.1" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@petamoriken/float16": { + "version": "3.8.7", + "license": "MIT" + }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==" + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==" + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "dependencies": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==" + }, + "node_modules/@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==" + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==" + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==" + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" + }, + "node_modules/@rtsao/scc": { + "version": "1.1.0", + "dev": true, + "license": "MIT" + }, + "node_modules/@serialport/binding-mock": { + "version": "10.2.2", + "license": "MIT", + "dependencies": { + "@serialport/bindings-interface": "^1.2.1", + "debug": "^4.3.3" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@serialport/bindings-cpp": { + "version": "11.0.3", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "@serialport/bindings-interface": "1.2.2", + "@serialport/parser-readline": "11.0.0", + "debug": "4.3.4", + "node-addon-api": "6.1.0", + "node-gyp-build": "4.6.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/@serialport/bindings-cpp/node_modules/@serialport/parser-delimiter": { + "version": "11.0.0", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/@serialport/bindings-cpp/node_modules/@serialport/parser-readline": { + "version": "11.0.0", + "license": "MIT", + "dependencies": { + "@serialport/parser-delimiter": "11.0.0" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/@serialport/bindings-cpp/node_modules/debug": { + "version": "4.3.4", + "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@serialport/bindings-cpp/node_modules/ms": { + "version": "2.1.2", + "license": "MIT" + }, + "node_modules/@serialport/bindings-interface": { + "version": "1.2.2", + "license": "MIT", + "engines": { + "node": "^12.22 || ^14.13 || >=16" + } + }, + "node_modules/@serialport/parser-byte-length": { + "version": "11.0.1", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/@serialport/parser-cctalk": { + "version": "11.0.1", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/@serialport/parser-delimiter": { + "version": "11.0.1", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/@serialport/parser-inter-byte-timeout": { + "version": "11.0.1", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/@serialport/parser-packet-length": { + "version": "11.0.1", + "license": "MIT", + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/@serialport/parser-readline": { + "version": "11.0.1", + "license": "MIT", + "dependencies": { + "@serialport/parser-delimiter": "11.0.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/@serialport/parser-ready": { + "version": "11.0.1", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/@serialport/parser-regex": { + "version": "11.0.1", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/@serialport/parser-slip-encoder": { + "version": "11.0.1", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/@serialport/parser-spacepacket": { + "version": "11.0.1", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/@serialport/stream": { + "version": "11.0.1", + "license": "MIT", + "dependencies": { + "@serialport/bindings-interface": "1.2.2", + "debug": "4.3.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/@serialport/stream/node_modules/debug": { + "version": "4.3.4", + "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@serialport/stream/node_modules/ms": { + "version": "2.1.2", + "license": "MIT" + }, + "node_modules/@servie/events": { + "version": "1.0.0", + "license": "MIT" + }, + "node_modules/@thingweb/thing-model": { + "version": "1.0.3", + "license": "EPL-2.0 OR W3C-20150513", + "dependencies": { + "ajv": "^8.11.0", + "ajv-formats": "^3.0.1", + "debug": "^4.3.4", + "json-placeholder-replacer": "^2.0.4", + "wot-thing-description-types": "1.1.0-09-November-2023", + "wot-thing-model-types": "^1.1.0-09-November-2023", + "wot-typescript-definitions": "0.8.0-SNAPSHOT.29" + } + }, + "node_modules/@thingweb/thing-model/node_modules/ajv-formats": { + "version": "3.0.1", + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/@thingweb/thing-model/node_modules/json-placeholder-replacer": { + "version": "2.0.5", + "license": "MIT", + "bin": { + "jpr": "dist/index.js", + "json-placeholder-replacer": "dist/index.js" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.11", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/body-parser": { + "version": "1.19.5", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/chai": { + "version": "4.3.19", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/chai-as-promised": { + "version": "7.1.8", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/chai": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.38", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/debug": { + "version": "4.1.12", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/ms": "*" + } + }, + "node_modules/@types/eventsource": { + "version": "1.1.10", + "license": "MIT" + }, + "node_modules/@types/express": { + "version": "4.17.21", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.33", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "4.19.5", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/http-errors": { + "version": "2.0.4", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json5": { + "version": "0.0.29", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/mime": { + "version": "1.3.5", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/mocha": { + "version": "10.0.8", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/ms": { + "version": "0.7.34", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "22.5.5", + "license": "MIT", + "dependencies": { + "undici-types": "~6.19.2" + } + }, + "node_modules/@types/node-fetch": { + "version": "2.6.11", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "form-data": "^4.0.0" + } + }, + "node_modules/@types/qs": { + "version": "6.9.16", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/readable-stream": { + "version": "4.0.15", + "license": "MIT", + "dependencies": { + "@types/node": "*", + "safe-buffer": "~5.1.1" + } + }, + "node_modules/@types/send": { + "version": "0.17.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "1.15.7", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*", + "@types/send": "*" + } + }, + "node_modules/@types/tough-cookie": { + "version": "4.0.5", + "license": "MIT" + }, + "node_modules/@types/triple-beam": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz", + "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==" + }, + "node_modules/@types/ws": { + "version": "8.5.12", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.6.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.6.0", + "@typescript-eslint/type-utils": "8.6.0", + "@typescript-eslint/utils": "8.6.0", + "@typescript-eslint/visitor-keys": "8.6.0", + "graphemer": "^1.4.0", + "ignore": "^5.3.1", + "natural-compare": "^1.4.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", + "eslint": "^8.57.0 || ^9.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.6.0", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@typescript-eslint/scope-manager": "8.6.0", + "@typescript-eslint/types": "8.6.0", + "@typescript-eslint/typescript-estree": "8.6.0", + "@typescript-eslint/visitor-keys": "8.6.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.6.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.6.0", + "@typescript-eslint/visitor-keys": "8.6.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.6.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/typescript-estree": "8.6.0", + "@typescript-eslint/utils": "8.6.0", + "debug": "^4.3.4", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.6.0", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.6.0", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@typescript-eslint/types": "8.6.0", + "@typescript-eslint/visitor-keys": "8.6.0", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.5", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.6.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "8.6.0", + "@typescript-eslint/types": "8.6.0", + "@typescript-eslint/typescript-estree": "8.6.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.6.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.6.0", + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@ungap/structured-clone": { + "version": "1.2.0", + "dev": true, + "license": "ISC" + }, + "node_modules/abort-controller": { + "version": "3.0.0", + "license": "MIT", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, + "node_modules/accept-language-parser": { + "version": "1.5.0", + "license": "MIT" + }, + "node_modules/accepts": { + "version": "1.3.8", + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.12.1", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.4", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/advanced-coffee-machine": { + "resolved": "things/advanced-coffee-machine/http/ts", + "link": true + }, + "node_modules/aedes": { + "version": "0.46.3", + "license": "MIT", + "dependencies": { + "aedes-packet": "^2.3.1", + "aedes-persistence": "^8.1.3", + "bulk-write-stream": "^2.0.1", + "end-of-stream": "^1.4.4", + "fastfall": "^1.5.1", + "fastparallel": "^2.4.1", + "fastseries": "^2.0.0", + "hyperid": "^3.0.0", + "mqemitter": "^4.5.0", + "mqtt-packet": "^7.1.2", + "readable-stream": "^3.6.0", + "retimer": "^3.0.0", + "reusify": "^1.0.4", + "uuid": "^8.3.2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/aedes-packet": { + "version": "2.3.1", + "license": "MIT", + "dependencies": { + "mqtt-packet": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/aedes-packet/node_modules/bl": { + "version": "4.1.0", + "license": "MIT", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/aedes-packet/node_modules/buffer": { + "version": "5.7.1", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/aedes-packet/node_modules/mqtt-packet": { + "version": "6.10.0", + "license": "MIT", + "dependencies": { + "bl": "^4.0.2", + "debug": "^4.1.1", + "process-nextick-args": "^2.0.1" + } + }, + "node_modules/aedes-persistence": { + "version": "8.1.3", + "license": "MIT", + "dependencies": { + "aedes-packet": "^2.3.1", + "from2": "^2.3.0", + "qlobber": "^5.0.3" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/aedes/node_modules/uuid": { + "version": "8.3.2", + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/ajv": { + "version": "8.17.1", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "2.1.1", + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "dev": true, + "license": "MIT" + }, + "node_modules/argparse": { + "version": "2.0.1", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.5", + "is-array-buffer": "^3.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "license": "MIT" + }, + "node_modules/array-includes": { + "version": "3.1.8", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.4", + "is-string": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.findlastindex": { + "version": "1.2.5", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flat": { + "version": "1.3.2", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-shim-unscopables": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flatmap": { + "version": "1.3.2", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-shim-unscopables": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", + "es-abstract": "^1.22.3", + "es-errors": "^1.2.1", + "get-intrinsic": "^1.2.3", + "is-array-buffer": "^3.0.4", + "is-shared-array-buffer": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/assertion-error": { + "version": "1.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==" + }, + "node_modules/async-exit-hook": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/async-exit-hook/-/async-exit-hook-2.0.1.tgz", + "integrity": "sha512-NW2cX8m1Q7KPA7a5M2ULQeZ2wR5qI5PAbw5L0UOMxdioVk9PMZ0h1TmyZEkPYrCvYjDlFICusOu1dlEKAAeXBw==", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "dev": true, + "license": "MIT" + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "dev": true, + "license": "MIT", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "license": "MIT" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/basic-auth": { + "version": "2.0.1", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.1.2" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bl": { + "version": "6.0.15", + "license": "MIT", + "dependencies": { + "@types/readable-stream": "^4.0.0", + "buffer": "^6.0.3", + "inherits": "^2.0.4", + "readable-stream": "^4.2.0" + } + }, + "node_modules/bl/node_modules/readable-stream": { + "version": "4.5.2", + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/body-parser": { + "version": "1.20.3", + "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" + } + }, + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browser-stdout": { + "version": "1.3.1", + "dev": true, + "license": "ISC" + }, + "node_modules/btoa": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/btoa/-/btoa-1.2.1.tgz", + "integrity": "sha512-SB4/MIGlsiVkMcHmT+pSmIPoNDoHg+7cMzmt3Uxt628MTz2487DKSqK/fuhFBrkuqrYv5UCEnACpF4dTFNKc/g==", + "bin": { + "btoa": "bin/btoa.js" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/buffer": { + "version": "6.0.3", + "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.2.1" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "license": "MIT" + }, + "node_modules/builtin-modules": { + "version": "3.3.0", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/builtins": { + "version": "5.1.0", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "semver": "^7.0.0" + } + }, + "node_modules/bulk-write-stream": { + "version": "2.0.1", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + } + }, + "node_modules/byte-length": { + "version": "1.0.2", + "license": "MIT" + }, + "node_modules/bytes": { + "version": "3.1.2", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind": { + "version": "1.0.7", + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "6.3.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/capitalize": { + "version": "2.0.4", + "license": "MIT" + }, + "node_modules/cbor": { + "version": "8.1.0", + "license": "MIT", + "dependencies": { + "nofilter": "^3.1.0" + }, + "engines": { + "node": ">=12.19" + } + }, + "node_modules/chai": { + "version": "4.5.0", + "dev": true, + "license": "MIT", + "dependencies": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.3", + "deep-eql": "^4.1.3", + "get-func-name": "^2.0.2", + "loupe": "^2.3.6", + "pathval": "^1.1.1", + "type-detect": "^4.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/chai-as-promised": { + "version": "7.1.2", + "dev": true, + "license": "WTFPL", + "dependencies": { + "check-error": "^1.0.2" + }, + "peerDependencies": { + "chai": ">= 2.1.2 < 6" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/check-error": { + "version": "1.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "get-func-name": "^2.0.2" + }, + "engines": { + "node": "*" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/client-oauth2": { + "version": "4.3.3", + "license": "Apache-2.0", + "dependencies": { + "popsicle": "^12.0.5", + "safe-buffer": "^5.2.0" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/client-oauth2/node_modules/safe-buffer": { + "version": "5.2.1", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/cliui": { + "version": "7.0.4", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/cliui/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/string-width": { + "version": "4.2.3", + "dev": 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/coap": { + "version": "1.4.0", + "license": "MIT", + "dependencies": { + "@types/readable-stream": "^4.0.14", + "bl": "^6.0.12", + "capitalize": "^2.0.4", + "coap-packet": "^1.1.1", + "debug": "^4.3.5", + "fastseries": "^2.0.0", + "lru-cache": "^10.2.2", + "readable-stream": "^4.5.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/coap-calculator": { + "resolved": "things/calculator/coap/js", + "link": true + }, + "node_modules/coap-packet": { + "version": "1.1.1", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/coap/node_modules/readable-stream": { + "version": "4.5.2", + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/color": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", + "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", + "dependencies": { + "color-convert": "^1.9.3", + "color-string": "^1.6.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "license": "MIT" + }, + "node_modules/color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "node_modules/color/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==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color/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==" + }, + "node_modules/colorspace": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.4.tgz", + "integrity": "sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==", + "dependencies": { + "color": "^3.1.3", + "text-hex": "1.0.x" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "dev": true, + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commist": { + "version": "3.2.0", + "license": "MIT" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "license": "MIT" + }, + "node_modules/concat-stream": { + "version": "2.0.0", + "engines": [ + "node >= 6.0" + ], + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.0.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/confbox": { + "version": "0.1.7", + "dev": true, + "license": "MIT" + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-disposition/node_modules/safe-buffer": { + "version": "5.2.1", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/content-type": { + "version": "1.0.5", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.6.0", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "license": "MIT" + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "license": "MIT" + }, + "node_modules/crc": { + "version": "4.3.2", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "buffer": ">=6.0.3" + }, + "peerDependenciesMeta": { + "buffer": { + "optional": true + } + } + }, + "node_modules/create-require": { + "version": "1.1.1", + "dev": true, + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/data-view-buffer": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-length": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-offset": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/debug": { + "version": "4.3.7", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decamelize": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decode-uri-component": { + "version": "0.2.2", + "license": "MIT", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/deep-eql": { + "version": "4.1.4", + "dev": true, + "license": "MIT", + "dependencies": { + "type-detect": "^4.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "dev": true, + "license": "MIT" + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "license": "MIT", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/diff": { + "version": "5.2.0", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/dns-packet": { + "version": "5.6.1", + "license": "MIT", + "dependencies": { + "@leichtgewicht/ip-codec": "^2.0.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/dotenv": { + "version": "16.4.5", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/duplexify": { + "version": "4.1.3", + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.4.1", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1", + "stream-shift": "^1.0.2" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "license": "MIT" + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/enabled": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", + "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/es-abstract": { + "version": "1.23.3", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "arraybuffer.prototype.slice": "^1.0.3", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", + "data-view-buffer": "^1.0.1", + "data-view-byte-length": "^1.0.1", + "data-view-byte-offset": "^1.0.0", + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-set-tostringtag": "^2.0.3", + "es-to-primitive": "^1.2.1", + "function.prototype.name": "^1.1.6", + "get-intrinsic": "^1.2.4", + "get-symbol-description": "^1.0.2", + "globalthis": "^1.0.3", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.0.3", + "has-symbols": "^1.0.3", + "hasown": "^2.0.2", + "internal-slot": "^1.0.7", + "is-array-buffer": "^3.0.4", + "is-callable": "^1.2.7", + "is-data-view": "^1.0.1", + "is-negative-zero": "^2.0.3", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.3", + "is-string": "^1.0.7", + "is-typed-array": "^1.1.13", + "is-weakref": "^1.0.2", + "object-inspect": "^1.13.1", + "object-keys": "^1.1.1", + "object.assign": "^4.1.5", + "regexp.prototype.flags": "^1.5.2", + "safe-array-concat": "^1.1.2", + "safe-regex-test": "^1.0.3", + "string.prototype.trim": "^1.2.9", + "string.prototype.trimend": "^1.0.8", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.2", + "typed-array-byte-length": "^1.0.1", + "typed-array-byte-offset": "^1.0.2", + "typed-array-length": "^1.0.6", + "unbox-primitive": "^1.0.2", + "which-typed-array": "^1.1.15" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-define-property": { + "version": "1.0.0", + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.2.4", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-shim-unscopables": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.0" + } + }, + "node_modules/es-to-primitive": { + "version": "1.2.1", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "license": "MIT" + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "8.57.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.1", + "@humanwhocodes/config-array": "^0.13.0", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-compat-utils": { + "version": "0.5.1", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "eslint": ">=6.0.0" + } + }, + "node_modules/eslint-config-prettier": { + "version": "9.1.0", + "dev": true, + "license": "MIT", + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-config-standard": { + "version": "17.1.0", + "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": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "eslint": "^8.0.1", + "eslint-plugin-import": "^2.25.2", + "eslint-plugin-n": "^15.0.0 || ^16.0.0 ", + "eslint-plugin-promise": "^6.0.0" + } + }, + "node_modules/eslint-import-resolver-node": { + "version": "0.3.9", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^3.2.7", + "is-core-module": "^2.13.0", + "resolve": "^1.22.4" + } + }, + "node_modules/eslint-import-resolver-node/node_modules/debug": { + "version": "3.2.7", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-module-utils": { + "version": "2.11.0", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^3.2.7" + }, + "engines": { + "node": ">=4" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/eslint-module-utils/node_modules/debug": { + "version": "3.2.7", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-es": { + "version": "3.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-utils": "^2.0.0", + "regexpp": "^3.0.0" + }, + "engines": { + "node": ">=8.10.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=4.19.1" + } + }, + "node_modules/eslint-plugin-es-x": { + "version": "7.8.0", + "dev": true, + "funding": [ + "https://github.com/sponsors/ota-meshi", + "https://opencollective.com/eslint" + ], + "license": "MIT", + "peer": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.1.2", + "@eslint-community/regexpp": "^4.11.0", + "eslint-compat-utils": "^0.5.1" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": ">=8" + } + }, + "node_modules/eslint-plugin-import": { + "version": "2.30.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@rtsao/scc": "^1.1.0", + "array-includes": "^3.1.8", + "array.prototype.findlastindex": "^1.2.5", + "array.prototype.flat": "^1.3.2", + "array.prototype.flatmap": "^1.3.2", + "debug": "^3.2.7", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.9", + "eslint-module-utils": "^2.9.0", + "hasown": "^2.0.2", + "is-core-module": "^2.15.1", + "is-glob": "^4.0.3", + "minimatch": "^3.1.2", + "object.fromentries": "^2.0.8", + "object.groupby": "^1.0.3", + "object.values": "^1.2.0", + "semver": "^6.3.1", + "tsconfig-paths": "^3.15.0" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" + } + }, + "node_modules/eslint-plugin-import/node_modules/brace-expansion": { + "version": "1.1.11", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/eslint-plugin-import/node_modules/debug": { + "version": "3.2.7", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-import/node_modules/doctrine": { + "version": "2.1.0", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eslint-plugin-import/node_modules/minimatch": { + "version": "3.1.2", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/eslint-plugin-import/node_modules/semver": { + "version": "6.3.1", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/eslint-plugin-n": { + "version": "16.6.2", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "builtins": "^5.0.1", + "eslint-plugin-es-x": "^7.5.0", + "get-tsconfig": "^4.7.0", + "globals": "^13.24.0", + "ignore": "^5.2.4", + "is-builtin-module": "^3.2.1", + "is-core-module": "^2.12.1", + "minimatch": "^3.1.2", + "resolve": "^1.22.2", + "semver": "^7.5.3" + }, + "engines": { + "node": ">=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-plugin-n/node_modules/brace-expansion": { + "version": "1.1.11", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/eslint-plugin-n/node_modules/minimatch": { + "version": "3.1.2", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/eslint-plugin-node": { + "version": "11.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-plugin-es": "^3.0.0", + "eslint-utils": "^2.0.0", + "ignore": "^5.1.1", + "minimatch": "^3.0.4", + "resolve": "^1.10.1", + "semver": "^6.1.0" + }, + "engines": { + "node": ">=8.10.0" + }, + "peerDependencies": { + "eslint": ">=5.16.0" + } + }, + "node_modules/eslint-plugin-node/node_modules/brace-expansion": { + "version": "1.1.11", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/eslint-plugin-node/node_modules/minimatch": { + "version": "3.1.2", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/eslint-plugin-node/node_modules/semver": { + "version": "6.3.1", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/eslint-plugin-notice": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "find-root": "^1.1.0", + "lodash": "^4.17.21", + "metric-lcs": "^0.1.2" + }, + "peerDependencies": { + "eslint": ">=3.0.0" + } + }, + "node_modules/eslint-plugin-promise": { + "version": "6.6.0", + "dev": true, + "license": "ISC", + "peer": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0 || ^9.0.0" + } + }, + "node_modules/eslint-plugin-unused-imports": { + "version": "4.1.4", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@typescript-eslint/eslint-plugin": "^8.0.0-0 || ^7.0.0 || ^6.0.0 || ^5.0.0", + "eslint": "^9.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "@typescript-eslint/eslint-plugin": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-workspaces": { + "version": "0.10.1", + "dev": true, + "license": "MIT", + "dependencies": { + "find-workspaces": "^0.3.0" + } + }, + "node_modules/eslint-scope": { + "version": "7.2.2", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-utils": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^1.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "1.3.0", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/ajv": { + "version": "6.12.6", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/eslint/node_modules/brace-expansion": { + "version": "1.1.11", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/eslint/node_modules/glob-parent": { + "version": "6.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/eslint/node_modules/json-schema-traverse": { + "version": "0.4.1", + "dev": true, + "license": "MIT" + }, + "node_modules/eslint/node_modules/minimatch": { + "version": "3.1.2", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/espree": { + "version": "9.6.1", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/events": { + "version": "3.3.0", + "license": "MIT", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/eventsource": { + "version": "2.0.2", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/express": { + "version": "4.21.0", + "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.6.0", + "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.10", + "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" + } + }, + "node_modules/express/node_modules/debug": { + "version": "2.6.9", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "license": "MIT" + }, + "node_modules/express/node_modules/safe-buffer": { + "version": "5.2.1", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/fast-decode-uri-component": { + "version": "1.0.1", + "license": "MIT" + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.2", + "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.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-querystring": { + "version": "1.1.2", + "license": "MIT", + "dependencies": { + "fast-decode-uri-component": "^1.0.1" + } + }, + "node_modules/fast-unique-numbers": { + "version": "8.0.13", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.23.8", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.1.0" + } + }, + "node_modules/fast-uri": { + "version": "3.0.1", + "license": "MIT" + }, + "node_modules/fastfall": { + "version": "1.5.1", + "license": "MIT", + "dependencies": { + "reusify": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fastparallel": { + "version": "2.4.1", + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4", + "xtend": "^4.0.2" + } + }, + "node_modules/fastq": { + "version": "1.17.1", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fastseries": { + "version": "2.0.0", + "license": "ISC" + }, + "node_modules/fecha": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", + "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==" + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/filter-obj": { + "version": "1.1.0", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/finalhandler": { + "version": "1.3.1", + "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/finalhandler/node_modules/debug": { + "version": "2.6.9", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "license": "MIT" + }, + "node_modules/find-my-way": { + "version": "7.7.0", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-querystring": "^1.0.0", + "safe-regex2": "^2.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/find-root": { + "version": "1.1.0", + "dev": true, + "license": "MIT" + }, + "node_modules/find-up": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/find-workspaces": { + "version": "0.3.1", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-glob": "^3.3.2", + "pkg-types": "^1.0.3", + "yaml": "^2.3.4" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "dev": true, + "license": "BSD-3-Clause", + "bin": { + "flat": "cli.js" + } + }, + "node_modules/flat-cache": { + "version": "3.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.3.1", + "dev": true, + "license": "ISC" + }, + "node_modules/fn.name": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", + "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" + }, + "node_modules/for-each": { + "version": "0.3.3", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.1.3" + } + }, + "node_modules/form-data": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/from2": { + "version": "2.3.0", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.0" + } + }, + "node_modules/from2/node_modules/readable-stream": { + "version": "2.3.8", + "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/string_decoder": { + "version": "1.1.1", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "dev": 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", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/function.prototype.name": { + "version": "1.1.6", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "functions-have-names": "^1.2.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-func-name": { + "version": "2.0.2", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.4", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-symbol-description": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-tsconfig": { + "version": "4.8.1", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/glob": { + "version": "8.1.0", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/globals": { + "version": "13.24.0", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globalthis": { + "version": "1.0.4", + "dev": true, + "license": "MIT", + "dependencies": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gopd": { + "version": "1.0.1", + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graphemer": { + "version": "1.4.0", + "dev": true, + "license": "MIT" + }, + "node_modules/has-bigints": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.3", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/he": { + "version": "1.2.0", + "dev": true, + "license": "MIT", + "bin": { + "he": "bin/he" + } + }, + "node_modules/help-me": { + "version": "5.0.0", + "license": "MIT" + }, + "node_modules/http-calculator": { + "resolved": "things/calculator/http/express", + "link": true + }, + "node_modules/http-data-schema-thing": { + "resolved": "things/data-schema-thing/http/ts", + "link": true + }, + "node_modules/http-errors": { + "version": "2.0.0", + "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/hyperid": { + "version": "3.3.0", + "license": "MIT", + "dependencies": { + "buffer": "^5.2.1", + "uuid": "^8.3.2", + "uuid-parse": "^1.1.0" + } + }, + "node_modules/hyperid/node_modules/buffer": { + "version": "5.7.1", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/hyperid/node_modules/uuid": { + "version": "8.3.2", + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/ignore": { + "version": "5.3.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "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/imurmurhash": { + "version": "0.1.4", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "license": "ISC" + }, + "node_modules/internal-slot": { + "version": "1.0.7", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "hasown": "^2.0.0", + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-absolute-url": { + "version": "3.0.3", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-array-buffer": { + "version": "3.0.4", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" + }, + "node_modules/is-bigint": { + "version": "1.0.4", + "dev": true, + "license": "MIT", + "dependencies": { + "has-bigints": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-boolean-object": { + "version": "1.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-builtin-module": { + "version": "3.2.1", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "builtin-modules": "^3.3.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "version": "2.15.1", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-data-view": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.0.5", + "dev": true, + "license": "MIT", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-negative-zero": { + "version": "2.0.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-number-object": { + "version": "1.0.7", + "dev": true, + "license": "MIT", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-plain-obj": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-regex": { + "version": "1.1.4", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-string": { + "version": "1.0.7", + "dev": true, + "license": "MIT", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.0.4", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.13", + "dev": true, + "license": "MIT", + "dependencies": { + "which-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-weakref": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/js-sdsl": { + "version": "4.3.0", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/js-sdsl" + } + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/json-placeholder-replacer": { + "version": "1.0.37", + "license": "MIT", + "bin": { + "jpr": "dist/index.js", + "json-placeholder-replacer": "dist/index.js" + } + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/kuler": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", + "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" + }, + "node_modules/leven": { + "version": "2.1.0", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "dev": true, + "license": "MIT" + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/logform": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.6.1.tgz", + "integrity": "sha512-CdaO738xRapbKIMVn2m4F6KTj4j7ooJ8POVnebSgKo3KBz5axNXRAL7ZdRjIV6NOr2Uf4vjtRkxrFETOioCqSA==", + "dependencies": { + "@colors/colors": "1.6.0", + "@types/triple-beam": "^1.3.2", + "fecha": "^4.2.0", + "ms": "^2.1.1", + "safe-stable-stringify": "^2.3.1", + "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/long": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", + "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==" + }, + "node_modules/loupe": { + "version": "2.3.7", + "dev": true, + "license": "MIT", + "dependencies": { + "get-func-name": "^2.0.1" + } + }, + "node_modules/lru-cache": { + "version": "10.4.3", + "license": "ISC" + }, + "node_modules/make-error": { + "version": "1.3.6", + "license": "ISC" + }, + "node_modules/make-error-cause": { + "version": "2.3.0", + "license": "Apache-2.0", + "dependencies": { + "make-error": "^1.3.5" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.3", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/metric-lcs": { + "version": "0.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/micromatch": { + "version": "4.0.8", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimatch": { + "version": "5.1.6", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/mlly": { + "version": "1.7.1", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.11.3", + "pathe": "^1.1.2", + "pkg-types": "^1.1.1", + "ufo": "^1.5.3" + } + }, + "node_modules/mocha": { + "version": "10.7.3", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-colors": "^4.1.3", + "browser-stdout": "^1.3.1", + "chokidar": "^3.5.3", + "debug": "^4.3.5", + "diff": "^5.2.0", + "escape-string-regexp": "^4.0.0", + "find-up": "^5.0.0", + "glob": "^8.1.0", + "he": "^1.2.0", + "js-yaml": "^4.1.0", + "log-symbols": "^4.1.0", + "minimatch": "^5.1.6", + "ms": "^2.1.3", + "serialize-javascript": "^6.0.2", + "strip-json-comments": "^3.1.1", + "supports-color": "^8.1.1", + "workerpool": "^6.5.1", + "yargs": "^16.2.0", + "yargs-parser": "^20.2.9", + "yargs-unparser": "^2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha.js" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/modbus": { + "version": "1.1.3", + "license": "MIT", + "dependencies": { + "crc": "^4.3.2", + "serialport": "^12.0.0" + } + }, + "node_modules/modbus-elevator": { + "resolved": "things/elevator/modbus/js", + "link": true + }, + "node_modules/modbus-serial": { + "version": "8.0.17", + "license": "ISC", + "dependencies": { + "debug": "^4.3.1", + "serialport": "^12.0.0" + } + }, + "node_modules/modbus-serial/node_modules/@serialport/bindings-cpp": { + "version": "12.0.1", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "@serialport/bindings-interface": "1.2.2", + "@serialport/parser-readline": "11.0.0", + "debug": "4.3.4", + "node-addon-api": "7.0.0", + "node-gyp-build": "4.6.0" + }, + "engines": { + "node": ">=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/modbus-serial/node_modules/@serialport/bindings-cpp/node_modules/@serialport/parser-delimiter": { + "version": "11.0.0", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/modbus-serial/node_modules/@serialport/bindings-cpp/node_modules/@serialport/parser-readline": { + "version": "11.0.0", + "license": "MIT", + "dependencies": { + "@serialport/parser-delimiter": "11.0.0" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/modbus-serial/node_modules/@serialport/parser-byte-length": { + "version": "12.0.0", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/modbus-serial/node_modules/@serialport/parser-cctalk": { + "version": "12.0.0", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/modbus-serial/node_modules/@serialport/parser-delimiter": { + "version": "12.0.0", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/modbus-serial/node_modules/@serialport/parser-inter-byte-timeout": { + "version": "12.0.0", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/modbus-serial/node_modules/@serialport/parser-packet-length": { + "version": "12.0.0", + "license": "MIT", + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/modbus-serial/node_modules/@serialport/parser-readline": { + "version": "12.0.0", + "license": "MIT", + "dependencies": { + "@serialport/parser-delimiter": "12.0.0" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/modbus-serial/node_modules/@serialport/parser-ready": { + "version": "12.0.0", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/modbus-serial/node_modules/@serialport/parser-regex": { + "version": "12.0.0", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/modbus-serial/node_modules/@serialport/parser-slip-encoder": { + "version": "12.0.0", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/modbus-serial/node_modules/@serialport/parser-spacepacket": { + "version": "12.0.0", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/modbus-serial/node_modules/@serialport/stream": { + "version": "12.0.0", + "license": "MIT", + "dependencies": { + "@serialport/bindings-interface": "1.2.2", + "debug": "4.3.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/modbus-serial/node_modules/debug": { + "version": "4.3.4", + "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/modbus-serial/node_modules/ms": { + "version": "2.1.2", + "license": "MIT" + }, + "node_modules/modbus-serial/node_modules/node-addon-api": { + "version": "7.0.0", + "license": "MIT" + }, + "node_modules/modbus-serial/node_modules/serialport": { + "version": "12.0.0", + "license": "MIT", + "dependencies": { + "@serialport/binding-mock": "10.2.2", + "@serialport/bindings-cpp": "12.0.1", + "@serialport/parser-byte-length": "12.0.0", + "@serialport/parser-cctalk": "12.0.0", + "@serialport/parser-delimiter": "12.0.0", + "@serialport/parser-inter-byte-timeout": "12.0.0", + "@serialport/parser-packet-length": "12.0.0", + "@serialport/parser-readline": "12.0.0", + "@serialport/parser-ready": "12.0.0", + "@serialport/parser-regex": "12.0.0", + "@serialport/parser-slip-encoder": "12.0.0", + "@serialport/parser-spacepacket": "12.0.0", + "@serialport/stream": "12.0.0", + "debug": "4.3.4" + }, + "engines": { + "node": ">=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/modbus/node_modules/@serialport/bindings-cpp": { + "version": "12.0.1", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "@serialport/bindings-interface": "1.2.2", + "@serialport/parser-readline": "11.0.0", + "debug": "4.3.4", + "node-addon-api": "7.0.0", + "node-gyp-build": "4.6.0" + }, + "engines": { + "node": ">=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/modbus/node_modules/@serialport/bindings-cpp/node_modules/@serialport/parser-delimiter": { + "version": "11.0.0", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/modbus/node_modules/@serialport/bindings-cpp/node_modules/@serialport/parser-readline": { + "version": "11.0.0", + "license": "MIT", + "dependencies": { + "@serialport/parser-delimiter": "11.0.0" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/modbus/node_modules/@serialport/parser-byte-length": { + "version": "12.0.0", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/modbus/node_modules/@serialport/parser-cctalk": { + "version": "12.0.0", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/modbus/node_modules/@serialport/parser-delimiter": { + "version": "12.0.0", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/modbus/node_modules/@serialport/parser-inter-byte-timeout": { + "version": "12.0.0", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/modbus/node_modules/@serialport/parser-packet-length": { + "version": "12.0.0", + "license": "MIT", + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/modbus/node_modules/@serialport/parser-readline": { + "version": "12.0.0", + "license": "MIT", + "dependencies": { + "@serialport/parser-delimiter": "12.0.0" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/modbus/node_modules/@serialport/parser-ready": { + "version": "12.0.0", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/modbus/node_modules/@serialport/parser-regex": { + "version": "12.0.0", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/modbus/node_modules/@serialport/parser-slip-encoder": { + "version": "12.0.0", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/modbus/node_modules/@serialport/parser-spacepacket": { + "version": "12.0.0", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/modbus/node_modules/@serialport/stream": { + "version": "12.0.0", + "license": "MIT", + "dependencies": { + "@serialport/bindings-interface": "1.2.2", + "debug": "4.3.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/modbus/node_modules/debug": { + "version": "4.3.4", + "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/modbus/node_modules/ms": { + "version": "2.1.2", + "license": "MIT" + }, + "node_modules/modbus/node_modules/node-addon-api": { + "version": "7.0.0", + "license": "MIT" + }, + "node_modules/modbus/node_modules/serialport": { + "version": "12.0.0", + "license": "MIT", + "dependencies": { + "@serialport/binding-mock": "10.2.2", + "@serialport/bindings-cpp": "12.0.1", + "@serialport/parser-byte-length": "12.0.0", + "@serialport/parser-cctalk": "12.0.0", + "@serialport/parser-delimiter": "12.0.0", + "@serialport/parser-inter-byte-timeout": "12.0.0", + "@serialport/parser-packet-length": "12.0.0", + "@serialport/parser-readline": "12.0.0", + "@serialport/parser-ready": "12.0.0", + "@serialport/parser-regex": "12.0.0", + "@serialport/parser-slip-encoder": "12.0.0", + "@serialport/parser-spacepacket": "12.0.0", + "@serialport/stream": "12.0.0", + "debug": "4.3.4" + }, + "engines": { + "node": ">=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/mqemitter": { + "version": "4.5.0", + "license": "ISC", + "dependencies": { + "fastparallel": "^2.3.0", + "qlobber": "^5.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mqtt": { + "version": "5.10.1", + "license": "MIT", + "dependencies": { + "@types/readable-stream": "^4.0.5", + "@types/ws": "^8.5.9", + "commist": "^3.2.0", + "concat-stream": "^2.0.0", + "debug": "^4.3.4", + "help-me": "^5.0.0", + "lru-cache": "^10.0.1", + "minimist": "^1.2.8", + "mqtt-packet": "^9.0.0", + "number-allocator": "^1.0.14", + "readable-stream": "^4.4.2", + "reinterval": "^1.1.0", + "rfdc": "^1.3.0", + "split2": "^4.2.0", + "worker-timers": "^7.1.4", + "ws": "^8.17.1" + }, + "bin": { + "mqtt": "build/bin/mqtt.js", + "mqtt_pub": "build/bin/pub.js", + "mqtt_sub": "build/bin/sub.js" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/mqtt-calculator": { + "resolved": "things/calculator/mqtt/js", + "link": true + }, + "node_modules/mqtt-packet": { + "version": "7.1.2", + "license": "MIT", + "dependencies": { + "bl": "^4.0.2", + "debug": "^4.1.1", + "process-nextick-args": "^2.0.1" + } + }, + "node_modules/mqtt-packet/node_modules/bl": { + "version": "4.1.0", + "license": "MIT", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/mqtt-packet/node_modules/buffer": { + "version": "5.7.1", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/mqtt/node_modules/mqtt-packet": { + "version": "9.0.0", + "license": "MIT", + "dependencies": { + "bl": "^6.0.8", + "debug": "^4.3.4", + "process-nextick-args": "^2.0.1" + } + }, + "node_modules/mqtt/node_modules/readable-stream": { + "version": "4.5.2", + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "license": "MIT" + }, + "node_modules/multicast-dns": { + "version": "7.2.5", + "license": "MIT", + "dependencies": { + "dns-packet": "^5.2.2", + "thunky": "^1.0.2" + }, + "bin": { + "multicast-dns": "cli.js" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "dev": true, + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "0.6.3", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/node-addon-api": { + "version": "6.1.0", + "license": "MIT" + }, + "node_modules/node-coap-client": { + "version": "1.0.8", + "license": "MIT", + "dependencies": { + "debug": "^4.1.1", + "node-dtls-client": "^0.6.0" + }, + "engines": { + "node": ">= 6.13.0" + } + }, + "node_modules/node-dtls-client": { + "version": "0.6.0", + "license": "MIT", + "dependencies": { + "debug": "^4.1.1", + "semver": "^7.0.0" + }, + "engines": { + "node": ">=6" + }, + "optionalDependencies": { + "node-aead-crypto": "^2.0.0" + } + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-gyp-build": { + "version": "4.6.0", + "license": "MIT", + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, + "node_modules/nofilter": { + "version": "3.1.0", + "license": "MIT", + "engines": { + "node": ">=12.19" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/number-allocator": { + "version": "1.0.14", + "license": "MIT", + "dependencies": { + "debug": "^4.3.1", + "js-sdsl": "4.3.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.2", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.5", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", + "has-symbols": "^1.0.3", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.fromentries": { + "version": "2.0.8", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.groupby": { + "version": "1.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.values": { + "version": "1.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/one-time": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", + "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", + "dependencies": { + "fn.name": "1.x.x" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "dev": true, + "license": "MIT" + }, + "node_modules/path-to-regexp": { + "version": "0.1.10", + "license": "MIT" + }, + "node_modules/pathe": { + "version": "1.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/pathval": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/picomatch": { + "version": "2.3.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pkg-types": { + "version": "1.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "confbox": "^0.1.7", + "mlly": "^1.7.1", + "pathe": "^1.1.2" + } + }, + "node_modules/popsicle": { + "version": "12.1.2", + "license": "MIT", + "dependencies": { + "popsicle-content-encoding": "^1.0.0", + "popsicle-cookie-jar": "^1.0.1", + "popsicle-redirects": "^1.1.0", + "popsicle-transport-http": "^1.1.0", + "popsicle-transport-xhr": "^2.0.0", + "popsicle-user-agent": "^1.0.0", + "servie": "^4.3.3", + "throwback": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/popsicle-content-encoding": { + "version": "1.0.0", + "license": "MIT", + "peerDependencies": { + "servie": "^4.0.0" + } + }, + "node_modules/popsicle-cookie-jar": { + "version": "1.0.1", + "license": "MIT", + "dependencies": { + "@types/tough-cookie": "^4.0.2", + "tough-cookie": "^4.1.3" + }, + "engines": { + "node": ">=8" + }, + "peerDependencies": { + "servie": "^4.0.0" + } + }, + "node_modules/popsicle-redirects": { + "version": "1.1.1", + "license": "MIT", + "peerDependencies": { + "servie": "^4.1.0" + } + }, + "node_modules/popsicle-transport-http": { + "version": "1.2.1", + "license": "MIT", + "dependencies": { + "make-error-cause": "^2.2.0" + }, + "peerDependencies": { + "servie": "^4.2.0" + } + }, + "node_modules/popsicle-transport-xhr": { + "version": "2.0.0", + "license": "MIT", + "peerDependencies": { + "servie": "^4.2.0" + } + }, + "node_modules/popsicle-user-agent": { + "version": "1.0.0", + "license": "MIT", + "peerDependencies": { + "servie": "^4.0.0" + } + }, + "node_modules/possible-typed-array-names": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", + "dev": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/process": { + "version": "0.11.10", + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "license": "MIT" + }, + "node_modules/protobufjs": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.4.0.tgz", + "integrity": "sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw==", + "hasInstallScript": true, + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/node": ">=13.7.0", + "long": "^5.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/psl": { + "version": "1.9.0", + "license": "MIT" + }, + "node_modules/pump": { + "version": "3.0.2", + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/qlobber": { + "version": "5.0.3", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/qs": { + "version": "6.13.0", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.0.6" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/query-string": { + "version": "7.1.3", + "license": "MIT", + "dependencies": { + "decode-uri-component": "^0.2.2", + "filter-obj": "^1.1.0", + "split-on-first": "^1.0.0", + "strict-uri-encode": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/querystringify": { + "version": "2.2.0", + "license": "MIT" + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "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": "MIT" + }, + "node_modules/randombytes": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.2", + "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/readable-stream": { + "version": "3.6.2", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.14.1", + "license": "MIT" + }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.2", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.6", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "set-function-name": "^2.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regexpp": { + "version": "3.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/reinterval": { + "version": "1.1.0", + "license": "MIT" + }, + "node_modules/require-directory": { + "version": "2.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/requires-port": { + "version": "1.0.0", + "license": "MIT" + }, + "node_modules/resolve": { + "version": "1.22.8", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "peer": true, + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/ret": { + "version": "0.2.2", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/retimer": { + "version": "3.0.0", + "license": "MIT" + }, + "node_modules/reusify": { + "version": "1.0.4", + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rfdc": { + "version": "1.4.1", + "license": "MIT" + }, + "node_modules/rimraf": { + "version": "3.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/brace-expansion": { + "version": "1.1.11", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/rimraf/node_modules/glob": { + "version": "7.2.3", + "dev": true, + "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/rimraf/node_modules/minimatch": { + "version": "3.1.2", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "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": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/rxjs": { + "version": "5.5.11", + "license": "Apache-2.0", + "dependencies": { + "symbol-observable": "1.0.1" + }, + "engines": { + "npm": ">=2.0.0" + } + }, + "node_modules/safe-array-concat": { + "version": "1.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "get-intrinsic": "^1.2.4", + "has-symbols": "^1.0.3", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-array-concat/node_modules/isarray": { + "version": "2.0.5", + "dev": true, + "license": "MIT" + }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "license": "MIT" + }, + "node_modules/safe-regex-test": { + "version": "1.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-regex": "^1.1.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-regex2": { + "version": "2.0.0", + "license": "MIT", + "dependencies": { + "ret": "~0.2.0" + } + }, + "node_modules/safe-stable-stringify": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", + "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", + "engines": { + "node": ">=10" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "license": "MIT" + }, + "node_modules/semver": { + "version": "7.6.3", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "0.19.0", + "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" + } + }, + "node_modules/send/node_modules/debug": { + "version": "2.6.9", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "license": "MIT" + }, + "node_modules/send/node_modules/encodeurl": { + "version": "1.0.2", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/serialize-javascript": { + "version": "6.0.2", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/serialport": { + "version": "11.0.1", + "license": "MIT", + "dependencies": { + "@serialport/binding-mock": "10.2.2", + "@serialport/bindings-cpp": "11.0.3", + "@serialport/parser-byte-length": "11.0.1", + "@serialport/parser-cctalk": "11.0.1", + "@serialport/parser-delimiter": "11.0.1", + "@serialport/parser-inter-byte-timeout": "11.0.1", + "@serialport/parser-packet-length": "11.0.1", + "@serialport/parser-readline": "11.0.1", + "@serialport/parser-ready": "11.0.1", + "@serialport/parser-regex": "11.0.1", + "@serialport/parser-slip-encoder": "11.0.1", + "@serialport/parser-spacepacket": "11.0.1", + "@serialport/stream": "11.0.1", + "debug": "4.3.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/serialport/node_modules/debug": { + "version": "4.3.4", + "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/serialport/node_modules/ms": { + "version": "2.1.2", + "license": "MIT" + }, + "node_modules/serve-static": { + "version": "1.16.2", + "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" + } + }, + "node_modules/servie": { + "version": "4.3.3", + "license": "Apache-2.0", + "dependencies": { + "@servie/events": "^1.0.0", + "byte-length": "^1.0.2", + "ts-expect": "^1.1.0" + } + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-function-name": { + "version": "2.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "license": "ISC" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.0.6", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "object-inspect": "^1.13.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, + "node_modules/slugify": { + "version": "1.6.6", + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/smart-home": { + "resolved": "mashups/smart-home", + "link": true + }, + "node_modules/snappy": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/snappy/-/snappy-7.2.2.tgz", + "integrity": "sha512-iADMq1kY0v3vJmGTuKcFWSXt15qYUz7wFkArOrsSg0IFfI3nJqIJvK2/ZbEIndg7erIJLtAVX2nSOqPz7DcwbA==", + "optional": true, + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "optionalDependencies": { + "@napi-rs/snappy-android-arm-eabi": "7.2.2", + "@napi-rs/snappy-android-arm64": "7.2.2", + "@napi-rs/snappy-darwin-arm64": "7.2.2", + "@napi-rs/snappy-darwin-x64": "7.2.2", + "@napi-rs/snappy-freebsd-x64": "7.2.2", + "@napi-rs/snappy-linux-arm-gnueabihf": "7.2.2", + "@napi-rs/snappy-linux-arm64-gnu": "7.2.2", + "@napi-rs/snappy-linux-arm64-musl": "7.2.2", + "@napi-rs/snappy-linux-x64-gnu": "7.2.2", + "@napi-rs/snappy-linux-x64-musl": "7.2.2", + "@napi-rs/snappy-win32-arm64-msvc": "7.2.2", + "@napi-rs/snappy-win32-ia32-msvc": "7.2.2", + "@napi-rs/snappy-win32-x64-msvc": "7.2.2" + } + }, + "node_modules/split-on-first": { + "version": "1.1.0", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/split2": { + "version": "4.2.0", + "license": "ISC", + "engines": { + "node": ">= 10.x" + } + }, + "node_modules/stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", + "engines": { + "node": "*" + } + }, + "node_modules/statuses": { + "version": "2.0.1", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/stream-shift": { + "version": "1.0.3", + "license": "MIT" + }, + "node_modules/strict-uri-encode": { + "version": "2.0.0", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.2.1", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/string.prototype.trim": { + "version": "1.2.9", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.0", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.8", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.8", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "8.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/symbol-observable": { + "version": "1.0.1", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/text-hex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", + "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==" + }, + "node_modules/text-table": { + "version": "0.2.0", + "dev": true, + "license": "MIT" + }, + "node_modules/throwback": { + "version": "4.1.0", + "license": "MIT" + }, + "node_modules/thunky": { + "version": "1.1.0", + "license": "MIT" + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tough-cookie": { + "version": "4.1.4", + "license": "BSD-3-Clause", + "dependencies": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.2.0", + "url-parse": "^1.5.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "license": "MIT" + }, + "node_modules/triple-beam": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz", + "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==", + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/ts-api-utils": { + "version": "1.3.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, + "node_modules/ts-expect": { + "version": "1.3.0", + "license": "MIT" + }, + "node_modules/ts-node": { + "version": "10.9.2", + "dev": 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 + } + } + }, + "node_modules/ts-node/node_modules/diff": { + "version": "4.0.2", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/tsconfig-paths": { + "version": "3.15.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json5": "^0.0.29", + "json5": "^1.0.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, + "node_modules/tslib": { + "version": "2.7.0", + "license": "0BSD" + }, + "node_modules/type-check": { + "version": "0.4.0", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-detect": { + "version": "4.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "license": "MIT", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typed-array-buffer": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.6", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13", + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typedarray": { + "version": "0.0.6", + "license": "MIT" + }, + "node_modules/typescript": { + "version": "4.9.5", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/ufo": { + "version": "1.5.4", + "dev": true, + "license": "MIT" + }, + "node_modules/unbox-primitive": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", + "which-boxed-primitive": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/undici-types": { + "version": "6.19.8", + "license": "MIT" + }, + "node_modules/universalify": { + "version": "0.2.0", + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/uritemplate": { + "version": "0.3.4" + }, + "node_modules/url-parse": { + "version": "1.5.10", + "license": "MIT", + "dependencies": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "node_modules/url-polyfill": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/url-polyfill/-/url-polyfill-1.1.12.tgz", + "integrity": "sha512-mYFmBHCapZjtcNHW0MDq9967t+z4Dmg5CJ0KqysK3+ZbyoNOWQHksGCTWwDhxGXllkWlOc10Xfko6v4a3ucM6A==" + }, + "node_modules/url-toolkit": { + "version": "2.1.6", + "license": "Apache-2.0" + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "license": "MIT" + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "7.0.3", + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/uuid-parse": { + "version": "1.1.0", + "license": "MIT" + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/vary": { + "version": "1.1.2", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/web-streams-polyfill": { + "version": "3.3.3", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "license": "BSD-2-Clause" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.15", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/winston": { + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.14.2.tgz", + "integrity": "sha512-CO8cdpBB2yqzEf8v895L+GNKYJiEq8eKlHU38af3snQBQ+sdAIUepjMSguOIJC7ICbzm0ZI+Af2If4vIJrtmOg==", + "dependencies": { + "@colors/colors": "^1.6.0", + "@dabh/diagnostics": "^2.0.2", + "async": "^3.2.3", + "is-stream": "^2.0.0", + "logform": "^2.6.0", + "one-time": "^1.0.0", + "readable-stream": "^3.4.0", + "safe-stable-stringify": "^2.3.1", + "stack-trace": "0.0.x", + "triple-beam": "^1.3.0", + "winston-transport": "^4.7.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/winston-loki": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/winston-loki/-/winston-loki-6.1.2.tgz", + "integrity": "sha512-l1iqDDaEUt63Q8arDsiVCXIrK3jLjPOEc5UTs+WMVnWf8D+A8ZRErAAXKDOduT240aNGzpTbCwe5zfdqqLlzMg==", + "dependencies": { + "async-exit-hook": "2.0.1", + "btoa": "^1.2.1", + "protobufjs": "^7.2.4", + "url-polyfill": "^1.1.12", + "winston-transport": "^4.3.0" + }, + "optionalDependencies": { + "snappy": "^7.2.2" + } + }, + "node_modules/winston-transport": { + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.7.1.tgz", + "integrity": "sha512-wQCXXVgfv/wUPOfb2x0ruxzwkcZfxcktz6JIMUaPLmcNhO4bZTwA/WtDWK74xV3F2dKu8YadrFv0qhwYjVEwhA==", + "dependencies": { + "logform": "^2.6.1", + "readable-stream": "^3.6.2", + "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/worker-timers": { + "version": "7.1.8", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.24.5", + "tslib": "^2.6.2", + "worker-timers-broker": "^6.1.8", + "worker-timers-worker": "^7.0.71" + } + }, + "node_modules/worker-timers-broker": { + "version": "6.1.8", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.24.5", + "fast-unique-numbers": "^8.0.13", + "tslib": "^2.6.2", + "worker-timers-worker": "^7.0.71" + } + }, + "node_modules/worker-timers-worker": { + "version": "7.0.71", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.24.5", + "tslib": "^2.6.2" + } + }, + "node_modules/workerpool": { + "version": "6.5.1", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/wot-thing-description-types": { + "version": "1.1.0-09-November-2023", + "license": "W3C-20150513" + }, + "node_modules/wot-thing-model-types": { + "version": "1.1.0-09-November-2023", + "license": "W3C-20150513" + }, + "node_modules/wot-typescript-definitions": { + "version": "0.8.0-SNAPSHOT.29", + "license": "W3C-20150513", + "dependencies": { + "wot-thing-description-types": "1.1.0-09-November-2023" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/string-width": { + "version": "4.2.3", + "dev": 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/wrappy": { + "version": "1.0.2", + "license": "ISC" + }, + "node_modules/ws": { + "version": "8.18.0", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xtend": { + "version": "4.0.2", + "license": "MIT", + "engines": { + "node": ">=0.4" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "license": "ISC" + }, + "node_modules/yaml": { + "version": "2.5.1", + "dev": true, + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/yargs": { + "version": "16.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.9", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-unparser": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/string-width": { + "version": "4.2.3", + "dev": 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/yn": { + "version": "3.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "things/advanced-coffee-machine/http/ts": { + "name": "advanced-coffee-machine", + "version": "1.0.0", + "license": "EPL-2.0 OR W3C-20150513", + "dependencies": { + "@node-wot/binding-http": "^0.8.14", + "@node-wot/core": "^0.8.14", + "dotenv": "^16.4.5", + "json-placeholder-replacer": "^2.0.5", + "wot-typescript-definitions": "^0.8.0-SNAPSHOT.29" + }, + "devDependencies": { + "@types/node": "^22.1.0", + "typescript": "^4.7.4" + } + }, + "things/advanced-coffee-machine/http/ts/node_modules/json-placeholder-replacer": { + "version": "2.0.5", + "license": "MIT", + "bin": { + "jpr": "dist/index.js", + "json-placeholder-replacer": "dist/index.js" + } + }, + "things/calculator/coap/js": { + "name": "coap-calculator", + "version": "1.0.0", + "license": "EPL-2.0 OR W3C-20150513", + "dependencies": { + "cbor": "^9.0.2", + "coap": "^1.3.0", + "dotenv": "^16.3.1", + "json-placeholder-replacer": "^1.0.35" + } + }, + "things/calculator/coap/js/node_modules/cbor": { + "version": "9.0.2", + "license": "MIT", + "dependencies": { + "nofilter": "^3.1.0" + }, + "engines": { + "node": ">=16" + } + }, + "things/calculator/coap/js/node_modules/json-placeholder-replacer": { + "version": "1.0.37", + "license": "MIT", + "bin": { + "jpr": "dist/index.js", + "json-placeholder-replacer": "dist/index.js" + } + }, + "things/calculator/http/express": { + "name": "http-calculator", + "version": "1.0.0", + "license": "EPL-2.0 OR W3C-20150513", + "dependencies": { + "body-parser": "^1.20.2", + "cbor": "^9.0.1", + "dotenv": "^16.3.1", + "eventsource": "^2.0.2", + "express": "^4.18.2", + "json-placeholder-replacer": "^1.0.35", + "winston": "^3.14.2", + "winston-loki": "^6.1.2" + }, + "devDependencies": { + "@types/express": "^4.17.17" + } + }, + "things/calculator/http/express/node_modules/cbor": { + "version": "9.0.2", + "license": "MIT", + "dependencies": { + "nofilter": "^3.1.0" + }, + "engines": { + "node": ">=16" + } + }, + "things/calculator/mqtt/js": { + "name": "mqtt-calculator", + "version": "1.0.0", + "license": "EPL-2.0 OR W3C-20150513", + "dependencies": { + "dotenv": "^16.3.1", + "json-placeholder-replacer": "^1.0.35", + "mqtt": "^4.3.8", + "winston": "^3.14.2", + "winston-loki": "^6.1.2" + } + }, + "things/calculator/mqtt/js/node_modules/bl": { + "version": "4.1.0", + "license": "MIT", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "things/calculator/mqtt/js/node_modules/brace-expansion": { + "version": "1.1.11", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "things/calculator/mqtt/js/node_modules/buffer": { + "version": "5.7.1", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "things/calculator/mqtt/js/node_modules/commist": { + "version": "1.1.0", + "license": "MIT", + "dependencies": { + "leven": "^2.1.0", + "minimist": "^1.1.0" + } + }, + "things/calculator/mqtt/js/node_modules/glob": { + "version": "7.2.3", + "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" + } + }, + "things/calculator/mqtt/js/node_modules/help-me": { + "version": "3.0.0", + "license": "MIT", + "dependencies": { + "glob": "^7.1.6", + "readable-stream": "^3.6.0" + } + }, + "things/calculator/mqtt/js/node_modules/json-placeholder-replacer": { + "version": "1.0.37", + "license": "MIT", + "bin": { + "jpr": "dist/index.js", + "json-placeholder-replacer": "dist/index.js" + } + }, + "things/calculator/mqtt/js/node_modules/lru-cache": { + "version": "6.0.0", + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "things/calculator/mqtt/js/node_modules/minimatch": { + "version": "3.1.2", + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "things/calculator/mqtt/js/node_modules/mqtt": { + "version": "4.3.8", + "license": "MIT", + "dependencies": { + "commist": "^1.0.0", + "concat-stream": "^2.0.0", + "debug": "^4.1.1", + "duplexify": "^4.1.1", + "help-me": "^3.0.0", + "inherits": "^2.0.3", + "lru-cache": "^6.0.0", + "minimist": "^1.2.5", + "mqtt-packet": "^6.8.0", + "number-allocator": "^1.0.9", + "pump": "^3.0.0", + "readable-stream": "^3.6.0", + "reinterval": "^1.1.0", + "rfdc": "^1.3.0", + "split2": "^3.1.0", + "ws": "^7.5.5", + "xtend": "^4.0.2" + }, + "bin": { + "mqtt": "bin/mqtt.js", + "mqtt_pub": "bin/pub.js", + "mqtt_sub": "bin/sub.js" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "things/calculator/mqtt/js/node_modules/mqtt-packet": { + "version": "6.10.0", + "license": "MIT", + "dependencies": { + "bl": "^4.0.2", + "debug": "^4.1.1", + "process-nextick-args": "^2.0.1" + } + }, + "things/calculator/mqtt/js/node_modules/split2": { + "version": "3.2.2", + "license": "ISC", + "dependencies": { + "readable-stream": "^3.0.0" + } + }, + "things/calculator/mqtt/js/node_modules/ws": { + "version": "7.5.10", + "license": "MIT", + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "things/data-schema-thing/http/ts": { + "name": "http-data-schema-thing", + "version": "1.0.0", + "license": "EPL-2.0 OR W3C-20150513", + "dependencies": { + "@node-wot/binding-http": "~0.8.16", + "@node-wot/core": "~0.8.16", + "dotenv": "^16.4.5", + "json-placeholder-replacer": "^2.0.5", + "winston": "^3.14.2", + "winston-loki": "^6.1.2", + "wot-typescript-definitions": "^0.8.0-SNAPSHOT.29" + }, + "devDependencies": { + "ts-node": "^10.9.2", + "typescript": "^4.7.4" + } + }, + "things/data-schema-thing/http/ts/node_modules/json-placeholder-replacer": { + "version": "2.0.5", + "license": "MIT", + "bin": { + "jpr": "dist/index.js", + "json-placeholder-replacer": "dist/index.js" + } + }, + "things/elevator/modbus/js": { + "name": "modbus-elevator", + "version": "1.0.0", + "license": "EPL-2.0 OR W3C-20150513", + "dependencies": { + "@node-wot/binding-modbus": "~0.8.16", + "dotenv": "^16.3.1", + "json-placeholder-replacer": "^1.0.35", + "modbus": "^1.1.1", + "modbus-serial": "^8.0.11", + "serialport": "^11.0.0" + } + }, + "things/elevator/modbus/js/node_modules/json-placeholder-replacer": { + "version": "1.0.37", + "license": "MIT", + "bin": { + "jpr": "dist/index.js", + "json-placeholder-replacer": "dist/index.js" + } } - } - }, - "node_modules/xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "engines": { - "node": ">=0.4" - } - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, - "node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "dev": true, - "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/yargs-unparser": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", - "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", - "dev": true, - "dependencies": { - "camelcase": "^6.0.0", - "decamelize": "^4.0.0", - "flat": "^5.0.2", - "is-plain-obj": "^2.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true, - "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==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "things/advanced-coffee-machine/http/ts": { - "name": "advanced-coffee-machine", - "version": "1.0.0", - "license": "EPL-2.0 OR W3C-20150513", - "dependencies": { - "@node-wot/binding-http": "^0.8.14", - "@node-wot/core": "^0.8.14", - "dotenv": "^16.4.5", - "json-placeholder-replacer": "^2.0.5", - "wot-typescript-definitions": "^0.8.0-SNAPSHOT.29" - }, - "devDependencies": { - "@types/node": "^22.1.0", - "typescript": "^4.7.4" - } - }, - "things/advanced-coffee-machine/http/ts/node_modules/json-placeholder-replacer": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/json-placeholder-replacer/-/json-placeholder-replacer-2.0.5.tgz", - "integrity": "sha512-pK/MgeylpZ1VAMO7s/tp5H6VVsbCIc9i+3cUILYnLTw2MT7xqKLowBQGQmFTtcz/O1414oK+f9C8jT79IADdag==", - "bin": { - "jpr": "dist/index.js", - "json-placeholder-replacer": "dist/index.js" - } - }, - "things/calculator/coap/js": { - "name": "coap-calculator", - "version": "1.0.0", - "license": "EPL-2.0 OR W3C-20150513", - "dependencies": { - "cbor": "^9.0.2", - "coap": "^1.3.0", - "dotenv": "^16.3.1", - "json-placeholder-replacer": "^1.0.35" - } - }, - "things/calculator/coap/js/node_modules/cbor": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/cbor/-/cbor-9.0.2.tgz", - "integrity": "sha512-JPypkxsB10s9QOWwa6zwPzqE1Md3vqpPc+cai4sAecuCsRyAtAl/pMyhPlMbT/xtPnm2dznJZYRLui57qiRhaQ==", - "dependencies": { - "nofilter": "^3.1.0" - }, - "engines": { - "node": ">=16" - } - }, - "things/calculator/http/express": { - "name": "http-calculator", - "version": "1.0.0", - "license": "EPL-2.0 OR W3C-20150513", - "dependencies": { - "body-parser": "^1.20.2", - "cbor": "^9.0.1", - "dotenv": "^16.3.1", - "eventsource": "^2.0.2", - "express": "^4.18.2", - "json-placeholder-replacer": "^1.0.35", - "winston": "^3.14.2", - "winston-loki": "^6.1.2" - }, - "devDependencies": { - "@types/express": "^4.17.17" - } - }, - "things/calculator/http/express/node_modules/cbor": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/cbor/-/cbor-9.0.2.tgz", - "integrity": "sha512-JPypkxsB10s9QOWwa6zwPzqE1Md3vqpPc+cai4sAecuCsRyAtAl/pMyhPlMbT/xtPnm2dznJZYRLui57qiRhaQ==", - "dependencies": { - "nofilter": "^3.1.0" - }, - "engines": { - "node": ">=16" - } - }, - "things/calculator/mqtt/js": { - "name": "mqtt-calculator", - "version": "1.0.0", - "license": "EPL-2.0 OR W3C-20150513", - "dependencies": { - "dotenv": "^16.3.1", - "json-placeholder-replacer": "^1.0.35", - "mqtt": "^4.3.8", - "winston": "^3.14.2", - "winston-loki": "^6.1.2" - } - }, - "things/data-schema-thing/http/ts": { - "name": "http-data-schema-thing", - "version": "1.0.0", - "license": "EPL-2.0 OR W3C-20150513", - "dependencies": { - "@node-wot/binding-http": "~0.8.16", - "@node-wot/core": "~0.8.16", - "dotenv": "^16.4.5", - "json-placeholder-replacer": "^2.0.5", - "winston": "^3.14.2", - "winston-loki": "^6.1.2", - "wot-typescript-definitions": "^0.8.0-SNAPSHOT.29" - }, - "devDependencies": { - "ts-node": "^10.9.2", - "typescript": "^4.7.4" - } - }, - "things/data-schema-thing/http/ts/node_modules/json-placeholder-replacer": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/json-placeholder-replacer/-/json-placeholder-replacer-2.0.5.tgz", - "integrity": "sha512-pK/MgeylpZ1VAMO7s/tp5H6VVsbCIc9i+3cUILYnLTw2MT7xqKLowBQGQmFTtcz/O1414oK+f9C8jT79IADdag==", - "bin": { - "jpr": "dist/index.js", - "json-placeholder-replacer": "dist/index.js" - } - }, - "things/elevator/modbus/js": { - "name": "modbus-elevator", - "version": "1.0.0", - "license": "EPL-2.0 OR W3C-20150513", - "dependencies": { - "@node-wot/binding-modbus": "^0.8.16", - "@node-wot/core": "^0.8.16", - "dotenv": "^16.3.1", - "json-placeholder-replacer": "^1.0.35", - "modbus": "^1.1.1", - "modbus-serial": "^8.0.11", - "serialport": "^11.0.0" - } } - } } diff --git a/package.json b/package.json index fc4c1ed..74182c3 100644 --- a/package.json +++ b/package.json @@ -1,31 +1,45 @@ { - "name": "test-things", - "description": "Test Things", - "scripts": { - "setup": "./scripts/installDependencies.sh", - "test": "./scripts/runTests.sh" - }, - "keywords": [ - "wot", - "thing", - "iot" - ], - "workspaces": [ - "./mashups/*", - "./things/*/*/*" - ], - "author": "Eclipse Thingweb (https://thingweb.io/)", - "license": "EPL-2.0 OR W3C-20150513", - "devDependencies": { - "@node-wot/binding-http": "~0.8.16", - "@node-wot/core": "~0.8.16", - "@types/chai": "^4.3.17", - "@types/chai-as-promised": "^7.1.8", - "@types/mocha": "^10.0.7", - "@types/node": "^22.4.1", - "ajv": "^8.12.0", - "chai": "^4.5.0", - "chai-as-promised": "^7.1.1", - "mocha": "^10.7.3" - } + "name": "test-things", + "description": "Test Things", + "scripts": { + "setup": "./scripts/installDependencies.sh", + "test": "./scripts/runTests.sh", + "format": "prettier --write . && npm run format --silent --workspaces --if-present", + "lint": "npm run lint --silent --workspaces --if-present", + "lint:fix": "npm run lint:fix --silent --workspaces --if-present" + }, + "keywords": [ + "wot", + "thing", + "iot" + ], + "workspaces": [ + "./mashups/*", + "./things/*/*/*" + ], + "author": "Eclipse Thingweb (https://thingweb.io/)", + "license": "EPL-2.0 OR W3C-20150513", + "devDependencies": { + "@node-wot/binding-http": "~0.8.16", + "@node-wot/core": "~0.8.16", + "@types/chai": "^4.3.17", + "@types/chai-as-promised": "^7.1.8", + "@types/mocha": "^10.0.7", + "@types/node": "^22.4.1", + "@typescript-eslint/eslint-plugin": "^8.6.0", + "@typescript-eslint/parser": "^8.6.0", + "ajv": "^8.12.0", + "chai": "^4.5.0", + "chai-as-promised": "^7.1.1", + "eslint": "^8.57.1", + "eslint-config-prettier": "^9.1.0", + "eslint-config-standard": "^17.1.0", + "eslint-plugin-import": "^2.30.0", + "eslint-plugin-node": "^11.1.0", + "eslint-plugin-notice": "^1.0.0", + "eslint-plugin-unused-imports": "^4.1.4", + "eslint-plugin-workspaces": "^0.10.1", + "mocha": "^10.7.3", + "prettier": "^2.3.2" + } } diff --git a/things/advanced-coffee-machine/advanced-coffee-machine.tm.json b/things/advanced-coffee-machine/advanced-coffee-machine.tm.json index 9e434b4..7891bb5 100644 --- a/things/advanced-coffee-machine/advanced-coffee-machine.tm.json +++ b/things/advanced-coffee-machine/advanced-coffee-machine.tm.json @@ -1,13 +1,13 @@ { - "@context":[ - "https://www.w3.org/2019/wot/td/v1", - "https://www.w3.org/2022/wot/td/v1.1", - { - "@language":"en" - } - ], - "@type":"tm:ThingModel", - "title":"{{THING_NAME}}", + "@context": [ + "https://www.w3.org/2019/wot/td/v1", + "https://www.w3.org/2022/wot/td/v1.1", + { + "@language": "en" + } + ], + "@type": "tm:ThingModel", + "title": "{{THING_NAME}}", "description": "A smart coffee machine with a range of capabilities.\\nA complementary tutorial is available at http: //www.thingweb.io/smart-coffee-machine.html.", "support": "https://github.com/eclipse-thingweb/node-wot/", "base": "{{PROTOCOL}}://{{HOSTNAME}}:{{PORT_NUMBER}}/{{THING_NAME}}", @@ -45,12 +45,7 @@ "uriVariables": { "id": { "type": "string", - "enum": [ - "water", - "milk", - "chocolate", - "coffeeBeans" - ] + "enum": ["water", "milk", "chocolate", "coffeeBeans"] } } }, @@ -86,11 +81,7 @@ "size": { "type": "string", "description": "Defines the size of a drink, s = small, m = medium, l = large.", - "enum": [ - "s", - "m", - "l" - ] + "enum": ["s", "m", "l"] }, "quantity": { "type": "integer", @@ -132,11 +123,7 @@ "size": { "type": "string", "description": "Defines the size of a drink, s = small, m = medium, l = large.", - "enum": [ - "s", - "m", - "l" - ] + "enum": ["s", "m", "l"] }, "quantity": { "type": "integer", @@ -170,11 +157,7 @@ "size": { "type": "string", "description": "Defines the size of a drink, s = small, m = medium, l = large.", - "enum": [ - "s", - "m", - "l" - ] + "enum": ["s", "m", "l"] }, "quantity": { "type": "integer", @@ -202,10 +185,7 @@ ] } }, - "required": [ - "time", - "mode" - ] + "required": ["time", "mode"] }, "output": { "type": "object", @@ -229,4 +209,4 @@ } } } -} \ No newline at end of file +} diff --git a/things/advanced-coffee-machine/http/ts/.mocharc.json b/things/advanced-coffee-machine/http/ts/.mocharc.json index b0ed45c..a0dd936 100644 --- a/things/advanced-coffee-machine/http/ts/.mocharc.json +++ b/things/advanced-coffee-machine/http/ts/.mocharc.json @@ -1,4 +1,4 @@ { "spec": "./test/**.test.ts", "require": ["ts-node/register", "./test/fixtures.ts"] -} \ No newline at end of file +} diff --git a/things/advanced-coffee-machine/http/ts/package.json b/things/advanced-coffee-machine/http/ts/package.json index d302444..8dff49f 100644 --- a/things/advanced-coffee-machine/http/ts/package.json +++ b/things/advanced-coffee-machine/http/ts/package.json @@ -1,30 +1,33 @@ { - "name": "advanced-coffee-machine", - "version": "1.0.0", - "description": "Advanced Coffee Machine", - "main": "main.js", - "scripts": { - "start": "node ./dist/main.js", - "build": "tsc -b", - "test": "mocha" - }, - "keywords": [ - "wot", - "thing", - "iot", - "http" - ], - "author": "Eclipse Thingweb (https://thingweb.io/)", - "license": "EPL-2.0 OR W3C-20150513", - "dependencies": { - "@node-wot/binding-http": "^0.8.14", - "@node-wot/core": "^0.8.14", - "dotenv": "^16.4.5", - "json-placeholder-replacer": "^2.0.5", - "wot-typescript-definitions": "^0.8.0-SNAPSHOT.29" - }, - "devDependencies": { - "@types/node": "^22.1.0", - "typescript": "^4.7.4" - } + "name": "advanced-coffee-machine", + "version": "1.0.0", + "description": "Advanced Coffee Machine", + "main": "main.js", + "scripts": { + "start": "node ./dist/main.js", + "build": "tsc -b", + "test": "mocha", + "lint": "eslint .", + "lint:fix": "eslint . --fix", + "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\" \"**/*.json\"" + }, + "keywords": [ + "wot", + "thing", + "iot", + "http" + ], + "author": "Eclipse Thingweb (https://thingweb.io/)", + "license": "EPL-2.0 OR W3C-20150513", + "dependencies": { + "@node-wot/binding-http": "^0.8.14", + "@node-wot/core": "^0.8.14", + "dotenv": "^16.4.5", + "json-placeholder-replacer": "^2.0.5", + "wot-typescript-definitions": "^0.8.0-SNAPSHOT.29" + }, + "devDependencies": { + "@types/node": "^22.1.0", + "typescript": "^4.7.4" + } } diff --git a/things/advanced-coffee-machine/http/ts/src/main.ts b/things/advanced-coffee-machine/http/ts/src/main.ts index 2c560b8..848f16e 100644 --- a/things/advanced-coffee-machine/http/ts/src/main.ts +++ b/things/advanced-coffee-machine/http/ts/src/main.ts @@ -1,5 +1,5 @@ /******************************************************************************** - * Copyright (c) 2020 Contributors to the Eclipse Foundation + * Copyright (c) 2024 Contributors to the Eclipse Foundation * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. @@ -16,19 +16,22 @@ // This is an example of Web of Things producer ("server" mode) Thing script. // It considers a fictional smart coffee machine in order to demonstrate the capabilities of Web of Things. // An accompanying tutorial is available at http://www.thingweb.io/smart-coffee-machine.html. -import WoT = require("wot-typescript-definitions") -const fs = require("fs") -const path = require("path") -const { parseArgs } = require("node:util") -const { JsonPlaceholderReplacer } = require('json-placeholder-replacer') -require("dotenv").config() - -const WotCore = require("@node-wot/core"); -const HttpServer = require("@node-wot/binding-http").HttpServer +import WoT from "wot-typescript-definitions"; +import fs from "fs"; +import path from "path"; +import { parseArgs } from "node:util"; +import { JsonPlaceholderReplacer } from "json-placeholder-replacer"; +import { Servient } from "@node-wot/core"; +import { HttpServer } from "@node-wot/binding-http"; +import dotenv from "dotenv"; +dotenv.config(); const hostname = process.env.HOSTNAME ?? "localhost"; -let portNumber = process.env.PORT ?? 3000; +let portNumber = + process.env.PORT != null && process.env.PORT !== "" + ? parseInt(process.env.PORT) + : 3000; const thingName = "http-advanced-coffee-machine"; let allAvailableResources: Record; @@ -48,223 +51,332 @@ function notify(subscribers: unknown, msg: string) { console.log(msg); } - const { values: { port }, } = parseArgs({ options: { port: { - type: "string", - short: "p", + type: "string", + short: "p", }, }, }); - -if (port && !isNaN(parseInt(port))) { + +if (port != null && !isNaN(parseInt(port))) { portNumber = parseInt(port); } - + const tmPath = process.env.TM_PATH; - + if (process.platform === "win32") { tmPath?.split(path.sep).join(path.win32.sep); } - -const thingModel = JSON.parse(fs.readFileSync(path.join(__dirname, tmPath))); - + +let thingModel; + +if (tmPath != null && tmPath !== "") { + thingModel = JSON.parse( + fs.readFileSync(path.join(__dirname, tmPath)).toString() + ); +} + const placeholderReplacer = new JsonPlaceholderReplacer(); placeholderReplacer.addVariableMap({ PROTOCOL: "http", THING_NAME: thingName, HOSTNAME: hostname, - PORT_NUMBER: portNumber + PORT_NUMBER: portNumber, }); -const thingDescription = placeholderReplacer.replace(thingModel); -thingDescription["@type"] = "Thing"; - -let servient = new WotCore.Servient(); -servient.addServer(new HttpServer({ - baseUri: `http://${hostname}:${portNumber}`, - port: portNumber -})); - -servient.start() - .then((WoT: any) => { - WoT.produce(thingDescription) - .then((thing: WoT.ExposedThing) => { - // Initialize the property values - allAvailableResources = { - water: readFromSensor("water"), - milk: readFromSensor("milk"), - chocolate: readFromSensor("chocolate"), - coffeeBeans: readFromSensor("coffeeBeans"), - }; - possibleDrinks = ["espresso", "americano", "cappuccino", "latte", "hotChocolate", "hotWater"]; - maintenanceNeeded = false; - schedules = []; - - thing.setPropertyReadHandler("allAvailableResources", async () => allAvailableResources); - thing.setPropertyReadHandler("possibleDrinks", async () => possibleDrinks); - thing.setPropertyReadHandler("maintenanceNeeded", async () => maintenanceNeeded); - thing.setPropertyReadHandler("schedules", async () => schedules); - - // Override a write handler for servedCounter property, - // raising maintenanceNeeded flag when the value exceeds 1000 drinks - thing.setPropertyWriteHandler("servedCounter", async (val) => { - servedCounter = (await val.value()) as number; - if (servedCounter > 1000) { - maintenanceNeeded = true; - thing.emitPropertyChange("maintenanceNeeded"); - - // Notify a "maintainer" when the value has changed - // (the notify function here simply logs a message to the console) - notify( - "admin@coffeeMachine.com", - `maintenanceNeeded property has changed, new value is: ${maintenanceNeeded}` - ); - } - }); - // Now initialize the servedCounter property - servedCounter = readFromSensor("servedCounter"); +let thingDescription = placeholderReplacer.replace(thingModel); +thingDescription = { + ...thingDescription, + "@type": "Thing", +}; + +const servient = new Servient(); +servient.addServer( + new HttpServer({ + baseUri: `http://${hostname}:${portNumber}`, + port: portNumber, + }) +); + +servient + .start() + .then((WoT) => { + WoT.produce(thingDescription).then((thing: WoT.ExposedThing) => { + // Initialize the property values + allAvailableResources = { + water: readFromSensor("water"), + milk: readFromSensor("milk"), + chocolate: readFromSensor("chocolate"), + coffeeBeans: readFromSensor("coffeeBeans"), + }; + possibleDrinks = [ + "espresso", + "americano", + "cappuccino", + "latte", + "hotChocolate", + "hotWater", + ]; + maintenanceNeeded = false; + schedules = []; + + thing.setPropertyReadHandler( + "allAvailableResources", + async () => allAvailableResources + ); + thing.setPropertyReadHandler( + "possibleDrinks", + async () => possibleDrinks + ); + thing.setPropertyReadHandler( + "maintenanceNeeded", + async () => maintenanceNeeded + ); + thing.setPropertyReadHandler("schedules", async () => schedules); + + // Override a write handler for servedCounter property, + // raising maintenanceNeeded flag when the value exceeds 1000 drinks + thing.setPropertyWriteHandler("servedCounter", async (val) => { + servedCounter = (await val.value()) as number; + if (servedCounter > 1000) { + maintenanceNeeded = true; + thing.emitPropertyChange("maintenanceNeeded"); + + // Notify a "maintainer" when the value has changed + // (the notify function here simply logs a message to the console) + notify( + "admin@coffeeMachine.com", + `maintenanceNeeded property has changed, new value is: ${maintenanceNeeded}` + ); + } + }); - // Override a write handler for availableResourceLevel property, - // utilizing the uriVariables properly - thing.setPropertyWriteHandler("availableResourceLevel", async (val, options) => { + // Now initialize the servedCounter property + servedCounter = readFromSensor("servedCounter"); + + // Override a write handler for availableResourceLevel property, + // utilizing the uriVariables properly + thing.setPropertyWriteHandler( + "availableResourceLevel", + async (val, options) => { // Check if uriVariables are provided - if (options && typeof options === "object" && "uriVariables" in options) { - const uriVariables = options.uriVariables as Record; + if ( + options && + typeof options === "object" && + "uriVariables" in options + ) { + const uriVariables = options.uriVariables as Record< + string, + string + >; if ("id" in uriVariables) { const id = uriVariables.id; - allAvailableResources[id] = (await val.value()) as number; + allAvailableResources[id] = + (await val.value()) as number; return; } } throw Error("Please specify id variable as uriVariables."); - }); + } + ); - // Override a read handler for availableResourceLevel property, - // utilizing the uriVariables properly - thing.setPropertyReadHandler("availableResourceLevel", async (options) => { + // Override a read handler for availableResourceLevel property, + // utilizing the uriVariables properly + thing.setPropertyReadHandler( + "availableResourceLevel", + async (options) => { // Check if uriVariables are provided - if (options && typeof options === "object" && "uriVariables" in options) { - const uriVariables = options.uriVariables as Record; + if ( + options && + typeof options === "object" && + "uriVariables" in options + ) { + const uriVariables = options.uriVariables as Record< + string, + string + >; if ("id" in uriVariables) { const id = uriVariables.id; return allAvailableResources[id]; } } throw Error("Please specify id variable as uriVariables."); - }); - - // Set up a handler for makeDrink action - thing.setActionHandler("makeDrink", async (_params, options) => { - // Default values - let drinkId = "americano"; - let size = "m"; - let quantity = 1; - - // Size quantifiers - const sizeQuantifiers: Record = { s: 0.1, m: 0.2, l: 0.3 }; - - // Drink recipes showing the amount of a resource consumed for a particular drink - const drinkRecipes: Record> = { - espresso: { - water: 1, - milk: 0, - chocolate: 0, - coffeeBeans: 2, - }, - americano: { - water: 2, - milk: 0, - chocolate: 0, - coffeeBeans: 2, - }, - cappuccino: { - water: 1, - milk: 1, - chocolate: 0, - coffeeBeans: 2, - }, - latte: { - water: 1, - milk: 2, - chocolate: 0, - coffeeBeans: 2, - }, - hotChocolate: { - water: 0, - milk: 0, - chocolate: 1, - coffeeBeans: 0, - }, - hotWater: { - water: 1, - milk: 0, - chocolate: 0, - coffeeBeans: 0, - }, - }; + } + ); - // Check if uriVariables are provided - if (options && typeof options === "object" && "uriVariables" in options) { - const uriVariables = options.uriVariables as Record; - drinkId = "drinkId" in uriVariables ? (uriVariables.drinkId as string) : drinkId; - size = "size" in uriVariables ? (uriVariables.size as string) : size; - quantity = "quantity" in uriVariables ? (uriVariables.quantity as number) : quantity; - } + // Set up a handler for makeDrink action + thing.setActionHandler("makeDrink", async (_params, options) => { + // Default values + let drinkId = "americano"; + let size = "m"; + let quantity = 1; - // Calculate the new level of resources - const newResources = Object.assign({}, allAvailableResources); - newResources.water -= Math.ceil(quantity * sizeQuantifiers[size] * drinkRecipes[drinkId].water); - newResources.milk -= Math.ceil(quantity * sizeQuantifiers[size] * drinkRecipes[drinkId].milk); - newResources.chocolate -= Math.ceil(quantity * sizeQuantifiers[size] * drinkRecipes[drinkId].chocolate); - newResources.coffeeBeans -= Math.ceil(quantity * sizeQuantifiers[size] * drinkRecipes[drinkId].coffeeBeans); - - // Check if the amount of available resources is sufficient to make a drink - for (const resource in newResources) { - if (newResources[resource] <= 0) { - thing.emitEvent("outOfResource", `Low level of ${resource}: ${newResources[resource]}%`); - return { result: false, message: `${resource} level is not sufficient` }; - } + // Size quantifiers + const sizeQuantifiers: Record = { + s: 0.1, + m: 0.2, + l: 0.3, + }; + + // Drink recipes showing the amount of a resource consumed for a particular drink + const drinkRecipes: Record> = { + espresso: { + water: 1, + milk: 0, + chocolate: 0, + coffeeBeans: 2, + }, + americano: { + water: 2, + milk: 0, + chocolate: 0, + coffeeBeans: 2, + }, + cappuccino: { + water: 1, + milk: 1, + chocolate: 0, + coffeeBeans: 2, + }, + latte: { + water: 1, + milk: 2, + chocolate: 0, + coffeeBeans: 2, + }, + hotChocolate: { + water: 0, + milk: 0, + chocolate: 1, + coffeeBeans: 0, + }, + hotWater: { + water: 1, + milk: 0, + chocolate: 0, + coffeeBeans: 0, + }, + }; + + // Check if uriVariables are provided + if ( + options && + typeof options === "object" && + "uriVariables" in options + ) { + const uriVariables = options.uriVariables as Record< + string, + string | number + >; + drinkId = + "drinkId" in uriVariables + ? (uriVariables.drinkId as string) + : drinkId; + size = + "size" in uriVariables + ? (uriVariables.size as string) + : size; + quantity = + "quantity" in uriVariables + ? (uriVariables.quantity as number) + : quantity; + } + + // Calculate the new level of resources + const newResources = Object.assign({}, allAvailableResources); + newResources.water -= Math.ceil( + quantity * + sizeQuantifiers[size] * + drinkRecipes[drinkId].water + ); + newResources.milk -= Math.ceil( + quantity * + sizeQuantifiers[size] * + drinkRecipes[drinkId].milk + ); + newResources.chocolate -= Math.ceil( + quantity * + sizeQuantifiers[size] * + drinkRecipes[drinkId].chocolate + ); + newResources.coffeeBeans -= Math.ceil( + quantity * + sizeQuantifiers[size] * + drinkRecipes[drinkId].coffeeBeans + ); + + // Check if the amount of available resources is sufficient to make a drink + for (const resource in newResources) { + if (newResources[resource] <= 0) { + thing.emitEvent( + "outOfResource", + `Low level of ${resource}: ${newResources[resource]}%` + ); + return { + result: false, + message: `${resource} level is not sufficient`, + }; } + } - // Now store the new level of allAvailableResources - allAvailableResources = newResources; - servedCounter = servedCounter + quantity; + // Now store the new level of allAvailableResources + allAvailableResources = newResources; + servedCounter = servedCounter + quantity; - // Finally deliver the drink - return { result: true, message: `Your ${drinkId} is in progress!` }; - }); + // Finally deliver the drink + return { + result: true, + message: `Your ${drinkId} is in progress!`, + }; + }); - // Set up a handler for setSchedule action - thing.setActionHandler("setSchedule", async (params, options) => { - const paramsp = (await params.value()) as Record; // : any = await Helpers.parseInteractionOutput(params); + // Set up a handler for setSchedule action + thing.setActionHandler("setSchedule", async (params, options) => { + const paramsp = (await params.value()) as Record< + string, + unknown + >; // : any = await Helpers.parseInteractionOutput(params); - // Check if uriVariables are provided - if (paramsp != null && typeof paramsp === "object" && "time" in paramsp && "mode" in paramsp) { - // Use default values if not provided - paramsp.drinkId = "drinkId" in paramsp ? paramsp.drinkId : "americano"; - paramsp.size = "size" in paramsp ? paramsp.size : "m"; - paramsp.quantity = "quantity" in paramsp ? paramsp.quantity : 1; + // Check if uriVariables are provided + if ( + paramsp != null && + typeof paramsp === "object" && + "time" in paramsp && + "mode" in paramsp + ) { + // Use default values if not provided + paramsp.drinkId = + "drinkId" in paramsp ? paramsp.drinkId : "americano"; + paramsp.size = "size" in paramsp ? paramsp.size : "m"; + paramsp.quantity = + "quantity" in paramsp ? paramsp.quantity : 1; - // Now add a new schedule - schedules.push(paramsp); + // Now add a new schedule + schedules.push(paramsp); - return { result: true, message: `Your schedule has been set!` }; - } + return { + result: true, + message: `Your schedule has been set!`, + }; + } - return { result: false, message: `Please provide all the required parameters: time and mode.` }; - }); + return { + result: false, + message: `Please provide all the required parameters: time and mode.`, + }; + }); - // Finally expose the thing - thing.expose().then(() => { - console.info(`${thing.getThingDescription().title} ready`); - console.info('ThingIsReady') - }); - console.log(`Produced ${thing.getThingDescription().title}`); - }) + // Finally expose the thing + thing.expose().then(() => { + console.info(`${thing.getThingDescription().title} ready`); + console.info("ThingIsReady"); + }); + console.log(`Produced ${thing.getThingDescription().title}`); + }); }) .catch((e: Error) => { console.log(e); diff --git a/things/advanced-coffee-machine/http/ts/test/client.test.ts b/things/advanced-coffee-machine/http/ts/test/client.test.ts index 642e36e..b7e2510 100644 --- a/things/advanced-coffee-machine/http/ts/test/client.test.ts +++ b/things/advanced-coffee-machine/http/ts/test/client.test.ts @@ -1,70 +1,87 @@ +/******************************************************************************** + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the W3C Software Notice and + * Document License (2015-05-13) which is available at + * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document. + * + * SPDX-License-Identifier: EPL-2.0 OR W3C-20150513 + ********************************************************************************/ import chai from "chai"; +import chaiAsPromised from "chai-as-promised"; -import chai from 'chai' -import chaiAsPromised from 'chai-as-promised' +import { Servient } from "@node-wot/core"; +import { HttpClientFactory } from "@node-wot/binding-http"; +import { port } from "./fixtures"; -import { Servient } from "@node-wot/core" -import { HttpClientFactory } from "@node-wot/binding-http" -import { port } from './fixtures' +chai.use(chaiAsPromised); +const expect = chai.expect; -chai.use(chaiAsPromised) -const expect = chai.expect +const servient = new Servient(); +servient.addClientFactory(new HttpClientFactory()); -let servient = new Servient() -servient.addClientFactory(new HttpClientFactory()) - -let thing: WoT.ConsumedThing +let thing: WoT.ConsumedThing; describe("Client Tests", () => { before(async () => { try { - const WoT = await servient.start() - const td: WoT.ThingDescription = await WoT.requestThingDescription(`http://localhost:${port}/http-advanced-coffee-machine`) - thing = await WoT.consume(td) - } catch(error) { - console.error(error) + const WoT = await servient.start(); + const td: WoT.ThingDescription = await WoT.requestThingDescription( + `http://localhost:${port}/http-advanced-coffee-machine` + ); + thing = await WoT.consume(td); + } catch (error) { + console.error(error); } - }) + }); - after(async () => { - await servient.shutdown() - }) - it("should read allAvailableResources property", async () => { - const response = await thing.readProperty("allAvailableResources") - const value = await response.value() + const response = await thing.readProperty("allAvailableResources"); + const value = await response.value(); expect(value).to.be.eql({ water: 100, milk: 100, chocolate: 100, - coffeeBeans: 100 - }) - }) + coffeeBeans: 100, + }); + }); it("should change water level to 80", async () => { - const waterLevel = 80 - await thing.writeProperty("availableResourceLevel", waterLevel, { uriVariables: { id: "water" }}) - const response = await thing.readProperty("availableResourceLevel", { uriVariables: { id: "water" }}) - const value = await response.value() - expect(value).to.be.equal(waterLevel) - }) + const waterLevel = 80; + await thing.writeProperty("availableResourceLevel", waterLevel, { + uriVariables: { id: "water" }, + }); + const response = await thing.readProperty("availableResourceLevel", { + uriVariables: { id: "water" }, + }); + const value = await response.value(); + expect(value).to.be.equal(waterLevel); + }); it("should observe maintenanceNeeded", async () => { await thing.observeProperty("maintenanceNeeded", async (data) => { - const value = await data.value() - expect(value).to.be.true - }) + const value = await data.value(); + expect(value).to.be.true; + }); - const servedCounter = 1001 - await thing.writeProperty("servedCounter", servedCounter) - }) + const servedCounter = 1001; + await thing.writeProperty("servedCounter", servedCounter); + }); it("should make 3 cups of latte", async () => { const makeCoffee = await thing.invokeAction("makeDrink", undefined, { uriVariables: { drinkId: "latte", size: "l", quantity: 3 }, }); - const makeCoffeeValue = (await makeCoffee?.value()) as Record; - expect(makeCoffeeValue.result).to.be.not.null - }) + const makeCoffeeValue = (await makeCoffee?.value()) as Record< + string, + unknown + >; + expect(makeCoffeeValue.result).to.be.not.null; + }); it("should schedule a task", async () => { const schedule = { @@ -73,22 +90,22 @@ describe("Client Tests", () => { quantity: 2, time: "10:00", mode: "everyday", - } + }; await thing.invokeAction("setSchedule", schedule); - const response = await thing.readProperty("schedules") - const value = await response.value() as object[] - expect(value.length).to.be.equal(1) - expect(value[0]).to.be.eql(schedule) - }) + const response = await thing.readProperty("schedules"); + const value = (await response.value()) as object[]; + expect(value.length).to.be.equal(1); + expect(value[0]).to.be.eql(schedule); + }); it("should subscribe to outOfResource event", async () => { await thing.subscribeEvent("outOfResource", async (data) => { - const value = await data.value() - expect(value).to.be.not.null - }) + const value = await data.value(); + expect(value).to.be.not.null; + }); await thing.invokeAction("makeDrink", undefined, { uriVariables: { drinkId: "latte", size: "l", quantity: 1000 }, }); - }) -}) + }); +}); diff --git a/things/advanced-coffee-machine/http/ts/test/fixtures.ts b/things/advanced-coffee-machine/http/ts/test/fixtures.ts index 2c66558..0579ccc 100644 --- a/things/advanced-coffee-machine/http/ts/test/fixtures.ts +++ b/things/advanced-coffee-machine/http/ts/test/fixtures.ts @@ -1,24 +1,42 @@ -import { ChildProcess } from "child_process" -import { getInitiateMain, ThingStartResponse } from "../../../../../util/util" -import path from "path" +/******************************************************************************** + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the W3C Software Notice and + * Document License (2015-05-13) which is available at + * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document. + * + * SPDX-License-Identifier: EPL-2.0 OR W3C-20150513 + ********************************************************************************/ -let thingProcess: ChildProcess | undefined -let response: ThingStartResponse -export const port = 3000 +import { ChildProcess } from "child_process"; +import { getInitiateMain, ThingStartResponse } from "../../../../../util/util"; +import path from "path"; + +let thingProcess: ChildProcess | undefined; +let response: ThingStartResponse; +export const port = 3000; export async function mochaGlobalSetup() { try { - response = await getInitiateMain('node', [path.join(__dirname, '..', 'dist', 'main.js'), '-p', `${port}`]) - thingProcess = response.process - } - catch(error: any) { - console.log(error) - thingProcess = error.process + response = await getInitiateMain("node", [ + path.join(__dirname, "..", "dist", "main.js"), + "-p", + `${port}`, + ]); + thingProcess = response.process; + } catch (error: unknown) { + console.log(error); + thingProcess = (error as ThingStartResponse).process; } } export function mochaGlobalTeardown() { if (thingProcess) { - thingProcess.kill() + thingProcess.kill(); } -} \ No newline at end of file +} diff --git a/things/advanced-coffee-machine/http/ts/test/td.test.ts b/things/advanced-coffee-machine/http/ts/test/td.test.ts index ae31885..8ab2f23 100644 --- a/things/advanced-coffee-machine/http/ts/test/td.test.ts +++ b/things/advanced-coffee-machine/http/ts/test/td.test.ts @@ -1,45 +1,66 @@ -import * as chai from 'chai' -import * as http from 'http' -import { getTDValidate } from '../../../../../util/util' -import { ValidateFunction } from 'ajv' -import { port } from './fixtures' +/******************************************************************************** + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the W3C Software Notice and + * Document License (2015-05-13) which is available at + * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document. + * + * SPDX-License-Identifier: EPL-2.0 OR W3C-20150513 + ********************************************************************************/ -const expect = chai.expect +import * as chai from "chai"; +import * as http from "http"; +import { getTDValidate } from "../../../../../util/util"; +import { ValidateFunction } from "ajv"; +import { port } from "./fixtures"; -let validate: ValidateFunction | undefined +const expect = chai.expect; + +let validate: ValidateFunction | undefined; describe("TD Test", () => { - before(async () => { - const tdValidate = getTDValidate() - - try { - const response = await Promise.all([tdValidate]) - validate = response[0].validate - } - catch (error) { - console.log(error) - } - }) - - it('should have a valid TD', (done) => { - http.get(`http://localhost:${port}/http-advanced-coffee-machine`, function (response: any) { - const body: Buffer[] = [] - response.on('data', (chunk: Buffer) => { - body.push(chunk) - }) - - response.on('end', () => { + before(async () => { + const tdValidate = getTDValidate(); + try { - const result = JSON.parse(Buffer.concat(body).toString()) - const valid = validate && result ! ? validate(result) : false - expect(valid).to.be.true - done() + const response = await Promise.all([tdValidate]); + validate = response[0].validate; } catch (error) { - console.log(error) - done(error) + console.log(error); } - }) - }) - }) -}) + }); + + it("should have a valid TD", (done) => { + http.get( + `http://localhost:${port}/http-advanced-coffee-machine`, + function (response: http.IncomingMessage) { + const body: Buffer[] = []; + response.on("data", (chunk: Buffer) => { + body.push(chunk); + }); + response.on("end", () => { + try { + const result = JSON.parse( + Buffer.concat(body).toString() + ); + const valid = + validate && result !== "" + ? validate(result) + : false; + expect(valid).to.be.true; + done(); + } catch (error) { + console.log(error); + done(error); + } + }); + } + ); + }); +}); diff --git a/things/advanced-coffee-machine/http/ts/tsconfig.json b/things/advanced-coffee-machine/http/ts/tsconfig.json index d4a580f..93aaf4a 100644 --- a/things/advanced-coffee-machine/http/ts/tsconfig.json +++ b/things/advanced-coffee-machine/http/ts/tsconfig.json @@ -1,14 +1,8 @@ { + "extends": "../../../..//tsconfig.json", "compilerOptions": { "outDir": "dist", - "rootDir": "src", - "target": "ES2018", - "module": "commonjs", - "skipLibCheck": false, - "strict": true, - "sourceMap": false, - "esModuleInterop": true, - "removeComments": false + "rootDir": "src" }, - "include": ["src/**/*"], -} \ No newline at end of file + "include": ["src/**/*"] +} diff --git a/things/advanced-coffee-machine/tm.test.js b/things/advanced-coffee-machine/tm.test.js index 51ef32e..34ffc5b 100644 --- a/things/advanced-coffee-machine/tm.test.js +++ b/things/advanced-coffee-machine/tm.test.js @@ -1,32 +1,35 @@ -const Ajv = require('ajv') -const chai = require('chai') -const https = require('https') +const Ajv = require("ajv"); +const chai = require("chai"); +const https = require("https"); -const ajv = new Ajv({ strict: false, allErrors: true, validateFormats: false }) +const ajv = new Ajv({ strict: false, allErrors: true, validateFormats: false }); -const expect = chai.expect +const expect = chai.expect; -describe('Advanced Coffee Machine', () => { - let validate +describe("Advanced Coffee Machine", () => { + let validate; - before((done) => { - https.get('https://raw.githubusercontent.com/w3c/wot-thing-description/main/validation/tm-json-schema-validation.json', function (response) { - const body = [] - response.on('data', (chunk) => { - body.push(chunk) - }) + before((done) => { + https.get( + "https://raw.githubusercontent.com/w3c/wot-thing-description/main/validation/tm-json-schema-validation.json", + function (response) { + const body = []; + response.on("data", (chunk) => { + body.push(chunk); + }); - response.on('end', () => { - const tmSchema = JSON.parse(Buffer.concat(body).toString()) - validate = ajv.compile(tmSchema) - done() - }) - }) - }) + response.on("end", () => { + const tmSchema = JSON.parse(Buffer.concat(body).toString()); + validate = ajv.compile(tmSchema); + done(); + }); + } + ); + }); - it('should have a valid TM', () => { - const advancedCoffeeMachineTM = require('./advanced-coffee-machine.tm.json') - const valid = validate(advancedCoffeeMachineTM) - expect(valid).to.be.true - }) -}) + it("should have a valid TM", () => { + const advancedCoffeeMachineTM = require("./advanced-coffee-machine.tm.json"); + const valid = validate(advancedCoffeeMachineTM); + expect(valid).to.be.true; + }); +}); diff --git a/things/calculator/calculator.tm.json b/things/calculator/calculator.tm.json index 864b9fd..f16156e 100644 --- a/things/calculator/calculator.tm.json +++ b/things/calculator/calculator.tm.json @@ -1,63 +1,61 @@ { - "@context":[ - "https://www.w3.org/2019/wot/td/v1", - "https://www.w3.org/2022/wot/td/v1.1", - { - "@language":"en" - } - ], - "@type":"tm:ThingModel", - "title":"{{THING_NAME}}", - "description":"Calculator Thing", - "securityDefinitions":{ - "nosec_sc":{ - "scheme":"nosec" - } - }, - "security":[ - "nosec_sc" - ], - "base":"{{PROTOCOL}}://{{HOSTNAME}}:{{PORT_NUMBER}}/{{THING_NAME}}/", - "properties":{ - "result":{ - "type":"number", - "readOnly":true, - "writeOnly":false, - "observable":"{{RESULT_OBSERVABLE}}" - }, - "lastChange":{ - "type":"string", - "format": "date-time", - "readOnly":true, - "writeOnly":false, - "observable":"{{LAST_CHANGE_OBSERVABLE}}" - } - }, - "actions":{ - "add":{ - "input":{ - "type":"number" - }, - "output":{ - "type":"number" - }, - "idempotent":false, - "safe":false - }, - "subtract":{ - "input":{ - "type":"number" - }, - "output":{ - "type":"number" - }, - "idempotent":false, - "safe":false - } - }, - "events":{ - "update":{ - "data":{} - } - } - } \ No newline at end of file + "@context": [ + "https://www.w3.org/2019/wot/td/v1", + "https://www.w3.org/2022/wot/td/v1.1", + { + "@language": "en" + } + ], + "@type": "tm:ThingModel", + "title": "{{THING_NAME}}", + "description": "Calculator Thing", + "securityDefinitions": { + "nosec_sc": { + "scheme": "nosec" + } + }, + "security": ["nosec_sc"], + "base": "{{PROTOCOL}}://{{HOSTNAME}}:{{PORT_NUMBER}}/{{THING_NAME}}/", + "properties": { + "result": { + "type": "number", + "readOnly": true, + "writeOnly": false, + "observable": "{{RESULT_OBSERVABLE}}" + }, + "lastChange": { + "type": "string", + "format": "date-time", + "readOnly": true, + "writeOnly": false, + "observable": "{{LAST_CHANGE_OBSERVABLE}}" + } + }, + "actions": { + "add": { + "input": { + "type": "number" + }, + "output": { + "type": "number" + }, + "idempotent": false, + "safe": false + }, + "subtract": { + "input": { + "type": "number" + }, + "output": { + "type": "number" + }, + "idempotent": false, + "safe": false + } + }, + "events": { + "update": { + "data": {} + } + } +} diff --git a/things/calculator/coap/client-tests/content-negotiation-coap-client.js b/things/calculator/coap/client-tests/content-negotiation-coap-client.js deleted file mode 100644 index 165c0c9..0000000 --- a/things/calculator/coap/client-tests/content-negotiation-coap-client.js +++ /dev/null @@ -1,356 +0,0 @@ -const coap = require('coap') -const cbor = require('cbor') -const hostname = 'localhost' -const portNumber = 5684 -const thingName = 'coap-calculator-content-negotiation' - -const fullTDEndpoint = `/${thingName}`, - resultEndPoint = `/${thingName}/properties/result`, - lastChangeEndPoint = `/${thingName}/properties/lastChange`, - additionEndPoint = `/${thingName}/actions/add`, - subtractionEndPoint = `/${thingName}/actions/subtract`, - updateEndPoint = `/${thingName}/events/update` - - - -/****************************************/ -/****** Thing Description Endpoint ******/ -/****************************************/ - -// GET request to retrieve thing description -function getFullTD(acceptType) { - - const getThingDescription = coap.request({ - method: 'GET', - host: hostname, - port: portNumber, - pathname: fullTDEndpoint, - headers: { - "Accept": acceptType - } - }) - - getThingDescription.on('response', (res) => { - //TODO: Fix the problem with block wise transfer to be able to parse the response accordingly - if (res.code === '2.05') { - if (acceptType === "application/json" || acceptType === "application/td+json") { - console.log('Thing Description (json):\n', JSON.parse(res.payload.toString())) - } - else { - const decodedData = cbor.decode(res.payload); - console.log('Thing Description (cbor):\n', JSON.parse(decodedData)) - } - - } else { - console.error(`Failed to get Thing Description: ${res.code}`) - } - }) - getThingDescription.end() -} - - -/****************************************/ -/*********** Result Endpoint ************/ -/****************************************/ - -// GET request to retrieve a property (result) -function getResult(acceptType) { - - const getPropertyResult = coap.request({ - method: 'GET', - host: hostname, - port: portNumber, - pathname: resultEndPoint, - headers: { - "Accept": acceptType - } - }); - - getPropertyResult.on('response', (res) => { - const contentType = res.headers["Content-Type"] - - if (res.code === '2.05') { - if (contentType.includes("application/json")) { - console.log("Result (json): ", JSON.parse(res.payload.toString())); - } - else { - const decodedData = cbor.decode(res.payload); - console.log("Result (cbor): ", decodedData); - } - } else { - console.error(`Failed to get Property "result": ${res.code}`) - } - }) - getPropertyResult.end() -} - -/** - * GET request to observe the property result. - * Uncomment to test the update functionality. - */ -function observeResultProperty(acceptType) { - - const observeResult = coap.request({ - method: 'GET', - observe: true, - host: hostname, - port: portNumber, - pathname: resultEndPoint, - headers: { - "Accept": acceptType - } - }); - - observeResult.on('response', (res) => { - - res.on('data', function () { - const contentType = res.headers["Content-Type"] - - if (res.code === '2.05') { - if (contentType.includes("application/json")) { - console.log("Observe result property (json): ", JSON.parse(res.payload.toString())); - } - else { - const decodedData = cbor.decode(res.payload); - console.log("Observe result property (cbor): ", decodedData); - } - } else { - console.error(`Failed to observe Event "update": ${res.code}`); - } - }) - }); - - observeResult.end(); -} - - -/****************************************/ -/********** lastChange Endpoint *********/ -/****************************************/ - -// GET request to retrieve a property (lastChange) -function getLastChange(acceptType) { - - const getPropertyLastChange = coap.request({ - method: 'GET', - host: hostname, - port: portNumber, - pathname: lastChangeEndPoint, - headers: { - "Accept": acceptType - } - }) - getPropertyLastChange.on('response', (res) => { - const contentType = res.headers["Content-Type"] - - if (res.code === '2.05') { - if (contentType.includes("application/json")) { - console.log("Last Change (json): ", JSON.parse(res.payload.toString())); - } - else { - const decodedData = cbor.decode(res.payload); - console.log("Last Change (cbor): ", decodedData); - } - } else { - console.error(`Failed to get Property "lastChange": ${res.code}`) - } - }) - getPropertyLastChange.end() -} - - -/** - * GET request to observe the property result. - * Uncomment to test the update functionality. - */ -function observeLastChangeProperty(acceptType) { - - const observeLastChange = coap.request({ - method: 'GET', - observe: true, - host: hostname, - port: portNumber, - pathname: lastChangeEndPoint, - headers: { - "Accept": acceptType - } - }); - - observeLastChange.on('response', (res) => { - - res.on('data', function () { - const contentType = res.headers["Content-Type"] - - if (res.code === '2.05') { - if (contentType.includes("application/json")) { - console.log("Observe lastChange property (json): ", JSON.parse(res.payload.toString())); - } - else { - const decodedData = cbor.decode(res.payload); - console.log("Observe lastChange property (cbor): ", decodedData); - } - } else { - console.error(`Failed to observe Event "update": ${res.code}`); - } - }) - - }); - - observeLastChange.end(); -} - - - -/****************************************/ -/*********** Addition Endpoint **********/ -/****************************************/ - -// POST request to perform the addition action -function addNumber(acceptType, contentType, numberToAdd) { - - const addNumberReq = coap.request({ - method: 'POST', - host: hostname, - port: portNumber, - pathname: additionEndPoint, - headers: { - "Accept": acceptType, - "Content-Format": contentType - } - }); - - // Set the payload with the input value - addNumberReq.write(contentType === 'application/json' ? JSON.stringify(numberToAdd) : cbor.encode(numberToAdd)) - - addNumberReq.on('response', (res) => { - const contentType = res.headers["Content-Type"] - - if (res.code === '2.05') { - if (contentType.includes("application/json")) { - console.log("Addition result (json): ", JSON.parse(res.payload.toString())); - } - else { - const decodedData = cbor.decode(res.payload); - console.log("Addition result (cbor): ", decodedData); - } - } else { - console.error(`Failed to call the Action "add": ${res.code}`) - - } - }); - addNumberReq.end(); -} - - - -/****************************************/ -/********** Subtraction Endpoint ********/ -/****************************************/ - -// POST request to perform the subtract action -function subtractNumber(acceptType, contentType, numberToSubtract) { - - const subtractNumberReq = coap.request({ - method: 'POST', - host: hostname, - port: portNumber, - pathname: subtractionEndPoint, - headers: { - "Accept": acceptType, - "Content-Format": contentType - } - }); - - // Set the payload with the input value - subtractNumberReq.write(contentType === 'application/json' ? JSON.stringify(numberToSubtract) : cbor.encode(numberToSubtract)) - - subtractNumberReq.on('response', (res) => { - const contentType = res.headers["Content-Type"] - - if (res.code === '2.05') { - if (contentType.includes("application/json")) { - console.log("Subtraction result (json): ", JSON.parse(res.payload.toString())); - } - else { - const decodedData = cbor.decode(res.payload); - console.log("Subtraction result (cbor): ", decodedData); - } - } else { - console.error(`Failed to call the Action "subtract": ${res.code}`) - } - }); - subtractNumberReq.end(); -} - - -/****************************************/ -/*********** Update Endpoint ************/ -/****************************************/ - -/** - * GET request to observe an event (update). - * Uncomment to test the update functionality. - */ -function observeUpdateEvent(acceptType) { - - const observeUpdate = coap.request({ - method: 'GET', - observe: true, // Enable observation - host: hostname, - port: portNumber, - pathname: updateEndPoint, - headers: { - "Accept": acceptType - } - }); - - observeUpdate.on('response', (res) => { - - res.on('data', function () { - const contentType = res.headers["Content-Type"] - - if (res.code === '2.05') { - if (contentType.includes("application/json")) { - console.log("Observe update event (json): ", JSON.parse(res.payload.toString())); - } - else { - const decodedData = cbor.decode(res.payload); - console.log("Observe update event (cbor): ", decodedData); - } - } else { - console.error(`Failed to observe Event "update": ${res.code}`); - } - }) - - }); - - // Start observing - observeUpdate.end(); -} - - -//Test the main functionality of the content-negotiation-calculator-thing -function runCalculatorInteractions() { - - //Main GET and POST requests - getFullTD("application/json") - getResult("application/cbor") - getLastChange("application/json") - addNumber("application/cbor", "application/cbor", 3) - subtractNumber("application/json", "application/json", 2) - - //Observation of properties and events after 1 second - setTimeout(() => { - console.log("\n-------- Start observation --------\n"); - observeResultProperty("application/json") - observeLastChangeProperty("application/cbor") - observeUpdateEvent("application/json") - }, 1000) - - //Update the property result after 2.5 seconds to test the observation - setTimeout(() => { - addNumber("application/cbor", "application/json", 1) - }, 2500) -} - -runCalculatorInteractions() \ No newline at end of file diff --git a/things/calculator/coap/client-tests/node-wot-content-negotiation-coap-client.js b/things/calculator/coap/client-tests/node-wot-content-negotiation-coap-client.js deleted file mode 100644 index 31e77a9..0000000 --- a/things/calculator/coap/client-tests/node-wot-content-negotiation-coap-client.js +++ /dev/null @@ -1,52 +0,0 @@ -const { Servient } = require("@node-wot/core"); -const { CoapClientFactory } = require("@node-wot/binding-coap"); - -// create Servient and add CoAP binding -const servient = new Servient(); -servient.addClientFactory(new CoapClientFactory()); - -servient - .start() - .then(async (WoT) => { - try { - const td = await WoT.requestThingDescription("coap://localhost:5684/coap-calculator-content-negotiation"); - - const thing = await WoT.consume(td); - console.log(td); - - // read property result - let result = await thing.readProperty("result", { formIndex: 2 }); - console.log("result: ", await result.value()); - - // read property lastChange - let lastChange = await thing.readProperty("lastChange", { formIndex: 2 }); - console.log("lastChange: ", await lastChange.value()); - - console.log('\n ---------- \n'); - - //Observe properties - thing.observeProperty("result", async (data) => { console.log("Result observe:", await data.value()); }); - thing.observeProperty("lastChange", async (data) => { console.log("lastChange observe:", await data.value()); }); - - // Subscribe to event update - thing.subscribeEvent("update", async (data) => { - console.log("Update event:", await data.value()); - }) - - - //Invoke addition action - let add = await thing.invokeAction("add", 3, { formIndex: 1 }) - console.log("Addition value:", await add.value()); - //Invoke subtraction action - let subtract = await thing.invokeAction("subtract", 1, { formIndex: 3 }) - console.log("Subtraction value:", await subtract.value()); - - console.log('\n ---------- \n'); - - } catch (err) { - console.error("Script error:", err); - } - }) - .catch((err) => { - console.error("Start error:", err); - }); \ No newline at end of file diff --git a/things/calculator/coap/client-tests/node-wot-simple-coap-client.js b/things/calculator/coap/client-tests/node-wot-simple-coap-client.js deleted file mode 100644 index 52e7266..0000000 --- a/things/calculator/coap/client-tests/node-wot-simple-coap-client.js +++ /dev/null @@ -1,54 +0,0 @@ -// example-client.js -const { Servient } = require("@node-wot/core"); -const { CoapClientFactory } = require("@node-wot/binding-coap"); - -// create Servient and add CoAP binding -const servient = new Servient(); -servient.addClientFactory(new CoapClientFactory()); - -servient - .start() - .then(async (WoT) => { - try { - const td = await WoT.requestThingDescription("coap://localhost:5683/coap-calculator-simple"); - - const thing = await WoT.consume(td); - console.log(td); - - // read property result - let result = await thing.readProperty("result"); - console.log("result: ", await result.value()); - - // read property lastChange - let lastChange = await thing.readProperty("lastChange"); - console.log("lastChange: ", await lastChange.value()); - - console.log("\n------------\n"); - - // Observe properties - thing.observeProperty("result", async (data) => { console.log("Result observe:", await data.value()); }); - thing.observeProperty("lastChange", async (data) => { console.log("lastChange observe:", await data.value()); }); - - // Subscribe to event update - thing.subscribeEvent("update", async (data) => { - console.log("Update event:", await data.value()); - }) - - - //Invoke addition action - let addition = await thing.invokeAction("add", 2) - console.log("Addition result: ", await addition.value()); - //Invoke addition subtraction - let subtraction = await thing.invokeAction("subtract", 3) - console.log("Subtraction result: ", await subtraction.value()); - - console.log("\n------------\n"); - - - } catch (err) { - console.error("Script error:", err); - } - }) - .catch((err) => { - console.error("Start error:", err); - }); \ No newline at end of file diff --git a/things/calculator/coap/client-tests/simple-coap-client.js b/things/calculator/coap/client-tests/simple-coap-client.js deleted file mode 100644 index ea38e52..0000000 --- a/things/calculator/coap/client-tests/simple-coap-client.js +++ /dev/null @@ -1,266 +0,0 @@ -const coap = require('coap') -const hostname = 'localhost' -const portNumber = 5683 -const thingName = 'coap-calculator-simple' - -const fullTDEndpoint = `/${thingName}`, - resultEndPoint = `/${thingName}/properties/result`, - lastChangeEndPoint = `/${thingName}/properties/lastChange`, - additionEndPoint = `/${thingName}/actions/add`, - subtractionEndPoint = `/${thingName}/actions/subtract`, - updateEndPoint = `/${thingName}/events/update` - - -/****************************************/ -/****** Thing Description Endpoint ******/ -/****************************************/ - -function getThingDescription() { - // GET request to retrieve thing description - const getFullTD = coap.request({ - method: 'GET', - host: hostname, - port: portNumber, - pathname: fullTDEndpoint - }) - - getFullTD.on('response', (res) => { - if (res.code === '2.05') { - console.log('Thing Description: \n', JSON.parse(res.payload.toString())) - } else { - console.error(`Failed to get Thing Description: ${res.code} - ${res.payload.toString()}`) - } - }) - getFullTD.end() -} - - -// /****************************************/ -// /*********** Result Endpoint ************/ -// /****************************************/ - -function getResult() { - // GET request to retrieve the property result - const getPropertyResult = coap.request({ - method: 'GET', - host: hostname, - port: portNumber, - pathname: resultEndPoint - }); - - getPropertyResult.on('response', (res) => { - if (res.code === '2.05') { - console.log('Result:', JSON.parse(res.payload.toString())) - } else { - console.error(`Failed to get Property "result": ${res.code} - ${res.payload.toString()}`) - } - }) - - getPropertyResult.end() -} - - -/** - * GET request to observe the property result. - * Uncomment to test the observe functionality - */ - -function observeResultProperty() { - const observeResult = coap.request({ - method: 'GET', - observe: 0, // Enable observation - host: hostname, - port: portNumber, - pathname: resultEndPoint - }); - - observeResult.on('response', (res) => { - res.on('data', function () { - if (res.code === '2.05') { - console.log('Observe result property:', JSON.parse(res.payload.toString())) - } else { - console.error(`Failed to observe Event "update": ${res.code} - ${res.payload.toString()}`); - } - }) - - }); - - // Start observing - observeResult.end(); -} - - -/****************************************/ -/********** lastChange Endpoint *********/ -/****************************************/ - -function getLastChange() { - // GET request to retrieve the property lastChange - const getPropertyLastChange = coap.request({ - method: 'GET', - host: hostname, - port: portNumber, - pathname: lastChangeEndPoint - }) - getPropertyLastChange.on('response', (res) => { - if (res.code === '2.05') { - console.log('Last Change:', JSON.parse(res.payload.toString())) - } else { - console.error(`Failed to get Property "lastChange": ${res.code} - ${res.payload.toString()}`) - } - }) - - getPropertyLastChange.end() -} - - -/** - * GET request to observe the property lastChange. - * Uncomment to test the observe functionality - */ - -function observeLastChangeProperty() { - const observeLastChange = coap.request({ - method: 'GET', - observe: 0, // Enable observation - host: hostname, - port: portNumber, - pathname: lastChangeEndPoint - }); - - observeLastChange.on('response', (res) => { - res.on('data', function () { - if (res.code === '2.05') { - console.log('Observe lastChange property:', JSON.parse(res.payload.toString())) - } else { - console.error(`Failed to observe Event "update": ${res.code} - ${res.payload.toString()}`); - } - }) - - }); - - // Start observing - observeLastChange.end(); -} - - -/****************************************/ -/*********** Addition Endpoint **********/ -/****************************************/ - -function addNumber(numberToAdd) { - // POST request to perform an action (add) - const addNumberAction = coap.request({ - method: 'POST', - host: hostname, - port: portNumber, - pathname: additionEndPoint, - headers: { - "Content-Format": "application/json" - } - }); - - // Set the payload with the input value - addNumberAction.write(JSON.stringify(numberToAdd)) - - addNumberAction.on('response', (res) => { - if (res.code === '2.05') { - console.log('Addition result:', JSON.parse(res.payload.toString())) - } else { - console.error(`Failed to call the Action "add": ${res.code} - ${res.payload.toString()}`) - } - }); - addNumberAction.end(); -} - - - -/****************************************/ -/********** Subtraction Endpoint ********/ -/****************************************/ - -function subtractNumber(numberToSubtract) { - // POST request to perform an action (subtract) - const subtractNumberAction = coap.request({ - method: 'POST', - host: hostname, - port: portNumber, - pathname: subtractionEndPoint, - headers: { - "Content-Format": "application/json" - } - }); - - // Set the payload with the input value - subtractNumberAction.write(JSON.stringify(numberToSubtract)) - - subtractNumberAction.on('response', (res) => { - if (res.code === '2.05') { - console.log('Subtraction result:', JSON.parse(res.payload.toString())) - } else { - console.error(`Failed to call the Action "subtract": ${res.code} - ${res.payload.toString()}`) - } - }); - subtractNumberAction.end(); -} - - -/****************************************/ -/*********** Update Endpoint ************/ -/****************************************/ - -/** - * GET request to observe an event (update). - * Uncomment to test the update functionality - */ - -function observeUpdateEvent() { - const observeEventChange = coap.request({ - method: 'GET', - observe: 0, // Enable observation - host: hostname, - port: portNumber, - pathname: updateEndPoint - }); - - observeEventChange.on('response', (res) => { - res.on('data', function () { - if (res.code === '2.05') { - console.log('Observe update event:', JSON.parse(res.payload.toString())) - } else { - console.error(`Failed to observe Event "update": ${res.code} - ${res.payload.toString()}`); - } - }) - - }); - - // Start observing - observeEventChange.end(); -} - - -function runCalculatorInteractions() { - - getThingDescription() - getResult() - getLastChange() - addNumber(3) - subtractNumber(2) - - - //Start the observation of properties and events after 1 second - setTimeout(() => { - console.log("\n-------- Start observation --------\n"); - observeResultProperty() - observeLastChangeProperty() - observeUpdateEvent() - }, 1000) - - - //Update the property result after 2.5 seconds to test the observation - setTimeout(() => { - addNumber(1) - }, 2500) -} - -runCalculatorInteractions() \ No newline at end of file diff --git a/things/calculator/coap/js/.mocharc.json b/things/calculator/coap/js/.mocharc.json index a665287..00d83c4 100644 --- a/things/calculator/coap/js/.mocharc.json +++ b/things/calculator/coap/js/.mocharc.json @@ -2,4 +2,4 @@ "exit": true, "spec": "./test/**.test.js", "require": ["./test/fixtures.js"] -} \ No newline at end of file +} diff --git a/things/calculator/coap/client-tests/README.md b/things/calculator/coap/js/client-tests/README.md similarity index 100% rename from things/calculator/coap/client-tests/README.md rename to things/calculator/coap/js/client-tests/README.md diff --git a/things/calculator/coap/js/client-tests/content-negotiation-coap-client.js b/things/calculator/coap/js/client-tests/content-negotiation-coap-client.js new file mode 100644 index 0000000..9922afe --- /dev/null +++ b/things/calculator/coap/js/client-tests/content-negotiation-coap-client.js @@ -0,0 +1,380 @@ +/******************************************************************************** + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the W3C Software Notice and + * Document License (2015-05-13) which is available at + * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document. + * + * SPDX-License-Identifier: EPL-2.0 OR W3C-20150513 + ********************************************************************************/ + +const coap = require("coap"); +const cbor = require("cbor"); +const hostname = "localhost"; +const portNumber = 5684; +const thingName = "coap-calculator-content-negotiation"; + +const fullTDEndpoint = `/${thingName}`; +const resultEndPoint = `/${thingName}/properties/result`; +const lastChangeEndPoint = `/${thingName}/properties/lastChange`; +const additionEndPoint = `/${thingName}/actions/add`; +const subtractionEndPoint = `/${thingName}/actions/subtract`; +const updateEndPoint = `/${thingName}/events/update`; + +/****************************************/ +/** **** Thing Description Endpoint ******/ +/****************************************/ + +// GET request to retrieve thing description +function getFullTD(acceptType) { + const getThingDescription = coap.request({ + method: "GET", + host: hostname, + port: portNumber, + pathname: fullTDEndpoint, + headers: { + Accept: acceptType, + }, + }); + + getThingDescription.on("response", (res) => { + // TODO: Fix the problem with block wise transfer to be able to parse the response accordingly + if (res.code === "2.05") { + if ( + acceptType === "application/json" || + acceptType === "application/td+json" + ) { + console.log( + "Thing Description (json):\n", + JSON.parse(res.payload.toString()) + ); + } else { + const decodedData = cbor.decode(res.payload); + console.log( + "Thing Description (cbor):\n", + JSON.parse(decodedData) + ); + } + } else { + console.error(`Failed to get Thing Description: ${res.code}`); + } + }); + getThingDescription.end(); +} + +/****************************************/ +/** ********* Result Endpoint ************/ +/****************************************/ + +// GET request to retrieve a property (result) +function getResult(acceptType) { + const getPropertyResult = coap.request({ + method: "GET", + host: hostname, + port: portNumber, + pathname: resultEndPoint, + headers: { + Accept: acceptType, + }, + }); + + getPropertyResult.on("response", (res) => { + const contentType = res.headers["Content-Type"]; + + if (res.code === "2.05") { + if (contentType.includes("application/json")) { + console.log( + "Result (json): ", + JSON.parse(res.payload.toString()) + ); + } else { + const decodedData = cbor.decode(res.payload); + console.log("Result (cbor): ", decodedData); + } + } else { + console.error(`Failed to get Property "result": ${res.code}`); + } + }); + getPropertyResult.end(); +} + +/** + * GET request to observe the property result. + * Uncomment to test the update functionality. + */ +function observeResultProperty(acceptType) { + const observeResult = coap.request({ + method: "GET", + observe: true, + host: hostname, + port: portNumber, + pathname: resultEndPoint, + headers: { + Accept: acceptType, + }, + }); + + observeResult.on("response", (res) => { + res.on("data", function () { + const contentType = res.headers["Content-Type"]; + + if (res.code === "2.05") { + if (contentType.includes("application/json")) { + console.log( + "Observe result property (json): ", + JSON.parse(res.payload.toString()) + ); + } else { + const decodedData = cbor.decode(res.payload); + console.log( + "Observe result property (cbor): ", + decodedData + ); + } + } else { + console.error(`Failed to observe Event "update": ${res.code}`); + } + }); + }); + + observeResult.end(); +} + +/****************************************/ +/** ******** lastChange Endpoint *********/ +/****************************************/ + +// GET request to retrieve a property (lastChange) +function getLastChange(acceptType) { + const getPropertyLastChange = coap.request({ + method: "GET", + host: hostname, + port: portNumber, + pathname: lastChangeEndPoint, + headers: { + Accept: acceptType, + }, + }); + getPropertyLastChange.on("response", (res) => { + const contentType = res.headers["Content-Type"]; + + if (res.code === "2.05") { + if (contentType.includes("application/json")) { + console.log( + "Last Change (json): ", + JSON.parse(res.payload.toString()) + ); + } else { + const decodedData = cbor.decode(res.payload); + console.log("Last Change (cbor): ", decodedData); + } + } else { + console.error(`Failed to get Property "lastChange": ${res.code}`); + } + }); + getPropertyLastChange.end(); +} + +/** + * GET request to observe the property result. + * Uncomment to test the update functionality. + */ +function observeLastChangeProperty(acceptType) { + const observeLastChange = coap.request({ + method: "GET", + observe: true, + host: hostname, + port: portNumber, + pathname: lastChangeEndPoint, + headers: { + Accept: acceptType, + }, + }); + + observeLastChange.on("response", (res) => { + res.on("data", function () { + const contentType = res.headers["Content-Type"]; + + if (res.code === "2.05") { + if (contentType.includes("application/json")) { + console.log( + "Observe lastChange property (json): ", + JSON.parse(res.payload.toString()) + ); + } else { + const decodedData = cbor.decode(res.payload); + console.log( + "Observe lastChange property (cbor): ", + decodedData + ); + } + } else { + console.error(`Failed to observe Event "update": ${res.code}`); + } + }); + }); + + observeLastChange.end(); +} + +/****************************************/ +/** ********* Addition Endpoint **********/ +/****************************************/ + +// POST request to perform the addition action +function addNumber(acceptType, contentType, numberToAdd) { + const addNumberReq = coap.request({ + method: "POST", + host: hostname, + port: portNumber, + pathname: additionEndPoint, + headers: { + Accept: acceptType, + "Content-Format": contentType, + }, + }); + + // Set the payload with the input value + addNumberReq.write( + contentType === "application/json" + ? JSON.stringify(numberToAdd) + : cbor.encode(numberToAdd) + ); + + addNumberReq.on("response", (res) => { + const contentType = res.headers["Content-Type"]; + + if (res.code === "2.05") { + if (contentType.includes("application/json")) { + console.log( + "Addition result (json): ", + JSON.parse(res.payload.toString()) + ); + } else { + const decodedData = cbor.decode(res.payload); + console.log("Addition result (cbor): ", decodedData); + } + } else { + console.error(`Failed to call the Action "add": ${res.code}`); + } + }); + addNumberReq.end(); +} + +/****************************************/ +/** ******** Subtraction Endpoint ********/ +/****************************************/ + +// POST request to perform the subtract action +function subtractNumber(acceptType, contentType, numberToSubtract) { + const subtractNumberReq = coap.request({ + method: "POST", + host: hostname, + port: portNumber, + pathname: subtractionEndPoint, + headers: { + Accept: acceptType, + "Content-Format": contentType, + }, + }); + + // Set the payload with the input value + subtractNumberReq.write( + contentType === "application/json" + ? JSON.stringify(numberToSubtract) + : cbor.encode(numberToSubtract) + ); + + subtractNumberReq.on("response", (res) => { + const contentType = res.headers["Content-Type"]; + + if (res.code === "2.05") { + if (contentType.includes("application/json")) { + console.log( + "Subtraction result (json): ", + JSON.parse(res.payload.toString()) + ); + } else { + const decodedData = cbor.decode(res.payload); + console.log("Subtraction result (cbor): ", decodedData); + } + } else { + console.error(`Failed to call the Action "subtract": ${res.code}`); + } + }); + subtractNumberReq.end(); +} + +/****************************************/ +/** ********* Update Endpoint ************/ +/****************************************/ + +/** + * GET request to observe an event (update). + * Uncomment to test the update functionality. + */ +function observeUpdateEvent(acceptType) { + const observeUpdate = coap.request({ + method: "GET", + observe: true, // Enable observation + host: hostname, + port: portNumber, + pathname: updateEndPoint, + headers: { + Accept: acceptType, + }, + }); + + observeUpdate.on("response", (res) => { + res.on("data", function () { + const contentType = res.headers["Content-Type"]; + + if (res.code === "2.05") { + if (contentType.includes("application/json")) { + console.log( + "Observe update event (json): ", + JSON.parse(res.payload.toString()) + ); + } else { + const decodedData = cbor.decode(res.payload); + console.log("Observe update event (cbor): ", decodedData); + } + } else { + console.error(`Failed to observe Event "update": ${res.code}`); + } + }); + }); + + // Start observing + observeUpdate.end(); +} + +// Test the main functionality of the content-negotiation-calculator-thing +function runCalculatorInteractions() { + // Main GET and POST requests + getFullTD("application/json"); + getResult("application/cbor"); + getLastChange("application/json"); + addNumber("application/cbor", "application/cbor", 3); + subtractNumber("application/json", "application/json", 2); + + // Observation of properties and events after 1 second + setTimeout(() => { + console.log("\n-------- Start observation --------\n"); + observeResultProperty("application/json"); + observeLastChangeProperty("application/cbor"); + observeUpdateEvent("application/json"); + }, 1000); + + // Update the property result after 2.5 seconds to test the observation + setTimeout(() => { + addNumber("application/cbor", "application/json", 1); + }, 2500); +} + +runCalculatorInteractions(); diff --git a/things/calculator/coap/js/client-tests/node-wot-content-negotiation-coap-client.js b/things/calculator/coap/js/client-tests/node-wot-content-negotiation-coap-client.js new file mode 100644 index 0000000..544f70c --- /dev/null +++ b/things/calculator/coap/js/client-tests/node-wot-content-negotiation-coap-client.js @@ -0,0 +1,75 @@ +/******************************************************************************** + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the W3C Software Notice and + * Document License (2015-05-13) which is available at + * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document. + * + * SPDX-License-Identifier: EPL-2.0 OR W3C-20150513 + ********************************************************************************/ + +const { Servient } = require("@node-wot/core"); +const { CoapClientFactory } = require("@node-wot/binding-coap"); + +// create Servient and add CoAP binding +const servient = new Servient(); +servient.addClientFactory(new CoapClientFactory()); + +servient + .start() + .then(async (WoT) => { + try { + const td = await WoT.requestThingDescription( + "coap://localhost:5684/coap-calculator-content-negotiation" + ); + + const thing = await WoT.consume(td); + console.log(td); + + // read property result + const result = await thing.readProperty("result", { formIndex: 2 }); + console.log("result: ", await result.value()); + + // read property lastChange + const lastChange = await thing.readProperty("lastChange", { + formIndex: 2, + }); + console.log("lastChange: ", await lastChange.value()); + + console.log("\n ---------- \n"); + + // Observe properties + thing.observeProperty("result", async (data) => { + console.log("Result observe:", await data.value()); + }); + thing.observeProperty("lastChange", async (data) => { + console.log("lastChange observe:", await data.value()); + }); + + // Subscribe to event update + thing.subscribeEvent("update", async (data) => { + console.log("Update event:", await data.value()); + }); + + // Invoke addition action + const add = await thing.invokeAction("add", 3, { formIndex: 1 }); + console.log("Addition value:", await add.value()); + // Invoke subtraction action + const subtract = await thing.invokeAction("subtract", 1, { + formIndex: 3, + }); + console.log("Subtraction value:", await subtract.value()); + + console.log("\n ---------- \n"); + } catch (err) { + console.error("Script error:", err); + } + }) + .catch((err) => { + console.error("Start error:", err); + }); diff --git a/things/calculator/coap/js/client-tests/node-wot-simple-coap-client.js b/things/calculator/coap/js/client-tests/node-wot-simple-coap-client.js new file mode 100644 index 0000000..298033c --- /dev/null +++ b/things/calculator/coap/js/client-tests/node-wot-simple-coap-client.js @@ -0,0 +1,71 @@ +/******************************************************************************** + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the W3C Software Notice and + * Document License (2015-05-13) which is available at + * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document. + * + * SPDX-License-Identifier: EPL-2.0 OR W3C-20150513 + ********************************************************************************/ + +const { Servient } = require("@node-wot/core"); +const { CoapClientFactory } = require("@node-wot/binding-coap"); + +// create Servient and add CoAP binding +const servient = new Servient(); +servient.addClientFactory(new CoapClientFactory()); + +servient + .start() + .then(async (WoT) => { + try { + const td = await WoT.requestThingDescription( + "coap://localhost:5683/coap-calculator-simple" + ); + + const thing = await WoT.consume(td); + console.log(td); + + // read property result + const result = await thing.readProperty("result"); + console.log("result: ", await result.value()); + + // read property lastChange + const lastChange = await thing.readProperty("lastChange"); + console.log("lastChange: ", await lastChange.value()); + + console.log("\n------------\n"); + + // Observe properties + thing.observeProperty("result", async (data) => { + console.log("Result observe:", await data.value()); + }); + thing.observeProperty("lastChange", async (data) => { + console.log("lastChange observe:", await data.value()); + }); + + // Subscribe to event update + thing.subscribeEvent("update", async (data) => { + console.log("Update event:", await data.value()); + }); + + // Invoke addition action + const addition = await thing.invokeAction("add", 2); + console.log("Addition result: ", await addition.value()); + // Invoke addition subtraction + const subtraction = await thing.invokeAction("subtract", 3); + console.log("Subtraction result: ", await subtraction.value()); + + console.log("\n------------\n"); + } catch (err) { + console.error("Script error:", err); + } + }) + .catch((err) => { + console.error("Start error:", err); + }); diff --git a/things/calculator/coap/js/client-tests/simple-coap-client.js b/things/calculator/coap/js/client-tests/simple-coap-client.js new file mode 100644 index 0000000..9f44867 --- /dev/null +++ b/things/calculator/coap/js/client-tests/simple-coap-client.js @@ -0,0 +1,312 @@ +/******************************************************************************** + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the W3C Software Notice and + * Document License (2015-05-13) which is available at + * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document. + * + * SPDX-License-Identifier: EPL-2.0 OR W3C-20150513 + ********************************************************************************/ + +const coap = require("coap"); +const hostname = "localhost"; +const portNumber = 5683; +const thingName = "coap-calculator-simple"; + +const fullTDEndpoint = `/${thingName}`; +const resultEndPoint = `/${thingName}/properties/result`; +const lastChangeEndPoint = `/${thingName}/properties/lastChange`; +const additionEndPoint = `/${thingName}/actions/add`; +const subtractionEndPoint = `/${thingName}/actions/subtract`; +const updateEndPoint = `/${thingName}/events/update`; + +/****************************************/ +/** **** Thing Description Endpoint ******/ +/****************************************/ + +function getThingDescription() { + // GET request to retrieve thing description + const getFullTD = coap.request({ + method: "GET", + host: hostname, + port: portNumber, + pathname: fullTDEndpoint, + }); + + getFullTD.on("response", (res) => { + if (res.code === "2.05") { + console.log( + "Thing Description: \n", + JSON.parse(res.payload.toString()) + ); + } else { + console.error( + `Failed to get Thing Description: ${ + res.code + } - ${res.payload.toString()}` + ); + } + }); + getFullTD.end(); +} + +// /****************************************/ +// /*********** Result Endpoint ************/ +// /****************************************/ + +function getResult() { + // GET request to retrieve the property result + const getPropertyResult = coap.request({ + method: "GET", + host: hostname, + port: portNumber, + pathname: resultEndPoint, + }); + + getPropertyResult.on("response", (res) => { + if (res.code === "2.05") { + console.log("Result:", JSON.parse(res.payload.toString())); + } else { + console.error( + `Failed to get Property "result": ${ + res.code + } - ${res.payload.toString()}` + ); + } + }); + + getPropertyResult.end(); +} + +/** + * GET request to observe the property result. + * Uncomment to test the observe functionality + */ + +function observeResultProperty() { + const observeResult = coap.request({ + method: "GET", + observe: 0, // Enable observation + host: hostname, + port: portNumber, + pathname: resultEndPoint, + }); + + observeResult.on("response", (res) => { + res.on("data", function () { + if (res.code === "2.05") { + console.log( + "Observe result property:", + JSON.parse(res.payload.toString()) + ); + } else { + console.error( + `Failed to observe Event "update": ${ + res.code + } - ${res.payload.toString()}` + ); + } + }); + }); + + // Start observing + observeResult.end(); +} + +/****************************************/ +/** ******** lastChange Endpoint *********/ +/****************************************/ + +function getLastChange() { + // GET request to retrieve the property lastChange + const getPropertyLastChange = coap.request({ + method: "GET", + host: hostname, + port: portNumber, + pathname: lastChangeEndPoint, + }); + getPropertyLastChange.on("response", (res) => { + if (res.code === "2.05") { + console.log("Last Change:", JSON.parse(res.payload.toString())); + } else { + console.error( + `Failed to get Property "lastChange": ${ + res.code + } - ${res.payload.toString()}` + ); + } + }); + + getPropertyLastChange.end(); +} + +/** + * GET request to observe the property lastChange. + * Uncomment to test the observe functionality + */ + +function observeLastChangeProperty() { + const observeLastChange = coap.request({ + method: "GET", + observe: 0, // Enable observation + host: hostname, + port: portNumber, + pathname: lastChangeEndPoint, + }); + + observeLastChange.on("response", (res) => { + res.on("data", function () { + if (res.code === "2.05") { + console.log( + "Observe lastChange property:", + JSON.parse(res.payload.toString()) + ); + } else { + console.error( + `Failed to observe Event "update": ${ + res.code + } - ${res.payload.toString()}` + ); + } + }); + }); + + // Start observing + observeLastChange.end(); +} + +/****************************************/ +/** ********* Addition Endpoint **********/ +/****************************************/ + +function addNumber(numberToAdd) { + // POST request to perform an action (add) + const addNumberAction = coap.request({ + method: "POST", + host: hostname, + port: portNumber, + pathname: additionEndPoint, + headers: { + "Content-Format": "application/json", + }, + }); + + // Set the payload with the input value + addNumberAction.write(JSON.stringify(numberToAdd)); + + addNumberAction.on("response", (res) => { + if (res.code === "2.05") { + console.log("Addition result:", JSON.parse(res.payload.toString())); + } else { + console.error( + `Failed to call the Action "add": ${ + res.code + } - ${res.payload.toString()}` + ); + } + }); + addNumberAction.end(); +} + +/****************************************/ +/** ******** Subtraction Endpoint ********/ +/****************************************/ + +function subtractNumber(numberToSubtract) { + // POST request to perform an action (subtract) + const subtractNumberAction = coap.request({ + method: "POST", + host: hostname, + port: portNumber, + pathname: subtractionEndPoint, + headers: { + "Content-Format": "application/json", + }, + }); + + // Set the payload with the input value + subtractNumberAction.write(JSON.stringify(numberToSubtract)); + + subtractNumberAction.on("response", (res) => { + if (res.code === "2.05") { + console.log( + "Subtraction result:", + JSON.parse(res.payload.toString()) + ); + } else { + console.error( + `Failed to call the Action "subtract": ${ + res.code + } - ${res.payload.toString()}` + ); + } + }); + subtractNumberAction.end(); +} + +/****************************************/ +/** ********* Update Endpoint ************/ +/****************************************/ + +/** + * GET request to observe an event (update). + * Uncomment to test the update functionality + */ + +function observeUpdateEvent() { + const observeEventChange = coap.request({ + method: "GET", + observe: 0, // Enable observation + host: hostname, + port: portNumber, + pathname: updateEndPoint, + }); + + observeEventChange.on("response", (res) => { + res.on("data", function () { + if (res.code === "2.05") { + console.log( + "Observe update event:", + JSON.parse(res.payload.toString()) + ); + } else { + console.error( + `Failed to observe Event "update": ${ + res.code + } - ${res.payload.toString()}` + ); + } + }); + }); + + // Start observing + observeEventChange.end(); +} + +function runCalculatorInteractions() { + getThingDescription(); + getResult(); + getLastChange(); + addNumber(3); + subtractNumber(2); + + // Start the observation of properties and events after 1 second + setTimeout(() => { + console.log("\n-------- Start observation --------\n"); + observeResultProperty(); + observeLastChangeProperty(); + observeUpdateEvent(); + }, 1000); + + // Update the property result after 2.5 seconds to test the observation + setTimeout(() => { + addNumber(1); + }, 2500); +} + +runCalculatorInteractions(); diff --git a/things/calculator/coap/js/coap-content-negotiation-calculator.js b/things/calculator/coap/js/coap-content-negotiation-calculator.js index 51431a0..7e34847 100644 --- a/things/calculator/coap/js/coap-content-negotiation-calculator.js +++ b/things/calculator/coap/js/coap-content-negotiation-calculator.js @@ -1,488 +1,533 @@ -const { parseArgs } = require('node:util') -const coap = require('coap') -const fs = require('fs') -const path = require('path') -const { JsonPlaceholderReplacer } = require('json-placeholder-replacer') -const cbor = require('cbor') -require('dotenv').config() - -const server = coap.createServer() -const hostname = process.env.HOSTNAME ?? 'localhost' -let portNumber = Number(process.env.PORT ?? 5684) -const thingName = 'coap-calculator-content-negotiation' - -const { values: { port } } = parseArgs({ - options: { - port: { - type: 'string', - short: 'p' - } - } -}) +/******************************************************************************** + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the W3C Software Notice and + * Document License (2015-05-13) which is available at + * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document. + * + * SPDX-License-Identifier: EPL-2.0 OR W3C-20150513 + ********************************************************************************/ +const { parseArgs } = require("node:util"); +const coap = require("coap"); +const fs = require("fs"); +const path = require("path"); +const { JsonPlaceholderReplacer } = require("json-placeholder-replacer"); +const cbor = require("cbor"); +require("dotenv").config(); + +const server = coap.createServer(); +const hostname = process.env.HOSTNAME ?? "localhost"; +let portNumber = Number(process.env.PORT ?? 5684); +const thingName = "coap-calculator-content-negotiation"; + +const { + values: { port }, +} = parseArgs({ + options: { + port: { + type: "string", + short: "p", + }, + }, +}); if (port && !isNaN(parseInt(port))) { - portNumber = parseInt(port) + portNumber = parseInt(port); } -const tmPath = process.env.TM_PATH +const tmPath = process.env.TM_PATH; -if (process.platform === 'win32') { - tmPath.split(path.sep).join(path.win32.sep) +if (process.platform === "win32") { + tmPath.split(path.sep).join(path.win32.sep); } -const thingModel = JSON.parse(fs.readFileSync(path.join(__dirname, tmPath))) +const thingModel = JSON.parse(fs.readFileSync(path.join(__dirname, tmPath))); -const placeholderReplacer = new JsonPlaceholderReplacer() +const placeholderReplacer = new JsonPlaceholderReplacer(); placeholderReplacer.addVariableMap({ - PROTOCOL: 'coap', - THING_NAME: thingName, - HOSTNAME: hostname, - PORT_NUMBER: portNumber, - RESULT_OBSERVABLE: true, - LAST_CHANGE_OBSERVABLE: true -}) + PROTOCOL: "coap", + THING_NAME: thingName, + HOSTNAME: hostname, + PORT_NUMBER: portNumber, + RESULT_OBSERVABLE: true, + LAST_CHANGE_OBSERVABLE: true, +}); /*****************************************/ -/************ Creating the TD ************/ +/** ********** Creating the TD ************/ /*****************************************/ -const thingDescription = placeholderReplacer.replace(thingModel) -thingDescription['@type'] = 'Thing' +const thingDescription = placeholderReplacer.replace(thingModel); +thingDescription["@type"] = "Thing"; -const supportedContentTypes = ['application/json', 'application/cbor']; +const supportedContentTypes = ["application/json", "application/cbor"]; const formatIdentifiers = { - 'application/json': 50, - 'application/cbor': 60 -} + "application/json": 50, + "application/cbor": 60, +}; const defaultForm = { - 'href': '', - 'contentType': 'application/json', - 'cov:contentFormat': 50, - 'op': '', - 'cov:method': '', - 'cov:accept': 50, - 'response': { - 'contentType': 'application/json', - 'cov:contentFormat': 50 - } -} - -//Adding headers to the Properties -for (const key in thingDescription['properties']) { - - thingDescription['properties'][key]['forms'] = [] - - const newFormRead = JSON.parse(JSON.stringify(defaultForm)) - newFormRead['href'] = `properties/${key}` - newFormRead['cov:method'] = 'GET' - newFormRead['op'] = 'readproperty' - - const newFormObs = JSON.parse(JSON.stringify(newFormRead)) - newFormObs['op'] = ['observeproperty', 'unobserveproperty'] - newFormObs['subprotocol'] = 'cov:observe' - - thingDescription['properties'][key]['forms'].push(newFormRead) - thingDescription['properties'][key]['forms'].push(newFormObs) - - const originalForm = thingDescription['properties'][key]['forms'][0] - - for (const identifier in formatIdentifiers) { - if (originalForm['contentType'] !== identifier) { - const newFormRead = JSON.parse(JSON.stringify(originalForm)) - newFormRead['contentType'] = identifier - newFormRead['cov:contentFormat'] = formatIdentifiers[identifier] - newFormRead['cov:accept'] = formatIdentifiers[identifier] - newFormRead['response']['contentType'] = identifier - newFormRead['response']['cov:contentFormat'] = formatIdentifiers[identifier] - thingDescription['properties'][key]['forms'].push(newFormRead) + href: "", + contentType: "application/json", + "cov:contentFormat": 50, + op: "", + "cov:method": "", + "cov:accept": 50, + response: { + contentType: "application/json", + "cov:contentFormat": 50, + }, +}; + +// Adding headers to the Properties +for (const key in thingDescription.properties) { + if (!Object.hasOwn(thingDescription.properties, key)) { + continue; + } - const newFormObs = JSON.parse(JSON.stringify(newFormRead)) - newFormObs['op'] = ['observeproperty', 'unobserveproperty'] - newFormObs['subprotocol'] = 'cov:observe' - thingDescription['properties'][key]['forms'].push(newFormObs) + thingDescription.properties[key].forms = []; + + const newFormRead = JSON.parse(JSON.stringify(defaultForm)); + newFormRead.href = `properties/${key}`; + newFormRead["cov:method"] = "GET"; + newFormRead.op = "readproperty"; + + const newFormObs = JSON.parse(JSON.stringify(newFormRead)); + newFormObs.op = ["observeproperty", "unobserveproperty"]; + newFormObs.subprotocol = "cov:observe"; + + thingDescription.properties[key].forms.push(newFormRead); + thingDescription.properties[key].forms.push(newFormObs); + + const originalForm = thingDescription.properties[key].forms[0]; + + for (const identifier in formatIdentifiers) { + if (originalForm.contentType !== identifier) { + const newFormRead = JSON.parse(JSON.stringify(originalForm)); + newFormRead.contentType = identifier; + newFormRead["cov:contentFormat"] = formatIdentifiers[identifier]; + newFormRead["cov:accept"] = formatIdentifiers[identifier]; + newFormRead.response.contentType = identifier; + newFormRead.response["cov:contentFormat"] = + formatIdentifiers[identifier]; + thingDescription.properties[key].forms.push(newFormRead); + + const newFormObs = JSON.parse(JSON.stringify(newFormRead)); + newFormObs.op = ["observeproperty", "unobserveproperty"]; + newFormObs.subprotocol = "cov:observe"; + thingDescription.properties[key].forms.push(newFormObs); + } } - } } -//Adding headers to the Actions -for (const key in thingDescription['actions']) { - - thingDescription['actions'][key]['forms'] = [] - - const newForm = JSON.parse(JSON.stringify(defaultForm)) - newForm['href'] = `actions/${key}` - newForm['cov:method'] = 'POST' - newForm['op'] = 'invokeaction' - - thingDescription['actions'][key]['forms'].push(newForm) - - const originalForm = thingDescription['actions'][key]['forms'][0] - - for (const identifier in formatIdentifiers) { - /** - * Checking if the original form does not have the formats from the 'formatIdentifiers' object and - * duplicating the original form with the new formats. - * If it does have it, duplicate the original one, but modify the response and accept header to include - * the other headers. - */ - if (originalForm['contentType'] !== identifier) { - const newForm = JSON.parse(JSON.stringify(originalForm)) - newForm['contentType'] = identifier - newForm['cov:contentFormat'] = formatIdentifiers[identifier] - newForm['cov:accept'] = formatIdentifiers[identifier] - newForm['response']['contentType'] = identifier - newForm['response']['cov:contentFormat'] = formatIdentifiers[identifier] - thingDescription['actions'][key]['forms'].push(newForm) - - /** - * Cloning the forms with the new format, but modifying the accept and response headers - * to include the different formats - */ - for (const identifier in formatIdentifiers) { - if (newForm['cov:accept'] !== formatIdentifiers[identifier]) { - const newFormAccept = JSON.parse(JSON.stringify(newForm)) - newFormAccept['cov:accept'] = formatIdentifiers[identifier] - newFormAccept['response']['contentType'] = identifier - newFormAccept['response']['cov:contentFormat'] = formatIdentifiers[identifier] - thingDescription['actions'][key]['forms'].push(newFormAccept) - } - } - } else { - for (const identifier in formatIdentifiers) { - if (originalForm['cov:accept'] !== formatIdentifiers[identifier]) { - const newForm = JSON.parse(JSON.stringify(originalForm)) - newForm['cov:accept'] = formatIdentifiers[identifier] - newForm['response']['contentType'] = identifier - newForm['response']['cov:contentFormat'] = formatIdentifiers[identifier] - thingDescription['actions'][key]['forms'].push(newForm) +// Adding headers to the Actions +for (const key in thingDescription.actions) { + if (!Object.hasOwn(thingDescription.actions, key)) { + continue; + } + + thingDescription.actions[key].forms = []; + + const newForm = JSON.parse(JSON.stringify(defaultForm)); + newForm.href = `actions/${key}`; + newForm["cov:method"] = "POST"; + newForm.op = "invokeaction"; + + thingDescription.actions[key].forms.push(newForm); + + const originalForm = thingDescription.actions[key].forms[0]; + + for (const identifier in formatIdentifiers) { + /** + * Checking if the original form does not have the formats from the 'formatIdentifiers' object and + * duplicating the original form with the new formats. + * If it does have it, duplicate the original one, but modify the response and accept header to include + * the other headers. + */ + if (originalForm.contentType !== identifier) { + const newForm = JSON.parse(JSON.stringify(originalForm)); + newForm.contentType = identifier; + newForm["cov:contentFormat"] = formatIdentifiers[identifier]; + newForm["cov:accept"] = formatIdentifiers[identifier]; + newForm.response.contentType = identifier; + newForm.response["cov:contentFormat"] = + formatIdentifiers[identifier]; + thingDescription.actions[key].forms.push(newForm); + + /** + * Cloning the forms with the new format, but modifying the accept and response headers + * to include the different formats + */ + for (const identifier in formatIdentifiers) { + if (newForm["cov:accept"] !== formatIdentifiers[identifier]) { + const newFormAccept = JSON.parse(JSON.stringify(newForm)); + newFormAccept["cov:accept"] = formatIdentifiers[identifier]; + newFormAccept.response.contentType = identifier; + newFormAccept.response["cov:contentFormat"] = + formatIdentifiers[identifier]; + thingDescription.actions[key].forms.push(newFormAccept); + } + } + } else { + for (const identifier in formatIdentifiers) { + if ( + originalForm["cov:accept"] !== formatIdentifiers[identifier] + ) { + const newForm = JSON.parse(JSON.stringify(originalForm)); + newForm["cov:accept"] = formatIdentifiers[identifier]; + newForm.response.contentType = identifier; + newForm.response["cov:contentFormat"] = + formatIdentifiers[identifier]; + thingDescription.actions[key].forms.push(newForm); + } + } } - } } - } } +// Adding headers to the Events +for (const key in thingDescription.events) { + if (!Object.hasOwn(thingDescription.events, key)) { + continue; + } -//Adding headers to the Events -for (const key in thingDescription['events']) { - - thingDescription['events'][key]['data']['type'] = "number" + thingDescription.events[key].data.type = "number"; - thingDescription['events'][key]['forms'] = [] + thingDescription.events[key].forms = []; - const newForm = JSON.parse(JSON.stringify(defaultForm)) - newForm['href'] = `events/${key}` - newForm['cov:method'] = 'GET' - newForm['op'] = ["subscribeevent", "unsubscribeevent"] - newForm['subprotocol'] = 'cov:observe' + const newForm = JSON.parse(JSON.stringify(defaultForm)); + newForm.href = `events/${key}`; + newForm["cov:method"] = "GET"; + newForm.op = ["subscribeevent", "unsubscribeevent"]; + newForm.subprotocol = "cov:observe"; - thingDescription['events'][key]['forms'].push(newForm) + thingDescription.events[key].forms.push(newForm); - const originalForm = thingDescription['events'][key]['forms'][0] + const originalForm = thingDescription.events[key].forms[0]; - for (const identifier in formatIdentifiers) { - if (originalForm['contentType'] !== identifier) { - const newForm = JSON.parse(JSON.stringify(originalForm)) - newForm['contentType'] = identifier - newForm['cov:contentFormat'] = formatIdentifiers[identifier] - newForm['cov:accept'] = formatIdentifiers[identifier] - newForm['response']['contentType'] = identifier - newForm['response']['cov:contentFormat'] = formatIdentifiers[identifier] - thingDescription['events'][key]['forms'].push(newForm) + for (const identifier in formatIdentifiers) { + if (originalForm.contentType !== identifier) { + const newForm = JSON.parse(JSON.stringify(originalForm)); + newForm.contentType = identifier; + newForm["cov:contentFormat"] = formatIdentifiers[identifier]; + newForm["cov:accept"] = formatIdentifiers[identifier]; + newForm.response.contentType = identifier; + newForm.response["cov:contentFormat"] = + formatIdentifiers[identifier]; + thingDescription.events[key].forms.push(newForm); + } } - } } -//Creating the TD for testing purposes +// Creating the TD for testing purposes try { - fs.writeFileSync('coap-content-negotiation-calculator-thing.td.jsonld', JSON.stringify(thingDescription, null, 2)) + fs.writeFileSync( + "coap-content-negotiation-calculator-thing.td.jsonld", + JSON.stringify(thingDescription, null, 2) + ); } catch (err) { - console.log(err); + console.log(err); } /*********************************************************/ -/************** Main server functionality ****************/ +/** ************ Main server functionality ****************/ /*********************************************************/ -let result = 0 -let lastChange = new Date().toISOString() - -server.on('request', (req, res) => { - const segments = req.url.split('/') - const acceptHeaders = req.headers['Accept'] || [] - const reqContentType = req.headers['Content-Type'] || req.headers['Content-Format'] || [] - - if (segments[1] !== thingName) { - res.code = 404 - res.end() - } else { - if (!segments[2]) { - if (req.method === 'GET') { - if (acceptHeaders.includes('application/json') || acceptHeaders.includes('application/td+json') || acceptHeaders.includes('application/*') || acceptHeaders === '*/*') { - res.setOption('Content-Format', 'application/json') - res.end(JSON.stringify(thingDescription)) - } - else if (acceptHeaders.includes('application/cbor')) { - const cborData = cbor.encode(JSON.stringify(thingDescription)) - res.setOption('Content-Format', 'application/cbor') - res.end(cborData) - } - else { - res.code = 406 - res.end() - } - } - else { - res.code = 405 - res.end() - } - } - } - - if (segments[2] === 'properties') { - if (req.method === 'GET') { - if (supportedContentTypes.includes(acceptHeaders)) { - - //Set the content-format header to the accepted header - res.setOption('Content-Format', acceptHeaders) - - //Result Endpoint - if (segments[3] === 'result') { - //Start the observation of the property if observe attribute is set to true - if (req.headers.Observe === 0) { - console.log('Observing result property...') - - let oldResult = result - - //Todo: observation functionality should not happen inside a loop - const changeInterval = setInterval(() => { - - if (oldResult !== result) { - res.statusCode = 205 - if (acceptHeaders.includes('application/json') || acceptHeaders.includes('application/*') || acceptHeaders === '*/*') { - res.write(JSON.stringify(result)) - oldResult = result - } - else { - const cborData = cbor.encode(result) - res.write(cborData) - oldResult = result +let result = 0; +let lastChange = new Date().toISOString(); + +server.on("request", (req, res) => { + const segments = req.url.split("/"); + const acceptHeaders = req.headers.Accept || []; + const reqContentType = + req.headers["Content-Type"] || req.headers["Content-Format"] || []; + + if (segments[1] !== thingName) { + res.code = 404; + res.end(); + } else { + if (!segments[2]) { + if (req.method === "GET") { + if ( + acceptHeaders.includes("application/json") || + acceptHeaders.includes("application/td+json") || + acceptHeaders.includes("application/*") || + acceptHeaders === "*/*" + ) { + res.setOption("Content-Format", "application/json"); + res.end(JSON.stringify(thingDescription)); + } else if (acceptHeaders.includes("application/cbor")) { + const cborData = cbor.encode( + JSON.stringify(thingDescription) + ); + res.setOption("Content-Format", "application/cbor"); + res.end(cborData); + } else { + res.code = 406; + res.end(); } - } - }, 1000) - - res.on('finish', () => { - console.log("Result property observation has been closed"); - clearInterval(changeInterval) - }) - - } - else { - //If no observation is required, send only the result and close connection - if (acceptHeaders.includes('application/json') || acceptHeaders.includes('application/*') || acceptHeaders === '*/*') { - res.end(JSON.stringify(result)) - } - else { - const cborData = cbor.encode(result) - res.end(cborData) + } else { + res.code = 405; + res.end(); } - - } } - //Last Change Endpoint - else if (segments[3] === 'lastChange') { - - //Start the observation of the property if observe attribute is set to true - if (req.headers.Observe === 0) { - console.log('Observing lastChange property...') - - let oldDate = lastChange - - const changeInterval = setInterval(() => { + } - if (oldDate !== lastChange) { - res.statusCode = 205 - if (acceptHeaders.includes('application/json') || acceptHeaders.includes('application/*') || acceptHeaders === '*/*') { - res.write(JSON.stringify(lastChange)) - oldDate = lastChange + if (segments[2] === "properties") { + if (req.method === "GET") { + if (supportedContentTypes.includes(acceptHeaders)) { + // Set the content-format header to the accepted header + res.setOption("Content-Format", acceptHeaders); + + // Result Endpoint + if (segments[3] === "result") { + // Start the observation of the property if observe attribute is set to true + if (req.headers.Observe === 0) { + console.log("Observing result property..."); + + let oldResult = result; + + // Todo: observation functionality should not happen inside a loop + const changeInterval = setInterval(() => { + if (oldResult !== result) { + res.statusCode = 205; + if ( + acceptHeaders.includes( + "application/json" + ) || + acceptHeaders.includes("application/*") || + acceptHeaders === "*/*" + ) { + res.write(JSON.stringify(result)); + oldResult = result; + } else { + const cborData = cbor.encode(result); + res.write(cborData); + oldResult = result; + } + } + }, 1000); + + res.on("finish", () => { + console.log( + "Result property observation has been closed" + ); + clearInterval(changeInterval); + }); + } else { + // If no observation is required, send only the result and close connection + if ( + acceptHeaders.includes("application/json") || + acceptHeaders.includes("application/*") || + acceptHeaders === "*/*" + ) { + res.end(JSON.stringify(result)); + } else { + const cborData = cbor.encode(result); + res.end(cborData); + } + } } - else { - const cborData = cbor.encode(lastChange) - res.write(cborData) - oldDate = lastChange + // Last Change Endpoint + else if (segments[3] === "lastChange") { + // Start the observation of the property if observe attribute is set to true + if (req.headers.Observe === 0) { + console.log("Observing lastChange property..."); + + let oldDate = lastChange; + + const changeInterval = setInterval(() => { + if (oldDate !== lastChange) { + res.statusCode = 205; + if ( + acceptHeaders.includes( + "application/json" + ) || + acceptHeaders.includes("application/*") || + acceptHeaders === "*/*" + ) { + res.write(JSON.stringify(lastChange)); + oldDate = lastChange; + } else { + const cborData = cbor.encode(lastChange); + res.write(cborData); + oldDate = lastChange; + } + } + }, 1000); + + res.on("finish", () => { + console.log( + "lastChange property observation has been closed" + ); + clearInterval(changeInterval); + }); + } else { + // If no observation is required, send only the result and close connection + if ( + acceptHeaders.includes("application/json") || + acceptHeaders.includes("application/*") || + acceptHeaders === "*/*" + ) { + res.end(JSON.stringify(lastChange)); + } else { + const cborData = cbor.encode(lastChange); + res.end(cborData); + } + } + } else { + res.code = 404; + res.end(); } - } - }, 1000) - - res.on('finish', () => { - console.log("lastChange property observation has been closed"); - clearInterval(changeInterval) - }) - - } - else { - - //If no observation is required, send only the result and close connection - if (acceptHeaders.includes('application/json') || acceptHeaders.includes('application/*') || acceptHeaders === '*/*') { - res.end(JSON.stringify(lastChange)) - } - else { - const cborData = cbor.encode(lastChange) - res.end(cborData) + } else { + res.statusCode = 406; + res.end(); } - - } - } - else { - res.code = 404 - res.end() + } else { + res.code = 405; + res.end(); } - - } - else { - res.statusCode = 406 - res.end() - } } - else { - res.code = 405 - res.end() - } - } - - if (segments[2] === 'actions') { - if (req.method === 'POST') { - if (supportedContentTypes.includes(reqContentType)) { - if (supportedContentTypes.includes(acceptHeaders)) { - //Set the content-format header to the accepted header - res.setOption('Content-Format', acceptHeaders) - - //Addition endpoint - if (segments[3] === 'add') { - let numberToAdd - - if (reqContentType.includes('application/json')) { - numberToAdd = JSON.parse(req.payload.toString()) - } - else { - numberToAdd = cbor.decode(req.payload); - } - - if (typeof numberToAdd !== "number" || !numberToAdd) { - res.code = 400 - res.end() - } - else { - result += numberToAdd - lastChange = new Date() - - if (acceptHeaders.includes('application/json') || acceptHeaders.includes('application/*') || acceptHeaders === '*/*') { - res.end(JSON.stringify(result)) - } - else { - const cborData = cbor.encode(result) - res.end(cborData) - } - } - } - //Subtraction endpoint - else if (segments[3] === 'subtract') { - - let numberToSubtract - - if (reqContentType.includes('application/json')) { - numberToSubtract = JSON.parse(req.payload.toString()) - } - else { - numberToSubtract = cbor.decode(req.payload); - } - - if (typeof numberToSubtract !== "number" || !numberToSubtract) { - res.code = 400 - res.end() - } - else { - result -= numberToSubtract - lastChange = new Date() - - if (acceptHeaders.includes('application/json') || acceptHeaders.includes('application/*') || acceptHeaders === '*/*') { - res.end(JSON.stringify(result)) - } - else { - const cborData = cbor.encode(result) - res.end(cborData) - } + if (segments[2] === "actions") { + if (req.method === "POST") { + if (supportedContentTypes.includes(reqContentType)) { + if (supportedContentTypes.includes(acceptHeaders)) { + // Set the content-format header to the accepted header + res.setOption("Content-Format", acceptHeaders); + + // Addition endpoint + if (segments[3] === "add") { + let numberToAdd; + + if (reqContentType.includes("application/json")) { + numberToAdd = JSON.parse(req.payload.toString()); + } else { + numberToAdd = cbor.decode(req.payload); + } + + if (typeof numberToAdd !== "number" || !numberToAdd) { + res.code = 400; + res.end(); + } else { + result += numberToAdd; + lastChange = new Date(); + + if ( + acceptHeaders.includes("application/json") || + acceptHeaders.includes("application/*") || + acceptHeaders === "*/*" + ) { + res.end(JSON.stringify(result)); + } else { + const cborData = cbor.encode(result); + res.end(cborData); + } + } + } + // Subtraction endpoint + else if (segments[3] === "subtract") { + let numberToSubtract; + + if (reqContentType.includes("application/json")) { + numberToSubtract = JSON.parse( + req.payload.toString() + ); + } else { + numberToSubtract = cbor.decode(req.payload); + } + + if ( + typeof numberToSubtract !== "number" || + !numberToSubtract + ) { + res.code = 400; + res.end(); + } else { + result -= numberToSubtract; + lastChange = new Date(); + + if ( + acceptHeaders.includes("application/json") || + acceptHeaders.includes("application/*") || + acceptHeaders === "*/*" + ) { + res.end(JSON.stringify(result)); + } else { + const cborData = cbor.encode(result); + res.end(cborData); + } + } + } else { + res.code = 404; + res.end(); + } + } else { + res.code = 406; + res.end(); + } + } else { + res.code = 415; + res.end(); } - } - else { - res.code = 404 - res.end() - } - } - else { - res.code = 406 - res.end() + } else { + res.code = 405; + res.end(); } - } - else { - res.code = 415 - res.end() - } } - else { - res.code = 405 - res.end() - } - } - - if (segments[2] === 'events' && req.method === 'GET') { - if (segments[3] === 'update') { - if (req.headers.Observe === 0) { - if (supportedContentTypes.includes(acceptHeaders)) { - console.log('Observing update event...') - - let oldResult = result - - const changeInterval = setInterval(() => { - //Set the content-format header to the accepted header - res.setOption('Content-Format', acceptHeaders) - - if (oldResult !== result) { - res.statusCode = 205 - if (acceptHeaders.includes('application/json') || acceptHeaders.includes('application/*') || acceptHeaders === '*/*') { - res.write(JSON.stringify(result)) - oldResult = result - } - else { - const cborData = cbor.encode(result) - res.write(cborData) - oldResult = result - } - } - }, 1000) - res.on('finish', () => { - clearInterval(changeInterval) - }) - } - else { - res.statusCode = 406 - res.end() + if (segments[2] === "events" && req.method === "GET") { + if (segments[3] === "update") { + if (req.headers.Observe === 0) { + if (supportedContentTypes.includes(acceptHeaders)) { + console.log("Observing update event..."); + + let oldResult = result; + + const changeInterval = setInterval(() => { + // Set the content-format header to the accepted header + res.setOption("Content-Format", acceptHeaders); + + if (oldResult !== result) { + res.statusCode = 205; + if ( + acceptHeaders.includes("application/json") || + acceptHeaders.includes("application/*") || + acceptHeaders === "*/*" + ) { + res.write(JSON.stringify(result)); + oldResult = result; + } else { + const cborData = cbor.encode(result); + res.write(cborData); + oldResult = result; + } + } + }, 1000); + + res.on("finish", () => { + clearInterval(changeInterval); + }); + } else { + res.statusCode = 406; + res.end(); + } + } else { + res.code = 402; + res.end(); + } + } else { + res.code = 404; + res.end(); } - } - else { - res.code = 402 - res.end() - } - } - else { - res.code = 404 - res.end() } - } -}) +}); server.listen(portNumber, () => { - console.log(`Started listening to localhost on port ${portNumber}...`) - console.log('ThingIsReady') -}) \ No newline at end of file + console.log(`Started listening to localhost on port ${portNumber}...`); + console.log("ThingIsReady"); +}); diff --git a/things/calculator/coap/js/coap-simple-calculator.js b/things/calculator/coap/js/coap-simple-calculator.js index f061fe9..fbfadd7 100644 --- a/things/calculator/coap/js/coap-simple-calculator.js +++ b/things/calculator/coap/js/coap-simple-calculator.js @@ -1,302 +1,317 @@ -const { parseArgs } = require('node:util') -const coap = require('coap') -const fs = require('fs') -const path = require('path') -const { JsonPlaceholderReplacer } = require('json-placeholder-replacer') -require('dotenv').config() - -const server = coap.createServer() -const hostname = process.env.HOSTNAME ?? 'localhost' -let portNumber = Number(process.env.PORT ?? 5683) -const thingName = 'coap-calculator-simple' - -const { values: { port } } = parseArgs({ +/******************************************************************************** + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the W3C Software Notice and + * Document License (2015-05-13) which is available at + * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document. + * + * SPDX-License-Identifier: EPL-2.0 OR W3C-20150513 + ********************************************************************************/ +const { parseArgs } = require("node:util"); +const coap = require("coap"); +const fs = require("fs"); +const path = require("path"); +const { JsonPlaceholderReplacer } = require("json-placeholder-replacer"); +require("dotenv").config(); + +const server = coap.createServer(); +const hostname = process.env.HOSTNAME ?? "localhost"; +let portNumber = Number(process.env.PORT ?? 5683); +const thingName = "coap-calculator-simple"; + +const { + values: { port }, +} = parseArgs({ options: { port: { - type: 'string', - short: 'p' - } - } -}) + type: "string", + short: "p", + }, + }, +}); if (port && !isNaN(parseInt(port))) { - portNumber = parseInt(port) + portNumber = parseInt(port); } -const tmPath = process.env.TM_PATH +const tmPath = process.env.TM_PATH; -if (process.platform === 'win32') { - tmPath.split(path.sep).join(path.win32.sep) +if (process.platform === "win32") { + tmPath.split(path.sep).join(path.win32.sep); } -const thingModel = JSON.parse(fs.readFileSync(path.join(__dirname, tmPath))) +const thingModel = JSON.parse(fs.readFileSync(path.join(__dirname, tmPath))); -const placeholderReplacer = new JsonPlaceholderReplacer() +const placeholderReplacer = new JsonPlaceholderReplacer(); placeholderReplacer.addVariableMap({ - PROTOCOL: 'coap', + PROTOCOL: "coap", THING_NAME: thingName, HOSTNAME: hostname, PORT_NUMBER: portNumber, RESULT_OBSERVABLE: true, - LAST_CHANGE_OBSERVABLE: true -}) + LAST_CHANGE_OBSERVABLE: true, +}); /*****************************************/ -/************ Creating the TD ************/ +/** ********** Creating the TD ************/ /*****************************************/ -const thingDescription = placeholderReplacer.replace(thingModel) -thingDescription['@type'] = 'Thing' +const thingDescription = placeholderReplacer.replace(thingModel); +thingDescription["@type"] = "Thing"; const defaultForm = { - "href": "", - "contentType": "application/json", - "op": [] -} - -//add properties forms -for (const key in thingDescription['properties']) { + href: "", + contentType: "application/json", + op: [], +}; + +// add properties forms +for (const key in thingDescription.properties) { + if (!Object.hasOwn(thingDescription.properties, key)) { + continue; + } - thingDescription['properties'][key]['forms'] = [] + thingDescription.properties[key].forms = []; - const newFormRead = JSON.parse(JSON.stringify(defaultForm)) - newFormRead['href'] = `properties/${key}` - newFormRead['op'] = ["readproperty"] + const newFormRead = JSON.parse(JSON.stringify(defaultForm)); + newFormRead.href = `properties/${key}`; + newFormRead.op = ["readproperty"]; - const newFormObs = JSON.parse(JSON.stringify(newFormRead)) - newFormObs['op'] = ["observeproperty", "unobserveproperty"] - newFormObs['subprotocol'] = "cov:observe" + const newFormObs = JSON.parse(JSON.stringify(newFormRead)); + newFormObs.op = ["observeproperty", "unobserveproperty"]; + newFormObs.subprotocol = "cov:observe"; - thingDescription['properties'][key]['forms'].push(newFormRead) - thingDescription['properties'][key]['forms'].push(newFormObs) + thingDescription.properties[key].forms.push(newFormRead); + thingDescription.properties[key].forms.push(newFormObs); } -//add actions forms -for (const key in thingDescription['actions']) { +// add actions forms +for (const key in thingDescription.actions) { + if (!Object.hasOwn(thingDescription.actions, key)) { + continue; + } - thingDescription['actions'][key]['forms'] = [] + thingDescription.actions[key].forms = []; - const newForm = JSON.parse(JSON.stringify(defaultForm)) - newForm['href'] = `actions/${key}` - newForm['op'] = ["invokeaction"] + const newForm = JSON.parse(JSON.stringify(defaultForm)); + newForm.href = `actions/${key}`; + newForm.op = ["invokeaction"]; - thingDescription['actions'][key]['forms'].push(newForm) + thingDescription.actions[key].forms.push(newForm); } -//add events forms -for (const key in thingDescription['events']) { +// add events forms +for (const key in thingDescription.events) { + if (!Object.hasOwn(thingDescription.events, key)) { + continue; + } - thingDescription['events'][key]['data']['type'] = "number" + thingDescription.events[key].data.type = "number"; - thingDescription['events'][key]['forms'] = [] + thingDescription.events[key].forms = []; - const newForm = JSON.parse(JSON.stringify(defaultForm)) - newForm['href'] = `events/${key}` - newForm['op'] = ["subscribeevent", "unsubscribeevent"] - newForm['subprotocol'] = 'cov:observe' + const newForm = JSON.parse(JSON.stringify(defaultForm)); + newForm.href = `events/${key}`; + newForm.op = ["subscribeevent", "unsubscribeevent"]; + newForm.subprotocol = "cov:observe"; - thingDescription['events'][key]['forms'].push(newForm) + thingDescription.events[key].forms.push(newForm); } -//Creating the TD for testing purposes +// Creating the TD for testing purposes try { - fs.writeFileSync('coap-simple-calculator-thing.td.jsonld', JSON.stringify(thingDescription, null, 2)) + fs.writeFileSync( + "coap-simple-calculator-thing.td.jsonld", + JSON.stringify(thingDescription, null, 2) + ); } catch (err) { console.log(err); } - /*********************************************************/ -/************** Main server functionality ****************/ +/** ************ Main server functionality ****************/ /*********************************************************/ -let result = 0 -let lastChange = new Date().toISOString() +let result = 0; +let lastChange = new Date().toISOString(); -server.on('request', (req, res) => { - const segments = req.url.split('/') +server.on("request", (req, res) => { + const segments = req.url.split("/"); if (segments[1] !== thingName) { - res.code = 404 - res.end() + res.code = 404; + res.end(); } else { if (!segments[2]) { - if (req.method === 'GET') { - res.setOption('Content-Format', "application/json") - res.end(JSON.stringify(thingDescription)) - } - else { - res.code = 405 - res.end() + if (req.method === "GET") { + res.setOption("Content-Format", "application/json"); + res.end(JSON.stringify(thingDescription)); + } else { + res.code = 405; + res.end(); } } } - if (segments[2] === 'properties') { - if (req.method === 'GET') { - - //Result endpoint - if (segments[3] === 'result') { + if (segments[2] === "properties") { + if (req.method === "GET") { + // Result endpoint + if (segments[3] === "result") { + // setting the content type header to json + res.setOption("Content-Format", "application/json"); - //setting the content type header to json - res.setOption('Content-Format', "application/json") - - //Checking for the observe option + // Checking for the observe option if (req.headers.Observe === 0) { - console.log('Observing the result property...') - res.statusCode = 205 + console.log("Observing the result property..."); + res.statusCode = 205; - let oldResult = result + let oldResult = result; const changeInterval = setInterval(() => { if (oldResult !== result) { - res.write(`${JSON.stringify(result)}`) - oldResult = result + res.write(`${JSON.stringify(result)}`); + oldResult = result; } - }, 1000) + }, 1000); - res.on('finish', () => { - console.log('Client stopped the result observation') - clearInterval(changeInterval) - }) + res.on("finish", () => { + console.log("Client stopped the result observation"); + clearInterval(changeInterval); + }); } - //If the observe option is false, then the value is given and the connection is closed + // If the observe option is false, then the value is given and the connection is closed else { - res.end(JSON.stringify(result)) + res.end(JSON.stringify(result)); } - } - else if (segments[3] === 'lastChange') { - - //setting the content type header to json - res.setOption('Content-Format', "application/json") + } else if (segments[3] === "lastChange") { + // setting the content type header to json + res.setOption("Content-Format", "application/json"); - //Checking for the observe option + // Checking for the observe option if (req.headers.Observe === 0) { - console.log('Observing the lastChange property...') - res.statusCode = 205 + console.log("Observing the lastChange property..."); + res.statusCode = 205; - let oldDate = lastChange + let oldDate = lastChange; const changeInterval = setInterval(() => { if (oldDate !== lastChange) { - res.write(`${JSON.stringify(lastChange)}`) - oldDate = lastChange + res.write(`${JSON.stringify(lastChange)}`); + oldDate = lastChange; } - }, 1000) - - res.on('finish', () => { - console.log('Client stopped the lastChange observation'); - clearInterval(changeInterval) - }) + }, 1000); + + res.on("finish", () => { + console.log( + "Client stopped the lastChange observation" + ); + clearInterval(changeInterval); + }); } - //If the observe option is false, then the value is given and the connection is closed + // If the observe option is false, then the value is given and the connection is closed else { - res.end(JSON.stringify(lastChange)) + res.end(JSON.stringify(lastChange)); } + } else { + res.code = 404; + res.end(); } - else { - res.code = 404 - res.end() - } - } - else { - res.code = 405 - res.end() + } else { + res.code = 405; + res.end(); } } + if (segments[2] === "actions") { + if (req.method === "POST") { + if (segments[3] === "add") { + // setting the content type header to json + res.setOption("Content-Format", "application/json"); - if (segments[2] === 'actions') { - if (req.method === 'POST') { - if (segments[3] === 'add') { - - //setting the content type header to json - res.setOption('Content-Format', "application/json") - - let inputNumber + let inputNumber; try { - inputNumber = JSON.parse(req.payload.toString()) + inputNumber = JSON.parse(req.payload.toString()); } catch (err) { - res.code = 400 - res.end() + console.log(err); + res.code = 400; + res.end(); } if (typeof inputNumber !== "number") { - res.code = 400 - res.end() + res.code = 400; + res.end(); } else { - result += inputNumber - lastChange = new Date() - res.end(JSON.stringify(result)) + result += inputNumber; + lastChange = new Date(); + res.end(JSON.stringify(result)); } - } - else if (segments[3] === 'subtract') { - - //setting the content type header to json - res.setOption('Content-Format', "application/json") + } else if (segments[3] === "subtract") { + // setting the content type header to json + res.setOption("Content-Format", "application/json"); - let inputNumber + let inputNumber; try { - inputNumber = JSON.parse(req.payload.toString()) + inputNumber = JSON.parse(req.payload.toString()); } catch (err) { - res.code = 400 - res.end() + console.log(err); + res.code = 400; + res.end(); } if (typeof inputNumber !== "number") { - res.code = 400 - res.end() + res.code = 400; + res.end(); } else { - result -= inputNumber - lastChange = new Date() - res.end(JSON.stringify(result)) + result -= inputNumber; + lastChange = new Date(); + res.end(JSON.stringify(result)); } + } else { + res.code = 404; + res.end(); } - else { - res.code = 404 - res.end() - } - } - else { - res.code = 405 - res.end() + } else { + res.code = 405; + res.end(); } } - if (segments[2] === 'events' && req.method === 'GET') { - if (segments[3] === 'update') { + if (segments[2] === "events" && req.method === "GET") { + if (segments[3] === "update") { if (req.headers.Observe === 0) { - console.log('Observing the update event...') - res.statusCode = 205 + console.log("Observing the update event..."); + res.statusCode = 205; - //setting the content type header to json - res.setOption('Content-Format', "application/json") + // setting the content type header to json + res.setOption("Content-Format", "application/json"); - let oldResult = result + let oldResult = result; const changeInterval = setInterval(() => { if (oldResult !== result) { - res.write(JSON.stringify(result)) - oldResult = result + res.write(JSON.stringify(result)); + oldResult = result; } - }, 1000) - - res.on('finish', () => { - console.log('Client stopped the update observation'); - clearInterval(changeInterval) - }) - - } - else { - res.code = 402 - res.end() + }, 1000); + + res.on("finish", () => { + console.log("Client stopped the update observation"); + clearInterval(changeInterval); + }); + } else { + res.code = 402; + res.end(); } - } - else { - res.code = 404 - res.end() + } else { + res.code = 404; + res.end(); } } -}) +}); server.listen(portNumber, () => { - console.log(`Started listening to localhost on port ${portNumber}...`) - console.log('ThingIsReady') -}) \ No newline at end of file + console.log(`Started listening to localhost on port ${portNumber}...`); + console.log("ThingIsReady"); +}); diff --git a/things/calculator/coap/js/package.json b/things/calculator/coap/js/package.json index ddcb9ec..8aac562 100644 --- a/things/calculator/coap/js/package.json +++ b/things/calculator/coap/js/package.json @@ -1,20 +1,25 @@ { - "name": "coap-calculator", - "version": "1.0.0", - "description": "CoAP Calculator", - "main": "simple-coap-calculator.js", - "keywords": [ - "wot", - "thing", - "iot", - "coap" - ], - "author": "Eclipse Thingweb (https://thingweb.io/)", - "license": "EPL-2.0 OR W3C-20150513", - "dependencies": { - "cbor": "^9.0.2", - "coap": "^1.3.0", - "dotenv": "^16.3.1", - "json-placeholder-replacer": "^1.0.35" - } + "name": "coap-calculator", + "version": "1.0.0", + "description": "CoAP Calculator", + "main": "simple-coap-calculator.js", + "keywords": [ + "wot", + "thing", + "iot", + "coap" + ], + "scripts": { + "lint": "eslint .", + "lint:fix": "eslint . --fix", + "format": "prettier --write \"**/*.js\" \"test/**/*.js\" \"client-tests/**/*.js\" \"**/*.json\"" + }, + "author": "Eclipse Thingweb (https://thingweb.io/)", + "license": "EPL-2.0 OR W3C-20150513", + "dependencies": { + "cbor": "^9.0.2", + "coap": "^1.3.0", + "dotenv": "^16.3.1", + "json-placeholder-replacer": "^1.0.35" + } } diff --git a/things/calculator/coap/js/test/client.test.js b/things/calculator/coap/js/test/client.test.js index d5d2e21..f71b54b 100644 --- a/things/calculator/coap/js/test/client.test.js +++ b/things/calculator/coap/js/test/client.test.js @@ -1,218 +1,266 @@ -const chai = require("chai") -const chaiAsPromised = require("chai-as-promised") +/******************************************************************************** + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the W3C Software Notice and + * Document License (2015-05-13) which is available at + * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document. + * + * SPDX-License-Identifier: EPL-2.0 OR W3C-20150513 + ********************************************************************************/ -const { Servient } = require("@node-wot/core") -const { CoapClientFactory } = require("@node-wot/binding-coap") -const { simplePort, contentNegotiationPort } = require('./fixtures') +const chai = require("chai"); +const chaiAsPromised = require("chai-as-promised"); -chai.use(chaiAsPromised) -const expect = chai.expect +const { Servient } = require("@node-wot/core"); +const { CoapClientFactory } = require("@node-wot/binding-coap"); +const { simplePort, contentNegotiationPort } = require("./fixtures"); -const servient = new Servient() -servient.addClientFactory(new CoapClientFactory()) -let WoT +chai.use(chaiAsPromised); +const expect = chai.expect; + +const servient = new Servient(); +servient.addClientFactory(new CoapClientFactory()); +let WoT; const readProperty = async (thing, propertyName) => { try { - const response = await thing.readProperty(propertyName) - return await response.value() - } catch(error) { - console.error(`Error: ${error}`) + const response = await thing.readProperty(propertyName); + return await response.value(); + } catch (error) { + console.error(`Error: ${error}`); } -} +}; describe("Client Tests", () => { before(async () => { try { - WoT = await servient.start() - } catch(error) { - console.error(error) + WoT = await servient.start(); + } catch (error) { + console.error(error); } - }) + }); after(async () => { - await servient.shutdown() - }) + await servient.shutdown(); + }); describe("Simple Calculator", () => { - let thing + let thing; before(async () => { try { - const td = await WoT.requestThingDescription(`coap://localhost:${simplePort}/coap-calculator-simple`) - thing = await WoT.consume(td) - } catch(error) { - console.error(error) + const td = await WoT.requestThingDescription( + `coap://localhost:${simplePort}/coap-calculator-simple` + ); + thing = await WoT.consume(td); + } catch (error) { + console.error(error); } - }) + }); describe("result property", () => { it("should return initial value", async () => { - const value = await readProperty(thing, "result") - expect(value).to.be.equal(0) - }) - + const value = await readProperty(thing, "result"); + expect(value).to.be.equal(0); + }); + it("should return sum when adding value to the existing result", async () => { - const resultValue = await readProperty(thing, "result") - const valueToAdd = 12 - await thing.invokeAction("add", valueToAdd) - const newResultValue = await readProperty(thing, "result") - expect(newResultValue).to.be.equal(resultValue + valueToAdd) - }) - - it("should return sum when subtracting value from the existing result", async() => { - const resultValue = await readProperty(thing, "result") - const valueToSubtract = 3 - await thing.invokeAction("subtract", valueToSubtract) - const newResultValue = await readProperty(thing, "result") - expect(newResultValue).to.be.equal(resultValue - valueToSubtract) - }) - }) + const resultValue = await readProperty(thing, "result"); + const valueToAdd = 12; + await thing.invokeAction("add", valueToAdd); + const newResultValue = await readProperty(thing, "result"); + expect(newResultValue).to.be.equal(resultValue + valueToAdd); + }); + + it("should return sum when subtracting value from the existing result", async () => { + const resultValue = await readProperty(thing, "result"); + const valueToSubtract = 3; + await thing.invokeAction("subtract", valueToSubtract); + const newResultValue = await readProperty(thing, "result"); + expect(newResultValue).to.be.equal( + resultValue - valueToSubtract + ); + }); + }); describe("lastChange property", () => { it("should observe a change when the result is changed", async () => { setTimeout(async () => { - await thing.invokeAction('add', 1) - }, 200) + await thing.invokeAction("add", 1); + }, 200); - let value - const subscription = thing.observeProperty('lastChange', async (response) => { - value = await response.value() - }) + let value; + const subscription = thing.observeProperty( + "lastChange", + async (response) => { + value = await response.value(); + } + ); setTimeout(async () => { - expect(value).to.be.not.undefined - await subscription.stop() - }) - }) - }) - + expect(value).to.be.not.undefined; + await subscription.stop(); + }); + }); + }); + describe("add action", () => { it("should return sum when adding value to the existing result", async () => { - const resultValue = await readProperty(thing, "result") - const valueToAdd = 12 - const response = await thing.invokeAction("add", valueToAdd) - const actionResultValue = await response.value() - expect(actionResultValue).to.be.equal(resultValue + valueToAdd) - }) - }) - + const resultValue = await readProperty(thing, "result"); + const valueToAdd = 12; + const response = await thing.invokeAction("add", valueToAdd); + const actionResultValue = await response.value(); + expect(actionResultValue).to.be.equal(resultValue + valueToAdd); + }); + }); + describe("subtract action", () => { - it("should return sum when subtracting value from the existing result", async() => { - const resultValue = await readProperty(thing, "result") - const valueToSubtract = 3 - const response = await thing.invokeAction("subtract", valueToSubtract) - const actionResultValue = await response.value() - expect(actionResultValue).to.be.equal(resultValue - valueToSubtract) - }) - }) - + it("should return sum when subtracting value from the existing result", async () => { + const resultValue = await readProperty(thing, "result"); + const valueToSubtract = 3; + const response = await thing.invokeAction( + "subtract", + valueToSubtract + ); + const actionResultValue = await response.value(); + expect(actionResultValue).to.be.equal( + resultValue - valueToSubtract + ); + }); + }); + describe("update event", () => { it("should return the update message when subscribed", async () => { - const resultValue = await readProperty(thing, "result") - const valueToAdd = 13 - - const actionTimeout = setInterval(async () => { - await thing.invokeAction('add', valueToAdd) - }, 200) - - const subscription = await thing.subscribeEvent("update", async (response) => { - await expect(response.value).to.have.eventually.be.equal(resultValue + valueToAdd) - }) - - await subscription.stop() - }) - }) - }) + const resultValue = await readProperty(thing, "result"); + const valueToAdd = 13; + + setTimeout(async () => { + await thing.invokeAction("add", valueToAdd); + }, 200); + + const subscription = await thing.subscribeEvent( + "update", + async (response) => { + await expect( + response.value + ).to.have.eventually.be.equal(resultValue + valueToAdd); + } + ); + + await subscription.stop(); + }); + }); + }); describe("Content Negotiation Calculator", () => { - let thing + let thing; before(async () => { try { - const td = await WoT.requestThingDescription(`coap://localhost:${contentNegotiationPort}/coap-calculator-content-negotiation`) - thing = await WoT.consume(td) - } catch(error) { - console.error(error) + const td = await WoT.requestThingDescription( + `coap://localhost:${contentNegotiationPort}/coap-calculator-content-negotiation` + ); + thing = await WoT.consume(td); + } catch (error) { + console.error(error); } - }) - + }); + describe("result property", () => { it("should return initial value", async () => { - const value = await readProperty(thing, "result") - expect(value).to.be.equal(0) - }) - + const value = await readProperty(thing, "result"); + expect(value).to.be.equal(0); + }); + it("should return sum when adding value to the existing result", async () => { - const resultValue = await readProperty(thing, "result") - const valueToAdd = 12 - await thing.invokeAction("add", valueToAdd) - const newResultValue = await readProperty(thing, "result") - expect(newResultValue).to.be.equal(resultValue + valueToAdd) - }) - + const resultValue = await readProperty(thing, "result"); + const valueToAdd = 12; + await thing.invokeAction("add", valueToAdd); + const newResultValue = await readProperty(thing, "result"); + expect(newResultValue).to.be.equal(resultValue + valueToAdd); + }); + it("should return sum when subtracting value from the existing result", async () => { - const resultValue = await readProperty(thing, "result") - const valueToSubtract = 3 - await thing.invokeAction("subtract", valueToSubtract) - const newResultValue = await readProperty(thing, "result") - expect(newResultValue).to.be.equal(resultValue - valueToSubtract) - }) - }) + const resultValue = await readProperty(thing, "result"); + const valueToSubtract = 3; + await thing.invokeAction("subtract", valueToSubtract); + const newResultValue = await readProperty(thing, "result"); + expect(newResultValue).to.be.equal( + resultValue - valueToSubtract + ); + }); + }); describe("lastChange property", () => { it("should observe a change when the result is changed", async () => { setTimeout(async () => { - await thing.invokeAction('add', 1) - }, 200) + await thing.invokeAction("add", 1); + }, 200); - let value - const subscription = thing.observeProperty('lastChange', async (response) => { - value = await response.value() - }) + let value; + const subscription = thing.observeProperty( + "lastChange", + async (response) => { + value = await response.value(); + } + ); setTimeout(async () => { - expect(value).to.be.not.undefined - await subscription.stop() - }) - }) - }) - + expect(value).to.be.not.undefined; + await subscription.stop(); + }); + }); + }); + describe("add action", () => { it("should return sum when adding value to the existing result", async () => { - const resultValue = await readProperty(thing, "result") - const valueToAdd = 12 - const response = await thing.invokeAction("add", valueToAdd) - const actionResultValue = await response.value() - expect(actionResultValue).to.be.equal(resultValue + valueToAdd) - }) - }) - + const resultValue = await readProperty(thing, "result"); + const valueToAdd = 12; + const response = await thing.invokeAction("add", valueToAdd); + const actionResultValue = await response.value(); + expect(actionResultValue).to.be.equal(resultValue + valueToAdd); + }); + }); + describe("subtract action", () => { - it("should return sum when subtracting value from the existing result", async() => { - const resultValue = await readProperty(thing, "result") - const valueToSubtract = 3 - const response = await thing.invokeAction("subtract", valueToSubtract) - const actionResultValue = await response.value() - expect(actionResultValue).to.be.equal(resultValue - valueToSubtract) - }) - }) - + it("should return sum when subtracting value from the existing result", async () => { + const resultValue = await readProperty(thing, "result"); + const valueToSubtract = 3; + const response = await thing.invokeAction( + "subtract", + valueToSubtract + ); + const actionResultValue = await response.value(); + expect(actionResultValue).to.be.equal( + resultValue - valueToSubtract + ); + }); + }); + describe("update event", () => { it("should return the update message when subscribed", async () => { - const resultValue = await readProperty(thing, "result") - const valueToAdd = 13 - - const actionTimeout = setInterval(async () => { - await thing.invokeAction('add', valueToAdd) - }, 200) - - const subscription = await thing.subscribeEvent("update", async (response) => { - await expect(response.value).to.have.eventually.be.equal(resultValue + valueToAdd) - }) - - await subscription.stop() - }) - }) - }) -}) + const resultValue = await readProperty(thing, "result"); + const valueToAdd = 13; + + setTimeout(async () => { + await thing.invokeAction("add", valueToAdd); + }, 200); + + const subscription = await thing.subscribeEvent( + "update", + async (response) => { + await expect( + response.value + ).to.have.eventually.be.equal(resultValue + valueToAdd); + } + ); + await subscription.stop(); + }); + }); + }); +}); diff --git a/things/calculator/coap/js/test/fixtures.js b/things/calculator/coap/js/test/fixtures.js index 1574569..07f564f 100644 --- a/things/calculator/coap/js/test/fixtures.js +++ b/things/calculator/coap/js/test/fixtures.js @@ -1,45 +1,70 @@ -const { getInitiateMain } = require("../../../../../util/dist/util") -const path = require("node:path") +/******************************************************************************** + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the W3C Software Notice and + * Document License (2015-05-13) which is available at + * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document. + * + * SPDX-License-Identifier: EPL-2.0 OR W3C-20150513 + ********************************************************************************/ -let simpleThingProcess -let contentNegotiationThingProcess -let response -const simplePort = 5683 -const contentNegotiationPort = 5684 +const { getInitiateMain } = require("../../../../../util/dist/util"); +const path = require("node:path"); -const mochaGlobalSetup = async function() { +let simpleThingProcess; +let contentNegotiationThingProcess; +let response; +const simplePort = 5683; +const contentNegotiationPort = 5684; + +const mochaGlobalSetup = async function () { try { - response = await getInitiateMain('node', [path.join(__dirname, '..', 'coap-simple-calculator.js'), '-p', `${simplePort}`]) - simpleThingProcess = response.process - } - catch(error) { - console.error(error) - simpleThingProcess = error.process + response = await getInitiateMain("node", [ + path.join(__dirname, "..", "coap-simple-calculator.js"), + "-p", + `${simplePort}`, + ]); + simpleThingProcess = response.process; + } catch (error) { + console.error(error); + simpleThingProcess = error.process; } try { - response = await getInitiateMain('node', [path.join(__dirname, '..', 'coap-content-negotiation-calculator.js'), '-p', `${contentNegotiationPort}`]) - contentNegotiationThingProcess = response.process - } - catch (error) { - console.error(error) - contentNegotiationThingProcess = error.process + response = await getInitiateMain("node", [ + path.join( + __dirname, + "..", + "coap-content-negotiation-calculator.js" + ), + "-p", + `${contentNegotiationPort}`, + ]); + contentNegotiationThingProcess = response.process; + } catch (error) { + console.error(error); + contentNegotiationThingProcess = error.process; } -} +}; -const mochaGlobalTeardown = function() { - if(simpleThingProcess) { - simpleThingProcess.kill() +const mochaGlobalTeardown = function () { + if (simpleThingProcess) { + simpleThingProcess.kill(); } - if(contentNegotiationThingProcess) { - contentNegotiationThingProcess.kill() + if (contentNegotiationThingProcess) { + contentNegotiationThingProcess.kill(); } -} +}; module.exports = { simplePort, contentNegotiationPort, mochaGlobalSetup, - mochaGlobalTeardown -} \ No newline at end of file + mochaGlobalTeardown, +}; diff --git a/things/calculator/coap/js/test/td.test.js b/things/calculator/coap/js/test/td.test.js index 9823a30..91eb852 100644 --- a/things/calculator/coap/js/test/td.test.js +++ b/things/calculator/coap/js/test/td.test.js @@ -1,83 +1,99 @@ -const chai = require('chai') -const coap = require('coap') -const cbor = require('cbor') -const { getTDValidate } = require('../../../../../util/dist/util') -const { simplePort, contentNegotiationPort } = require('./fixtures') +/******************************************************************************** + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the W3C Software Notice and + * Document License (2015-05-13) which is available at + * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document. + * + * SPDX-License-Identifier: EPL-2.0 OR W3C-20150513 + ********************************************************************************/ -const expect = chai.expect +const chai = require("chai"); +const coap = require("coap"); +const cbor = require("cbor"); +const { getTDValidate } = require("../../../../../util/dist/util"); +const { simplePort, contentNegotiationPort } = require("./fixtures"); -describe('Calculator CoAP JS', () => { - let validate +const expect = chai.expect; - before(async () => { - const tdValidate = getTDValidate() - - try { - const response = await Promise.all([tdValidate]) - validate = response[0].validate - } - catch (error) { - console.log(error) - } - }) +describe("Calculator CoAP JS", () => { + let validate; - describe('Calculator Simple', () => { - it('should have a valid TD', (done) => { - const req = coap.request(`coap://localhost:${simplePort}/coap-calculator-simple`) - - req.on('response', (res) => { - const valid = validate(JSON.parse(res.payload.toString())) - expect(valid).to.be.true - done() - }) - - req.end() - }) - }) + before(async () => { + const tdValidate = getTDValidate(); - describe('Calculator Content Negotiation', () => { - it('should have a valid application/json TD', (done) => { - const req = coap.request({ - method: 'GET', - observe: false, - host: 'localhost', - port: contentNegotiationPort, - pathname: 'coap-calculator-content-negotiation', - headers: { - "Accept": 'application/json' - } - }) - - req.on('response', (res) => { - const valid = validate(JSON.parse(res.payload.toString())) - expect(valid).to.be.true - done() - }) + try { + const response = await Promise.all([tdValidate]); + validate = response[0].validate; + } catch (error) { + console.log(error); + } + }); - req.end() - }) + describe("Calculator Simple", () => { + it("should have a valid TD", (done) => { + const req = coap.request( + `coap://localhost:${simplePort}/coap-calculator-simple` + ); - it('should have a valid application/cbor TD', (done) => { - const req = coap.request({ - method: 'GET', - observe: false, - host: 'localhost', - port: contentNegotiationPort, - pathname: 'coap-calculator-content-negotiation', - headers: { - "Accept": 'application/cbor' - } - }) - - req.on('response', (res) => { - // console.log(res.payload.toString()) - const decodedPayload = cbor.decode(res.payload) - const valid = validate(JSON.parse(decodedPayload)) - expect(valid).to.be.true - done() - }) + req.on("response", (res) => { + const valid = validate(JSON.parse(res.payload.toString())); + expect(valid).to.be.true; + done(); + }); - req.end() - }) - }) -}) + req.end(); + }); + }); + + describe("Calculator Content Negotiation", () => { + it("should have a valid application/json TD", (done) => { + const req = coap.request({ + method: "GET", + observe: false, + host: "localhost", + port: contentNegotiationPort, + pathname: "coap-calculator-content-negotiation", + headers: { + Accept: "application/json", + }, + }); + + req.on("response", (res) => { + const valid = validate(JSON.parse(res.payload.toString())); + expect(valid).to.be.true; + done(); + }); + + req.end(); + }); + + it("should have a valid application/cbor TD", (done) => { + const req = coap.request({ + method: "GET", + observe: false, + host: "localhost", + port: contentNegotiationPort, + pathname: "coap-calculator-content-negotiation", + headers: { + Accept: "application/cbor", + }, + }); + + req.on("response", (res) => { + // console.log(res.payload.toString()) + const decodedPayload = cbor.decode(res.payload); + const valid = validate(JSON.parse(decodedPayload)); + expect(valid).to.be.true; + done(); + }); + + req.end(); + }); + }); +}); diff --git a/things/calculator/http/client-tests/content-negotiation-http-client.js b/things/calculator/http/client-tests/content-negotiation-http-client.js deleted file mode 100644 index b953e86..0000000 --- a/things/calculator/http/client-tests/content-negotiation-http-client.js +++ /dev/null @@ -1,313 +0,0 @@ -/** - * @file The `content-negotiation-http-client.js` file acts as a client for the content-negotiation-calculator.js. - * This client is mostly used for testing the content negotiation functionality of the http thing. - * Requests as well as responses can be sent and received in JSON and CBOR formats. - */ - -const cbor = require('cbor') -const EventSource = require('eventsource') - -const url = "http://localhost:3000/http-express-calculator-content-negotiation", - resultEndPoint = "/properties/result", - resultEndPointObserve = `${resultEndPoint}/observe`, - lastChangeEndPoint = "/properties/lastChange", - lastChangeEndPointObserve = `${lastChangeEndPoint}/observe`, - additionEndPoint = "/actions/add", - subtractionEndPoint = "/actions/subtract", - updateEndPoint = "/events/update" - - -/** - * Return the Full TD - * @param { String } acceptType - Which content type is accepted by the client - * @returns Thing description as either a String, JSON or CBOR - */ -async function getFullTD(acceptType) { - - const res = await fetch(url, { - method: "GET", - headers: { - "Accept": acceptType - } - }) - - const contentType = res.headers.get("content-type") - - if (contentType.includes("application/json")) { - return res.json() - } - else { - const buffer = await res.arrayBuffer() - const decodedData = cbor.decode(buffer); - return decodedData - } -} - -/** - * Fetch current calculator result - * @param { String } acceptType - Which content type is accepted by the client - * @returns result - a string or number depending on the request - */ -async function getCurrentResult(acceptType) { - - const res = await fetch(url + resultEndPoint, { - method: "GET", - headers: { - "Accept": acceptType - } - }) - - const contentType = res.headers.get("content-type") - - if (contentType.includes("application/json")) { - return res.json() - } - else { - const buffer = await res.arrayBuffer() - const decodedData = cbor.decode(buffer); - return decodedData - } -} - -/** - * Create an EventSource for the result observe property. - */ -function listenToResultProperty(acceptType) { - - const resultEventSource = new EventSource(url + resultEndPointObserve, { - headers: { - 'Accept': acceptType - } - }); - - resultEventSource.onmessage = (e) => { - - const body = e.data; - - if (acceptType === 'application/json') { - console.log('Result SSE:', JSON.parse(body)) - } - else { - const buffer = Buffer.from(body); - const decodedData = cbor.decode(buffer) - console.log('Result SSE:', decodedData); - } - }; - - resultEventSource.onerror = (error) => { - console.error('Error with Result property SSE:', error); - }; - - //Closing the event source after 6 seconds - setTimeout(() => { - resultEventSource.close() - console.log("- Closing Result Property SSE"); - }, 6000) -} - -/** - * Fetches the last change made - * @param { String } acceptType - Which content type is accepted by the client - * @returns lastChange - A string of the date when it was last changed - */ -async function getLatestChange(acceptType) { - - const res = await fetch(url + lastChangeEndPoint, { - method: "GET", - headers: { - "Accept": acceptType - } - }) - - const contentType = res.headers.get("content-type") - - if (contentType.includes("application/json")) { - return res.json() - } - else { - const buffer = await res.arrayBuffer() - const decodedData = cbor.decode(buffer); - return decodedData - } -} - -/** - * Create an EventSource for the last change observe property. - */ -function listenToLastChangeProperty(acceptType) { - - const lastChangeEventSource = new EventSource(url + lastChangeEndPointObserve, { - headers: { - 'Accept': acceptType - } - }); - - lastChangeEventSource.onmessage = (e) => { - const body = e.data; - - if (acceptType === 'application/json') { - console.log('lastChange SSE:', JSON.parse(body)) - } - else { - const buffer = Buffer.from(body); - const decodedData = cbor.decode(buffer) - console.log('lastChange SSE:', decodedData); - } - }; - - lastChangeEventSource.onerror = (error) => { - console.error('Error with lastChange property SSE:', error); - }; - - //Closing the event source after 6 seconds - setTimeout(() => { - lastChangeEventSource.close() - console.log("- Closing lastChange Property SSE"); - }, 6000) -} - -/** - * Adds a number to the current result - * @param { Number } number - the number to be added - * @param { String } contentType - Which content type is accepted by the server - * @param { String } acceptType - Which content type is accepted by the client - * @returns addedNumber - the number to be added to the calculator - */ -async function addNumber(number, contentType, acceptType) { - - const inputNumber = contentType === "application/json" ? JSON.stringify(number) : cbor.encode(number) - - const res = await fetch(url + additionEndPoint, { - method: "POST", - headers: { - "Content-Type": contentType, - "Accept": acceptType, - }, - body: inputNumber, - }); - - if (res.ok) { - const contentType = res.headers.get("content-type") - - if (contentType.includes("application/json")) { - return res.json() - } - else { - const buffer = await res.arrayBuffer() - const decodedData = cbor.decode(buffer); - return decodedData - } - } else { - throw new Error(await res.text()); - } -} - -/** - * Subtracts a number to the current result - * @param { Number } number - the number to be subtracted - * @param { String } contentType - Which content type is accepted by the server - * @param { String } acceptType - Which content type is accepted by the client - * @returns subtractedNumber - the number to be added to the calculator - */ -async function subtractNumber(number, contentType, acceptType) { - - const inputNumber = contentType === "application/json" ? JSON.stringify(number) : cbor.encode(number) - - const res = await fetch(url + subtractionEndPoint, { - method: "POST", - headers: { - "Content-Type": contentType, - "Accept": acceptType, - }, - body: inputNumber, - }); - - if (res.ok) { - const contentType = res.headers.get("content-type") - - if (contentType.includes("application/json")) { - return res.json() - } - else { - const buffer = await res.arrayBuffer() - const decodedData = cbor.decode(buffer); - return decodedData - } - } else { - throw new Error(await res.text()); - } -} - -/** -* Create an EventSource for the update endpoint. -*/ -function listenToUpdateEvent(acceptType) { - - const updateEventSource = new EventSource(url + updateEndPoint, { - headers: { - 'Accept': acceptType - } - }); - - updateEventSource.onmessage = (e) => { - - const body = e.data; - - if (acceptType === 'application/json') { - console.log('Update SSE:', JSON.parse(body)); - } - else { - const buffer = Buffer.from(body); - const decodedData = cbor.decode(buffer); - console.log('Update SSE:', decodedData); - } - }; - - updateEventSource.onerror = (error) => { - console.error('Error with Update event SSE:', error); - }; - - //Closing the event source after 6 seconds - setTimeout(() => { - updateEventSource.close() - console.log("- Closing Update Event SSE"); - }, 6000) -} - - -/** - * Runs all the previous functions to test the full functionality of the calculator - */ -async function runCalculatorInteractions() { - - try { - console.log("-------- Basic functionality --------\n"); - console.log("Full thing: \n", await getFullTD("application/cbor")) - console.log("Current number: ", await getCurrentResult("application/json")) - console.log("Last Change: ", await getLatestChange("application/cbor")); - console.log("Result of the addition is: ", await addNumber(5, "application/cbor", "application/json")) - console.log("Result of the subtraction is: ", await subtractNumber(3, "application/json", "application/cbor")) - console.log("Current number: ", await getCurrentResult("application/cbor")) - console.log("Last Change: ", await getLatestChange("application/json")) - - /** - * Start listening to the update event, result property and lastChange property. - */ - console.log("\n-------- Start listening to properties and events --------\n"); - listenToResultProperty("application/cbor") - listenToUpdateEvent("application/json") - listenToLastChangeProperty("application/cbor") - - setTimeout(async () => { - console.log("Adding 1 to test observation: ", await addNumber(1,"application/cbor", "application/json"), "\n") - - }, 2000) - - - } catch (err) { - console.log(err); - } - -} - -runCalculatorInteractions() \ No newline at end of file diff --git a/things/calculator/http/client-tests/node-wot-content-negotiation-http-client.js b/things/calculator/http/client-tests/node-wot-content-negotiation-http-client.js deleted file mode 100644 index 04331d5..0000000 --- a/things/calculator/http/client-tests/node-wot-content-negotiation-http-client.js +++ /dev/null @@ -1,45 +0,0 @@ -const { Servient } = require("@node-wot/core"); -const { HttpClientFactory } = require("@node-wot/binding-http"); - -const servient = new Servient(); -servient.addClientFactory(new HttpClientFactory(null)); - -servient.start().then(async (WoT) => { - const td = await WoT.requestThingDescription("http://localhost:3001/http-express-calculator-content-negotiation"); - - let thing = await WoT.consume(td); - console.log(td); - - - let result = await thing.readProperty("result", {formIndex: 0}) - console.log("Result property: ",await result.value()); - - let lastChange = await thing.readProperty("lastChange", {formIndex: 0}) - console.log("lastChange property: ",await lastChange.value()); - - //Actions endpoints - //TODO: Add this when it gets fixed in node-wot - // let addition = await thing.invokeAction("add", 3, {formIndex: 0}) - // console.log(await addition.value()); - - // let subtraction = await thing.invokeAction("subtract", 5, {formIndex: 0}) - // console.log(await subtraction.value()); - - // let addition2 = await thing.invokeAction("add", 3, {formIndex: 1}) - // console.log(await addition2.value()); - - // let subtraction2 = await thing.invokeAction("subtract", 5, {formIndex: 3}) - // console.log(await subtraction2.value()); - - - - //Update event property - // thing.subscribeEvent("update", async (data) => { - // console.log("Update event:", await data.value()); - // }) - - // //Properties observation - // thing.observeProperty("result", async (data) => { console.log("Result observe:", await data.value()); }); - // thing.observeProperty("lastChange", async (data) => { console.log("lastChange observe:", await data.value()); }); - -}).catch((err) => { console.error(err); }); \ No newline at end of file diff --git a/things/calculator/http/client-tests/node-wot-simple-http-client.js b/things/calculator/http/client-tests/node-wot-simple-http-client.js deleted file mode 100644 index 68d9c8b..0000000 --- a/things/calculator/http/client-tests/node-wot-simple-http-client.js +++ /dev/null @@ -1,38 +0,0 @@ -const { Servient } = require("@node-wot/core") -const { HttpClientFactory } = require("@node-wot/binding-http") - -const servient = new Servient() -servient.addClientFactory(new HttpClientFactory(null)) - -servient.start().then(async (WoT) => { - const td = await WoT.requestThingDescription("http://localhost:3000/http-express-calculator-simple") - - let thing = await WoT.consume(td) - console.log(td) - - //Property endpoints - let result = await (await thing.readProperty("result")).value() - console.log("Read result:", result) - - let lastChange = await (await thing.readProperty("lastChange")).value() - console.log("Read lastChange:", lastChange) - - //Update event observation - thing.subscribeEvent("update", async (data) => { - console.log("Update event:", (await data.value())["data"]); - }) - - //Action endpoints - let additionResult = await thing.invokeAction("add", 3) - console.log("Addition result: ", await additionResult.value()) - - let subtractionResult = await thing.invokeAction("subtract", 3) - console.log("Subtraction result: ", await subtractionResult.value()) - - - - //TODO: Property Observation failing do to returning wrong type (SSE returns object rather than a number) - // thing.observeProperty("result", async (data) => { console.log("Result observe:", await data.value()); }); - // thing.observeProperty("lastChange", async (data) => { console.log("lastChange observe:", await data.value()); }); - -}).catch((err) => { console.error(err); }); \ No newline at end of file diff --git a/things/calculator/http/client-tests/simple-http-client.js b/things/calculator/http/client-tests/simple-http-client.js deleted file mode 100644 index eaae894..0000000 --- a/things/calculator/http/client-tests/simple-http-client.js +++ /dev/null @@ -1,182 +0,0 @@ -/** - * @file The `simple-http-client.js` file acts a client for the simple-calculator.js. - * This client is mostly used for testing the overall basic functionality of the http thing. - */ - -const EventSource = require('eventsource') - -const url = "http://localhost:3000/http-express-calculator-simple", - resultEndPoint = "/properties/result", - resultEndPointObserve = `${resultEndPoint}/observe`, - lastChangeEndPoint = "/properties/lastChange", - lastChangeEndPointObserve = `${lastChangeEndPoint}/observe`, - additionEndPoint = "/actions/add", - subtractionEndPoint = "/actions/subtract", - updateEndPoint = "/events/update" - - -/** - * Return the Full TD - * @returns TD - JSON object - */ -async function getFullTD() { - const res = await fetch(url) - - return res.json() -} - -/** - * Fetch current calculator result - * @returns result - Number - */ -async function getCurrentResult() { - const res = await fetch(url + resultEndPoint) - - return res.json() -} - -/** - * Create an EventSource for the result observe endpoint. - * Uncomment to test the SSE functionality. - */ -function listenToResult() { - const resultEventSource = new EventSource(url + resultEndPointObserve); - - resultEventSource.onmessage = (e) => { - console.log('Result SSE:', JSON.parse(e.data)); - }; - - resultEventSource.onerror = (error) => { - console.error('Error with Result SSE:', error); - }; - - //Closing the event source after 6 seconds - setTimeout(() => { - resultEventSource.close() - console.log("- Closing Result Property SSE"); - }, 6000) -} - -/** - * Fetches when the latest change was made - * @returns lastChange - String - */ -async function getLatestChange() { - const res = await fetch(url + lastChangeEndPoint) - - return res.json() -} - -/** - * Create an EventSource for the result observe endpoint. - * Uncomment to test the SSE functionality. - */ -function listenToLastChange() { - const lastChangeEventSource = new EventSource(url + lastChangeEndPointObserve); - - lastChangeEventSource.onmessage = (e) => { - console.log('lastChange SSE:', JSON.parse(e.data)); - }; - - lastChangeEventSource.onerror = (error) => { - console.error('Error with lastChange SSE:', error); - }; - - //Closing the event source after 6 seconds - setTimeout(() => { - lastChangeEventSource.close() - console.log("- Closing lastChange Property SSE"); - }, 6000) -} - -/** - * Adds a number to the current result - * @param { Number } number - the number to be added - * @returns result - the new result after the addition - */ -async function addNumber(number) { - const res = await fetch(url + additionEndPoint, { - method: "POST", - headers: { - "Content-Type": "application/json" - }, - body: number, - }); - - return res.json() -} - -/** - * Subtracts a number to the current result - * @param { Number } number - the number to be subtracted - * @returns result - the new result after the subtraction - */ -async function subtractNumber(number) { - const res = await fetch(url + subtractionEndPoint, { - method: "POST", - headers: { - "Content-Type": "application/json" - }, - body: number, - }); - - return res.json() -} - - -/** - * Create an EventSource for the update endpoint. - */ -function listenToUpdateEvent() { - - // Listening to the update event - const updateEventSource = new EventSource(url + updateEndPoint); - - updateEventSource.onmessage = (e) => { - console.log('Update Event SSE:', JSON.parse(e.data)); - }; - - updateEventSource.onerror = (error) => { - console.error('Error with Update SSE:', error); - }; - - //Closing the event source after 6 seconds - setTimeout(() => { - updateEventSource.close() - console.log("- Closing Update Event SSE"); - }, 6000) -} - - -/** - * Runs all the previous functions to test the full functionality of the calculator - */ -async function runCalculatorInteractions() { - try { - console.log("-------- Basic functionality --------\n"); - console.log("Full thing: \n", await getFullTD()) - console.log("Current result: ", await getCurrentResult()) - console.log("Last Change: ", await getLatestChange()); - console.log("Result of the addition is: ", await addNumber(5)) - console.log("Result of the subtraction is: ", await subtractNumber(3)) - console.log("Current result: ", await getCurrentResult()) - console.log("Last Change: ", await getLatestChange()) - - /** - * Start listening to the update event, result property and lastChange property. - */ - console.log("\n-------- Start listening to properties and events --------\n"); - listenToUpdateEvent() - listenToLastChange() - listenToResult() - - setTimeout(async () => { - await addNumber(1) - }, 2000) - - } catch (err) { - console.log(err); - } -} - -runCalculatorInteractions() diff --git a/things/calculator/http/express/.mocharc.json b/things/calculator/http/express/.mocharc.json index a665287..00d83c4 100644 --- a/things/calculator/http/express/.mocharc.json +++ b/things/calculator/http/express/.mocharc.json @@ -2,4 +2,4 @@ "exit": true, "spec": "./test/**.test.js", "require": ["./test/fixtures.js"] -} \ No newline at end of file +} diff --git a/things/calculator/http/client-tests/README.md b/things/calculator/http/express/client-tests/README.md similarity index 100% rename from things/calculator/http/client-tests/README.md rename to things/calculator/http/express/client-tests/README.md diff --git a/things/calculator/http/express/client-tests/content-negotiation-http-client.js b/things/calculator/http/express/client-tests/content-negotiation-http-client.js new file mode 100644 index 0000000..a18a19e --- /dev/null +++ b/things/calculator/http/express/client-tests/content-negotiation-http-client.js @@ -0,0 +1,324 @@ +/******************************************************************************** + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the W3C Software Notice and + * Document License (2015-05-13) which is available at + * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document. + * + * SPDX-License-Identifier: EPL-2.0 OR W3C-20150513 + ********************************************************************************/ + +const cbor = require("cbor"); +const EventSource = require("eventsource"); + +const url = "http://localhost:3000/http-express-calculator-content-negotiation"; +const resultEndPoint = "/properties/result"; +const resultEndPointObserve = `${resultEndPoint}/observe`; +const lastChangeEndPoint = "/properties/lastChange"; +const lastChangeEndPointObserve = `${lastChangeEndPoint}/observe`; +const additionEndPoint = "/actions/add"; +const subtractionEndPoint = "/actions/subtract"; +const updateEndPoint = "/events/update"; + +/** + * Return the Full TD + * @param { String } acceptType - Which content type is accepted by the client + * @returns Thing description as either a String, JSON or CBOR + */ +async function getFullTD(acceptType) { + const res = await fetch(url, { + method: "GET", + headers: { + Accept: acceptType, + }, + }); + + const contentType = res.headers.get("content-type"); + + if (contentType.includes("application/json")) { + return res.json(); + } else { + const buffer = await res.arrayBuffer(); + const decodedData = cbor.decode(buffer); + return decodedData; + } +} + +/** + * Fetch current calculator result + * @param { String } acceptType - Which content type is accepted by the client + * @returns result - a string or number depending on the request + */ +async function getCurrentResult(acceptType) { + const res = await fetch(url + resultEndPoint, { + method: "GET", + headers: { + Accept: acceptType, + }, + }); + + const contentType = res.headers.get("content-type"); + + if (contentType.includes("application/json")) { + return res.json(); + } else { + const buffer = await res.arrayBuffer(); + const decodedData = cbor.decode(buffer); + return decodedData; + } +} + +/** + * Create an EventSource for the result observe property. + */ +function listenToResultProperty(acceptType) { + const resultEventSource = new EventSource(url + resultEndPointObserve, { + headers: { + Accept: acceptType, + }, + }); + + resultEventSource.onmessage = (e) => { + const body = e.data; + + if (acceptType === "application/json") { + console.log("Result SSE:", JSON.parse(body)); + } else { + const buffer = Buffer.from(body); + const decodedData = cbor.decode(buffer); + console.log("Result SSE:", decodedData); + } + }; + + resultEventSource.onerror = (error) => { + console.error("Error with Result property SSE:", error); + }; + + // Closing the event source after 6 seconds + setTimeout(() => { + resultEventSource.close(); + console.log("- Closing Result Property SSE"); + }, 6000); +} + +/** + * Fetches the last change made + * @param { String } acceptType - Which content type is accepted by the client + * @returns lastChange - A string of the date when it was last changed + */ +async function getLatestChange(acceptType) { + const res = await fetch(url + lastChangeEndPoint, { + method: "GET", + headers: { + Accept: acceptType, + }, + }); + + const contentType = res.headers.get("content-type"); + + if (contentType.includes("application/json")) { + return res.json(); + } else { + const buffer = await res.arrayBuffer(); + const decodedData = cbor.decode(buffer); + return decodedData; + } +} + +/** + * Create an EventSource for the last change observe property. + */ +function listenToLastChangeProperty(acceptType) { + const lastChangeEventSource = new EventSource( + url + lastChangeEndPointObserve, + { + headers: { + Accept: acceptType, + }, + } + ); + + lastChangeEventSource.onmessage = (e) => { + const body = e.data; + + if (acceptType === "application/json") { + console.log("lastChange SSE:", JSON.parse(body)); + } else { + const buffer = Buffer.from(body); + const decodedData = cbor.decode(buffer); + console.log("lastChange SSE:", decodedData); + } + }; + + lastChangeEventSource.onerror = (error) => { + console.error("Error with lastChange property SSE:", error); + }; + + // Closing the event source after 6 seconds + setTimeout(() => { + lastChangeEventSource.close(); + console.log("- Closing lastChange Property SSE"); + }, 6000); +} + +/** + * Adds a number to the current result + * @param { Number } number - the number to be added + * @param { String } contentType - Which content type is accepted by the server + * @param { String } acceptType - Which content type is accepted by the client + * @returns addedNumber - the number to be added to the calculator + */ +async function addNumber(number, contentType, acceptType) { + const inputNumber = + contentType === "application/json" + ? JSON.stringify(number) + : cbor.encode(number); + + const res = await fetch(url + additionEndPoint, { + method: "POST", + headers: { + "Content-Type": contentType, + Accept: acceptType, + }, + body: inputNumber, + }); + + if (res.ok) { + const contentType = res.headers.get("content-type"); + + if (contentType.includes("application/json")) { + return res.json(); + } else { + const buffer = await res.arrayBuffer(); + const decodedData = cbor.decode(buffer); + return decodedData; + } + } else { + throw new Error(await res.text()); + } +} + +/** + * Subtracts a number to the current result + * @param { Number } number - the number to be subtracted + * @param { String } contentType - Which content type is accepted by the server + * @param { String } acceptType - Which content type is accepted by the client + * @returns subtractedNumber - the number to be added to the calculator + */ +async function subtractNumber(number, contentType, acceptType) { + const inputNumber = + contentType === "application/json" + ? JSON.stringify(number) + : cbor.encode(number); + + const res = await fetch(url + subtractionEndPoint, { + method: "POST", + headers: { + "Content-Type": contentType, + Accept: acceptType, + }, + body: inputNumber, + }); + + if (res.ok) { + const contentType = res.headers.get("content-type"); + + if (contentType.includes("application/json")) { + return res.json(); + } else { + const buffer = await res.arrayBuffer(); + const decodedData = cbor.decode(buffer); + return decodedData; + } + } else { + throw new Error(await res.text()); + } +} + +/** + * Create an EventSource for the update endpoint. + */ +function listenToUpdateEvent(acceptType) { + const updateEventSource = new EventSource(url + updateEndPoint, { + headers: { + Accept: acceptType, + }, + }); + + updateEventSource.onmessage = (e) => { + const body = e.data; + + if (acceptType === "application/json") { + console.log("Update SSE:", JSON.parse(body)); + } else { + const buffer = Buffer.from(body); + const decodedData = cbor.decode(buffer); + console.log("Update SSE:", decodedData); + } + }; + + updateEventSource.onerror = (error) => { + console.error("Error with Update event SSE:", error); + }; + + // Closing the event source after 6 seconds + setTimeout(() => { + updateEventSource.close(); + console.log("- Closing Update Event SSE"); + }, 6000); +} + +/** + * Runs all the previous functions to test the full functionality of the calculator + */ +async function runCalculatorInteractions() { + try { + console.log("-------- Basic functionality --------\n"); + console.log("Full thing: \n", await getFullTD("application/cbor")); + console.log( + "Current number: ", + await getCurrentResult("application/json") + ); + console.log("Last Change: ", await getLatestChange("application/cbor")); + console.log( + "Result of the addition is: ", + await addNumber(5, "application/cbor", "application/json") + ); + console.log( + "Result of the subtraction is: ", + await subtractNumber(3, "application/json", "application/cbor") + ); + console.log( + "Current number: ", + await getCurrentResult("application/cbor") + ); + console.log("Last Change: ", await getLatestChange("application/json")); + + /** + * Start listening to the update event, result property and lastChange property. + */ + console.log( + "\n-------- Start listening to properties and events --------\n" + ); + listenToResultProperty("application/cbor"); + listenToUpdateEvent("application/json"); + listenToLastChangeProperty("application/cbor"); + + setTimeout(async () => { + console.log( + "Adding 1 to test observation: ", + await addNumber(1, "application/cbor", "application/json"), + "\n" + ); + }, 2000); + } catch (err) { + console.log(err); + } +} + +runCalculatorInteractions(); diff --git a/things/calculator/http/express/client-tests/node-wot-content-negotiation-http-client.js b/things/calculator/http/express/client-tests/node-wot-content-negotiation-http-client.js new file mode 100644 index 0000000..4754ea1 --- /dev/null +++ b/things/calculator/http/express/client-tests/node-wot-content-negotiation-http-client.js @@ -0,0 +1,65 @@ +/******************************************************************************** + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the W3C Software Notice and + * Document License (2015-05-13) which is available at + * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document. + * + * SPDX-License-Identifier: EPL-2.0 OR W3C-20150513 + ********************************************************************************/ + +const { Servient } = require("@node-wot/core"); +const { HttpClientFactory } = require("@node-wot/binding-http"); + +const servient = new Servient(); +servient.addClientFactory(new HttpClientFactory(null)); + +servient + .start() + .then(async (WoT) => { + const td = await WoT.requestThingDescription( + "http://localhost:3001/http-express-calculator-content-negotiation" + ); + + const thing = await WoT.consume(td); + console.log(td); + + const result = await thing.readProperty("result", { formIndex: 0 }); + console.log("Result property: ", await result.value()); + + const lastChange = await thing.readProperty("lastChange", { + formIndex: 0, + }); + console.log("lastChange property: ", await lastChange.value()); + + // Actions endpoints + // TODO: Add this when it gets fixed in node-wot + // let addition = await thing.invokeAction("add", 3, {formIndex: 0}) + // console.log(await addition.value()); + + // let subtraction = await thing.invokeAction("subtract", 5, {formIndex: 0}) + // console.log(await subtraction.value()); + + // let addition2 = await thing.invokeAction("add", 3, {formIndex: 1}) + // console.log(await addition2.value()); + + // let subtraction2 = await thing.invokeAction("subtract", 5, {formIndex: 3}) + // console.log(await subtraction2.value()); + + // Update event property + // thing.subscribeEvent("update", async (data) => { + // console.log("Update event:", await data.value()); + // }) + + // //Properties observation + // thing.observeProperty("result", async (data) => { console.log("Result observe:", await data.value()); }); + // thing.observeProperty("lastChange", async (data) => { console.log("lastChange observe:", await data.value()); }); + }) + .catch((err) => { + console.error(err); + }); diff --git a/things/calculator/http/express/client-tests/node-wot-simple-http-client.js b/things/calculator/http/express/client-tests/node-wot-simple-http-client.js new file mode 100644 index 0000000..9d79700 --- /dev/null +++ b/things/calculator/http/express/client-tests/node-wot-simple-http-client.js @@ -0,0 +1,59 @@ +/******************************************************************************** + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the W3C Software Notice and + * Document License (2015-05-13) which is available at + * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document. + * + * SPDX-License-Identifier: EPL-2.0 OR W3C-20150513 + ********************************************************************************/ + +const { Servient } = require("@node-wot/core"); +const { HttpClientFactory } = require("@node-wot/binding-http"); + +const servient = new Servient(); +servient.addClientFactory(new HttpClientFactory(null)); + +servient + .start() + .then(async (WoT) => { + const td = await WoT.requestThingDescription( + "http://localhost:3000/http-express-calculator-simple" + ); + + const thing = await WoT.consume(td); + console.log(td); + + // Property endpoints + const result = await (await thing.readProperty("result")).value(); + console.log("Read result:", result); + + const lastChange = await ( + await thing.readProperty("lastChange") + ).value(); + console.log("Read lastChange:", lastChange); + + // Update event observation + thing.subscribeEvent("update", async (data) => { + console.log("Update event:", (await data.value()).data); + }); + + // Action endpoints + const additionResult = await thing.invokeAction("add", 3); + console.log("Addition result: ", await additionResult.value()); + + const subtractionResult = await thing.invokeAction("subtract", 3); + console.log("Subtraction result: ", await subtractionResult.value()); + + // TODO: Property Observation failing do to returning wrong type (SSE returns object rather than a number) + // thing.observeProperty("result", async (data) => { console.log("Result observe:", await data.value()); }); + // thing.observeProperty("lastChange", async (data) => { console.log("lastChange observe:", await data.value()); }); + }) + .catch((err) => { + console.error(err); + }); diff --git a/things/calculator/http/express/client-tests/simple-http-client.js b/things/calculator/http/express/client-tests/simple-http-client.js new file mode 100644 index 0000000..a0617c4 --- /dev/null +++ b/things/calculator/http/express/client-tests/simple-http-client.js @@ -0,0 +1,191 @@ +/******************************************************************************** + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the W3C Software Notice and + * Document License (2015-05-13) which is available at + * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document. + * + * SPDX-License-Identifier: EPL-2.0 OR W3C-20150513 + ********************************************************************************/ + +const EventSource = require("eventsource"); + +const url = "http://localhost:3000/http-express-calculator-simple"; +const resultEndPoint = "/properties/result"; +const resultEndPointObserve = `${resultEndPoint}/observe`; +const lastChangeEndPoint = "/properties/lastChange"; +const lastChangeEndPointObserve = `${lastChangeEndPoint}/observe`; +const additionEndPoint = "/actions/add"; +const subtractionEndPoint = "/actions/subtract"; +const updateEndPoint = "/events/update"; + +/** + * Return the Full TD + * @returns TD - JSON object + */ +async function getFullTD() { + const res = await fetch(url); + + return res.json(); +} + +/** + * Fetch current calculator result + * @returns result - Number + */ +async function getCurrentResult() { + const res = await fetch(url + resultEndPoint); + + return res.json(); +} + +/** + * Create an EventSource for the result observe endpoint. + * Uncomment to test the SSE functionality. + */ +function listenToResult() { + const resultEventSource = new EventSource(url + resultEndPointObserve); + + resultEventSource.onmessage = (e) => { + console.log("Result SSE:", JSON.parse(e.data)); + }; + + resultEventSource.onerror = (error) => { + console.error("Error with Result SSE:", error); + }; + + // Closing the event source after 6 seconds + setTimeout(() => { + resultEventSource.close(); + console.log("- Closing Result Property SSE"); + }, 6000); +} + +/** + * Fetches when the latest change was made + * @returns lastChange - String + */ +async function getLatestChange() { + const res = await fetch(url + lastChangeEndPoint); + + return res.json(); +} + +/** + * Create an EventSource for the result observe endpoint. + * Uncomment to test the SSE functionality. + */ +function listenToLastChange() { + const lastChangeEventSource = new EventSource( + url + lastChangeEndPointObserve + ); + + lastChangeEventSource.onmessage = (e) => { + console.log("lastChange SSE:", JSON.parse(e.data)); + }; + + lastChangeEventSource.onerror = (error) => { + console.error("Error with lastChange SSE:", error); + }; + + // Closing the event source after 6 seconds + setTimeout(() => { + lastChangeEventSource.close(); + console.log("- Closing lastChange Property SSE"); + }, 6000); +} + +/** + * Adds a number to the current result + * @param { Number } number - the number to be added + * @returns result - the new result after the addition + */ +async function addNumber(number) { + const res = await fetch(url + additionEndPoint, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: number, + }); + + return res.json(); +} + +/** + * Subtracts a number to the current result + * @param { Number } number - the number to be subtracted + * @returns result - the new result after the subtraction + */ +async function subtractNumber(number) { + const res = await fetch(url + subtractionEndPoint, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: number, + }); + + return res.json(); +} + +/** + * Create an EventSource for the update endpoint. + */ +function listenToUpdateEvent() { + // Listening to the update event + const updateEventSource = new EventSource(url + updateEndPoint); + + updateEventSource.onmessage = (e) => { + console.log("Update Event SSE:", JSON.parse(e.data)); + }; + + updateEventSource.onerror = (error) => { + console.error("Error with Update SSE:", error); + }; + + // Closing the event source after 6 seconds + setTimeout(() => { + updateEventSource.close(); + console.log("- Closing Update Event SSE"); + }, 6000); +} + +/** + * Runs all the previous functions to test the full functionality of the calculator + */ +async function runCalculatorInteractions() { + try { + console.log("-------- Basic functionality --------\n"); + console.log("Full thing: \n", await getFullTD()); + console.log("Current result: ", await getCurrentResult()); + console.log("Last Change: ", await getLatestChange()); + console.log("Result of the addition is: ", await addNumber(5)); + console.log("Result of the subtraction is: ", await subtractNumber(3)); + console.log("Current result: ", await getCurrentResult()); + console.log("Last Change: ", await getLatestChange()); + + /** + * Start listening to the update event, result property and lastChange property. + */ + console.log( + "\n-------- Start listening to properties and events --------\n" + ); + listenToUpdateEvent(); + listenToLastChange(); + listenToResult(); + + setTimeout(async () => { + await addNumber(1); + }, 2000); + } catch (err) { + console.log(err); + } +} + +runCalculatorInteractions(); diff --git a/things/calculator/http/express/http-content-negotiation-calculator-thing.td.jsonld b/things/calculator/http/express/http-content-negotiation-calculator-thing.td.jsonld index 6096347..5790c7d 100644 --- a/things/calculator/http/express/http-content-negotiation-calculator-thing.td.jsonld +++ b/things/calculator/http/express/http-content-negotiation-calculator-thing.td.jsonld @@ -72,8 +72,9 @@ "htv:headers": [ { "@type": "htv:RequestHeader", - "htv:fieldValue": "application/cbor", - "htv:fieldName": "Accept" + "htv:fieldValue": "application/json", + "htv:fieldName": "Accept", + "fieldValue": "application/cbor" } ] }, @@ -91,8 +92,9 @@ "htv:headers": [ { "@type": "htv:RequestHeader", - "htv:fieldValue": "application/cbor", - "htv:fieldName": "Accept" + "htv:fieldValue": "application/json", + "htv:fieldName": "Accept", + "fieldValue": "application/cbor" } ], "subprotocol": "sse" @@ -153,8 +155,9 @@ "htv:headers": [ { "@type": "htv:RequestHeader", - "htv:fieldValue": "application/cbor", - "htv:fieldName": "Accept" + "htv:fieldValue": "application/json", + "htv:fieldName": "Accept", + "fieldValue": "application/cbor" } ] }, @@ -172,8 +175,9 @@ "htv:headers": [ { "@type": "htv:RequestHeader", - "htv:fieldValue": "application/cbor", - "htv:fieldName": "Accept" + "htv:fieldValue": "application/json", + "htv:fieldName": "Accept", + "fieldValue": "application/cbor" } ], "subprotocol": "sse" @@ -219,8 +223,9 @@ "htv:headers": [ { "@type": "htv:RequestHeader", - "htv:fieldValue": "application/cbor", - "htv:fieldName": "Accept" + "htv:fieldValue": "application/json", + "htv:fieldName": "Accept", + "fieldValue": "application/cbor" } ] }, @@ -251,8 +256,9 @@ "htv:headers": [ { "@type": "htv:RequestHeader", - "htv:fieldValue": "application/cbor", - "htv:fieldName": "Accept" + "htv:fieldValue": "application/json", + "htv:fieldName": "Accept", + "fieldValue": "application/cbor" } ] } @@ -295,8 +301,9 @@ "htv:headers": [ { "@type": "htv:RequestHeader", - "htv:fieldValue": "application/cbor", - "htv:fieldName": "Accept" + "htv:fieldValue": "application/json", + "htv:fieldName": "Accept", + "fieldValue": "application/cbor" } ] }, @@ -327,8 +334,9 @@ "htv:headers": [ { "@type": "htv:RequestHeader", - "htv:fieldValue": "application/cbor", - "htv:fieldName": "Accept" + "htv:fieldValue": "application/json", + "htv:fieldName": "Accept", + "fieldValue": "application/cbor" } ] } @@ -369,8 +377,9 @@ "htv:headers": [ { "@type": "htv:RequestHeader", - "htv:fieldValue": "application/cbor", - "htv:fieldName": "Accept" + "htv:fieldValue": "application/json", + "htv:fieldName": "Accept", + "fieldValue": "application/cbor" } ], "subprotocol": "sse" diff --git a/things/calculator/http/express/http-content-negotiation-calculator.js b/things/calculator/http/express/http-content-negotiation-calculator.js index 578cb2f..ab8c8a4 100644 --- a/things/calculator/http/express/http-content-negotiation-calculator.js +++ b/things/calculator/http/express/http-content-negotiation-calculator.js @@ -1,402 +1,464 @@ -const express = require('express') -const fs = require('fs') -const path = require('path') -const bodyParser = require('body-parser') -const { parseArgs } = require('node:util') -const { JsonPlaceholderReplacer } = require('json-placeholder-replacer') -const cbor = require('cbor') -require('dotenv').config() - -const app = express() +/******************************************************************************** + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the W3C Software Notice and + * Document License (2015-05-13) which is available at + * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document. + * + * SPDX-License-Identifier: EPL-2.0 OR W3C-20150513 + ********************************************************************************/ +const express = require("express"); +const fs = require("fs"); +const path = require("path"); +const bodyParser = require("body-parser"); +const { parseArgs } = require("node:util"); +const { JsonPlaceholderReplacer } = require("json-placeholder-replacer"); +const cbor = require("cbor"); +require("dotenv").config(); + +const app = express(); app.use(express.json({ strict: false })); -const hostname = process.env.HOSTNAME ?? 'localhost' -let portNumber = process.env.PORT ?? 3001 -const thingName = 'http-express-calculator-content-negotiation' - -const TDEndPoint = `/${thingName}`, - resultEndPoint = `/${thingName}/properties/result`, - resultEndPointObserve = `${resultEndPoint}/observe`, - lastChangeEndPoint = `/${thingName}/properties/lastChange`, - lastChangeEndPointObserve = `${lastChangeEndPoint}/observe`, - additionEndPoint = `/${thingName}/actions/add`, - subtractionEndPoint = `/${thingName}/actions/subtract`, - updateEndPoint = `/${thingName}/events/update` - -const existingEndpoints = [TDEndPoint, resultEndPoint, resultEndPointObserve, lastChangeEndPoint, lastChangeEndPointObserve, additionEndPoint, subtractionEndPoint, updateEndPoint] - -let result = 0 +const hostname = process.env.HOSTNAME ?? "localhost"; +let portNumber = process.env.PORT ?? 3001; +const thingName = "http-express-calculator-content-negotiation"; + +const TDEndPoint = `/${thingName}`; +const resultEndPoint = `/${thingName}/properties/result`; +const resultEndPointObserve = `${resultEndPoint}/observe`; +const lastChangeEndPoint = `/${thingName}/properties/lastChange`; +const lastChangeEndPointObserve = `${lastChangeEndPoint}/observe`; +const additionEndPoint = `/${thingName}/actions/add`; +const subtractionEndPoint = `/${thingName}/actions/subtract`; +const updateEndPoint = `/${thingName}/events/update`; + +const existingEndpoints = [ + TDEndPoint, + resultEndPoint, + resultEndPointObserve, + lastChangeEndPoint, + lastChangeEndPointObserve, + additionEndPoint, + subtractionEndPoint, + updateEndPoint, +]; + +let result = 0; let lastChange = new Date().toISOString(); -const { values: { port } } = parseArgs({ - options: { - port: { - type: 'string', - short: 'p' - } - } -}) +const { + values: { port }, +} = parseArgs({ + options: { + port: { + type: "string", + short: "p", + }, + }, +}); if (port && !isNaN(parseInt(port))) { - portNumber = parseInt(port) + portNumber = parseInt(port); } -const tmPath = process.env.TM_PATH +const tmPath = process.env.TM_PATH; -if (process.platform === 'win32') { - tmPath.split(path.sep).join(path.win32.sep) +if (process.platform === "win32") { + tmPath.split(path.sep).join(path.win32.sep); } -const thingModel = JSON.parse(fs.readFileSync(path.join(__dirname, tmPath))) +const thingModel = JSON.parse(fs.readFileSync(path.join(__dirname, tmPath))); -const placeholderReplacer = new JsonPlaceholderReplacer() +const placeholderReplacer = new JsonPlaceholderReplacer(); placeholderReplacer.addVariableMap({ - PROTOCOL: 'http', - THING_NAME: thingName, - HOSTNAME: hostname, - PORT_NUMBER: portNumber, - RESULT_OBSERVABLE: true, - LAST_CHANGE_OBSERVABLE: true -}) - -const defaultForm = -{ - 'href': '', - 'contentType': 'application/json', - 'response': { - 'contentType': 'application/json' - }, - 'op': '', - 'htv:methodName': '', - 'htv:headers': [ - { - '@type': 'htv:RequestHeader', - 'htv:fieldValue': 'application/json', - 'htv:fieldName': 'Accept' - } - ] -} - -const thingDescription = placeholderReplacer.replace(thingModel) -thingDescription['@type'] = 'Thing' - -const supportedContentTypes = ['application/json', 'application/cbor']; - -//Adding headers to the Properties -for (const key in thingDescription['properties']) { - - thingDescription['properties'][key]['forms'] = [] - - const newFormRead = JSON.parse(JSON.stringify(defaultForm)) - newFormRead['href'] = `properties/${key}` - newFormRead['htv:methodName'] = 'GET' - newFormRead['op'] = 'readproperty' - thingDescription['properties'][key]['forms'].push(newFormRead) - - const newFormObs = JSON.parse(JSON.stringify(newFormRead)) - newFormObs['href'] = `properties/${key}/observe` - newFormObs['op'] = ['observeproperty', 'unobserveproperty'] - newFormObs['subprotocol'] = 'sse' - thingDescription['properties'][key]['forms'].push(newFormObs) - - const originalForm = thingDescription['properties'][key]['forms'][0] - - supportedContentTypes.forEach(type => { - if (!thingDescription['properties'][key]['forms'][0]['contentType'].includes(type)) { - const newFormRead = JSON.parse(JSON.stringify(originalForm)) - newFormRead['contentType'] = type - newFormRead['response'].contentType = type - newFormRead['htv:headers'][0]['htv:fieldValue'] = type - thingDescription['properties'][key]['forms'].push(newFormRead) + PROTOCOL: "http", + THING_NAME: thingName, + HOSTNAME: hostname, + PORT_NUMBER: portNumber, + RESULT_OBSERVABLE: true, + LAST_CHANGE_OBSERVABLE: true, +}); - const newFormObs = JSON.parse(JSON.stringify(newFormRead)) - newFormObs['href'] = `properties/${key}/observe` - newFormObs['op'] = ['observeproperty', 'unobserveproperty'] - newFormObs['subprotocol'] = 'sse' - thingDescription['properties'][key]['forms'].push(newFormObs) +const defaultForm = { + href: "", + contentType: "application/json", + response: { + contentType: "application/json", + }, + op: "", + "htv:methodName": "", + "htv:headers": [ + { + "@type": "htv:RequestHeader", + "htv:fieldValue": "application/json", + "htv:fieldName": "Accept", + }, + ], +}; + +const thingDescription = placeholderReplacer.replace(thingModel); +thingDescription["@type"] = "Thing"; + +const supportedContentTypes = ["application/json", "application/cbor"]; + +// Adding headers to the Properties +for (const key in thingDescription.properties) { + if (!Object.hasOwn(thingDescription.properties, key)) { + continue; } - }) -} - -//Adding headers to the Actions -for (const key in thingDescription['actions']) { - thingDescription['actions'][key]['forms'] = [] - - const newForm = JSON.parse(JSON.stringify(defaultForm)) - newForm['href'] = `actions/${key}` - newForm['htv:methodName'] = 'POST' - newForm['op'] = 'invokeaction' - - thingDescription['actions'][key]['forms'].push(newForm) - - const originalForm = thingDescription['actions'][key]['forms'][0] + thingDescription.properties[key].forms = []; + + const newFormRead = JSON.parse(JSON.stringify(defaultForm)); + newFormRead.href = `properties/${key}`; + newFormRead["htv:methodName"] = "GET"; + newFormRead.op = "readproperty"; + thingDescription.properties[key].forms.push(newFormRead); + + const newFormObs = JSON.parse(JSON.stringify(newFormRead)); + newFormObs.href = `properties/${key}/observe`; + newFormObs.op = ["observeproperty", "unobserveproperty"]; + newFormObs.subprotocol = "sse"; + thingDescription.properties[key].forms.push(newFormObs); + + const originalForm = thingDescription.properties[key].forms[0]; + + supportedContentTypes.forEach((type) => { + if ( + !thingDescription.properties[key].forms[0].contentType.includes( + type + ) + ) { + const newFormRead = JSON.parse(JSON.stringify(originalForm)); + newFormRead.contentType = type; + newFormRead.response.contentType = type; + newFormRead["htv:headers"][0].fieldValue = type; + thingDescription.properties[key].forms.push(newFormRead); + + const newFormObs = JSON.parse(JSON.stringify(newFormRead)); + newFormObs.href = `properties/${key}/observe`; + newFormObs.op = ["observeproperty", "unobserveproperty"]; + newFormObs.subprotocol = "sse"; + thingDescription.properties[key].forms.push(newFormObs); + } + }); +} - supportedContentTypes.forEach(type => { - if (!thingDescription['actions'][key]['forms'][0]['contentType'].includes(type)) { - const newForm = JSON.parse(JSON.stringify(originalForm)) - newForm['contentType'] = type - thingDescription['actions'][key]['forms'].push(newForm) +// Adding headers to the Actions +for (const key in thingDescription.actions) { + if (!Object.hasOwn(thingDescription.actions, key)) { + continue; + } - supportedContentTypes.forEach(type => { - if (!thingDescription['actions'][key]['forms'][0]['response'].contentType.includes(type)) { - const newFormAccept = JSON.parse(JSON.stringify(newForm)) - newFormAccept['response'].contentType = type; - newFormAccept['htv:headers'][0]['htv:fieldValue'] = type - thingDescription['actions'][key]['forms'].push(newFormAccept) - } - }) - } else { - supportedContentTypes.forEach(type => { - if (!originalForm['response'].contentType.includes(type)) { - const newForm = JSON.parse(JSON.stringify(originalForm)); - newForm['response'].contentType = type; - newForm['htv:headers'][0]['htv:fieldValue'] = type; - thingDescription['actions'][key]['forms'].push(newForm); + thingDescription.actions[key].forms = []; + + const newForm = JSON.parse(JSON.stringify(defaultForm)); + newForm.href = `actions/${key}`; + newForm["htv:methodName"] = "POST"; + newForm.op = "invokeaction"; + + thingDescription.actions[key].forms.push(newForm); + + const originalForm = thingDescription.actions[key].forms[0]; + + supportedContentTypes.forEach((type) => { + if ( + !thingDescription.actions[key].forms[0].contentType.includes(type) + ) { + const newForm = JSON.parse(JSON.stringify(originalForm)); + newForm.contentType = type; + thingDescription.actions[key].forms.push(newForm); + + supportedContentTypes.forEach((type) => { + if ( + !thingDescription.actions[ + key + ].forms[0].response.contentType.includes(type) + ) { + const newFormAccept = JSON.parse(JSON.stringify(newForm)); + newFormAccept.response.contentType = type; + newFormAccept["htv:headers"][0].fieldValue = type; + thingDescription.actions[key].forms.push(newFormAccept); + } + }); + } else { + supportedContentTypes.forEach((type) => { + if (!originalForm.response.contentType.includes(type)) { + const newForm = JSON.parse(JSON.stringify(originalForm)); + newForm.response.contentType = type; + newForm["htv:headers"][0].fieldValue = type; + thingDescription.actions[key].forms.push(newForm); + } + }); } - }) - } - }) + }); } -//Adding headers to the Events +// Adding headers to the Events -for (const key in thingDescription['events']) { +for (const key in thingDescription.events) { + if (!Object.hasOwn(thingDescription.events, key)) { + continue; + } - thingDescription['events'][key]['data']['type'] = "object" + thingDescription.events[key].data.type = "object"; - thingDescription['events'][key]['forms'] = [] + thingDescription.events[key].forms = []; - const newForm = JSON.parse(JSON.stringify(defaultForm)) - newForm['href'] = `events/${key}` - newForm['htv:methodName'] = 'GET' - newForm['op'] = 'subscribeevent' - newForm['subprotocol'] = 'sse' + const newForm = JSON.parse(JSON.stringify(defaultForm)); + newForm.href = `events/${key}`; + newForm["htv:methodName"] = "GET"; + newForm.op = "subscribeevent"; + newForm.subprotocol = "sse"; - thingDescription['events'][key]['forms'].push(newForm) + thingDescription.events[key].forms.push(newForm); - const originalForm = thingDescription['events'][key]['forms'][0] + const originalForm = thingDescription.events[key].forms[0]; - supportedContentTypes.forEach(type => { - if (!thingDescription['events'][key]['forms'][0]['contentType'].includes(type)) { - const newForm = JSON.parse(JSON.stringify(originalForm)) - newForm['contentType'] = type - newForm['response'].contentType = type; - newForm['htv:headers'][0]['htv:fieldValue'] = type - thingDescription['events'][key]['forms'].push(newForm) - } - }) + supportedContentTypes.forEach((type) => { + if (!thingDescription.events[key].forms[0].contentType.includes(type)) { + const newForm = JSON.parse(JSON.stringify(originalForm)); + newForm.contentType = type; + newForm.response.contentType = type; + newForm["htv:headers"][0].fieldValue = type; + thingDescription.events[key].forms.push(newForm); + } + }); } -//Creating the TD for testing purposes +// Creating the TD for testing purposes try { - fs.writeFileSync('http-content-negotiation-calculator-thing.td.jsonld', JSON.stringify(thingDescription, null, 2)) + fs.writeFileSync( + "http-content-negotiation-calculator-thing.td.jsonld", + JSON.stringify(thingDescription, null, 2) + ); } catch (err) { - console.log(err); + console.log(err); } - /******************************************/ -/************** Middleware ****************/ +/** ************ Middleware ****************/ /******************************************/ -//Middleware to ensure the right endpoints are being called +// Middleware to ensure the right endpoints are being called app.use((req, res, next) => { - const endpoint = req.url + const endpoint = req.url; - if (!existingEndpoints.includes(endpoint)) { - res.status(404).json("Not Found") - } - else { - next() - } -}) + if (!existingEndpoints.includes(endpoint)) { + res.status(404).json("Not Found"); + } else { + next(); + } +}); -//Middleware to ensure the right method is been used for each endpoint +// Middleware to ensure the right method is been used for each endpoint app.use((req, res, next) => { - const method = req.method - const endpoint = req.url - - if (endpoint === TDEndPoint || endpoint === resultEndPoint || endpoint === resultEndPointObserve || endpoint === lastChangeEndPoint || endpoint === lastChangeEndPointObserve || endpoint === updateEndPoint) { - if (method === 'GET') { - next() - } else { - res.status(405).json('Method Not Allowed'); + const method = req.method; + const endpoint = req.url; + + if ( + endpoint === TDEndPoint || + endpoint === resultEndPoint || + endpoint === resultEndPointObserve || + endpoint === lastChangeEndPoint || + endpoint === lastChangeEndPointObserve || + endpoint === updateEndPoint + ) { + if (method === "GET") { + next(); + } else { + res.status(405).json("Method Not Allowed"); + } } - } - if (endpoint === additionEndPoint || endpoint === subtractionEndPoint) { - if (method === 'POST') { - next() - } else { - res.status(405).json('Method Not Allowed'); + if (endpoint === additionEndPoint || endpoint === subtractionEndPoint) { + if (method === "POST") { + next(); + } else { + res.status(405).json("Method Not Allowed"); + } } - } -}) +}); // Middleware to check if supported 'Accept' values have been sent app.use((req, res, next) => { - const acceptHeader = req.get('Accept') - - if (acceptHeader === undefined) { - res.status(406).json('Not Acceptable: Supported formats are application/json, and application/cbor'); - } - else if (acceptHeader.includes('*/*') || acceptHeader.includes('application/*') || acceptHeader.includes('application/json') || acceptHeader.includes('application/cbor') || acceptHeader.includes('application/td+json') || acceptHeader.includes('text/event-stream')) { - next() - } else { - res.status(406).json('Not Acceptable: Supported formats are application/json, and application/cbor'); - } + const acceptHeader = req.get("Accept"); + + if (acceptHeader === undefined) { + res.status(406).json( + "Not Acceptable: Supported formats are application/json, and application/cbor" + ); + } else if ( + acceptHeader.includes("*/*") || + acceptHeader.includes("application/*") || + acceptHeader.includes("application/json") || + acceptHeader.includes("application/cbor") || + acceptHeader.includes("application/td+json") || + acceptHeader.includes("text/event-stream") + ) { + next(); + } else { + res.status(406).json( + "Not Acceptable: Supported formats are application/json, and application/cbor" + ); + } }); - // Middleware to accept only the content-types: json and cbor app.use((req, res, next) => { - const contentType = req.get('Content-Type') - const method = req.method - - if (contentType === undefined) { - if (method === 'POST') { - res.status(415).json('Unsupported Media Type'); - } - else { - next() + const contentType = req.get("Content-Type"); + const method = req.method; + + if (contentType === undefined) { + if (method === "POST") { + res.status(415).json("Unsupported Media Type"); + } else { + next(); + } + } else { + next(); } - } - else { - next() - } }); // Use body-parser middleware to parse the request body app.use(bodyParser.text()); -app.use(bodyParser.json({ type: 'application/json' })); -app.use(bodyParser.raw({ type: 'application/cbor' })); - +app.use(bodyParser.json({ type: "application/json" })); +app.use(bodyParser.raw({ type: "application/cbor" })); /*****************************************************/ -/************** Properties Endpoints *****************/ +/** ************ Properties Endpoints *****************/ /*****************************************************/ // Get full thing app.get(TDEndPoint, (req, res) => { - const jsonData = JSON.stringify(thingDescription) - res.setHeader('Content-Type', 'application/json') - res.send(jsonData) -}) + const jsonData = JSON.stringify(thingDescription); + res.setHeader("Content-Type", "application/json"); + res.send(jsonData); +}); -//Get current result +// Get current result app.get(resultEndPoint, (req, res) => { - const acceptHeader = req.get('Accept') - - //JSON media type is utilized as the default for the wild card - if (acceptHeader.includes('application/json') || acceptHeader.includes('*/*') || acceptHeader.includes('application/*')) { - const jsonData = JSON.stringify(result) - res.setHeader('Content-Type', 'application/json') - res.send(jsonData) - } - else { - const cborData = cbor.encode(result) - res.setHeader('Content-Type', 'application/cbor') - res.send(cborData) - } -}) - -//Observe the current result property -app.get(resultEndPointObserve, (req, res) => { - const acceptHeader = req.get('Accept') - res.setHeader('Access-Control-Allow-Origin', '*') - res.setHeader('Cache-Control', 'no-cache') - res.setHeader('Connection', 'keep-alive') - res.setHeader('Content-Type', 'text/event-stream') - - console.log("Client is listening to result property") - let oldResult = result - - const changeInterval = setInterval(() => { - - if (oldResult !== result) { - let message - - if (acceptHeader.includes('application/json') || acceptHeader.includes('*/*') || acceptHeader.includes('application/*')) { - message = `data: ${JSON.stringify(result)}\n\n` - } - else { - message = `data: ${cbor.encode(result)}\n\n` - } - - res.statusCode = 200 - res.write(message) - oldResult = result + const acceptHeader = req.get("Accept"); + + // JSON media type is utilized as the default for the wild card + if ( + acceptHeader.includes("application/json") || + acceptHeader.includes("*/*") || + acceptHeader.includes("application/*") + ) { + const jsonData = JSON.stringify(result); + res.setHeader("Content-Type", "application/json"); + res.send(jsonData); + } else { + const cborData = cbor.encode(result); + res.setHeader("Content-Type", "application/cbor"); + res.send(cborData); } +}); - }, 1000) - +// Observe the current result property +app.get(resultEndPointObserve, (req, res) => { + const acceptHeader = req.get("Accept"); + res.setHeader("Access-Control-Allow-Origin", "*"); + res.setHeader("Cache-Control", "no-cache"); + res.setHeader("Connection", "keep-alive"); + res.setHeader("Content-Type", "text/event-stream"); + + console.log("Client is listening to result property"); + let oldResult = result; + + const changeInterval = setInterval(() => { + if (oldResult !== result) { + let message; + + if ( + acceptHeader.includes("application/json") || + acceptHeader.includes("*/*") || + acceptHeader.includes("application/*") + ) { + message = `data: ${JSON.stringify(result)}\n\n`; + } else { + message = `data: ${cbor.encode(result)}\n\n`; + } + + res.statusCode = 200; + res.write(message); + oldResult = result; + } + }, 1000); - res.on('finish', () => { - clearInterval(changeInterval) - }) + res.on("finish", () => { + clearInterval(changeInterval); + }); - res.on("close", () => { - console.log("Client stopped listening to result property"); - }) -}) + res.on("close", () => { + console.log("Client stopped listening to result property"); + }); +}); // Get the time of the last change app.get(lastChangeEndPoint, (req, res) => { - const acceptHeader = req.get('Accept') - - if (acceptHeader.includes('application/json') || acceptHeader.includes('*/*') || acceptHeader.includes('application/*')) { - const jsonData = JSON.stringify(lastChange) - res.setHeader('Content-Type', 'application/json') - res.send(jsonData) - } - else { - const cborData = cbor.encode(lastChange) - res.setHeader('Content-Type', 'application/cbor') - res.send(cborData) - } -}) - -//Observe the last change property -app.get(lastChangeEndPointObserve, (req, res) => { - const acceptHeader = req.get('Accept') - res.setHeader('Access-Control-Allow-Origin', '*') - res.setHeader('Cache-Control', 'no-cache') - res.setHeader('Connection', 'keep-alive') - res.setHeader('Content-Type', 'text/event-stream') - - console.log("Client is listening to lastChange property"); - let oldLastChange = lastChange - - const changeInterval = setInterval(() => { - - if (oldLastChange !== lastChange) { - let message - - if (acceptHeader.includes('application/json') || acceptHeader.includes('*/*') || acceptHeader.includes('application/*')) { - message = `data: ${JSON.stringify(lastChange)}\n\n` - } - else { - message = `data: ${cbor.encode(lastChange.toISOString())}\n\n` - } - - res.statusCode = 200 - res.write(message) - oldLastChange = lastChange + const acceptHeader = req.get("Accept"); + + if ( + acceptHeader.includes("application/json") || + acceptHeader.includes("*/*") || + acceptHeader.includes("application/*") + ) { + const jsonData = JSON.stringify(lastChange); + res.setHeader("Content-Type", "application/json"); + res.send(jsonData); + } else { + const cborData = cbor.encode(lastChange); + res.setHeader("Content-Type", "application/cbor"); + res.send(cborData); } +}); - }, 1000) - - - res.on('finish', () => { - clearInterval(changeInterval) - }) +// Observe the last change property +app.get(lastChangeEndPointObserve, (req, res) => { + const acceptHeader = req.get("Accept"); + res.setHeader("Access-Control-Allow-Origin", "*"); + res.setHeader("Cache-Control", "no-cache"); + res.setHeader("Connection", "keep-alive"); + res.setHeader("Content-Type", "text/event-stream"); + + console.log("Client is listening to lastChange property"); + let oldLastChange = lastChange; + + const changeInterval = setInterval(() => { + if (oldLastChange !== lastChange) { + let message; + + if ( + acceptHeader.includes("application/json") || + acceptHeader.includes("*/*") || + acceptHeader.includes("application/*") + ) { + message = `data: ${JSON.stringify(lastChange)}\n\n`; + } else { + message = `data: ${cbor.encode(lastChange.toISOString())}\n\n`; + } + + res.statusCode = 200; + res.write(message); + oldLastChange = lastChange; + } + }, 1000); - res.on("close", () => { - console.log("Client stopped listening to lastChange property"); - }) -}) + res.on("finish", () => { + clearInterval(changeInterval); + }); + res.on("close", () => { + console.log("Client stopped listening to lastChange property"); + }); +}); // /*****************************************************/ // /*************** Actions Endpoints *******************/ @@ -404,96 +466,103 @@ app.get(lastChangeEndPointObserve, (req, res) => { // Add a number to the current result endpoint app.post(additionEndPoint, (req, res) => { - const acceptHeader = req.get('Accept') - let bodyInput - - //check if the data was sent as cbor or json and if not get the body normally - if (req.get('Content-Type') === 'application/cbor') { - try { - bodyInput = cbor.decode(req.body); - } catch (err) { - res.status(400).json('Bad Request'); - } - } - else { - try { - bodyInput = JSON.parse(req.body) - } catch (err) { - res.status(400).json('Bad Request'); - } - } - - /**Check if given input is a number, if not return an error message, - * if yes add the new number to the result, update the lastChange variable and - * return the added number in the accepted format - */ - if (typeof bodyInput !== 'number') { - res.status(400).json('Input should be a valid number') - } else { - if (acceptHeader.includes('application/json') || acceptHeader.includes('*/*') || acceptHeader.includes('application/*')) { - result += bodyInput - lastChange = new Date() - const jsonData = JSON.stringify(result) - res.setHeader('Content-Type', 'application/json') - res.send(jsonData) + const acceptHeader = req.get("Accept"); + let bodyInput; + + // check if the data was sent as cbor or json and if not get the body normally + if (req.get("Content-Type") === "application/cbor") { + try { + bodyInput = cbor.decode(req.body); + } catch (err) { + console.error(err); + res.status(400).json("Bad Request"); + } + } else { + try { + bodyInput = JSON.parse(req.body); + } catch (err) { + console.error(err); + res.status(400).json("Bad Request"); + } } - else { - result += bodyInput - lastChange = new Date() - console.log("cbor before encoded: ", result); - const cborData = cbor.encode(result) - console.log("cbor encoded: ", cborData); - res.setHeader('Content-Type', 'application/cbor') - res.send(cborData) + + /** Check if given input is a number, if not return an error message, + * if yes add the new number to the result, update the lastChange variable and + * return the added number in the accepted format + */ + if (typeof bodyInput !== "number") { + res.status(400).json("Input should be a valid number"); + } else { + if ( + acceptHeader.includes("application/json") || + acceptHeader.includes("*/*") || + acceptHeader.includes("application/*") + ) { + result += bodyInput; + lastChange = new Date(); + const jsonData = JSON.stringify(result); + res.setHeader("Content-Type", "application/json"); + res.send(jsonData); + } else { + result += bodyInput; + lastChange = new Date(); + console.log("cbor before encoded: ", result); + const cborData = cbor.encode(result); + console.log("cbor encoded: ", cborData); + res.setHeader("Content-Type", "application/cbor"); + res.send(cborData); + } } - } -}) +}); // Subtract a number from the current result endpoint app.post(subtractionEndPoint, (req, res) => { - const acceptHeader = req.get('Accept') - let bodyInput - - //check if the data was sent as cbor, json or text, and if not get the body normally - if (req.get('Content-Type') === 'application/cbor') { - try { - bodyInput = cbor.decode(req.body); - } catch (err) { - res.status(400).json('Bad Request'); - } - } - else { - try { - bodyInput = JSON.parse(req.body) - } catch (err) { - res.status(400).json('Bad Request'); - } - } - - /**Check if given input is a valid number, if not return an error message, - * if yes add the new number to the result, update the lastChange variable and - * return the added number in the accepted format - */ - if (typeof bodyInput !== 'number') { - res.status(400).json('Input should be a valid number') - } else { - if (acceptHeader.includes('application/json') || acceptHeader.includes('*/*') || acceptHeader.includes('application/*')) { - result -= bodyInput - lastChange = new Date() - const jsonData = JSON.stringify(result) - res.setHeader('Content-Type', 'application/json') - res.send(jsonData) - } - else { - result -= bodyInput - lastChange = new Date() - const cborData = cbor.encode(result) - res.setHeader('Content-Type', 'application/cbor') - res.send(cborData) + const acceptHeader = req.get("Accept"); + let bodyInput; + + // check if the data was sent as cbor, json or text, and if not get the body normally + if (req.get("Content-Type") === "application/cbor") { + try { + bodyInput = cbor.decode(req.body); + } catch (err) { + console.error(err); + res.status(400).json("Bad Request"); + } + } else { + try { + bodyInput = JSON.parse(req.body); + } catch (err) { + console.error(err); + res.status(400).json("Bad Request"); + } } - } -}) + /** Check if given input is a valid number, if not return an error message, + * if yes add the new number to the result, update the lastChange variable and + * return the added number in the accepted format + */ + if (typeof bodyInput !== "number") { + res.status(400).json("Input should be a valid number"); + } else { + if ( + acceptHeader.includes("application/json") || + acceptHeader.includes("*/*") || + acceptHeader.includes("application/*") + ) { + result -= bodyInput; + lastChange = new Date(); + const jsonData = JSON.stringify(result); + res.setHeader("Content-Type", "application/json"); + res.send(jsonData); + } else { + result -= bodyInput; + lastChange = new Date(); + const cborData = cbor.encode(result); + res.setHeader("Content-Type", "application/cbor"); + res.send(cborData); + } + } +}); // /*****************************************************/ // /**************** Events Endpoints *******************/ @@ -501,48 +570,49 @@ app.post(subtractionEndPoint, (req, res) => { // Get updates when a change to the main result happens endpoint app.get(updateEndPoint, (req, res) => { - const acceptHeader = req.get('Accept') - res.setHeader('Access-Control-Allow-Origin', '*') - res.setHeader('Cache-Control', 'no-cache') - res.setHeader('Connection', 'keep-alive') - res.setHeader('Content-Type', 'text/event-stream') - - console.log("Client is listening to update event"); - let oldResult = result - - const changeInterval = setInterval(() => { - if (oldResult !== result) { - let message - - if (acceptHeader.includes('application/json') || acceptHeader.includes('*/*') || acceptHeader.includes('application/*')) { - message = `data: ${JSON.stringify(result)}\n\n` - } - else { - message = `data: ${cbor.encode(result)}\n\n` - } - - res.statusCode = 200 - res.write(message) - oldResult = result - } - }, 1000) - - - res.on('finish', () => { - clearInterval(changeInterval) - }) + const acceptHeader = req.get("Accept"); + res.setHeader("Access-Control-Allow-Origin", "*"); + res.setHeader("Cache-Control", "no-cache"); + res.setHeader("Connection", "keep-alive"); + res.setHeader("Content-Type", "text/event-stream"); + + console.log("Client is listening to update event"); + let oldResult = result; + + const changeInterval = setInterval(() => { + if (oldResult !== result) { + let message; + + if ( + acceptHeader.includes("application/json") || + acceptHeader.includes("*/*") || + acceptHeader.includes("application/*") + ) { + message = `data: ${JSON.stringify(result)}\n\n`; + } else { + message = `data: ${cbor.encode(result)}\n\n`; + } + + res.statusCode = 200; + res.write(message); + oldResult = result; + } + }, 1000); - res.on("close", () => { - console.log("Client stopped listening to update event"); - }) + res.on("finish", () => { + clearInterval(changeInterval); + }); -}) + res.on("close", () => { + console.log("Client stopped listening to update event"); + }); +}); /************************************************/ -/************** Starting Server *****************/ +/** ************ Starting Server *****************/ /************************************************/ app.listen(portNumber, () => { - console.log(`Started listening to localhost on port ${portNumber}`) - console.log('ThingIsReady') -}) \ No newline at end of file + console.log(`Started listening to localhost on port ${portNumber}`); + console.log("ThingIsReady"); +}); diff --git a/things/calculator/http/express/http-simple-calculator.js b/things/calculator/http/express/http-simple-calculator.js index 43108ca..ce895de 100644 --- a/things/calculator/http/express/http-simple-calculator.js +++ b/things/calculator/http/express/http-simple-calculator.js @@ -1,3 +1,17 @@ +/******************************************************************************** + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the W3C Software Notice and + * Document License (2015-05-13) which is available at + * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document. + * + * SPDX-License-Identifier: EPL-2.0 OR W3C-20150513 + ********************************************************************************/ const express = require("express"); const fs = require("fs"); const path = require("path"); @@ -6,8 +20,8 @@ const { parseArgs } = require("node:util"); const { JsonPlaceholderReplacer } = require("json-placeholder-replacer"); require("dotenv").config(); -const { createLogger, transports, format } = require('winston') -const LokiTransport = require('winston-loki') +const { createLogger, transports, format } = require("winston"); +const LokiTransport = require("winston-loki"); const app = express(); app.use(express.json({ strict: false })); @@ -16,378 +30,605 @@ const hostname = process.env.HOSTNAME ?? "localhost"; let portNumber = process.env.PORT ?? 3000; const thingName = "http-express-calculator-simple"; -let logger = createLogger({ - transports: [new LokiTransport({ - host:`${process.env.LOKI_HOSTNAME}:${process.env.LOKI_PORT}`, - labels: { thing: thingName }, - json: true, - format: format.json(), - replaceTimestamp: true, - onConnectionError: (err) => console.error(err) - }), - new transports.Console({ - format: format.combine(format.simple(), format.colorize()) - })] -}) - -const TDEndPoint = `/${thingName}`, - resultEndPoint = `/${thingName}/properties/result`, - resultEndPointObserve = `${resultEndPoint}/observe`, - lastChangeEndPoint = `/${thingName}/properties/lastChange`, - lastChangeEndPointObserve = `${lastChangeEndPoint}/observe`, - additionEndPoint = `/${thingName}/actions/add`, - subtractionEndPoint = `/${thingName}/actions/subtract`, - updateEndPoint = `/${thingName}/events/update` - -const existingEndpoints = [TDEndPoint, resultEndPoint, resultEndPointObserve, lastChangeEndPoint, lastChangeEndPointObserve, additionEndPoint, subtractionEndPoint, updateEndPoint] +const logger = createLogger({ + transports: [ + new LokiTransport({ + host: `${process.env.LOKI_HOSTNAME}:${process.env.LOKI_PORT}`, + labels: { thing: thingName }, + json: true, + format: format.json(), + replaceTimestamp: true, + onConnectionError: (err) => console.error(err), + }), + new transports.Console({ + format: format.combine(format.simple(), format.colorize()), + }), + ], +}); + +const TDEndPoint = `/${thingName}`; +const resultEndPoint = `/${thingName}/properties/result`; +const resultEndPointObserve = `${resultEndPoint}/observe`; +const lastChangeEndPoint = `/${thingName}/properties/lastChange`; +const lastChangeEndPointObserve = `${lastChangeEndPoint}/observe`; +const additionEndPoint = `/${thingName}/actions/add`; +const subtractionEndPoint = `/${thingName}/actions/subtract`; +const updateEndPoint = `/${thingName}/events/update`; + +const existingEndpoints = [ + TDEndPoint, + resultEndPoint, + resultEndPointObserve, + lastChangeEndPoint, + lastChangeEndPointObserve, + additionEndPoint, + subtractionEndPoint, + updateEndPoint, +]; const { - values: { port }, + values: { port }, } = parseArgs({ - options: { - port: { - type: "string", - short: "p", + options: { + port: { + type: "string", + short: "p", + }, }, - }, }); if (port && !isNaN(parseInt(port))) { - portNumber = parseInt(port); + portNumber = parseInt(port); } const tmPath = process.env.TM_PATH; if (process.platform === "win32") { - tmPath.split(path.sep).join(path.win32.sep); + tmPath.split(path.sep).join(path.win32.sep); } const thingModel = JSON.parse(fs.readFileSync(path.join(__dirname, tmPath))); const placeholderReplacer = new JsonPlaceholderReplacer(); placeholderReplacer.addVariableMap({ - PROTOCOL: "http", - THING_NAME: thingName, - HOSTNAME: hostname, - PORT_NUMBER: portNumber, - RESULT_OBSERVABLE: true, - LAST_CHANGE_OBSERVABLE: true + PROTOCOL: "http", + THING_NAME: thingName, + HOSTNAME: hostname, + PORT_NUMBER: portNumber, + RESULT_OBSERVABLE: true, + LAST_CHANGE_OBSERVABLE: true, }); const thingDescription = placeholderReplacer.replace(thingModel); thingDescription["@type"] = "Thing"; const defaultForm = { - "href": "", - "contentType": "application/json", - "op": [] -} - -//add properties forms -for (const key in thingDescription['properties']) { + href: "", + contentType: "application/json", + op: [], +}; + +// add properties forms +for (const key in thingDescription.properties) { + if (!Object.hasOwn(thingDescription.properties, key)) { + continue; + } - thingDescription['properties'][key]['forms'] = [] + thingDescription.properties[key].forms = []; - const newFormRead = JSON.parse(JSON.stringify(defaultForm)) - newFormRead['href'] = `properties/${key}` - newFormRead['op'] = ["readproperty"] + const newFormRead = JSON.parse(JSON.stringify(defaultForm)); + newFormRead.href = `properties/${key}`; + newFormRead.op = ["readproperty"]; - const newFormObs = JSON.parse(JSON.stringify(newFormRead)) - newFormObs['href'] = `properties/${key}/observe` - newFormObs['op'] = ["observeproperty", "unobserveproperty"] - newFormObs['subprotocol'] = "sse" + const newFormObs = JSON.parse(JSON.stringify(newFormRead)); + newFormObs.href = `properties/${key}/observe`; + newFormObs.op = ["observeproperty", "unobserveproperty"]; + newFormObs.subprotocol = "sse"; - thingDescription['properties'][key]['forms'].push(newFormRead) - thingDescription['properties'][key]['forms'].push(newFormObs) + thingDescription.properties[key].forms.push(newFormRead); + thingDescription.properties[key].forms.push(newFormObs); } -//add actions forms -for (const key in thingDescription['actions']) { +// add actions forms +for (const key in thingDescription.actions) { + if (!Object.hasOwn(thingDescription.actions, key)) { + continue; + } - thingDescription['actions'][key]['forms'] = [] + thingDescription.actions[key].forms = []; - const newForm = JSON.parse(JSON.stringify(defaultForm)) - newForm['href'] = `actions/${key}` - newForm['op'] = ["invokeaction"] + const newForm = JSON.parse(JSON.stringify(defaultForm)); + newForm.href = `actions/${key}`; + newForm.op = ["invokeaction"]; - thingDescription['actions'][key]['forms'].push(newForm) + thingDescription.actions[key].forms.push(newForm); } -//add events forms -for (const key in thingDescription['events']) { +// add events forms +for (const key in thingDescription.events) { + if (!Object.hasOwn(thingDescription.events, key)) { + continue; + } - thingDescription['events'][key]['data']['type'] = "object" + thingDescription.events[key].data.type = "object"; - thingDescription['events'][key]['forms'] = [] + thingDescription.events[key].forms = []; - const newForm = JSON.parse(JSON.stringify(defaultForm)) - newForm['href'] = `events/${key}` - newForm['op'] = ["subscribeevent", "unsubscribeevent"] - newForm['subprotocol'] = "sse" + const newForm = JSON.parse(JSON.stringify(defaultForm)); + newForm.href = `events/${key}`; + newForm.op = ["subscribeevent", "unsubscribeevent"]; + newForm.subprotocol = "sse"; - thingDescription['events'][key]['forms'].push(newForm) + thingDescription.events[key].forms.push(newForm); } -//Creating the TD for testing purposes +// Creating the TD for testing purposes try { - fs.writeFileSync('http-simple-calculator-thing.td.jsonld', JSON.stringify(thingDescription, null, 2)) + fs.writeFileSync( + "http-simple-calculator-thing.td.jsonld", + JSON.stringify(thingDescription, null, 2) + ); } catch (err) { - console.log(err); + console.log(err); } const reqParser = bodyParser.text({ type: "*/*" }); // Logging and monitoring variables and functions const setResult = (value) => { - result = value - logger.info({ message: `${result}`, labels: { affordance: 'property', affordanceName: 'result', messageType: 'updateProperty' }}) -} + result = value; + logger.info({ + message: `${result}`, + labels: { + affordance: "property", + affordanceName: "result", + messageType: "updateProperty", + }, + }); +}; const setLastChange = (value) => { - lastChange = value - logger.info({ message: `${lastChange}`, labels: { affordance: 'property', affordanceName: 'lastChange', messageType: 'updateProperty' }}) -} - -let result -setResult(0) - -let lastChange -setLastChange(new Date().toISOString()) - -let updateSubscriptionCount = 0 -logger.info({ message: `${updateSubscriptionCount}`, labels: { affordance: 'event', affordanceName: 'update', messageType: 'subscriptionCount' }}) + lastChange = value; + logger.info({ + message: `${lastChange}`, + labels: { + affordance: "property", + affordanceName: "lastChange", + messageType: "updateProperty", + }, + }); +}; + +let result; +setResult(0); + +let lastChange; +setLastChange(new Date().toISOString()); + +let updateSubscriptionCount = 0; +logger.info({ + message: `${updateSubscriptionCount}`, + labels: { + affordance: "event", + affordanceName: "update", + messageType: "subscriptionCount", + }, +}); const increaseUpdateSubscriptionCount = () => { - updateSubscriptionCount++ - logger.info({ message: `${updateSubscriptionCount}`, labels: { affordance: 'event', affordanceName: 'update', messageType: 'subscriptionCount' }}) -} + updateSubscriptionCount++; + logger.info({ + message: `${updateSubscriptionCount}`, + labels: { + affordance: "event", + affordanceName: "update", + messageType: "subscriptionCount", + }, + }); +}; const decreaseUpdateSubscriptionCount = () => { - updateSubscriptionCount-- - logger.info({ message: `${updateSubscriptionCount}`, labels: { affordance: 'event', affordanceName: 'update', messageType: 'subscriptionCount' }}) -} - -let resultObserveCount = 0 -logger.info({ message: `${resultObserveCount}`, labels: { affordance: 'property', affordanceName: 'result', messageType: 'observeCount' }}) + updateSubscriptionCount--; + logger.info({ + message: `${updateSubscriptionCount}`, + labels: { + affordance: "event", + affordanceName: "update", + messageType: "subscriptionCount", + }, + }); +}; + +let resultObserveCount = 0; +logger.info({ + message: `${resultObserveCount}`, + labels: { + affordance: "property", + affordanceName: "result", + messageType: "observeCount", + }, +}); const increaseResultObserveCount = () => { - resultObserveCount++ - logger.info({ message: `${resultObserveCount}`, labels: { affordance: 'property', affordanceName: 'result', messageType: 'observeCount' }}) -} + resultObserveCount++; + logger.info({ + message: `${resultObserveCount}`, + labels: { + affordance: "property", + affordanceName: "result", + messageType: "observeCount", + }, + }); +}; const decreaseResultObserveCount = () => { - resultObserveCount-- - logger.info({ message: `${resultObserveCount}`, labels: { affordance: 'property', affordanceName: 'result', messageType: 'observeCount' }}) -} - -let lastChangeObserveCount = 0 -logger.info({ message: `${lastChangeObserveCount}`, labels: { affordance: 'property', affordanceName: 'lastChange', messageType: 'observeCount' }}) + resultObserveCount--; + logger.info({ + message: `${resultObserveCount}`, + labels: { + affordance: "property", + affordanceName: "result", + messageType: "observeCount", + }, + }); +}; + +let lastChangeObserveCount = 0; +logger.info({ + message: `${lastChangeObserveCount}`, + labels: { + affordance: "property", + affordanceName: "lastChange", + messageType: "observeCount", + }, +}); const increaseLastChangeObserveCount = () => { - lastChangeObserveCount++ - logger.info({ message: `${lastChangeObserveCount}`, labels: { affordance: 'property', affordanceName: 'lastChange', messageType: 'observeCount' }}) -} + lastChangeObserveCount++; + logger.info({ + message: `${lastChangeObserveCount}`, + labels: { + affordance: "property", + affordanceName: "lastChange", + messageType: "observeCount", + }, + }); +}; const decreaseLastChangeObserveCount = () => { - lastChangeObserveCount-- - logger.info({ message: `${lastChangeObserveCount}`, labels: { affordance: 'property', affordanceName: 'lastChange', messageType: 'observeCount' }}) -} + lastChangeObserveCount--; + logger.info({ + message: `${lastChangeObserveCount}`, + labels: { + affordance: "property", + affordanceName: "lastChange", + messageType: "observeCount", + }, + }); +}; /******************************************/ -/************** Middleware ****************/ +/** ************ Middleware ****************/ /******************************************/ -//Middleware to ensure the right endpoints are being called -app.use((req, res, next) => { - const endpoint = req.url - - if (!existingEndpoints.includes(endpoint)) { - res.status(404).json("Not Found") - } - else { - next() - } -}) - -//Middleware to ensure the right method is been used for each endpoint +// Middleware to ensure the right endpoints are being called app.use((req, res, next) => { - const method = req.method - const endpoint = req.url + const endpoint = req.url; - if (endpoint === TDEndPoint || endpoint === resultEndPoint || endpoint === resultEndPointObserve || endpoint === lastChangeEndPoint || endpoint === lastChangeEndPointObserve || endpoint === updateEndPoint) { - if (method === 'GET') { - next() + if (!existingEndpoints.includes(endpoint)) { + res.status(404).json("Not Found"); } else { - res.status(405).json('Method Not Allowed'); + next(); } - } +}); - if (endpoint === additionEndPoint || endpoint === subtractionEndPoint) { - if (method === 'POST') { - next() - } else { - res.status(405).json('Method Not Allowed'); +// Middleware to ensure the right method is been used for each endpoint +app.use((req, res, next) => { + const method = req.method; + const endpoint = req.url; + + if ( + endpoint === TDEndPoint || + endpoint === resultEndPoint || + endpoint === resultEndPointObserve || + endpoint === lastChangeEndPoint || + endpoint === lastChangeEndPointObserve || + endpoint === updateEndPoint + ) { + if (method === "GET") { + next(); + } else { + res.status(405).json("Method Not Allowed"); + } } - } - -}) + if (endpoint === additionEndPoint || endpoint === subtractionEndPoint) { + if (method === "POST") { + next(); + } else { + res.status(405).json("Method Not Allowed"); + } + } +}); /******************************************/ -/*************** Endpoints ****************/ +/** ************* Endpoints ****************/ /******************************************/ app.get(TDEndPoint, (req, res) => { - res.json(thingDescription); + res.json(thingDescription); }); app.get(resultEndPoint, (req, res) => { - logger.info({ message: `${result}`, labels: { affordance: 'property', op: 'readproperty', affordanceName: 'result' }}) - res.json(result); + logger.info({ + message: `${result}`, + labels: { + affordance: "property", + op: "readproperty", + affordanceName: "result", + }, + }); + res.json(result); }); app.get(resultEndPointObserve, (req, res) => { - res.statusCode = 200; - res.setHeader("Access-Control-Allow-Origin", "*"); - res.setHeader("Cache-Control", "no-cache"); - res.setHeader("connection", "keep-alive"); - res.setHeader("Content-Type", "text/event-stream"); - - console.log("Client is listening to result property") - logger.info({ message: `${result}`, labels: { affordance: 'property', op: 'observeproperty', affordanceName: 'result' }}) - increaseResultObserveCount() - let oldResult = result; - - const changeInterval = setInterval(() => { - if (oldResult !== result) { - logger.info({ message: result, labels: { affordance: 'property', op: 'observeproperty', affordanceName: 'result' }}) - logger.info(`Observed data: ${result}`) - res.write(`data: ${JSON.stringify(result)}\n\n`); - oldResult = result; - } - }, 1000); - - res.on("close", () => { - clearInterval(changeInterval); - decreaseResultObserveCount() - logger.info({ message: 'Client stopped listening to result property', labels: { affordance: 'property', op: 'unobserveproperty', affordanceName: 'result' }}) - console.log("Client stopped listening to result property"); - res.end() - }) + res.statusCode = 200; + res.setHeader("Access-Control-Allow-Origin", "*"); + res.setHeader("Cache-Control", "no-cache"); + res.setHeader("connection", "keep-alive"); + res.setHeader("Content-Type", "text/event-stream"); + + console.log("Client is listening to result property"); + logger.info({ + message: `${result}`, + labels: { + affordance: "property", + op: "observeproperty", + affordanceName: "result", + }, + }); + increaseResultObserveCount(); + let oldResult = result; + + const changeInterval = setInterval(() => { + if (oldResult !== result) { + logger.info({ + message: result, + labels: { + affordance: "property", + op: "observeproperty", + affordanceName: "result", + }, + }); + logger.info(`Observed data: ${result}`); + res.write(`data: ${JSON.stringify(result)}\n\n`); + oldResult = result; + } + }, 1000); + + res.on("close", () => { + clearInterval(changeInterval); + decreaseResultObserveCount(); + logger.info({ + message: "Client stopped listening to result property", + labels: { + affordance: "property", + op: "unobserveproperty", + affordanceName: "result", + }, + }); + console.log("Client stopped listening to result property"); + res.end(); + }); }); app.get(lastChangeEndPoint, (req, res) => { - logger.info({ message: lastChange, labels: { affordance: 'property', op: 'readproperty', affordanceName: 'lastChange' }}) - res.json(lastChange); + logger.info({ + message: lastChange, + labels: { + affordance: "property", + op: "readproperty", + affordanceName: "lastChange", + }, + }); + res.json(lastChange); }); app.get(lastChangeEndPointObserve, (req, res) => { - res.statusCode = 200; - res.setHeader("Access-Control-Allow-Origin", "*"); - res.setHeader("Cache-Control", "no-cache"); - res.setHeader("connection", "keep-alive"); - res.setHeader("Content-Type", "text/event-stream"); - - console.log("Client is listening to lastChange property"); - logger.info({ message: `${result}`, labels: { affordance: 'property', op: 'observeproperty', affordanceName: 'lastChange' }}) - increaseLastChangeObserveCount() - let oldLastChange = lastChange; - - const changeInterval = setInterval(() => { - - if (oldLastChange !== lastChange) { - logger.info({ message: lastChange, labels: { affordance: 'property', op: 'observeproperty', affordanceName: 'lastChange' }}) - res.write(`data: ${JSON.stringify(lastChange)}\n\n`); - oldLastChange = lastChange; - } - }, 1000); - - res.on("finish", () => { - clearInterval(changeInterval); - }); - - res.on("close", () => { - logger.info({ message: 'Client stopped listening to result property', labels: { affordance: 'property', op: 'unobserveproperty', affordanceName: 'lastChange' }}) - decreaseLastChangeObserveCount() - console.log("Client stopped listening to lastChange property"); - }) + res.statusCode = 200; + res.setHeader("Access-Control-Allow-Origin", "*"); + res.setHeader("Cache-Control", "no-cache"); + res.setHeader("connection", "keep-alive"); + res.setHeader("Content-Type", "text/event-stream"); + + console.log("Client is listening to lastChange property"); + logger.info({ + message: `${result}`, + labels: { + affordance: "property", + op: "observeproperty", + affordanceName: "lastChange", + }, + }); + increaseLastChangeObserveCount(); + let oldLastChange = lastChange; + + const changeInterval = setInterval(() => { + if (oldLastChange !== lastChange) { + logger.info({ + message: lastChange, + labels: { + affordance: "property", + op: "observeproperty", + affordanceName: "lastChange", + }, + }); + res.write(`data: ${JSON.stringify(lastChange)}\n\n`); + oldLastChange = lastChange; + } + }, 1000); + + res.on("finish", () => { + clearInterval(changeInterval); + }); + + res.on("close", () => { + logger.info({ + message: "Client stopped listening to result property", + labels: { + affordance: "property", + op: "unobserveproperty", + affordanceName: "lastChange", + }, + }); + decreaseLastChangeObserveCount(); + console.log("Client stopped listening to lastChange property"); + }); }); app.post(additionEndPoint, reqParser, (req, res) => { - - try { - const bodyInput = JSON.parse(req.body) - - if (typeof bodyInput !== "number") { - res.status(400).json("Input should be a valid number"); - } else { - logger.info({ message: 'Action invoked.', labels: { affordance: 'action', op: 'invokeaction', affordanceName: 'add' }}) - logger.info({ message: `${bodyInput}`, labels: { affordance: 'action', op: 'invokeaction', affordanceName: 'add', messageType: 'actionInput' }}) - setResult(result + bodyInput); - setLastChange(new Date()); - logger.info({ message: `${result}`, labels: { affordance: 'action', op: 'invokeaction', affordanceName: 'add', messageType: 'actionOutput' }}) - res.json(result); + try { + const bodyInput = JSON.parse(req.body); + + if (typeof bodyInput !== "number") { + res.status(400).json("Input should be a valid number"); + } else { + logger.info({ + message: "Action invoked.", + labels: { + affordance: "action", + op: "invokeaction", + affordanceName: "add", + }, + }); + logger.info({ + message: `${bodyInput}`, + labels: { + affordance: "action", + op: "invokeaction", + affordanceName: "add", + messageType: "actionInput", + }, + }); + setResult(result + bodyInput); + setLastChange(new Date()); + logger.info({ + message: `${result}`, + labels: { + affordance: "action", + op: "invokeaction", + affordanceName: "add", + messageType: "actionOutput", + }, + }); + res.json(result); + } + } catch (error) { + console.error(error); + res.status(400).json("Input should be a valid number"); } - } catch (error) { - res.status(400).json("Input should be a valid number"); - } - }); app.post(subtractionEndPoint, reqParser, (req, res) => { - try { - const bodyInput = JSON.parse(req.body) - - if (typeof bodyInput !== "number") { - res.status(400).json("Input should be a valid number"); - } else { - logger.info({ message: 'Action invoked.', labels: { affordance: 'action', op: 'invokeaction', affordanceName: 'subtract' }}) - logger.info({ message: `${bodyInput}`, labels: { affordance: 'action', op: 'invokeaction', affordanceName: 'subtract', messageType: 'actionInput' }}) - setResult(result - bodyInput); - setLastChange(new Date()); - logger.info({ message: `${result}`, labels: { affordance: 'action', op: 'invokeaction', affordanceName: 'subtract', messageType: 'actionOutput' }}) - res.json(result); + try { + const bodyInput = JSON.parse(req.body); + + if (typeof bodyInput !== "number") { + res.status(400).json("Input should be a valid number"); + } else { + logger.info({ + message: "Action invoked.", + labels: { + affordance: "action", + op: "invokeaction", + affordanceName: "subtract", + }, + }); + logger.info({ + message: `${bodyInput}`, + labels: { + affordance: "action", + op: "invokeaction", + affordanceName: "subtract", + messageType: "actionInput", + }, + }); + setResult(result - bodyInput); + setLastChange(new Date()); + logger.info({ + message: `${result}`, + labels: { + affordance: "action", + op: "invokeaction", + affordanceName: "subtract", + messageType: "actionOutput", + }, + }); + res.json(result); + } + } catch (error) { + console.error(error); + res.status(400).json("Input should be a valid number"); } - } catch (error) { - res.status(400).json("Input should be a valid number"); - } }); app.get(updateEndPoint, (req, res) => { - res.statusCode = 200; - res.setHeader("Access-Control-Allow-Origin", "*"); - res.setHeader("Cache-Control", "no-cache"); - res.setHeader("connection", "keep-alive"); - res.setHeader("Content-Type", "text/event-stream"); - - let oldResult = result; - console.log("Client is listening to update event"); - logger.info({ message: 'Client is listening to update event', labels: { affordance: 'event', op: 'subscribeevent', affordanceName: 'update' }}) - increaseUpdateSubscriptionCount() - - /** - * The SSE specification defines the structure of SSE messages, and - * it expects event data to be formatted with "data:" followed by the - * actual data. When you deviate from this standard, it might not be - * interpreted correctly by the client, which could create empty values. - */ - const changeInterval = setInterval(() => { - if (oldResult !== result) { - logger.info({ message: `${result}`, labels: { affordance: 'event', op: 'subscribeevent', affordanceName: 'update', messageType: 'eventOutput' }}) - res.write(`data: ${result}\n\n`); - oldResult = result; - } - }, 1000); - - res.on("close", () => { - logger.info({ message: 'Client stopped listening to update event', labels: { affordance: 'event', op: 'unsubscribeevent', affordanceName: 'update' }}) - decreaseUpdateSubscriptionCount() - console.log("Client stopped listening to update event"); - clearInterval(changeInterval); - res.end() - }) + res.statusCode = 200; + res.setHeader("Access-Control-Allow-Origin", "*"); + res.setHeader("Cache-Control", "no-cache"); + res.setHeader("connection", "keep-alive"); + res.setHeader("Content-Type", "text/event-stream"); + + let oldResult = result; + console.log("Client is listening to update event"); + logger.info({ + message: "Client is listening to update event", + labels: { + affordance: "event", + op: "subscribeevent", + affordanceName: "update", + }, + }); + increaseUpdateSubscriptionCount(); + + /** + * The SSE specification defines the structure of SSE messages, and + * it expects event data to be formatted with "data:" followed by the + * actual data. When you deviate from this standard, it might not be + * interpreted correctly by the client, which could create empty values. + */ + const changeInterval = setInterval(() => { + if (oldResult !== result) { + logger.info({ + message: `${result}`, + labels: { + affordance: "event", + op: "subscribeevent", + affordanceName: "update", + messageType: "eventOutput", + }, + }); + res.write(`data: ${result}\n\n`); + oldResult = result; + } + }, 1000); + + res.on("close", () => { + logger.info({ + message: "Client stopped listening to update event", + labels: { + affordance: "event", + op: "unsubscribeevent", + affordanceName: "update", + }, + }); + decreaseUpdateSubscriptionCount(); + console.log("Client stopped listening to update event"); + clearInterval(changeInterval); + res.end(); + }); }); app.listen(portNumber, () => { - console.log(`Started listening to localhost on port ${portNumber}`); - console.log("ThingIsReady"); + console.log(`Started listening to localhost on port ${portNumber}`); + console.log("ThingIsReady"); }); diff --git a/things/calculator/http/express/package.json b/things/calculator/http/express/package.json index 9a0de21..d33934a 100644 --- a/things/calculator/http/express/package.json +++ b/things/calculator/http/express/package.json @@ -1,30 +1,33 @@ { - "name": "http-calculator", - "version": "1.0.0", - "description": "HTTP Calculator", - "main": "index.js", - "keywords": [ - "wot", - "thing", - "iot", - "http" - ], - "scripts": { - "test": "node simple-calculator.js ; pid = $! ; result=$(mocha --exit *.test.js)) & kill $pid" - }, - "author": "Eclipse Thingweb (https://thingweb.io/)", - "license": "EPL-2.0 OR W3C-20150513", - "dependencies": { - "body-parser": "^1.20.2", - "cbor": "^9.0.1", - "dotenv": "^16.3.1", - "eventsource": "^2.0.2", - "express": "^4.18.2", - "json-placeholder-replacer": "^1.0.35", - "winston": "^3.14.2", - "winston-loki": "^6.1.2" - }, - "devDependencies": { - "@types/express": "^4.17.17" - } + "name": "http-calculator", + "version": "1.0.0", + "description": "HTTP Calculator", + "main": "index.js", + "keywords": [ + "wot", + "thing", + "iot", + "http" + ], + "scripts": { + "test": "node simple-calculator.js ; pid = $! ; result=$(mocha --exit *.test.js)) & kill $pid", + "lint": "eslint .", + "lint:fix": "eslint . --fix", + "format": "prettier --write \"**/*.js\" \"test/**/*.js\" \"client-tests/**/*.js\" \"**/*.json\"" + }, + "author": "Eclipse Thingweb (https://thingweb.io/)", + "license": "EPL-2.0 OR W3C-20150513", + "dependencies": { + "body-parser": "^1.20.2", + "cbor": "^9.0.1", + "dotenv": "^16.3.1", + "eventsource": "^2.0.2", + "express": "^4.18.2", + "json-placeholder-replacer": "^1.0.35", + "winston": "^3.14.2", + "winston-loki": "^6.1.2" + }, + "devDependencies": { + "@types/express": "^4.17.17" + } } diff --git a/things/calculator/http/express/test/client.test.js b/things/calculator/http/express/test/client.test.js index 13fb724..e545655 100644 --- a/things/calculator/http/express/test/client.test.js +++ b/things/calculator/http/express/test/client.test.js @@ -1,219 +1,267 @@ -const chai = require("chai") -const chaiAsPromised = require("chai-as-promised") +/******************************************************************************** + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the W3C Software Notice and + * Document License (2015-05-13) which is available at + * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document. + * + * SPDX-License-Identifier: EPL-2.0 OR W3C-20150513 + ********************************************************************************/ -const { Servient } = require("@node-wot/core") -const { HttpClientFactory } = require("@node-wot/binding-http") -const { simplePort, contentNegotiationPort } = require('./fixtures') +const chai = require("chai"); +const chaiAsPromised = require("chai-as-promised"); -chai.use(chaiAsPromised) -const expect = chai.expect +const { Servient } = require("@node-wot/core"); +const { HttpClientFactory } = require("@node-wot/binding-http"); +const { simplePort, contentNegotiationPort } = require("./fixtures"); -const servient = new Servient() -servient.addClientFactory(new HttpClientFactory()) -let WoT +chai.use(chaiAsPromised); +const expect = chai.expect; + +const servient = new Servient(); +servient.addClientFactory(new HttpClientFactory()); +let WoT; const readProperty = async (thing, propertyName) => { try { - const response = await thing.readProperty(propertyName) - return await response.value() - } catch(error) { - console.error(`Error: ${error}`) + const response = await thing.readProperty(propertyName); + return await response.value(); + } catch (error) { + console.error(`Error: ${error}`); } -} +}; describe("Client Tests", () => { before(async () => { try { - WoT = await servient.start() - } catch(error) { - console.error(error) + WoT = await servient.start(); + } catch (error) { + console.error(error); } - }) - + }); + after(async () => { - await servient.shutdown() - }) + await servient.shutdown(); + }); describe("Simple Calculator", () => { - let thing + let thing; before(async () => { try { - const td = await WoT.requestThingDescription(`http://localhost:${simplePort}/http-express-calculator-simple`) - thing = await WoT.consume(td) - } catch(error) { - console.error(error) + const td = await WoT.requestThingDescription( + `http://localhost:${simplePort}/http-express-calculator-simple` + ); + thing = await WoT.consume(td); + } catch (error) { + console.error(error); } - }) + }); describe("result property", () => { it("should return initial value", async () => { - const value = await readProperty(thing, "result") - expect(value).to.be.equal(0) - }) - + const value = await readProperty(thing, "result"); + expect(value).to.be.equal(0); + }); + it("should return sum when adding value to the existing result", async () => { - const resultValue = await readProperty(thing, "result") - const valueToAdd = 12 - await thing.invokeAction("add", valueToAdd) - const newResultValue = await readProperty(thing, "result") - expect(newResultValue).to.be.equal(resultValue + valueToAdd) - }) - - it("should return sum when subtracting value from the existing result", async() => { - const resultValue = await readProperty(thing, "result") - const valueToSubtract = 3 - await thing.invokeAction("subtract", valueToSubtract) - const newResultValue = await readProperty(thing, "result") - expect(newResultValue).to.be.equal(resultValue - valueToSubtract) - }) - }) + const resultValue = await readProperty(thing, "result"); + const valueToAdd = 12; + await thing.invokeAction("add", valueToAdd); + const newResultValue = await readProperty(thing, "result"); + expect(newResultValue).to.be.equal(resultValue + valueToAdd); + }); + + it("should return sum when subtracting value from the existing result", async () => { + const resultValue = await readProperty(thing, "result"); + const valueToSubtract = 3; + await thing.invokeAction("subtract", valueToSubtract); + const newResultValue = await readProperty(thing, "result"); + expect(newResultValue).to.be.equal( + resultValue - valueToSubtract + ); + }); + }); describe("lastChange property", () => { it("should observe a change when the result is changed", async () => { setTimeout(async () => { - await thing.invokeAction('add', 1) - }, 200) + await thing.invokeAction("add", 1); + }, 200); - let value - const subscription = thing.observeProperty('lastChange', async (response) => { - value = await response.value() - }) + let value; + const subscription = thing.observeProperty( + "lastChange", + async (response) => { + value = await response.value(); + } + ); setTimeout(async () => { - expect(value).to.be.not.undefined - await subscription.stop() - }) - }) - }) - + expect(value).to.be.not.undefined; + await subscription.stop(); + }); + }); + }); + describe("add action", () => { it("should return sum when adding value to the existing result", async () => { - const resultValue = await readProperty(thing, "result") - const valueToAdd = 12 - const response = await thing.invokeAction("add", valueToAdd) - const actionResultValue = await response.value() - expect(actionResultValue).to.be.equal(resultValue + valueToAdd) - }) - }) - + const resultValue = await readProperty(thing, "result"); + const valueToAdd = 12; + const response = await thing.invokeAction("add", valueToAdd); + const actionResultValue = await response.value(); + expect(actionResultValue).to.be.equal(resultValue + valueToAdd); + }); + }); + describe("subtract action", () => { - it("should return sum when subtracting value from the existing result", async() => { - const resultValue = await readProperty(thing, "result") - const valueToSubtract = 3 - const response = await thing.invokeAction("subtract", valueToSubtract) - const actionResultValue = await response.value() - expect(actionResultValue).to.be.equal(resultValue - valueToSubtract) - }) - }) - + it("should return sum when subtracting value from the existing result", async () => { + const resultValue = await readProperty(thing, "result"); + const valueToSubtract = 3; + const response = await thing.invokeAction( + "subtract", + valueToSubtract + ); + const actionResultValue = await response.value(); + expect(actionResultValue).to.be.equal( + resultValue - valueToSubtract + ); + }); + }); + describe("update event", () => { it("should return the update message when subscribed", async () => { - const resultValue = await readProperty(thing, "result") - const valueToAdd = 13 - - const actionTimeout = setInterval(async () => { - await thing.invokeAction('add', valueToAdd) - }, 200) - - const subscription = await thing.subscribeEvent("update", async (response) => { - await expect(response.value).to.have.eventually.be.equal(resultValue + valueToAdd) - }) - - await subscription.stop() - }) - }) - }) + const resultValue = await readProperty(thing, "result"); + const valueToAdd = 13; + + setTimeout(async () => { + await thing.invokeAction("add", valueToAdd); + }, 200); + + const subscription = await thing.subscribeEvent( + "update", + async (response) => { + await expect( + response.value + ).to.have.eventually.be.equal(resultValue + valueToAdd); + } + ); + + await subscription.stop(); + }); + }); + }); describe("Content Negotiation Calculator", () => { - let thing + let thing; before(async () => { try { - const td = await WoT.requestThingDescription(`http://localhost:${contentNegotiationPort}/http-express-calculator-content-negotiation`) - thing = await WoT.consume(td) - } catch(error) { - console.error(error) + const td = await WoT.requestThingDescription( + `http://localhost:${contentNegotiationPort}/http-express-calculator-content-negotiation` + ); + thing = await WoT.consume(td); + } catch (error) { + console.error(error); } - }) - + }); + describe("result property", () => { it("should return initial value", async () => { - const value = await readProperty(thing, "result") - expect(value).to.be.equal(0) - }) - + const value = await readProperty(thing, "result"); + expect(value).to.be.equal(0); + }); + it("should return sum when adding value to the existing result", async () => { - const resultValue = await readProperty(thing, "result") - const valueToAdd = 12 - await thing.invokeAction("add", valueToAdd) - const newResultValue = await readProperty(thing, "result") - expect(newResultValue).to.be.equal(resultValue + valueToAdd) - }) - + const resultValue = await readProperty(thing, "result"); + const valueToAdd = 12; + await thing.invokeAction("add", valueToAdd); + const newResultValue = await readProperty(thing, "result"); + expect(newResultValue).to.be.equal(resultValue + valueToAdd); + }); + it("should return sum when subtracting value from the existing result", async () => { - const resultValue = await readProperty(thing, "result") - const valueToSubtract = 3 - await thing.invokeAction("subtract", valueToSubtract) - const newResultValue = await readProperty(thing, "result") - expect(newResultValue).to.be.equal(resultValue - valueToSubtract) - }) - }) + const resultValue = await readProperty(thing, "result"); + const valueToSubtract = 3; + await thing.invokeAction("subtract", valueToSubtract); + const newResultValue = await readProperty(thing, "result"); + expect(newResultValue).to.be.equal( + resultValue - valueToSubtract + ); + }); + }); describe("lastChange property", () => { it("should observe a change when the result is changed", async () => { setTimeout(async () => { - await thing.invokeAction('add', 1) - }, 200) + await thing.invokeAction("add", 1); + }, 200); - let value - const subscription = thing.observeProperty('lastChange', async (response) => { - value = await response.value() - }) + let value; + const subscription = thing.observeProperty( + "lastChange", + async (response) => { + value = await response.value(); + } + ); setTimeout(async () => { - expect(value).to.be.not.undefined - await subscription.stop() - }) - }) - }) - + expect(value).to.be.not.undefined; + await subscription.stop(); + }); + }); + }); + describe("add action", () => { it("should return sum when adding value to the existing result", async () => { - const resultValue = await readProperty(thing, "result") - const valueToAdd = 12 - const response = await thing.invokeAction("add", valueToAdd) - const actionResultValue = await response.value() - expect(actionResultValue).to.be.equal(resultValue + valueToAdd) - }) - }) - + const resultValue = await readProperty(thing, "result"); + const valueToAdd = 12; + const response = await thing.invokeAction("add", valueToAdd); + const actionResultValue = await response.value(); + expect(actionResultValue).to.be.equal(resultValue + valueToAdd); + }); + }); + describe("subtract action", () => { - it("should return sum when subtracting value from the existing result", async() => { - const resultValue = await readProperty(thing, "result") - const valueToSubtract = 3 - const response = await thing.invokeAction("subtract", valueToSubtract) - const actionResultValue = await response.value() - expect(actionResultValue).to.be.equal(resultValue - valueToSubtract) - }) - }) - + it("should return sum when subtracting value from the existing result", async () => { + const resultValue = await readProperty(thing, "result"); + const valueToSubtract = 3; + const response = await thing.invokeAction( + "subtract", + valueToSubtract + ); + const actionResultValue = await response.value(); + expect(actionResultValue).to.be.equal( + resultValue - valueToSubtract + ); + }); + }); + describe("update event", () => { it("should return the update message when subscribed", async () => { - const resultValue = await readProperty(thing, "result") - const valueToAdd = 13 - - const actionTimeout = setInterval(async () => { - await thing.invokeAction('add', valueToAdd) - }, 200) - - const subscription = await thing.subscribeEvent("update", async (response) => { - await expect(response.value).to.have.eventually.be.equal(resultValue + valueToAdd) - }) - - await subscription.stop() - }) - }) - }) -}) + const resultValue = await readProperty(thing, "result"); + const valueToAdd = 13; + + setTimeout(async () => { + await thing.invokeAction("add", valueToAdd); + }, 200); + + const subscription = await thing.subscribeEvent( + "update", + async (response) => { + await expect( + response.value + ).to.have.eventually.be.equal(resultValue + valueToAdd); + } + ); + await subscription.stop(); + }); + }); + }); +}); diff --git a/things/calculator/http/express/test/fixtures.js b/things/calculator/http/express/test/fixtures.js index f865377..eefa5a3 100644 --- a/things/calculator/http/express/test/fixtures.js +++ b/things/calculator/http/express/test/fixtures.js @@ -1,45 +1,70 @@ -const { getInitiateMain } = require("../../../../../util/dist/util") -const path = require("node:path") +/******************************************************************************** + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the W3C Software Notice and + * Document License (2015-05-13) which is available at + * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document. + * + * SPDX-License-Identifier: EPL-2.0 OR W3C-20150513 + ********************************************************************************/ -let simpleThingProcess -let contentNegotiationThingProcess -let response -const simplePort = 3000 -const contentNegotiationPort = 3001 +const { getInitiateMain } = require("../../../../../util/dist/util"); +const path = require("node:path"); -const mochaGlobalSetup = async function() { +let simpleThingProcess; +let contentNegotiationThingProcess; +let response; +const simplePort = 3000; +const contentNegotiationPort = 3001; + +const mochaGlobalSetup = async function () { try { - response = await getInitiateMain('node', [path.join(__dirname, '..', 'http-simple-calculator.js'), '-p', `${simplePort}`]) - simpleThingProcess = response.process - } - catch(error) { - console.error(error) - simpleThingProcess = error.process + response = await getInitiateMain("node", [ + path.join(__dirname, "..", "http-simple-calculator.js"), + "-p", + `${simplePort}`, + ]); + simpleThingProcess = response.process; + } catch (error) { + console.error(error); + simpleThingProcess = error.process; } try { - response = await getInitiateMain('node', [path.join(__dirname, '..', 'http-content-negotiation-calculator.js'), '-p', `${contentNegotiationPort}`]) - contentNegotiationThingProcess = response.process - } - catch (error) { - console.error(error) - contentNegotiationThingProcess = error.process + response = await getInitiateMain("node", [ + path.join( + __dirname, + "..", + "http-content-negotiation-calculator.js" + ), + "-p", + `${contentNegotiationPort}`, + ]); + contentNegotiationThingProcess = response.process; + } catch (error) { + console.error(error); + contentNegotiationThingProcess = error.process; } -} +}; -const mochaGlobalTeardown = function() { - if(simpleThingProcess) { - simpleThingProcess.kill() +const mochaGlobalTeardown = function () { + if (simpleThingProcess) { + simpleThingProcess.kill(); } - if(contentNegotiationThingProcess) { - contentNegotiationThingProcess.kill() + if (contentNegotiationThingProcess) { + contentNegotiationThingProcess.kill(); } -} +}; module.exports = { simplePort, contentNegotiationPort, mochaGlobalSetup, - mochaGlobalTeardown -} \ No newline at end of file + mochaGlobalTeardown, +}; diff --git a/things/calculator/http/express/test/td.test.js b/things/calculator/http/express/test/td.test.js index 5e2cdad..3a863f6 100644 --- a/things/calculator/http/express/test/td.test.js +++ b/things/calculator/http/express/test/td.test.js @@ -1,74 +1,98 @@ -const chai = require('chai') -const http = require('http') -const { getTDValidate } = require('../../../../../util/dist/util') -const { simplePort, contentNegotiationPort } = require('./fixtures') +/******************************************************************************** + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the W3C Software Notice and + * Document License (2015-05-13) which is available at + * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document. + * + * SPDX-License-Identifier: EPL-2.0 OR W3C-20150513 + ********************************************************************************/ -const expect = chai.expect +const chai = require("chai"); +const http = require("http"); +const { getTDValidate } = require("../../../../../util/dist/util"); +const { simplePort, contentNegotiationPort } = require("./fixtures"); -describe('Calculator HTTP JS', () => { - let validate +const expect = chai.expect; - before(async () => { - const tdValidate = getTDValidate() - - try { - const response = await Promise.all([tdValidate]) - validate = response[0].validate - } - catch (error) { - console.log(error) - } - }) +describe("Calculator HTTP JS", () => { + let validate; - describe('Calculator Simple', () => { - it('should have a valid TD', (done) => { - http.get(`http://localhost:${simplePort}/http-express-calculator-simple`, function (response) { - const body = [] - response.on('data', (chunk) => { - body.push(chunk) - }) - - response.on('end', () => { - try { - const result = JSON.parse(Buffer.concat(body).toString()) - const valid = validate(result) - expect(valid).to.be.true - done() - } catch (error) { - console.log(error) - } - }) - }) - }) - }) - - describe('Calculator Content Negotiation', () => { - it('should have a valid TD', (done) => { - http.get({ - hostname: 'localhost', - port: contentNegotiationPort, - path: '/http-express-calculator-content-negotiation', - method: 'GET', - headers: { - accept: 'application/json' + before(async () => { + const tdValidate = getTDValidate(); + + try { + const response = await Promise.all([tdValidate]); + validate = response[0].validate; + } catch (error) { + console.log(error); } - }, function (response) { - const body = [] - response.on('data', (chunk) => { - body.push(chunk) - }) - - response.on('end', () => { - try { - const result = JSON.parse(Buffer.concat(body).toString()) - const valid = validate(result) - expect(valid).to.be.true - done() - } catch (error) { - console.log(error) - } - }) - }) - }) - }) -}) + }); + + describe("Calculator Simple", () => { + it("should have a valid TD", (done) => { + http.get( + `http://localhost:${simplePort}/http-express-calculator-simple`, + function (response) { + const body = []; + response.on("data", (chunk) => { + body.push(chunk); + }); + + response.on("end", () => { + try { + const result = JSON.parse( + Buffer.concat(body).toString() + ); + const valid = validate(result); + expect(valid).to.be.true; + done(); + } catch (error) { + console.log(error); + } + }); + } + ); + }); + }); + + describe("Calculator Content Negotiation", () => { + it("should have a valid TD", (done) => { + http.get( + { + hostname: "localhost", + port: contentNegotiationPort, + path: "/http-express-calculator-content-negotiation", + method: "GET", + headers: { + accept: "application/json", + }, + }, + function (response) { + const body = []; + response.on("data", (chunk) => { + body.push(chunk); + }); + + response.on("end", () => { + try { + const result = JSON.parse( + Buffer.concat(body).toString() + ); + const valid = validate(result); + expect(valid).to.be.true; + done(); + } catch (error) { + console.log(error); + } + }); + } + ); + }); + }); +}); diff --git a/things/calculator/http/flask/test/td.test.js b/things/calculator/http/flask/test/td.test.js index 0eeb85a..aba3bd5 100644 --- a/things/calculator/http/flask/test/td.test.js +++ b/things/calculator/http/flask/test/td.test.js @@ -1,86 +1,94 @@ -const Ajv = require('ajv') -const chai = require('chai') -const http = require('http') -const https = require('https') -const path = require('path') +const Ajv = require("ajv"); +const chai = require("chai"); +const http = require("http"); +const https = require("https"); +const path = require("path"); -const spawn = require('child_process').spawn +const spawn = require("child_process").spawn; -const ajv = new Ajv({ strict: false, allErrors: true, validateFormats: false }) +const ajv = new Ajv({ strict: false, allErrors: true, validateFormats: false }); -const expect = chai.expect -const port = 5000 -let thingProcess +const expect = chai.expect; +const port = 5000; +let thingProcess; -describe('Calculator HTTP Flask', () => { - let validate +describe("Calculator HTTP Flask", () => { + let validate; - before(async () => { - const initiateMain = new Promise(async (resolve, reject) => { - thingProcess = spawn( - 'poetry', - ['run', 'python', 'main.py'], - { cwd: path.join(__dirname, '..') } - ) - thingProcess.stdout.on('data', (data) => { - if (data.toString().trim() === 'ThingIsReady') { - resolve('Success') - } - }) - thingProcess.stderr.on('data', (data) => { - console.log(`Error: ${data}`) - }) - thingProcess.on('error', (error) => { - reject(`Error: ${error}`) - }) - thingProcess.on('close', () => { - reject('Failed to initiate the main script.') - }) - }) + before(async () => { + const initiateMain = new Promise(async (resolve, reject) => { + thingProcess = spawn("poetry", ["run", "python", "main.py"], { + cwd: path.join(__dirname, ".."), + }); + thingProcess.stdout.on("data", (data) => { + if (data.toString().trim() === "ThingIsReady") { + resolve("Success"); + } + }); + thingProcess.stderr.on("data", (data) => { + console.log(`Error: ${data}`); + }); + thingProcess.on("error", (error) => { + reject(`Error: ${error}`); + }); + thingProcess.on("close", () => { + reject("Failed to initiate the main script."); + }); + }); - const getJSONSchema = new Promise((resolve, reject) => { - https.get('https://raw.githubusercontent.com/w3c/wot-thing-description/main/validation/td-json-schema-validation.json', function (response) { - const body = [] - response.on('data', (chunk) => { - body.push(chunk) - }) + const getJSONSchema = new Promise((resolve, reject) => { + https.get( + "https://raw.githubusercontent.com/w3c/wot-thing-description/main/validation/td-json-schema-validation.json", + function (response) { + const body = []; + response.on("data", (chunk) => { + body.push(chunk); + }); - response.on('end', () => { - const tdSchema = JSON.parse(Buffer.concat(body).toString()) - validate = ajv.compile(tdSchema) - resolve('Success') - }) - }) - }) + response.on("end", () => { + const tdSchema = JSON.parse( + Buffer.concat(body).toString() + ); + validate = ajv.compile(tdSchema); + resolve("Success"); + }); + } + ); + }); - await Promise.all([initiateMain, getJSONSchema]).then(data => { - if (data[0] !== 'Success' || data[1] !== 'Success') { - console.log(`initiateMain: ${data[0]}`) - console.log(`getJSONSchema: ${data[1]}`) - } - }) - }) + await Promise.all([initiateMain, getJSONSchema]).then((data) => { + if (data[0] !== "Success" || data[1] !== "Success") { + console.log(`initiateMain: ${data[0]}`); + console.log(`getJSONSchema: ${data[1]}`); + } + }); + }); - after(() => { - thingProcess.kill() - }) + after(() => { + thingProcess.kill(); + }); - it('should have a valid TD', (done) => { - // wait for server to initiate - setTimeout(() => { - http.get(`http://127.0.0.1:${port}/http-flask-calculator`, function (response) { - const body = [] - response.on('data', (chunk) => { - body.push(chunk) - }) + it("should have a valid TD", (done) => { + // wait for server to initiate + setTimeout(() => { + http.get( + `http://127.0.0.1:${port}/http-flask-calculator`, + function (response) { + const body = []; + response.on("data", (chunk) => { + body.push(chunk); + }); - response.on('end', () => { - const result = JSON.parse(Buffer.concat(body).toString()) - const valid = validate(result) - expect(valid).to.be.true - done() - }) - }) - }, 1000) - }) -}) + response.on("end", () => { + const result = JSON.parse( + Buffer.concat(body).toString() + ); + const valid = validate(result); + expect(valid).to.be.true; + done(); + }); + } + ); + }, 1000); + }); +}); diff --git a/things/calculator/mqtt/js/.mocharc.json b/things/calculator/mqtt/js/.mocharc.json index 9d68ca0..4ac9c80 100644 --- a/things/calculator/mqtt/js/.mocharc.json +++ b/things/calculator/mqtt/js/.mocharc.json @@ -1,4 +1,4 @@ { "spec": "./test/**.test.js", "require": ["./test/fixtures.js"] -} \ No newline at end of file +} diff --git a/things/calculator/mqtt/js/main.js b/things/calculator/mqtt/js/main.js index 21928e8..1b62993 100644 --- a/things/calculator/mqtt/js/main.js +++ b/things/calculator/mqtt/js/main.js @@ -1,200 +1,323 @@ -const mqtt = require('mqtt') -const { parseArgs } = require('node:util') -const fs = require('fs') -const path = require('path') -const { JsonPlaceholderReplacer } = require('json-placeholder-replacer') -require('dotenv').config() - -const { createLogger, transports, format } = require('winston') -const LokiTransport = require('winston-loki') - -const brokerURI = process.env.BROKER_URI ?? 'test.mosquitto.org' -let portNumber = process.env.PORT ?? 1883 - -const { values: { port } } = parseArgs({ - options: { - port: { - type: 'string', - short: 'p' - } - } -}) +/******************************************************************************** + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the W3C Software Notice and + * Document License (2015-05-13) which is available at + * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document. + * + * SPDX-License-Identifier: EPL-2.0 OR W3C-20150513 + ********************************************************************************/ + +const mqtt = require("mqtt"); +const { parseArgs } = require("node:util"); +const fs = require("fs"); +const path = require("path"); +const { JsonPlaceholderReplacer } = require("json-placeholder-replacer"); +require("dotenv").config(); + +const { createLogger, transports, format } = require("winston"); +const LokiTransport = require("winston-loki"); + +const brokerURI = process.env.BROKER_URI ?? "test.mosquitto.org"; +let portNumber = process.env.PORT ?? 1883; + +const { + values: { port }, +} = parseArgs({ + options: { + port: { + type: "string", + short: "p", + }, + }, +}); if (port && !isNaN(parseInt(port))) { - portNumber = parseInt(port) + portNumber = parseInt(port); } -const thingName = 'mqtt-calculator' -const PROPERTIES = 'properties' -const ACTIONS = 'actions' -const EVENTS = 'events' - -let logger = createLogger({ - transports: [new LokiTransport({ - host:`${process.env.LOKI_HOSTNAME}:${process.env.LOKI_PORT}`, - labels: { thing: thingName }, - json: true, - format: format.json(), - replaceTimestamp: true, - onConnectionError: (err) => console.error(err) - }), - new transports.Console({ - format: format.combine(format.simple(), format.colorize()) - })] -}) - -const broker = mqtt.connect(`mqtt://${brokerURI}`, { port: portNumber }) -const tmPath = process.env.TM_PATH - -if (process.platform === 'win32') { - tmPath.split(path.sep).join(path.win32.sep) +const thingName = "mqtt-calculator"; +const PROPERTIES = "properties"; +const ACTIONS = "actions"; +const EVENTS = "events"; + +const logger = createLogger({ + transports: [ + new LokiTransport({ + host: `${process.env.LOKI_HOSTNAME}:${process.env.LOKI_PORT}`, + labels: { thing: thingName }, + json: true, + format: format.json(), + replaceTimestamp: true, + onConnectionError: (err) => console.error(err), + }), + new transports.Console({ + format: format.combine(format.simple(), format.colorize()), + }), + ], +}); + +const broker = mqtt.connect(`mqtt://${brokerURI}`, { port: portNumber }); +const tmPath = process.env.TM_PATH; + +if (process.platform === "win32") { + tmPath.split(path.sep).join(path.win32.sep); } -const thingModel = JSON.parse(fs.readFileSync(path.join(__dirname, tmPath))) +const thingModel = JSON.parse(fs.readFileSync(path.join(__dirname, tmPath))); -const placeholderReplacer = new JsonPlaceholderReplacer() +const placeholderReplacer = new JsonPlaceholderReplacer(); placeholderReplacer.addVariableMap({ - PROTOCOL: 'mqtt', - THING_NAME: thingName, - PROPERTIES, - ACTIONS, - EVENTS, - HOSTNAME: brokerURI, - PORT_NUMBER: portNumber, - RESULT_OBSERVABLE: true, - LAST_CHANGE_OBSERVABLE: true -}) -const thingDescription = placeholderReplacer.replace(thingModel) -thingDescription['@type'] = 'Thing' + PROTOCOL: "mqtt", + THING_NAME: thingName, + PROPERTIES, + ACTIONS, + EVENTS, + HOSTNAME: brokerURI, + PORT_NUMBER: portNumber, + RESULT_OBSERVABLE: true, + LAST_CHANGE_OBSERVABLE: true, +}); +const thingDescription = placeholderReplacer.replace(thingModel); +thingDescription["@type"] = "Thing"; const defaultForm = { - "href": "", - "contentType": "application/json", - "op": [] -} - -//add properties forms -for (const key in thingDescription['properties']) { + href: "", + contentType: "application/json", + op: [], +}; + +// add properties forms +for (const key in thingDescription.properties) { + if (!Object.hasOwn(thingDescription.properties, key)) { + continue; + } - thingDescription['properties'][key]['forms'] = [] + thingDescription.properties[key].forms = []; - const newFormRead = JSON.parse(JSON.stringify(defaultForm)) - newFormRead['href'] = `properties/${key}` - newFormRead['op'] = ["readproperty"] + const newFormRead = JSON.parse(JSON.stringify(defaultForm)); + newFormRead.href = `properties/${key}`; + newFormRead.op = ["readproperty"]; - thingDescription['properties'][key]['forms'].push(newFormRead) + thingDescription.properties[key].forms.push(newFormRead); } -//add actions forms -for (const key in thingDescription['actions']) { +// add actions forms +for (const key in thingDescription.actions) { + if (!Object.hasOwn(thingDescription.actions, key)) { + continue; + } - thingDescription['actions'][key]['forms'] = [] + thingDescription.actions[key].forms = []; - const newForm = JSON.parse(JSON.stringify(defaultForm)) - newForm['href'] = `actions/${key}` - newForm['op'] = ["invokeaction"] + const newForm = JSON.parse(JSON.stringify(defaultForm)); + newForm.href = `actions/${key}`; + newForm.op = ["invokeaction"]; - thingDescription['actions'][key]['forms'].push(newForm) + thingDescription.actions[key].forms.push(newForm); } -//add events forms -for (const key in thingDescription['events']) { +// add events forms +for (const key in thingDescription.events) { + if (!Object.hasOwn(thingDescription.events, key)) { + continue; + } - thingDescription['events'][key]['data']['type'] = "string" + thingDescription.events[key].data.type = "string"; - thingDescription['events'][key]['forms'] = [] + thingDescription.events[key].forms = []; - const newForm = JSON.parse(JSON.stringify(defaultForm)) - newForm['href'] = `events/${key}` - newForm['op'] = ["subscribeevent", "unsubscribeevent"] + const newForm = JSON.parse(JSON.stringify(defaultForm)); + newForm.href = `events/${key}`; + newForm.op = ["subscribeevent", "unsubscribeevent"]; - thingDescription['events'][key]['forms'].push(newForm) + thingDescription.events[key].forms.push(newForm); } -fs.writeFile(`${thingName}.td.json`, JSON.stringify(thingDescription, 4, 4), 'utf-8', function(){}) - -broker.on('connect', () => { - console.log(`Connected to broker via port ${portNumber}`) - broker.subscribe(`${thingName}/${PROPERTIES}/result`) - broker.subscribe(`${thingName}/${PROPERTIES}/lastChange`) - broker.subscribe(`${thingName}/${ACTIONS}/add`) - broker.subscribe(`${thingName}/${ACTIONS}/subtract`) - broker.subscribe(`${thingName}/${EVENTS}/update`) -}) +fs.writeFile( + `${thingName}.td.json`, + JSON.stringify(thingDescription, 4, 4), + "utf-8", + function () {} +); + +broker.on("connect", () => { + console.log(`Connected to broker via port ${portNumber}`); + broker.subscribe(`${thingName}/${PROPERTIES}/result`); + broker.subscribe(`${thingName}/${PROPERTIES}/lastChange`); + broker.subscribe(`${thingName}/${ACTIONS}/add`); + broker.subscribe(`${thingName}/${ACTIONS}/subtract`); + broker.subscribe(`${thingName}/${EVENTS}/update`); +}); const setResult = (value) => { - result = value - logger.info({ message: `${result}`, labels: { affordance: 'property', affordanceName: 'result', messageType: 'updateProperty' }}) -} + result = value; + logger.info({ + message: `${result}`, + labels: { + affordance: "property", + affordanceName: "result", + messageType: "updateProperty", + }, + }); +}; const setLastChange = (value) => { - lastChange = value - logger.info({ message: `${lastChange}`, labels: { affordance: 'property', affordanceName: 'lastChange', messageType: 'updateProperty' }}) -} - -let result -setResult(0) - -let lastChange -setLastChange(new Date().toISOString()) - -broker.on('message', (topic, payload, packet) => { - const segments = topic.split('/') - - if (segments[0] !== thingName) { - return - } - - if (segments[1] === PROPERTIES) { - if (segments.length === 3 && segments[2] === 'result') { - console.log(`Result is : ${result}`) - logger.info({ message: `${result}`, labels: { affordance: 'property', affordanceName: 'result', messageType: 'updateProperty' }}) + lastChange = value; + logger.info({ + message: `${lastChange}`, + labels: { + affordance: "property", + affordanceName: "lastChange", + messageType: "updateProperty", + }, + }); +}; + +let result; +setResult(0); + +let lastChange; +setLastChange(new Date().toISOString()); + +broker.on("message", (topic, payload, packet) => { + const segments = topic.split("/"); + + if (segments[0] !== thingName) { + return; } - if (segments.length === 3 && segments[2] === 'lastChange') { - console.log(`Last change : ${lastChange}`) - logger.info({ message: `${lastChange}`, labels: { affordance: 'property', affordanceName: 'lastChange', messageType: 'updateProperty' }}) - } - } - - if (segments[1] === ACTIONS) { - if (segments.length === 3 && segments[2] === 'add') { - const parsedValue = parseInt(payload.toString()) - - if (isNaN(parsedValue)) { - return - } else { - logger.info({ message: 'Action invoked.', labels: { affordance: 'action', op: 'invokeaction', affordanceName: 'add' }}) - logger.info({ message: `${parsedValue}`, labels: { affordance: 'action', op: 'invokeaction', affordanceName: 'add', messageType: 'actionInput' }}) - setResult(result + parsedValue); - setLastChange(new Date()); - logger.info({ message: `${result}`, labels: { affordance: 'action', op: 'invokeaction', affordanceName: 'add', messageType: 'actionOutput' }}) - broker.publish(`${thingName}/${PROPERTIES}/result`, `${result}`, { retain: true }) - broker.publish(`${thingName}/${PROPERTIES}/lastChange`, `${lastChange}`, { retain: true }) - } + if (segments[1] === PROPERTIES) { + if (segments.length === 3 && segments[2] === "result") { + console.log(`Result is : ${result}`); + logger.info({ + message: `${result}`, + labels: { + affordance: "property", + affordanceName: "result", + messageType: "updateProperty", + }, + }); + } + + if (segments.length === 3 && segments[2] === "lastChange") { + console.log(`Last change : ${lastChange}`); + logger.info({ + message: `${lastChange}`, + labels: { + affordance: "property", + affordanceName: "lastChange", + messageType: "updateProperty", + }, + }); + } } - if (segments.length === 3 && segments[2] === 'subtract') { - const parsedValue = parseInt(payload.toString()) - - if (isNaN(parsedValue)) { - return - } else { - logger.info({ message: 'Action invoked.', labels: { affordance: 'action', op: 'invokeaction', affordanceName: 'subtract' }}) - logger.info({ message: `${parsedValue}`, labels: { affordance: 'action', op: 'invokeaction', affordanceName: 'subtract', messageType: 'actionInput' }}) - setResult(result - parsedValue); - setLastChange(new Date()); - logger.info({ message: `${result}`, labels: { affordance: 'action', op: 'invokeaction', affordanceName: 'subtract', messageType: 'actionOutput' }}) - broker.publish(`${thingName}/${PROPERTIES}/result`, `${result}`, { retain: true }) - broker.publish(`${thingName}/${PROPERTIES}/lastChange`, `${lastChange}`, { retain: true }) - } + if (segments[1] === ACTIONS) { + if (segments.length === 3 && segments[2] === "add") { + const parsedValue = parseInt(payload.toString()); + + if (isNaN(parsedValue)) { + return; + } else { + logger.info({ + message: "Action invoked.", + labels: { + affordance: "action", + op: "invokeaction", + affordanceName: "add", + }, + }); + logger.info({ + message: `${parsedValue}`, + labels: { + affordance: "action", + op: "invokeaction", + affordanceName: "add", + messageType: "actionInput", + }, + }); + setResult(result + parsedValue); + setLastChange(new Date()); + logger.info({ + message: `${result}`, + labels: { + affordance: "action", + op: "invokeaction", + affordanceName: "add", + messageType: "actionOutput", + }, + }); + broker.publish( + `${thingName}/${PROPERTIES}/result`, + `${result}`, + { retain: true } + ); + broker.publish( + `${thingName}/${PROPERTIES}/lastChange`, + `${lastChange}`, + { retain: true } + ); + } + } + + if (segments.length === 3 && segments[2] === "subtract") { + const parsedValue = parseInt(payload.toString()); + + if (!isNaN(parsedValue)) { + logger.info({ + message: "Action invoked.", + labels: { + affordance: "action", + op: "invokeaction", + affordanceName: "subtract", + }, + }); + logger.info({ + message: `${parsedValue}`, + labels: { + affordance: "action", + op: "invokeaction", + affordanceName: "subtract", + messageType: "actionInput", + }, + }); + setResult(result - parsedValue); + setLastChange(new Date()); + logger.info({ + message: `${result}`, + labels: { + affordance: "action", + op: "invokeaction", + affordanceName: "subtract", + messageType: "actionOutput", + }, + }); + broker.publish( + `${thingName}/${PROPERTIES}/result`, + `${result}`, + { retain: true } + ); + broker.publish( + `${thingName}/${PROPERTIES}/lastChange`, + `${lastChange}`, + { retain: true } + ); + } + } } - } -}) +}); setInterval(() => { - broker.publish(`${thingName}/${EVENTS}/update`, 'Updated the thing!') -}, 500) + broker.publish(`${thingName}/${EVENTS}/update`, "Updated the thing!"); +}, 500); -broker.publish(`${thingName}`, JSON.stringify(thingDescription), { retain: true }) -console.log('ThingIsReady') \ No newline at end of file +broker.publish(`${thingName}`, JSON.stringify(thingDescription), { + retain: true, +}); +console.log("ThingIsReady"); diff --git a/things/calculator/mqtt/js/mqtt-calculator.td.json b/things/calculator/mqtt/js/mqtt-calculator.td.json index c863bd3..b10141c 100644 --- a/things/calculator/mqtt/js/mqtt-calculator.td.json +++ b/things/calculator/mqtt/js/mqtt-calculator.td.json @@ -14,9 +14,7 @@ "scheme": "nosec" } }, - "security": [ - "nosec_sc" - ], + "security": ["nosec_sc"], "base": "mqtt://test.mosquitto.org:1883/mqtt-calculator/", "properties": { "result": { @@ -28,9 +26,7 @@ { "href": "properties/result", "contentType": "application/json", - "op": [ - "readproperty" - ] + "op": ["readproperty"] } ] }, @@ -44,9 +40,7 @@ { "href": "properties/lastChange", "contentType": "application/json", - "op": [ - "readproperty" - ] + "op": ["readproperty"] } ] } @@ -65,9 +59,7 @@ { "href": "actions/add", "contentType": "application/json", - "op": [ - "invokeaction" - ] + "op": ["invokeaction"] } ] }, @@ -84,9 +76,7 @@ { "href": "actions/subtract", "contentType": "application/json", - "op": [ - "invokeaction" - ] + "op": ["invokeaction"] } ] } @@ -100,12 +90,9 @@ { "href": "events/update", "contentType": "application/json", - "op": [ - "subscribeevent", - "unsubscribeevent" - ] + "op": ["subscribeevent", "unsubscribeevent"] } ] } } -} \ No newline at end of file +} diff --git a/things/calculator/mqtt/js/package.json b/things/calculator/mqtt/js/package.json index 5d0ba50..bf2f167 100644 --- a/things/calculator/mqtt/js/package.json +++ b/things/calculator/mqtt/js/package.json @@ -1,21 +1,26 @@ { - "name": "mqtt-calculator", - "version": "1.0.0", - "description": "MQTT Calculator", - "main": "main.js", - "keywords": [ - "wot", - "thing", - "iot", - "mqtt" - ], - "author": "Eclipse Thingweb (https://thingweb.io/)", - "license": "EPL-2.0 OR W3C-20150513", - "dependencies": { - "dotenv": "^16.3.1", - "json-placeholder-replacer": "^1.0.35", - "mqtt": "^4.3.8", - "winston": "^3.14.2", - "winston-loki": "^6.1.2" - } + "name": "mqtt-calculator", + "version": "1.0.0", + "description": "MQTT Calculator", + "main": "main.js", + "keywords": [ + "wot", + "thing", + "iot", + "mqtt" + ], + "scripts": { + "lint": "eslint .", + "lint:fix": "eslint . --fix", + "format": "prettier --write \"**/*.js\" \"test/**/*.js\" \"**/*.json\"" + }, + "author": "Eclipse Thingweb (https://thingweb.io/)", + "license": "EPL-2.0 OR W3C-20150513", + "dependencies": { + "dotenv": "^16.3.1", + "json-placeholder-replacer": "^1.0.35", + "mqtt": "^4.3.8", + "winston": "^3.14.2", + "winston-loki": "^6.1.2" + } } diff --git a/things/calculator/mqtt/js/test/client.test.js b/things/calculator/mqtt/js/test/client.test.js index d651c55..b74e0e7 100644 --- a/things/calculator/mqtt/js/test/client.test.js +++ b/things/calculator/mqtt/js/test/client.test.js @@ -1,99 +1,120 @@ -const chai = require("chai") -const chaiAsPromised = require("chai-as-promised") +/******************************************************************************** + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the W3C Software Notice and + * Document License (2015-05-13) which is available at + * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document. + * + * SPDX-License-Identifier: EPL-2.0 OR W3C-20150513 + ********************************************************************************/ -const { Servient } = require("@node-wot/core") -const { MqttClientFactory } = require("@node-wot/binding-mqtt") -const mqttTd = require('../mqtt-calculator.td.json') +const chai = require("chai"); +const chaiAsPromised = require("chai-as-promised"); -chai.use(chaiAsPromised) -const expect = chai.expect +const { Servient } = require("@node-wot/core"); +const { MqttClientFactory } = require("@node-wot/binding-mqtt"); +const mqttTd = require("../mqtt-calculator.td.json"); -const servient = new Servient() -servient.addClientFactory(new MqttClientFactory()) -let thing +chai.use(chaiAsPromised); +const expect = chai.expect; +const servient = new Servient(); +servient.addClientFactory(new MqttClientFactory()); +let thing; const readProperty = async (thing, propertyName) => { try { - const response = await thing.readProperty(propertyName) - return await response.value() - } catch(error) { - console.error(`Error: ${error}`) + const response = await thing.readProperty(propertyName); + return await response.value(); + } catch (error) { + console.error(`Error: ${error}`); } -} +}; /** * FIXME: To be able to test readProperty, issues https://github.com/eclipse-thingweb/node-wot/issues/980 * and https://github.com/eclipse-thingweb/node-wot/issues/1241 must be resolved. * Until then we can use subscriptions. */ - + describe.skip("Client Tests", () => { before(async () => { try { - const WoT = await servient.start() - thing = await WoT.consume(mqttTd) - } catch(error) { - console.error(error) + const WoT = await servient.start(); + thing = await WoT.consume(mqttTd); + } catch (error) { + console.error(error); } - }) + }); after(async () => { - await servient.shutdown() - }) + await servient.shutdown(); + }); describe("result property", () => { it("should return initial value", async () => { - const value = await readProperty(thing, "result") - expect(value).to.be.equal(0) - }) + const value = await readProperty(thing, "result"); + expect(value).to.be.equal(0); + }); it("should return sum when adding value to the existing result", async () => { - const resultValue = await readProperty(thing, "result") - const valueToAdd = 12 - await thing.invokeAction("add", valueToAdd) - const newResultValue = await readProperty(thing, "result") - expect(newResultValue).to.be.equal(resultValue + valueToAdd) - }) + const resultValue = await readProperty(thing, "result"); + const valueToAdd = 12; + await thing.invokeAction("add", valueToAdd); + const newResultValue = await readProperty(thing, "result"); + expect(newResultValue).to.be.equal(resultValue + valueToAdd); + }); - it("should return sum when subtracting value from the existing result", async() => { - const resultValue = await readProperty(thing, "result") - const valueToSubtract = 3 - await thing.invokeAction("subtract", valueToSubtract) - const newResultValue = await readProperty(thing, "result") - expect(newResultValue).to.be.equal(resultValue - valueToSubtract) - }) - }) + it("should return sum when subtracting value from the existing result", async () => { + const resultValue = await readProperty(thing, "result"); + const valueToSubtract = 3; + await thing.invokeAction("subtract", valueToSubtract); + const newResultValue = await readProperty(thing, "result"); + expect(newResultValue).to.be.equal(resultValue - valueToSubtract); + }); + }); describe("add action", () => { it("should return sum when adding value to the existing result", async () => { - const resultValue = await readProperty(thing, "result") - const valueToAdd = 12 - const response = await thing.invokeAction("add", valueToAdd) - const actionResultValue = await response.value() - expect(actionResultValue).to.be.equal(resultValue + valueToAdd) - }) - }) + const resultValue = await readProperty(thing, "result"); + const valueToAdd = 12; + const response = await thing.invokeAction("add", valueToAdd); + const actionResultValue = await response.value(); + expect(actionResultValue).to.be.equal(resultValue + valueToAdd); + }); + }); describe("subtract action", () => { - it("should return sum when subtracting value from the existing result", async() => { - const resultValue = await readProperty(thing, "result") - const valueToSubtract = 3 - const response = await thing.invokeAction("subtract", valueToSubtract) - const actionResultValue = await response.value() - expect(actionResultValue).to.be.equal(resultValue - valueToSubtract) - }) - }) + it("should return sum when subtracting value from the existing result", async () => { + const resultValue = await readProperty(thing, "result"); + const valueToSubtract = 3; + const response = await thing.invokeAction( + "subtract", + valueToSubtract + ); + const actionResultValue = await response.value(); + expect(actionResultValue).to.be.equal( + resultValue - valueToSubtract + ); + }); + }); describe("update event", () => { it("should return the update message when subscribed", async () => { - const subscription = await thing.subscribeEvent("update", async (response) => { - const value = await response.value() - console.log(value) - expect(value).to.be.equal("Updated the thing!") - subscription.stop() - }) - }) - }) -}) - + const subscription = await thing.subscribeEvent( + "update", + async (response) => { + const value = await response.value(); + console.log(value); + expect(value).to.be.equal("Updated the thing!"); + subscription.stop(); + } + ); + }); + }); +}); diff --git a/things/calculator/mqtt/js/test/fixtures.js b/things/calculator/mqtt/js/test/fixtures.js index eb72af6..7c22c05 100644 --- a/things/calculator/mqtt/js/test/fixtures.js +++ b/things/calculator/mqtt/js/test/fixtures.js @@ -1,23 +1,41 @@ -const { getInitiateMain } = require("../../../../../util/dist/util") -const path = require("node:path") +/******************************************************************************** + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the W3C Software Notice and + * Document License (2015-05-13) which is available at + * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document. + * + * SPDX-License-Identifier: EPL-2.0 OR W3C-20150513 + ********************************************************************************/ -let thingProcess -let response -const port = 1883 +const { getInitiateMain } = require("../../../../../util/dist/util"); +const path = require("node:path"); -exports.mochaGlobalSetup = async function() { +let thingProcess; +let response; +const port = 1883; + +exports.mochaGlobalSetup = async function () { try { - response = await getInitiateMain('node', [path.join(__dirname, '..', 'main.js'), '-p', `${port}`]) - thingProcess = response.process - } - catch(error) { - console.log(error) - thingProcess = error.process + response = await getInitiateMain("node", [ + path.join(__dirname, "..", "main.js"), + "-p", + `${port}`, + ]); + thingProcess = response.process; + } catch (error) { + console.log(error); + thingProcess = error.process; } -} +}; -exports.mochaGlobalTeardown = function() { +exports.mochaGlobalTeardown = function () { if (thingProcess) { - thingProcess.kill() + thingProcess.kill(); } -} \ No newline at end of file +}; diff --git a/things/calculator/mqtt/js/test/td.test.js b/things/calculator/mqtt/js/test/td.test.js index 7ecfc95..ea6b69c 100644 --- a/things/calculator/mqtt/js/test/td.test.js +++ b/things/calculator/mqtt/js/test/td.test.js @@ -1,41 +1,54 @@ -const chai = require('chai') -const mqtt = require('mqtt') -const { getTDValidate } = require("../../../../../util/dist/util") -const { port } = require('./fixtures') - - -const expect = chai.expect -const hostname = 'test.mosquitto.org' - -describe('Calculator MQTT JS', () => { - let validate - - before(async () => { - const tdValidate = getTDValidate() - - try { - const response = await Promise.all([tdValidate]) - validate = response[0].validate - } - catch (error) { - console.log(error) - } - }) - - it('should have a valid TD', (done) => { - const broker = mqtt.connect(`mqtt://${hostname}`, { port }) - broker.subscribe('mqtt-calculator') - - let valid = false - - broker.on('message', (topic, payload, packet) => { - valid = validate(JSON.parse(payload.toString())) - broker.end() - }) - - broker.on('close', () => { - expect(valid).to.be.true - done() - }) - }) -}) +/******************************************************************************** + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the W3C Software Notice and + * Document License (2015-05-13) which is available at + * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document. + * + * SPDX-License-Identifier: EPL-2.0 OR W3C-20150513 + ********************************************************************************/ + +const chai = require("chai"); +const mqtt = require("mqtt"); +const { getTDValidate } = require("../../../../../util/dist/util"); +const { port } = require("./fixtures"); + +const expect = chai.expect; +const hostname = "test.mosquitto.org"; + +describe("Calculator MQTT JS", () => { + let validate; + + before(async () => { + const tdValidate = getTDValidate(); + + try { + const response = await Promise.all([tdValidate]); + validate = response[0].validate; + } catch (error) { + console.log(error); + } + }); + + it("should have a valid TD", (done) => { + const broker = mqtt.connect(`mqtt://${hostname}`, { port }); + broker.subscribe("mqtt-calculator"); + + let valid = false; + + broker.on("message", (topic, payload, packet) => { + valid = validate(JSON.parse(payload.toString())); + broker.end(); + }); + + broker.on("close", () => { + expect(valid).to.be.true; + done(); + }); + }); +}); diff --git a/things/calculator/tm.test.js b/things/calculator/tm.test.js index 17728b5..48f9c5a 100644 --- a/things/calculator/tm.test.js +++ b/things/calculator/tm.test.js @@ -1,32 +1,35 @@ -const Ajv = require('ajv') -const chai = require('chai') -const https = require('https') +const Ajv = require("ajv"); +const chai = require("chai"); +const https = require("https"); -const ajv = new Ajv({ strict: false, allErrors: true, validateFormats: false }) +const ajv = new Ajv({ strict: false, allErrors: true, validateFormats: false }); -const expect = chai.expect +const expect = chai.expect; -describe('Calculator', () => { - let validate +describe("Calculator", () => { + let validate; - before((done) => { - https.get('https://raw.githubusercontent.com/w3c/wot-thing-description/main/validation/tm-json-schema-validation.json', function (response) { - const body = [] - response.on('data', (chunk) => { - body.push(chunk) - }) + before((done) => { + https.get( + "https://raw.githubusercontent.com/w3c/wot-thing-description/main/validation/tm-json-schema-validation.json", + function (response) { + const body = []; + response.on("data", (chunk) => { + body.push(chunk); + }); - response.on('end', () => { - const tmSchema = JSON.parse(Buffer.concat(body).toString()) - validate = ajv.compile(tmSchema) - done() - }) - }) - }) + response.on("end", () => { + const tmSchema = JSON.parse(Buffer.concat(body).toString()); + validate = ajv.compile(tmSchema); + done(); + }); + } + ); + }); - it('should have a valid TM', () => { - const calculatorTM = require('./calculator.tm.json') - const valid = validate(calculatorTM) - expect(valid).to.be.true - }) -}) + it("should have a valid TM", () => { + const calculatorTM = require("./calculator.tm.json"); + const valid = validate(calculatorTM); + expect(valid).to.be.true; + }); +}); diff --git a/things/data-schema-thing/data-schema-thing.tm.json b/things/data-schema-thing/data-schema-thing.tm.json index 947a8be..4d506d3 100644 --- a/things/data-schema-thing/data-schema-thing.tm.json +++ b/things/data-schema-thing/data-schema-thing.tm.json @@ -1,141 +1,141 @@ { - "@context":[ - "https://www.w3.org/2019/wot/td/v1", - "https://www.w3.org/2022/wot/td/v1.1", - { - "@language":"en" - } - ], - "@type":"tm:ThingModel", - "title":"{{THING_NAME}}", - "description": "Test Thing", - "base": "{{PROTOCOL}}://{{HOSTNAME}}:{{PORT_NUMBER}}/{{THING_NAME}}", - "properties": { - "bool": { - "title": "Boolean", - "description": "Property that can be set to true or false", - "type": "boolean" - }, - "int": { - "title": "Integer", - "description": "An integer value that can be read and written", - "type": "integer" - }, - "num": { - "title": "Number", - "description": "A floating point value that can be read and written", - "type": "number" - }, - "string": { - "title": "String", - "description": "A string value that can be read and written", - "type": "string" - }, - "array": { - "title": "Array", - "description": "An Array (List) with no structure that can be read and written", - "type": "array", - "items": {} - }, - "object": { - "title": "Object", - "description": "An object with id and name that can be read and written", - "type": "object", - "properties": { - "id": { - "title": "ID", - "description": "Integer identifier", - "type": "integer" - }, - "name": { - "title": "Name", - "description": "Name associated to the identifier", - "type": "string" + "@context": [ + "https://www.w3.org/2019/wot/td/v1", + "https://www.w3.org/2022/wot/td/v1.1", + { + "@language": "en" + } + ], + "@type": "tm:ThingModel", + "title": "{{THING_NAME}}", + "description": "Test Thing", + "base": "{{PROTOCOL}}://{{HOSTNAME}}:{{PORT_NUMBER}}/{{THING_NAME}}", + "properties": { + "bool": { + "title": "Boolean", + "description": "Property that can be set to true or false", + "type": "boolean" + }, + "int": { + "title": "Integer", + "description": "An integer value that can be read and written", + "type": "integer" + }, + "num": { + "title": "Number", + "description": "A floating point value that can be read and written", + "type": "number" + }, + "string": { + "title": "String", + "description": "A string value that can be read and written", + "type": "string" + }, + "array": { + "title": "Array", + "description": "An Array (List) with no structure that can be read and written", + "type": "array", + "items": {} + }, + "object": { + "title": "Object", + "description": "An object with id and name that can be read and written", + "type": "object", + "properties": { + "id": { + "title": "ID", + "description": "Integer identifier", + "type": "integer" + }, + "name": { + "title": "Name", + "description": "Name associated to the identifier", + "type": "string" + } + } } - } - } - }, - "actions": { - "void-void": { - "title": "void-void Action", - "description": "Action without input nor output" - }, - "void-int": { - "title": "void-int Action", - "description": "Action without input, but with integer output" - }, - "int-void": { - "title": "int-void Action", - "description": "Action with integer input, but without output", - "input": { "type": "integer" } - }, - "int-int": { - "title": "int-int Action", - "description": "Action with integer input and output", - "input": { "type": "integer" }, - "output": { "type": "integer" } - }, - "int-string": { - "title": "int-string Action", - "description": "Action with integer input and string output", - "input": { "type": "integer" }, - "output": { "type": "string" } - }, - "void-obj": { - "title": "void-obj Action", - "description": "Action without input, but with object output", - "output": { - "type": "object", - "properties": { - "prop1": { "type": "integer" }, - "prop2": { "type": "string" } - }, - "required": ["prop1", "prop2"] - } - }, - "obj-void": { - "title": "obj-void Action", - "description": "Action with object input, but without output", - "input": { - "type": "object", - "properties": { - "prop1": { "type": "integer" }, - "prop2": { "type": "string" } - }, - "required": ["prop1", "prop2"] - } - } - }, - "events": { - "on-bool": { - "title": "Bool Property Change", - "description": "Event with boolean data that is emitted when the bool property is written to", - "data": { "type": "boolean" } - }, - "on-int": { - "title": "Int Property Change", - "description": "Event with integer data that is emitted when the int property is written to ", - "data": { "type": "integer" } - }, - "on-num": { - "title": "Num Property Change", - "description": "Event with number data that is emitted when the num property is written to", - "data": { "type": "number" } - }, - "on-string": { - "title": "String Property Change", - "description": "Event with number data that is emitted when the string property is written to", - "data": { "type": "number" } }, - "on-array": { - "title": "Array Property Change", - "description": "Event with number data that is emitted when the array property is written to", - "data": { "type": "number" } + "actions": { + "void-void": { + "title": "void-void Action", + "description": "Action without input nor output" + }, + "void-int": { + "title": "void-int Action", + "description": "Action without input, but with integer output" + }, + "int-void": { + "title": "int-void Action", + "description": "Action with integer input, but without output", + "input": { "type": "integer" } + }, + "int-int": { + "title": "int-int Action", + "description": "Action with integer input and output", + "input": { "type": "integer" }, + "output": { "type": "integer" } + }, + "int-string": { + "title": "int-string Action", + "description": "Action with integer input and string output", + "input": { "type": "integer" }, + "output": { "type": "string" } + }, + "void-obj": { + "title": "void-obj Action", + "description": "Action without input, but with object output", + "output": { + "type": "object", + "properties": { + "prop1": { "type": "integer" }, + "prop2": { "type": "string" } + }, + "required": ["prop1", "prop2"] + } + }, + "obj-void": { + "title": "obj-void Action", + "description": "Action with object input, but without output", + "input": { + "type": "object", + "properties": { + "prop1": { "type": "integer" }, + "prop2": { "type": "string" } + }, + "required": ["prop1", "prop2"] + } + } }, - "on-object": { - "title": "Object Property Change", - "description": "Event with number data that is emitted when the object property is written to", - "data": { "type": "number" } + "events": { + "on-bool": { + "title": "Bool Property Change", + "description": "Event with boolean data that is emitted when the bool property is written to", + "data": { "type": "boolean" } + }, + "on-int": { + "title": "Int Property Change", + "description": "Event with integer data that is emitted when the int property is written to ", + "data": { "type": "integer" } + }, + "on-num": { + "title": "Num Property Change", + "description": "Event with number data that is emitted when the num property is written to", + "data": { "type": "number" } + }, + "on-string": { + "title": "String Property Change", + "description": "Event with number data that is emitted when the string property is written to", + "data": { "type": "number" } + }, + "on-array": { + "title": "Array Property Change", + "description": "Event with number data that is emitted when the array property is written to", + "data": { "type": "number" } + }, + "on-object": { + "title": "Object Property Change", + "description": "Event with number data that is emitted when the object property is written to", + "data": { "type": "number" } + } } - } } diff --git a/things/data-schema-thing/http/ts/.mocharc.json b/things/data-schema-thing/http/ts/.mocharc.json index b0ed45c..a0dd936 100644 --- a/things/data-schema-thing/http/ts/.mocharc.json +++ b/things/data-schema-thing/http/ts/.mocharc.json @@ -1,4 +1,4 @@ { "spec": "./test/**.test.ts", "require": ["ts-node/register", "./test/fixtures.ts"] -} \ No newline at end of file +} diff --git a/things/data-schema-thing/http/ts/package.json b/things/data-schema-thing/http/ts/package.json index 30be274..d8b0e50 100644 --- a/things/data-schema-thing/http/ts/package.json +++ b/things/data-schema-thing/http/ts/package.json @@ -1,31 +1,33 @@ { - "name": "http-data-schema-thing", - "version": "1.0.0", - "description": "HTTP Test Thing", - "main": "main.js", - "scripts": { - "build": "tsc -b", - "test": "mocha" - }, - "keywords": [ - "wot", - "thing", - "iot", - "http" - ], - "author": "Eclipse Thingweb (https://thingweb.io/)", - "license": "EPL-2.0 OR W3C-20150513", - "dependencies": { - "@node-wot/binding-http": "~0.8.16", - "@node-wot/core": "~0.8.16", - "dotenv": "^16.4.5", - "json-placeholder-replacer": "^2.0.5", - "wot-typescript-definitions": "^0.8.0-SNAPSHOT.29", - "winston": "^3.14.2", - "winston-loki": "^6.1.2" - }, - "devDependencies": { - "ts-node": "^10.9.2", - "typescript": "^4.7.4" - } + "name": "http-data-schema-thing", + "version": "1.0.0", + "description": "HTTP Test Thing", + "main": "main.js", + "scripts": { + "build": "tsc -b", + "test": "mocha", + "lint": "eslint .", + "lint:fix": "eslint . --fix" + }, + "keywords": [ + "wot", + "thing", + "iot", + "http" + ], + "author": "Eclipse Thingweb (https://thingweb.io/)", + "license": "EPL-2.0 OR W3C-20150513", + "dependencies": { + "@node-wot/binding-http": "~0.8.16", + "@node-wot/core": "~0.8.16", + "dotenv": "^16.4.5", + "json-placeholder-replacer": "^2.0.5", + "wot-typescript-definitions": "^0.8.0-SNAPSHOT.29", + "winston": "^3.14.2", + "winston-loki": "^6.1.2" + }, + "devDependencies": { + "ts-node": "^10.9.2", + "typescript": "^4.7.4" + } } diff --git a/things/data-schema-thing/http/ts/src/main.ts b/things/data-schema-thing/http/ts/src/main.ts index 723c23f..81acff7 100644 --- a/things/data-schema-thing/http/ts/src/main.ts +++ b/things/data-schema-thing/http/ts/src/main.ts @@ -1,4 +1,3 @@ - /******************************************************************************** * Copyright (c) 2020 Contributors to the Eclipse Foundation * @@ -14,49 +13,55 @@ * SPDX-License-Identifier: EPL-2.0 OR W3C-20150513 ********************************************************************************/ -import WoT = require("wot-typescript-definitions") - -const fs = require("fs") -const path = require("path") -const { parseArgs } = require("node:util") -const { JsonPlaceholderReplacer } = require('json-placeholder-replacer') -require("dotenv").config() - -const { createLogger, transports, format } = require('winston') -const LokiTransport = require('winston-loki') -const WotCore = require("@node-wot/core"); -const HttpServer = require("@node-wot/binding-http").HttpServer +import WoT from "wot-typescript-definitions"; +import fs from "fs"; +import path from "path"; +import { parseArgs } from "node:util"; +import { JsonPlaceholderReplacer } from "json-placeholder-replacer"; +import { Servient } from "@node-wot/core"; +import { HttpServer } from "@node-wot/binding-http"; +import { createLogger, transports, format } from "winston"; +import LokiTransport from "winston-loki"; +import dotenv from "dotenv"; +dotenv.config(); const hostname = process.env.HOSTNAME ?? "localhost"; -let portNumber = process.env.PORT ?? 3000; +let portNumber = + process.env.PORT != null && process.env.PORT !== "" + ? parseInt(process.env.PORT) + : 3000; const thingName = "http-data-schema-thing"; -let logger = createLogger({ +const logger = createLogger({ transports: [ new LokiTransport({ - host:`${process.env.LOKI_HOSTNAME}:${process.env.LOKI_PORT}`, + host: `${process.env.LOKI_HOSTNAME}:${process.env.LOKI_PORT}`, labels: { thing: thingName }, json: true, format: format.json(), replaceTimestamp: true, - onConnectionError: (err: any) => console.error(err) + onConnectionError: (err: unknown) => console.error(err), }), new transports.Console({ - format: format.combine(format.simple(), format.colorize()) - }) - ] -}) + format: format.combine(format.simple(), format.colorize()), + }), + ], +}); function checkPropertyWrite(expected: string, actual: unknown) { const output = "Property " + expected + " written with " + actual; - if (expected === actual) { + if (expected === actual) { console.info("PASS: " + output); } else { throw new Error("FAIL: " + output); } } -function checkActionInvocation(name: string, expected: string, actual: unknown) { +function checkActionInvocation( + name: string, + expected: string, + actual: unknown +) { const output = "Action " + name + " invoked with " + actual; if (expected === actual) { console.info("PASS: " + output); @@ -67,45 +72,60 @@ function checkActionInvocation(name: string, expected: string, actual: unknown) const { values: { port }, - } = parseArgs({ +} = parseArgs({ options: { - port: { - type: "string", - short: "p", - }, + port: { + type: "string", + short: "p", + }, }, - }); - - if (port && !isNaN(parseInt(port))) { +}); + +if (port != null && !isNaN(parseInt(port))) { portNumber = parseInt(port); - } - - const tmPath = process.env.TM_PATH; - - if (process.platform === "win32") { +} + +const tmPath = process.env.TM_PATH; + +if (process.platform === "win32") { tmPath?.split(path.sep).join(path.win32.sep); - } - - const thingModel = JSON.parse(fs.readFileSync(path.join(__dirname, tmPath))); - - const placeholderReplacer = new JsonPlaceholderReplacer(); - placeholderReplacer.addVariableMap({ +} + +let thingModel; +if (tmPath != null && tmPath !== "") { + thingModel = JSON.parse( + fs.readFileSync(path.join(__dirname, tmPath)).toString() + ); +} + +const placeholderReplacer = new JsonPlaceholderReplacer(); +placeholderReplacer.addVariableMap({ PROTOCOL: "http", THING_NAME: thingName, HOSTNAME: hostname, - PORT_NUMBER: portNumber - }); - const thingDescription = placeholderReplacer.replace(thingModel); - thingDescription["@type"] = "Thing"; + PORT_NUMBER: portNumber, +}); +let thingDescription = placeholderReplacer.replace(thingModel); +thingDescription = { + ...thingDescription, + "@type": "Thing", +}; // init property values let bool: boolean; const setBool = (value: boolean) => { bool = value; - logger.info({ message: `${bool}`, labels: { affordance: 'property', affordanceName: 'bool', messageType: 'updateProperty' }}); -} + logger.info({ + message: `${bool}`, + labels: { + affordance: "property", + affordanceName: "bool", + messageType: "updateProperty", + }, + }); +}; setBool(false); @@ -113,17 +133,31 @@ let int: number; const setInt = (value: number) => { int = value; - logger.info({ message: `${int}`, labels: { affordance: 'property', affordanceName: 'int', messageType: 'updateProperty' }}); -} - -setInt(42) + logger.info({ + message: `${int}`, + labels: { + affordance: "property", + affordanceName: "int", + messageType: "updateProperty", + }, + }); +}; + +setInt(42); let num: number; const setNum = (value: number) => { num = value; - logger.info({ message: `${num}`, labels: { affordance: 'property', affordanceName: 'num', messageType: 'updateProperty' }}); -} + logger.info({ + message: `${num}`, + labels: { + affordance: "property", + affordanceName: "num", + messageType: "updateProperty", + }, + }); +}; setNum(3.14); @@ -131,8 +165,15 @@ let string: string; const setString = (value: string) => { string = value; - logger.info({ message: `${string}`, labels: { affordance: 'property', affordanceName: 'string', messageType: 'updateProperty' }}); -} + logger.info({ + message: `${string}`, + labels: { + affordance: "property", + affordanceName: "string", + messageType: "updateProperty", + }, + }); +}; setString("unset"); @@ -140,8 +181,15 @@ let array: unknown[]; const setArray = (value: unknown[]) => { array = value; - logger.info({ message: `${array}`, labels: { affordance: 'property', affordanceName: 'array', messageType: 'updateProperty' }}); -} + logger.info({ + message: `${array}`, + labels: { + affordance: "property", + affordanceName: "array", + messageType: "updateProperty", + }, + }); +}; setArray([2, "unset"]); @@ -149,173 +197,333 @@ let object: Record; const setObject = (value: Record) => { object = value; - logger.info({ message: `${JSON.stringify(object)}`, labels: { affordance: 'property', affordanceName: 'object', messageType: 'updateProperty' }}); -} + logger.info({ + message: `${JSON.stringify(object)}`, + labels: { + affordance: "property", + affordanceName: "object", + messageType: "updateProperty", + }, + }); +}; setObject({ id: 123, name: "abc" }); -let servient = new WotCore.Servient(); -servient.addServer(new HttpServer({ - baseUri: `http://${hostname}:${portNumber}`, - port: portNumber, -})); - - -servient.start() - .then((WoT: any) => { - WoT.produce(thingDescription) - .then((thing: WoT.ExposedThing) => { - console.log("Produced " + thing.getThingDescription().title); - - // set property read/write handlers - thing - .setPropertyWriteHandler("bool", async (value) => { - const localBool = await value.value(); - checkPropertyWrite("boolean", typeof localBool); - setBool(localBool as boolean); - thing.emitEvent("on-bool", bool); - }) - .setPropertyReadHandler("bool", async () => bool) - .setPropertyWriteHandler("int", async (value) => { - const localInt = await value.value(); - if (localInt === Math.floor(localInt as number)) { - checkPropertyWrite("integer", "integer"); - } else { - checkPropertyWrite("integer", typeof value); - } - setInt(localInt as number); - thing.emitEvent("on-int", int); - }) - .setPropertyReadHandler("int", async () => int) - .setPropertyWriteHandler("num", async (value) => { - const localNum = await value.value(); - checkPropertyWrite("number", typeof localNum); - setNum(localNum as number); - thing.emitEvent("on-num", num); - }) - .setPropertyReadHandler("num", async () => num) - .setPropertyWriteHandler("string", async (value) => { - const localString = await value.value(); - checkPropertyWrite("string", typeof localString); - setString(localString as string); - thing.emitEvent("on-string", string); - }) - .setPropertyReadHandler("string", async () => string) - .setPropertyWriteHandler("array", async (value) => { - const localArray = await value.value(); - if (Array.isArray(localArray)) { - checkPropertyWrite("array", "array"); - } else { - checkPropertyWrite("array", typeof localArray); - } - setArray(localArray as unknown[]); - thing.emitEvent("on-array", array); - }) - .setPropertyReadHandler("array", async () => array) - .setPropertyWriteHandler("object", async (value) => { - const localObject = await value.value(); - if (Array.isArray(localObject)) { - checkPropertyWrite("object", "array"); - } else { - checkPropertyWrite("object", typeof localObject); - } - setObject(localObject as Record); - thing.emitEvent("on-object", object); - }) - .setPropertyReadHandler("object", async () => object); - - // set action handlers - thing - .setActionHandler("void-void", async (parameters) => { - logger.info({ message: 'Action invoked.', labels: { affordance: 'action', op: 'invokeaction', affordanceName: 'void-void' }}); - checkActionInvocation("void-void", "undefined", typeof (await parameters.value())); - return undefined; - }) - .setActionHandler("void-int", async (parameters) => { - const value = 0; - logger.info({ message: 'Action invoked.', labels: { affordance: 'action', op: 'invokeaction', affordanceName: 'void-int' }}); - checkActionInvocation("void-int", "undefined", typeof (await parameters.value())); - logger.info({ message: `${value}`, labels: { affordance: 'action', op: 'invokeaction', affordanceName: 'void-int', messageType: 'actionOutput' }}); - return value; - }) - .setActionHandler("int-void", async (parameters) => { - const localParameters = await parameters.value(); - logger.info({ message: 'Action invoked.', labels: { affordance: 'action', op: 'invokeaction', affordanceName: 'int-void' }}); - logger.info({ message: `${localParameters}`, labels: { affordance: 'action', op: 'invokeaction', affordanceName: 'int-void', messageType: 'actionInput' }}); - if (localParameters === Math.floor(localParameters as number)) { - checkActionInvocation("int-void", "integer", "integer"); - } else { - checkActionInvocation("int-void", "integer", typeof parameters); - } - return undefined; - }) - .setActionHandler("int-int", async (parameters) => { - const localParameters = await parameters.value(); - logger.info({ message: 'Action invoked.', labels: { affordance: 'action', op: 'invokeaction', affordanceName: 'int-int' }}); - logger.info({ message: `${localParameters}`, labels: { affordance: 'action', op: 'invokeaction', affordanceName: 'int-int', messageType: 'actionInput' }}); - if (localParameters === Math.floor(localParameters as number)) { - checkActionInvocation("int-int", "integer", "integer"); - } else { - checkActionInvocation("int-int", "integer", typeof localParameters); - } - const value = (localParameters as number) + 1 - logger.info({ message: `${value}`, labels: { affordance: 'action', op: 'invokeaction', affordanceName: 'int-int', messageType: 'actionOutput' }}); - return value; - }) - .setActionHandler("int-string", async (parameters) => { - const localParameters = await parameters.value(); - const inputtype = typeof localParameters; - logger.info({ message: 'Action invoked.', labels: { affordance: 'action', op: 'invokeaction', affordanceName: 'int-string' }}); - logger.info({ message: `${localParameters}`, labels: { affordance: 'action', op: 'invokeaction', affordanceName: 'int-string', messageType: 'actionInput' }}); - if (localParameters === Math.floor(localParameters as number)) { - checkActionInvocation("int-string", "integer", "integer"); - } else { - checkActionInvocation("int-string", "integer", typeof localParameters); - } - - let value: string; - if (inputtype === "number") { - // eslint-disable-next-line no-new-wrappers - value = new String(localParameters) - .replace(/0/g, "zero-") - .replace(/1/g, "one-") - .replace(/2/g, "two-") - .replace(/3/g, "three-") - .replace(/4/g, "four-") - .replace(/5/g, "five-") - .replace(/6/g, "six-") - .replace(/7/g, "seven-") - .replace(/8/g, "eight-") - .replace(/9/g, "nine-"); - } else { - throw new Error("ERROR"); - } - logger.info({ message: `${value}`, labels: { affordance: 'action', op: 'invokeaction', affordanceName: 'int-string', messageType: 'actionOutput' }}); - return value; - }) - .setActionHandler("void-obj", async (parameters) => { - logger.info({ message: 'Action invoked.', labels: { affordance: 'action', op: 'invokeaction', affordanceName: 'void-obj' }}); - checkActionInvocation("void-complex", "undefined", typeof (await parameters.value())); - const value = { prop1: 123, prop2: "abc" }; - logger.info({ message: `${JSON.stringify(value)}`, labels: { affordance: 'action', op: 'invokeaction', affordanceName: 'void-obj', messageType: 'actionOutput' }}); - return value; - }) - .setActionHandler("obj-void", async (parameters) => { - const localParameters = await parameters.value() - logger.info({ message: 'Action invoked.', labels: { affordance: 'action', op: 'invokeaction', affordanceName: 'obj-void' }}); - logger.info({ message: `${JSON.stringify(localParameters)}`, labels: { affordance: 'action', op: 'invokeaction', affordanceName: 'obj-void', messageType: 'actionInput' }}) - checkActionInvocation("complex-void", "object", typeof (await parameters.value())); - return undefined; +const servient = new Servient(); +servient.addServer( + new HttpServer({ + baseUri: `http://${hostname}:${portNumber}`, + port: portNumber, + }) +); + +servient.start().then((WoT) => { + WoT.produce(thingDescription) + .then((thing: WoT.ExposedThing) => { + console.log("Produced " + thing.getThingDescription().title); + + // set property read/write handlers + thing + .setPropertyWriteHandler("bool", async (value) => { + const localBool = await value.value(); + checkPropertyWrite("boolean", typeof localBool); + setBool(localBool as boolean); + thing.emitEvent("on-bool", bool); + }) + .setPropertyReadHandler("bool", async () => bool) + .setPropertyWriteHandler("int", async (value) => { + const localInt = await value.value(); + if (localInt === Math.floor(localInt as number)) { + checkPropertyWrite("integer", "integer"); + } else { + checkPropertyWrite("integer", typeof value); + } + setInt(localInt as number); + thing.emitEvent("on-int", int); + }) + .setPropertyReadHandler("int", async () => int) + .setPropertyWriteHandler("num", async (value) => { + const localNum = await value.value(); + checkPropertyWrite("number", typeof localNum); + setNum(localNum as number); + thing.emitEvent("on-num", num); + }) + .setPropertyReadHandler("num", async () => num) + .setPropertyWriteHandler("string", async (value) => { + const localString = await value.value(); + checkPropertyWrite("string", typeof localString); + setString(localString as string); + thing.emitEvent("on-string", string); + }) + .setPropertyReadHandler("string", async () => string) + .setPropertyWriteHandler("array", async (value) => { + const localArray = await value.value(); + if (Array.isArray(localArray)) { + checkPropertyWrite("array", "array"); + } else { + checkPropertyWrite("array", typeof localArray); + } + setArray(localArray as unknown[]); + thing.emitEvent("on-array", array); + }) + .setPropertyReadHandler("array", async () => array) + .setPropertyWriteHandler("object", async (value) => { + const localObject = await value.value(); + if (Array.isArray(localObject)) { + checkPropertyWrite("object", "array"); + } else { + checkPropertyWrite("object", typeof localObject); + } + setObject(localObject as Record); + thing.emitEvent("on-object", object); + }) + .setPropertyReadHandler("object", async () => object); + + // set action handlers + thing + .setActionHandler("void-void", async (parameters) => { + logger.info({ + message: "Action invoked.", + labels: { + affordance: "action", + op: "invokeaction", + affordanceName: "void-void", + }, }); - - // expose the thing - thing.expose().then(async () => { - console.info(thing.getThingDescription().title + " ready"); - console.log("ThingIsReady"); + checkActionInvocation( + "void-void", + "undefined", + typeof (await parameters.value()) + ); + return undefined; + }) + .setActionHandler("void-int", async (parameters) => { + const value = 0; + logger.info({ + message: "Action invoked.", + labels: { + affordance: "action", + op: "invokeaction", + affordanceName: "void-int", + }, + }); + checkActionInvocation( + "void-int", + "undefined", + typeof (await parameters.value()) + ); + logger.info({ + message: `${value}`, + labels: { + affordance: "action", + op: "invokeaction", + affordanceName: "void-int", + messageType: "actionOutput", + }, + }); + return value; + }) + .setActionHandler("int-void", async (parameters) => { + const localParameters = await parameters.value(); + logger.info({ + message: "Action invoked.", + labels: { + affordance: "action", + op: "invokeaction", + affordanceName: "int-void", + }, + }); + logger.info({ + message: `${localParameters}`, + labels: { + affordance: "action", + op: "invokeaction", + affordanceName: "int-void", + messageType: "actionInput", + }, + }); + if ( + localParameters === + Math.floor(localParameters as number) + ) { + checkActionInvocation("int-void", "integer", "integer"); + } else { + checkActionInvocation( + "int-void", + "integer", + typeof parameters + ); + } + return undefined; + }) + .setActionHandler("int-int", async (parameters) => { + const localParameters = await parameters.value(); + logger.info({ + message: "Action invoked.", + labels: { + affordance: "action", + op: "invokeaction", + affordanceName: "int-int", + }, + }); + logger.info({ + message: `${localParameters}`, + labels: { + affordance: "action", + op: "invokeaction", + affordanceName: "int-int", + messageType: "actionInput", + }, + }); + if ( + localParameters === + Math.floor(localParameters as number) + ) { + checkActionInvocation("int-int", "integer", "integer"); + } else { + checkActionInvocation( + "int-int", + "integer", + typeof localParameters + ); + } + const value = (localParameters as number) + 1; + logger.info({ + message: `${value}`, + labels: { + affordance: "action", + op: "invokeaction", + affordanceName: "int-int", + messageType: "actionOutput", + }, + }); + return value; + }) + .setActionHandler("int-string", async (parameters) => { + const localParameters = await parameters.value(); + const inputtype = typeof localParameters; + logger.info({ + message: "Action invoked.", + labels: { + affordance: "action", + op: "invokeaction", + affordanceName: "int-string", + }, + }); + logger.info({ + message: `${localParameters}`, + labels: { + affordance: "action", + op: "invokeaction", + affordanceName: "int-string", + messageType: "actionInput", + }, + }); + if ( + localParameters === + Math.floor(localParameters as number) + ) { + checkActionInvocation( + "int-string", + "integer", + "integer" + ); + } else { + checkActionInvocation( + "int-string", + "integer", + typeof localParameters + ); + } + + let value: string; + if (inputtype === "number") { + // eslint-disable-next-line no-new-wrappers + value = new String(localParameters) + .replace(/0/g, "zero-") + .replace(/1/g, "one-") + .replace(/2/g, "two-") + .replace(/3/g, "three-") + .replace(/4/g, "four-") + .replace(/5/g, "five-") + .replace(/6/g, "six-") + .replace(/7/g, "seven-") + .replace(/8/g, "eight-") + .replace(/9/g, "nine-"); + } else { + throw new Error("ERROR"); + } + logger.info({ + message: `${value}`, + labels: { + affordance: "action", + op: "invokeaction", + affordanceName: "int-string", + messageType: "actionOutput", + }, + }); + return value; + }) + .setActionHandler("void-obj", async (parameters) => { + logger.info({ + message: "Action invoked.", + labels: { + affordance: "action", + op: "invokeaction", + affordanceName: "void-obj", + }, + }); + checkActionInvocation( + "void-complex", + "undefined", + typeof (await parameters.value()) + ); + const value = { prop1: 123, prop2: "abc" }; + logger.info({ + message: `${JSON.stringify(value)}`, + labels: { + affordance: "action", + op: "invokeaction", + affordanceName: "void-obj", + messageType: "actionOutput", + }, + }); + return value; + }) + .setActionHandler("obj-void", async (parameters) => { + const localParameters = await parameters.value(); + logger.info({ + message: "Action invoked.", + labels: { + affordance: "action", + op: "invokeaction", + affordanceName: "obj-void", + }, + }); + logger.info({ + message: `${JSON.stringify(localParameters)}`, + labels: { + affordance: "action", + op: "invokeaction", + affordanceName: "obj-void", + messageType: "actionInput", + }, + }); + checkActionInvocation( + "complex-void", + "object", + typeof (await parameters.value()) + ); + return undefined; }); - }) - .catch((e: Error) => { - console.log(e) - }) - }) - \ No newline at end of file + + // expose the thing + thing.expose().then(async () => { + console.info(thing.getThingDescription().title + " ready"); + console.log("ThingIsReady"); + }); + }) + .catch((e: Error) => { + console.log(e); + }); +}); diff --git a/things/data-schema-thing/http/ts/test/client.test.ts b/things/data-schema-thing/http/ts/test/client.test.ts index 5b72d9e..9064c89 100644 --- a/things/data-schema-thing/http/ts/test/client.test.ts +++ b/things/data-schema-thing/http/ts/test/client.test.ts @@ -1,188 +1,211 @@ +/******************************************************************************** + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the W3C Software Notice and + * Document License (2015-05-13) which is available at + * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document. + * + * SPDX-License-Identifier: EPL-2.0 OR W3C-20150513 + ********************************************************************************/ -import chai from 'chai' -import chaiAsPromised from 'chai-as-promised'; +import chai from "chai"; +import chaiAsPromised from "chai-as-promised"; import { Servient } from "@node-wot/core"; -import { HttpClientFactory } from "@node-wot/binding-http" -import { port } from './fixtures' +import { HttpClientFactory } from "@node-wot/binding-http"; +import { port } from "./fixtures"; -chai.use(chaiAsPromised) -const expect = chai.expect +chai.use(chaiAsPromised); +const expect = chai.expect; -let servient = new Servient() -servient.addClientFactory(new HttpClientFactory()) +const servient = new Servient(); +servient.addClientFactory(new HttpClientFactory()); -let thing: WoT.ConsumedThing +let thing: WoT.ConsumedThing; -const readProperty = async (thing: WoT.ConsumedThing, name: string): Promise => { +const readProperty = async ( + thing: WoT.ConsumedThing, + name: string +): Promise => { try { - const res = await thing.readProperty(name) - const value = await res.value() - return value - } - catch (error) { - console.error(`Error: ${error}`) + const res = await thing.readProperty(name); + const value = await res.value(); + return value; + } catch (error) { + console.error(`Error: ${error}`); + return null; } -} +}; describe("Client Tests", () => { before(async () => { try { - const WoT = await servient.start() - const td: WoT.ThingDescription = await WoT.requestThingDescription(`http://localhost:${port}/http-data-schema-thing`) - thing = await WoT.consume(td) - } catch(error) { - console.error(error) + const WoT = await servient.start(); + const td: WoT.ThingDescription = await WoT.requestThingDescription( + `http://localhost:${port}/http-data-schema-thing` + ); + thing = await WoT.consume(td); + } catch (error) { + console.error(error); } - }) + }); after(async () => { - await servient.shutdown() - }) - + await servient.shutdown(); + }); + describe("bool property", () => { it("should read property bool", async () => { - const value = await readProperty(thing, 'bool') - expect(value).to.be.false - }) - + const value = await readProperty(thing, "bool"); + expect(value).to.be.false; + }); + it("should write value true", async () => { - await thing.writeProperty('bool', true) - const value = await readProperty(thing, 'bool') - expect(value).to.be.true - }) - + await thing.writeProperty("bool", true); + const value = await readProperty(thing, "bool"); + expect(value).to.be.true; + }); + it("should write value false", async () => { - await thing.writeProperty('bool', false) - const value = await readProperty(thing, 'bool') - expect(value).to.be.false - }) - + await thing.writeProperty("bool", false); + const value = await readProperty(thing, "bool"); + expect(value).to.be.false; + }); + it("should fail to write string value 'true'", async () => { - await expect(thing.writeProperty('bool', 'true')).to.be.rejected - }) - }) - + await expect(thing.writeProperty("bool", "true")).to.be.rejected; + }); + }); + describe("int property", () => { it("should read property int", async () => { - const value = await readProperty(thing, 'int') - expect(value).to.be.equal(42) - }) - + const value = await readProperty(thing, "int"); + expect(value).to.be.equal(42); + }); + it("should write value 4711", async () => { - const intValue = 4711 - await thing.writeProperty('int', intValue) - const value = await readProperty(thing, 'int') - expect(value).to.be.equal(intValue) - }) - + const intValue = 4711; + await thing.writeProperty("int", intValue); + const value = await readProperty(thing, "int"); + expect(value).to.be.equal(intValue); + }); + it("should fail to write value 3.1415", async () => { - const intValue = 3.1415 - await expect(thing.writeProperty('int', intValue)).to.be.rejected - }) - + const intValue = 3.1415; + await expect(thing.writeProperty("int", intValue)).to.be.rejected; + }); + it("should fail to write string value 'Pi'", async () => { - const intValue = 'Pi' - await expect(thing.writeProperty('int', intValue)).to.be.rejected - }) - }) - + const intValue = "Pi"; + await expect(thing.writeProperty("int", intValue)).to.be.rejected; + }); + }); + describe("num property", () => { it("should read property num", async () => { - const value = await readProperty(thing, 'num') - expect(value).to.be.equal(3.14) - }) - + const value = await readProperty(thing, "num"); + expect(value).to.be.equal(3.14); + }); + it("should write value 4711", async () => { - const numValue = 4711 - await thing.writeProperty('num', numValue) - const value = await readProperty(thing, 'num') - expect(value).to.be.equal(numValue) - }) - + const numValue = 4711; + await thing.writeProperty("num", numValue); + const value = await readProperty(thing, "num"); + expect(value).to.be.equal(numValue); + }); + it("should write value 3.1415", async () => { - const numValue = 3.1415 - await thing.writeProperty('num', numValue) - const value = await readProperty(thing, 'num') - expect(value).to.be.equal(numValue) - }) - + const numValue = 3.1415; + await thing.writeProperty("num", numValue); + const value = await readProperty(thing, "num"); + expect(value).to.be.equal(numValue); + }); + it("should fail to write string value 'Pi'", async () => { - const numValue = 'Pi' - expect(thing.writeProperty('num', numValue)).to.be.rejected - }) - }) - + const numValue = "Pi"; + expect(thing.writeProperty("num", numValue)).to.be.rejected; + }); + }); + describe("string property", () => { it("should read property string", async () => { - const value = await readProperty(thing, 'string') - expect(value).to.be.equal('unset') - }) - + const value = await readProperty(thing, "string"); + expect(value).to.be.equal("unset"); + }); + it("should write value 'testclient'", async () => { - const stringValue = 'testclient' - await thing.writeProperty('string', stringValue) - const value = await readProperty(thing, 'string') - expect(value).to.be.equal(stringValue) - }) - + const stringValue = "testclient"; + await thing.writeProperty("string", stringValue); + const value = await readProperty(thing, "string"); + expect(value).to.be.equal(stringValue); + }); + it("should fail to write value 13", async () => { - const stringValue = 13 - await expect(thing.writeProperty('string', stringValue)).to.be.rejected - - }) - + const stringValue = 13; + await expect(thing.writeProperty("string", stringValue)).to.be + .rejected; + }); + it("should fail to write value null", async () => { - const stringValue = null - await expect(thing.writeProperty('string', stringValue)).to.be.rejected - }) - }) - + const stringValue = null; + await expect(thing.writeProperty("string", stringValue)).to.be + .rejected; + }); + }); + describe("array property", () => { it("should read property array", async () => { - const value = await readProperty(thing, 'array') - expect(value).to.be.eql([2, 'unset']) - }) - + const value = await readProperty(thing, "array"); + expect(value).to.be.eql([2, "unset"]); + }); + it("should write value [23, 'illuminated']", async () => { - const arrayValue = [23, 'illuminated'] - await thing.writeProperty('array', arrayValue) - const value = await readProperty(thing, 'array') - expect(value).to.be.eql(arrayValue) - }) - + const arrayValue = [23, "illuminated"]; + await thing.writeProperty("array", arrayValue); + const value = await readProperty(thing, "array"); + expect(value).to.be.eql(arrayValue); + }); + it("should fail to write object value", async () => { - const arrayValue = { id: 24, name: 'dark' } - expect(thing.writeProperty('array', arrayValue)).to.be.rejected - }) - + const arrayValue = { id: 24, name: "dark" }; + expect(thing.writeProperty("array", arrayValue)).to.be.rejected; + }); + it("should fail to write value null", async () => { - const arrayValue = null - await expect(thing.writeProperty('array', arrayValue)).to.be.rejected - }) - }) - + const arrayValue = null; + await expect(thing.writeProperty("array", arrayValue)).to.be + .rejected; + }); + }); + describe("object property", () => { it("should read property object", async () => { - const value = await readProperty(thing, 'object') - expect(value).to.be.eql({ id: 123, name: 'abc' }) - }) - + const value = await readProperty(thing, "object"); + expect(value).to.be.eql({ id: 123, name: "abc" }); + }); + it("should write value { id: 23, name: 'illuminated' }", async () => { - const objectValue = { id: 23, name: 'illuminated' } - await thing.writeProperty('object', objectValue) - const value = await readProperty(thing, 'object') - expect(value).to.be.eql(objectValue) - }) - + const objectValue = { id: 23, name: "illuminated" }; + await thing.writeProperty("object", objectValue); + const value = await readProperty(thing, "object"); + expect(value).to.be.eql(objectValue); + }); + it("should fail to write value null", async () => { - const objectValue = null - await expect(thing.writeProperty('object', objectValue)).to.be.rejected - }) - + const objectValue = null; + await expect(thing.writeProperty("object", objectValue)).to.be + .rejected; + }); + it("should fail to write array value", async () => { - const objectValue = [24, "dark"] - await expect(thing.writeProperty('object', objectValue)).to.be.rejected - }) - }) -}) + const objectValue = [24, "dark"]; + await expect(thing.writeProperty("object", objectValue)).to.be + .rejected; + }); + }); +}); diff --git a/things/data-schema-thing/http/ts/test/fixtures.ts b/things/data-schema-thing/http/ts/test/fixtures.ts index e304662..15e158b 100644 --- a/things/data-schema-thing/http/ts/test/fixtures.ts +++ b/things/data-schema-thing/http/ts/test/fixtures.ts @@ -1,25 +1,42 @@ -import { ChildProcess } from "child_process" -import { getInitiateMain, ThingStartResponse } from "../../../../../util/util" -import path from "path" +/******************************************************************************** + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the W3C Software Notice and + * Document License (2015-05-13) which is available at + * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document. + * + * SPDX-License-Identifier: EPL-2.0 OR W3C-20150513 + ********************************************************************************/ -let thingProcess: ChildProcess | undefined -let response: ThingStartResponse -export const port = 3000 +import { ChildProcess } from "child_process"; +import { getInitiateMain, ThingStartResponse } from "../../../../../util/util"; +import path from "path"; + +let thingProcess: ChildProcess | undefined; +let response: ThingStartResponse; +export const port = 3000; export async function mochaGlobalSetup() { try { - response = await getInitiateMain('node', [path.join(__dirname, '..', 'dist', 'main.js'), '-p', `${port}`]) - } - catch(error) { - console.log(error) - } - finally { - thingProcess = response.process + response = await getInitiateMain("node", [ + path.join(__dirname, "..", "dist", "main.js"), + "-p", + `${port}`, + ]); + } catch (error) { + console.log(error); + } finally { + thingProcess = response.process; } } export function mochaGlobalTeardown() { if (thingProcess) { - thingProcess.kill() + thingProcess.kill(); } -} \ No newline at end of file +} diff --git a/things/data-schema-thing/http/ts/test/td.test.ts b/things/data-schema-thing/http/ts/test/td.test.ts index cc5267f..30c5557 100644 --- a/things/data-schema-thing/http/ts/test/td.test.ts +++ b/things/data-schema-thing/http/ts/test/td.test.ts @@ -1,45 +1,66 @@ -import * as chai from 'chai' -import * as http from 'http' -import { getTDValidate } from '../../../../../util/util' -import { ValidateFunction } from 'ajv' -import { port } from './fixtures' +/******************************************************************************** + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the W3C Software Notice and + * Document License (2015-05-13) which is available at + * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document. + * + * SPDX-License-Identifier: EPL-2.0 OR W3C-20150513 + ********************************************************************************/ -const expect = chai.expect +import * as chai from "chai"; +import * as http from "http"; +import { getTDValidate } from "../../../../../util/util"; +import { ValidateFunction } from "ajv"; +import { port } from "./fixtures"; -let validate: ValidateFunction | undefined +const expect = chai.expect; + +let validate: ValidateFunction | undefined; describe("TD Test", () => { - before(async () => { - const tdValidate = getTDValidate() - - try { - const response = await Promise.all([tdValidate]) - validate = response[0].validate - } - catch (error) { - console.log(error) - } - }) - - it('should have a valid TD', (done) => { - http.get(`http://localhost:${port}/http-data-schema-thing`, function (response: any) { - const body: Buffer[] = [] - response.on('data', (chunk: Buffer) => { - body.push(chunk) - }) - - response.on('end', () => { + before(async () => { + const tdValidate = getTDValidate(); + try { - const result = JSON.parse(Buffer.concat(body).toString()) - const valid = validate && result ! ? validate(result) : false - expect(valid).to.be.true - done() + const response = await Promise.all([tdValidate]); + validate = response[0].validate; } catch (error) { - console.log(error) - done(error) + console.log(error); } - }) - }) - }) -}) + }); + + it("should have a valid TD", (done) => { + http.get( + `http://localhost:${port}/http-data-schema-thing`, + function (response: http.IncomingMessage) { + const body: Buffer[] = []; + response.on("data", (chunk: Buffer) => { + body.push(chunk); + }); + response.on("end", () => { + try { + const result = JSON.parse( + Buffer.concat(body).toString() + ); + const valid = + validate && result !== "" + ? validate(result) + : false; + expect(valid).to.be.true; + done(); + } catch (error) { + console.log(error); + done(error); + } + }); + } + ); + }); +}); diff --git a/things/data-schema-thing/http/ts/tsconfig.json b/things/data-schema-thing/http/ts/tsconfig.json index d4a580f..93aaf4a 100644 --- a/things/data-schema-thing/http/ts/tsconfig.json +++ b/things/data-schema-thing/http/ts/tsconfig.json @@ -1,14 +1,8 @@ { + "extends": "../../../..//tsconfig.json", "compilerOptions": { "outDir": "dist", - "rootDir": "src", - "target": "ES2018", - "module": "commonjs", - "skipLibCheck": false, - "strict": true, - "sourceMap": false, - "esModuleInterop": true, - "removeComments": false + "rootDir": "src" }, - "include": ["src/**/*"], -} \ No newline at end of file + "include": ["src/**/*"] +} diff --git a/things/data-schema-thing/tm.test.js b/things/data-schema-thing/tm.test.js index 316cec0..1efa70c 100644 --- a/things/data-schema-thing/tm.test.js +++ b/things/data-schema-thing/tm.test.js @@ -1,32 +1,35 @@ -const Ajv = require('ajv') -const chai = require('chai') -const https = require('https') +const Ajv = require("ajv"); +const chai = require("chai"); +const https = require("https"); -const ajv = new Ajv({ strict: false, allErrors: true, validateFormats: false }) +const ajv = new Ajv({ strict: false, allErrors: true, validateFormats: false }); -const expect = chai.expect +const expect = chai.expect; -describe('Test Thing', () => { - let validate +describe("Test Thing", () => { + let validate; - before((done) => { - https.get('https://raw.githubusercontent.com/w3c/wot-thing-description/main/validation/tm-json-schema-validation.json', function (response) { - const body = [] - response.on('data', (chunk) => { - body.push(chunk) - }) + before((done) => { + https.get( + "https://raw.githubusercontent.com/w3c/wot-thing-description/main/validation/tm-json-schema-validation.json", + function (response) { + const body = []; + response.on("data", (chunk) => { + body.push(chunk); + }); - response.on('end', () => { - const tmSchema = JSON.parse(Buffer.concat(body).toString()) - validate = ajv.compile(tmSchema) - done() - }) - }) - }) + response.on("end", () => { + const tmSchema = JSON.parse(Buffer.concat(body).toString()); + validate = ajv.compile(tmSchema); + done(); + }); + } + ); + }); - it('should have a valid TM', () => { - const testThingTM = require('./data-schema-thing.tm.json') - const valid = validate(testThingTM) - expect(valid).to.be.true - }) -}) + it("should have a valid TM", () => { + const testThingTM = require("./data-schema-thing.tm.json"); + const valid = validate(testThingTM); + expect(valid).to.be.true; + }); +}); diff --git a/things/elevator/elevator.tm.json b/things/elevator/elevator.tm.json index ebb1a8c..43e6554 100644 --- a/things/elevator/elevator.tm.json +++ b/things/elevator/elevator.tm.json @@ -1,43 +1,41 @@ { - "@context":[ - "https://www.w3.org/2019/wot/td/v1", - "https://www.w3.org/2022/wot/td/v1.1", - { - "@language":"en" - } - ], - "@type":"tm:ThingModel", - "title":"{{THING_NAME}}", - "description":"Elevator Thing", - "securityDefinitions":{ - "nosec_sc":{ - "scheme":"nosec" - } - }, - "security":[ - "nosec_sc" - ], - "base": "{{PROTOCOL}}://{{HOSTNAME}}:{{PORT_NUMBER}}/{{UNIT_ID}}", - "properties":{ - "lightSwitch": { - "type": "boolean", - "readOnly": false, - "writeOnly": false, - "observable": false - }, - "onTheMove": { - "type": "boolean", - "readOnly": true, - "writeOnly": false, - "observable": true - }, - "floorNumber": { - "type": "integer", - "minimum": 0, - "maximum": 15, - "readOnly": false, - "writeOnly": false, - "observable": false - } - } -} \ No newline at end of file + "@context": [ + "https://www.w3.org/2019/wot/td/v1", + "https://www.w3.org/2022/wot/td/v1.1", + { + "@language": "en" + } + ], + "@type": "tm:ThingModel", + "title": "{{THING_NAME}}", + "description": "Elevator Thing", + "securityDefinitions": { + "nosec_sc": { + "scheme": "nosec" + } + }, + "security": ["nosec_sc"], + "base": "{{PROTOCOL}}://{{HOSTNAME}}:{{PORT_NUMBER}}/{{UNIT_ID}}", + "properties": { + "lightSwitch": { + "type": "boolean", + "readOnly": false, + "writeOnly": false, + "observable": false + }, + "onTheMove": { + "type": "boolean", + "readOnly": true, + "writeOnly": false, + "observable": true + }, + "floorNumber": { + "type": "integer", + "minimum": 0, + "maximum": 15, + "readOnly": false, + "writeOnly": false, + "observable": false + } + } +} diff --git a/things/elevator/modbus/js/.mocharc.json b/things/elevator/modbus/js/.mocharc.json index 9d68ca0..4ac9c80 100644 --- a/things/elevator/modbus/js/.mocharc.json +++ b/things/elevator/modbus/js/.mocharc.json @@ -1,4 +1,4 @@ { "spec": "./test/**.test.js", "require": ["./test/fixtures.js"] -} \ No newline at end of file +} diff --git a/things/elevator/modbus/js/main.js b/things/elevator/modbus/js/main.js index 5b350d9..602f691 100644 --- a/things/elevator/modbus/js/main.js +++ b/things/elevator/modbus/js/main.js @@ -1,274 +1,328 @@ -const { ServerTCP } = require("modbus-serial") -const fs = require('fs') -const path = require('path') -const { JsonPlaceholderReplacer } = require('json-placeholder-replacer') -const { parseArgs } = require('node:util') -const { get } = require("http") -require('dotenv').config() - -const thingName = "modbus-elevator" +/******************************************************************************** + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the W3C Software Notice and + * Document License (2015-05-13) which is available at + * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document. + * + * SPDX-License-Identifier: EPL-2.0 OR W3C-20150513 + ********************************************************************************/ + +const { ServerTCP } = require("modbus-serial"); +const fs = require("fs"); +const path = require("path"); +const { JsonPlaceholderReplacer } = require("json-placeholder-replacer"); +const { parseArgs } = require("node:util"); +require("dotenv").config(); + +const thingName = "modbus-elevator"; // The following is needed since the modbus library we use does not support localhost but does support 0.0.0.0 -const hostname = process.env.HOSTNAME - ? process.env.HOSTNAME === "localhost" ? "0.0.0.0" : process.env.HOSTNAME - : "0.0.0.0" -let portNumber = process.env.PORT ?? "8502" -const thingUnitID = 1 - -const { values: { port, isTestRun } } = parseArgs({ +const hostname = process.env.HOSTNAME + ? process.env.HOSTNAME === "localhost" + ? "0.0.0.0" + : process.env.HOSTNAME + : "0.0.0.0"; +let portNumber = process.env.PORT ?? "8502"; +const thingUnitID = 1; + +const { + values: { port, isTestRun }, +} = parseArgs({ options: { port: { - type: 'string', - short: 'p' + type: "string", + short: "p", }, isTestRun: { - type: 'boolean', - short: 't', - default: false - } - } -}) - + type: "boolean", + short: "t", + default: false, + }, + }, +}); + if (port && !isNaN(parseInt(port))) { - portNumber = parseInt(port) + portNumber = parseInt(port); } -const tmPath = process.env.TM_PATH +const tmPath = process.env.TM_PATH; -if (process.platform === 'win32') { - tmPath.split(path.sep).join(path.win32.sep) +if (process.platform === "win32") { + tmPath.split(path.sep).join(path.win32.sep); } -const thingModel = JSON.parse(fs.readFileSync(path.join(__dirname, tmPath))) +const thingModel = JSON.parse(fs.readFileSync(path.join(__dirname, tmPath))); -const placeholderReplacer = new JsonPlaceholderReplacer() +const placeholderReplacer = new JsonPlaceholderReplacer(); placeholderReplacer.addVariableMap({ - PROTOCOL: 'modbus+tcp', + PROTOCOL: "modbus+tcp", THING_NAME: thingName, HOSTNAME: hostname, PORT_NUMBER: portNumber, - UNIT_ID: thingUnitID -}) -const thingDescription = placeholderReplacer.replace(thingModel) -thingDescription['@type'] = 'Thing' - -const coils = new Array(9999) -const discreteInputs = new Array(9999) -const holdingRegisters = new Array(9999) - -coils[0] = 0 - -const lightSwitchForms = [{ - "href": `modbus+tcp://0.0.0.0:8502/1/1?quantity=1`, - "op": "readproperty", - "modv:entity": "Coil", - "modv:function": "readCoil", - "contentType": "application/octet-stream" -}, { - "href": `modbus+tcp://0.0.0.0:8502/1/1?quantity=1`, - "op": "writeproperty", - "modv:entity": "Coil", - "modv:function": "writeSingleCoil", - "contentType": "application/octet-stream" -}] - -thingDescription['properties']['lightSwitch']['forms'] = lightSwitchForms - -const onTheMoveAddress = 0 -const onTheMovePollingTime = 1000 - -const onTheMoveForms = [{ - "href": `modbus+tcp://0.0.0.0:8502/1/10001?quantity=1`, - "op": [ - "readproperty", - "observeproperty" - ], - "modv:entity": "DiscreteInput", - "modv:function": "readDiscreteInput", - "modv:pollingTime": onTheMovePollingTime, - "contentType": "application/octet-stream" -}] - -discreteInputs[0] = 0 -let onTheMoveIsPolled = false -thingDescription['properties']['onTheMove']['forms'] = onTheMoveForms - -const floorNumberForms = [{ - "href": `modbus+tcp://0.0.0.0:8502/1/40001?quantity=2`, - "op": "readproperty", - "modv:entity": "HoldingRegister", - "modv:function": "readHoldingRegisters", - "contentType": "application/octet-stream" -}, { - "href": `modbus+tcp://0.0.0.0:8502/1/40001?quantity=2`, - "op": "writeproperty", - "modv:entity": "HoldingRegister", - "modv:function": "writeSingleHoldingRegister", - "contentType": "application/octet-stream" -}] - -const floorNumberAddress = 0 -const floorNumberQuantity = 2 + UNIT_ID: thingUnitID, +}); +const thingDescription = placeholderReplacer.replace(thingModel); +thingDescription["@type"] = "Thing"; + +const coils = new Array(9999); +const discreteInputs = new Array(9999); +const holdingRegisters = new Array(9999); + +coils[0] = 0; + +const lightSwitchForms = [ + { + href: `modbus+tcp://0.0.0.0:8502/1/1?quantity=1`, + op: "readproperty", + "modv:entity": "Coil", + "modv:function": "readCoil", + contentType: "application/octet-stream", + }, + { + href: `modbus+tcp://0.0.0.0:8502/1/1?quantity=1`, + op: "writeproperty", + "modv:entity": "Coil", + "modv:function": "writeSingleCoil", + contentType: "application/octet-stream", + }, +]; + +thingDescription.properties.lightSwitch.forms = lightSwitchForms; + +const onTheMoveAddress = 0; +const onTheMovePollingTime = 1000; + +const onTheMoveForms = [ + { + href: `modbus+tcp://0.0.0.0:8502/1/10001?quantity=1`, + op: ["readproperty", "observeproperty"], + "modv:entity": "DiscreteInput", + "modv:function": "readDiscreteInput", + "modv:pollingTime": onTheMovePollingTime, + contentType: "application/octet-stream", + }, +]; + +discreteInputs[0] = 0; +let onTheMoveIsPolled = false; +thingDescription.properties.onTheMove.forms = onTheMoveForms; + +const floorNumberForms = [ + { + href: `modbus+tcp://0.0.0.0:8502/1/40001?quantity=2`, + op: "readproperty", + "modv:entity": "HoldingRegister", + "modv:function": "readHoldingRegisters", + contentType: "application/octet-stream", + }, + { + href: `modbus+tcp://0.0.0.0:8502/1/40001?quantity=2`, + op: "writeproperty", + "modv:entity": "HoldingRegister", + "modv:function": "writeSingleHoldingRegister", + contentType: "application/octet-stream", + }, +]; + +const floorNumberAddress = 0; +const floorNumberQuantity = 2; const getFloorNumberValue = () => { return holdingRegisters .slice(floorNumberAddress, floorNumberAddress + floorNumberQuantity) - .reduce((sum, e) => sum + e) -} - -holdingRegisters[0] = 0 -const minFloorNumber = 0 -const maxFloorNumber = 15 -thingDescription['properties']['floorNumber']['forms'] = floorNumberForms + .reduce((sum, e) => sum + e); +}; -fs.writeFile(`${thingName}.td.json`, JSON.stringify(thingDescription, 4, 4), 'utf-8', function(){}) +holdingRegisters[0] = 0; +const minFloorNumber = 0; +const maxFloorNumber = 15; +thingDescription.properties.floorNumber.forms = floorNumberForms; +fs.writeFile( + `${thingName}.td.json`, + JSON.stringify(thingDescription, 4, 4), + "utf-8", + function () {} +); -const coilMemoryRange = [1, 9999] -const discreteInputMemoryRange = [10001, 19999] -const inputRegisterMemoryRange = [30001, 39999] -const holdingRegisterMemoryRange = [40001, 49999] +const coilMemoryRange = [1, 9999]; +const discreteInputMemoryRange = [10001, 19999]; +// const inputRegisterMemoryRange = [30001, 39999] +const holdingRegisterMemoryRange = [40001, 49999]; const isAddressInRange = (address, range) => { - return address >= range[0] && address <= range[1] -} + return address >= range[0] && address <= range[1]; +}; const getNormalizedAddress = (address, range) => { - return address - range[0] -} + return address - range[0]; +}; const vector = { - getDiscreteInput: function(addr, unitID) { + getDiscreteInput: function (addr, unitID) { if (thingUnitID === unitID) { if (!isAddressInRange(addr, discreteInputMemoryRange)) { - console.log(`Address is out of discrete input memory range.`) - return + console.log(`Address is out of discrete input memory range.`); + return; } - console.log(`Reading discrete input @${addr}`) - const normalizedAddress = getNormalizedAddress(addr, discreteInputMemoryRange) + console.log(`Reading discrete input @${addr}`); + const normalizedAddress = getNormalizedAddress( + addr, + discreteInputMemoryRange + ); if (normalizedAddress === onTheMoveAddress) { if (onTheMoveIsPolled) { - console.log(`Polling onTheMove too frequently. You should poll it every ${onTheMovePollingTime} ms.`) - return + console.log( + `Polling onTheMove too frequently. You should poll it every ${onTheMovePollingTime} ms.` + ); + return; } - onTheMoveIsPolled = true - let returnValue - + onTheMoveIsPolled = true; + let returnValue; + if (isTestRun) { - onTheMoveIsPolled = false - returnValue = discreteInputs[normalizedAddress] - discreteInputs[normalizedAddress] = 0 + onTheMoveIsPolled = false; + returnValue = discreteInputs[normalizedAddress]; + discreteInputs[normalizedAddress] = 0; } else { - setTimeout(function() { - onTheMoveIsPolled = false - }, onTheMovePollingTime) + setTimeout(function () { + onTheMoveIsPolled = false; + }, onTheMovePollingTime); - returnValue = discreteInputs[normalizedAddress] + returnValue = discreteInputs[normalizedAddress]; } - - return returnValue + + return returnValue; } } }, - getHoldingRegister: function(addr, unitID, callback) { + getHoldingRegister: function (addr, unitID, callback) { if (thingUnitID === unitID) { if (!isAddressInRange(addr, holdingRegisterMemoryRange)) { - console.log(`Address is out of holding register memory range.`) - return + console.log(`Address is out of holding register memory range.`); + return; } - const normalizedAddress = getNormalizedAddress(addr, holdingRegisterMemoryRange) - - setTimeout(function() { - callback(null, holdingRegisters[normalizedAddress]) - }, 10) + const normalizedAddress = getNormalizedAddress( + addr, + holdingRegisterMemoryRange + ); + + setTimeout(function () { + callback(null, holdingRegisters[normalizedAddress]); + }, 10); } }, - getCoil: function(addr, unitID) { + getCoil: function (addr, unitID) { if (thingUnitID === unitID) { if (!isAddressInRange(addr, coilMemoryRange)) { - console.log(`Address is out of coil memory range.`) - return + console.log(`Address is out of coil memory range.`); + return; } - return new Promise(function(resolve) { - console.log(`Reading coil @${addr}`) - const normalizedAddress = getNormalizedAddress(addr, coilMemoryRange) - resolve(coils[normalizedAddress]) - }) + return new Promise(function (resolve) { + console.log(`Reading coil @${addr}`); + const normalizedAddress = getNormalizedAddress( + addr, + coilMemoryRange + ); + resolve(coils[normalizedAddress]); + }); } }, - setRegister: function(addr, value, unitID) { + setRegister: function (addr, value, unitID) { if (thingUnitID === unitID) { if (!isAddressInRange(addr, holdingRegisterMemoryRange)) { - console.log(`Address is out of holding register memory range.`) - return + console.log(`Address is out of holding register memory range.`); + return; } - console.log(`Setting register @${addr} to ${value}`) - const normalizedAddress = getNormalizedAddress(addr, holdingRegisterMemoryRange) + console.log(`Setting register @${addr} to ${value}`); + const normalizedAddress = getNormalizedAddress( + addr, + holdingRegisterMemoryRange + ); // trying to change floor number - holdingRegisters[normalizedAddress] = value + holdingRegisters[normalizedAddress] = value; // writing last part of the value and running the thing logic - if (normalizedAddress === floorNumberAddress + floorNumberQuantity - 1) { + if ( + normalizedAddress === + floorNumberAddress + floorNumberQuantity - 1 + ) { // elevator is on the move if (discreteInputs[onTheMoveAddress] && !isTestRun) { - console.log("Elevator is on the move, cannot change the floor number") + console.log( + "Elevator is on the move, cannot change the floor number" + ); } else { - const floorNumberValue = getFloorNumberValue() + const floorNumberValue = getFloorNumberValue(); if (floorNumberValue < minFloorNumber) { - console.log(`Floor number should not be under ${minFloorNumber}`) - return -1 + console.log( + `Floor number should not be under ${minFloorNumber}` + ); + return -1; } if (floorNumberValue > maxFloorNumber) { - console.log(`Floor number should not be above ${maxFloorNumber}`) - return -1 + console.log( + `Floor number should not be above ${maxFloorNumber}` + ); + return -1; } - console.log(`Changing the floor number to ${value}`) - + console.log(`Changing the floor number to ${value}`); + // simulating elevator movement - discreteInputs[onTheMoveAddress] = 1 + discreteInputs[onTheMoveAddress] = 1; // elevator completes its movement in 5 seconds if (!isTestRun) { setTimeout(() => { - discreteInputs[onTheMoveAddress] = 0 - }, 5000) + discreteInputs[onTheMoveAddress] = 0; + }, 5000); } } } } - - return }, - setCoil: function(addr, value, unitID) { + setCoil: function (addr, value, unitID) { if (thingUnitID === unitID) { if (!isAddressInRange(addr, coilMemoryRange)) { - console.log(`Address is out of coil memory range.`) - return + console.log(`Address is out of coil memory range.`); + return; } - const normalizedAddress = getNormalizedAddress(addr, coilMemoryRange) + const normalizedAddress = getNormalizedAddress( + addr, + coilMemoryRange + ); - console.log(`Setting coil @${addr} to ${value}`) - coils[normalizedAddress] = value + console.log(`Setting coil @${addr} to ${value}`); + coils[normalizedAddress] = value; } - - return - } + }, }; // set the server to answer for modbus requests -console.log(`Started listening to on port ${portNumber}`) -console.log("ThingIsReady") - -const serverTCP = new ServerTCP(vector, { host: hostname, port: portNumber, debug: true, unitID: thingUnitID }) +console.log(`Started listening to on port ${portNumber}`); +console.log("ThingIsReady"); + +const serverTCP = new ServerTCP(vector, { + host: hostname, + port: portNumber, + debug: true, + unitID: thingUnitID, +}); -serverTCP.on("socketError", function(err){ +serverTCP.on("socketError", function (err) { // Handle socket error if needed, can be ignored - console.error(err) + console.error(err); }); diff --git a/things/elevator/modbus/js/modbus-elevator.td.json b/things/elevator/modbus/js/modbus-elevator.td.json index e3bd3c6..1f8cd65 100644 --- a/things/elevator/modbus/js/modbus-elevator.td.json +++ b/things/elevator/modbus/js/modbus-elevator.td.json @@ -14,9 +14,7 @@ "scheme": "nosec" } }, - "security": [ - "nosec_sc" - ], + "security": ["nosec_sc"], "base": "modbus+tcp://0.0.0.0:8502/1", "properties": { "lightSwitch": { @@ -49,10 +47,7 @@ "forms": [ { "href": "modbus+tcp://0.0.0.0:8502/1/10001?quantity=1", - "op": [ - "readproperty", - "observeproperty" - ], + "op": ["readproperty", "observeproperty"], "modv:entity": "DiscreteInput", "modv:function": "readDiscreteInput", "modv:pollingTime": 1000, @@ -85,4 +80,4 @@ ] } } -} \ No newline at end of file +} diff --git a/things/elevator/modbus/js/package.json b/things/elevator/modbus/js/package.json index 50ffe3b..9e58152 100644 --- a/things/elevator/modbus/js/package.json +++ b/things/elevator/modbus/js/package.json @@ -1,23 +1,27 @@ { - "name": "modbus-elevator", - "version": "1.0.0", - "description": "Modbus Elevator", - "main": "main.js", - "keywords": [ - "wot", - "thing", - "iot", - "modbus" - ], - "author": "Eclipse Thingweb (https://thingweb.io/)", - "license": "EPL-2.0 OR W3C-20150513", - "dependencies": { - "@node-wot/binding-modbus": "^0.8.16", - "@node-wot/core": "^0.8.16", - "dotenv": "^16.3.1", - "json-placeholder-replacer": "^1.0.35", - "modbus": "^1.1.1", - "modbus-serial": "^8.0.11", - "serialport": "^11.0.0" - } + "name": "modbus-elevator", + "version": "1.0.0", + "description": "Modbus Elevator", + "main": "main.js", + "scripts": { + "lint": "eslint .", + "lint:fix": "eslint . --fix", + "format": "prettier --write \"**/*.js\" \"test/**/*.js\" \"**/*.json\"" + }, + "keywords": [ + "wot", + "thing", + "iot", + "modbus" + ], + "author": "Eclipse Thingweb (https://thingweb.io/)", + "license": "EPL-2.0 OR W3C-20150513", + "dependencies": { + "json-placeholder-replacer": "^1.0.35", + "modbus": "^1.1.1", + "modbus-serial": "^8.0.11", + "serialport": "^11.0.0", + "dotenv": "^16.3.1", + "@node-wot/binding-modbus": "~0.8.16" + } } diff --git a/things/elevator/modbus/js/test/client.test.js b/things/elevator/modbus/js/test/client.test.js index 2f1fe29..5cf72b7 100644 --- a/things/elevator/modbus/js/test/client.test.js +++ b/things/elevator/modbus/js/test/client.test.js @@ -1,96 +1,113 @@ -const chai = require('chai') -const chaiAsPromised = require('chai-as-promised') - -const { Servient } = require('@node-wot/core') -const { ModbusClientFactory } = require('@node-wot/binding-modbus') -const modbusTd = require('../modbus-elevator.td.json') - -chai.use(chaiAsPromised) -const expect = chai.expect - -let servient = new Servient() -servient.addClientFactory(new ModbusClientFactory()) - -let thing +/******************************************************************************** + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the W3C Software Notice and + * Document License (2015-05-13) which is available at + * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document. + * + * SPDX-License-Identifier: EPL-2.0 OR W3C-20150513 + ********************************************************************************/ + +const chai = require("chai"); +const chaiAsPromised = require("chai-as-promised"); + +const { Servient } = require("@node-wot/core"); +const { ModbusClientFactory } = require("@node-wot/binding-modbus"); +const modbusTd = require("../modbus-elevator.td.json"); + +chai.use(chaiAsPromised); +const expect = chai.expect; + +const servient = new Servient(); +servient.addClientFactory(new ModbusClientFactory()); + +let thing; const readProperty = async (thing, propertyName) => { try { - const response = await thing.readProperty(propertyName) - return await response.value() - } catch(error) { - console.error(`Error: ${error}`) + const response = await thing.readProperty(propertyName); + return await response.value(); + } catch (error) { + console.error(`Error: ${error}`); } -} +}; const writeProperty = async (thing, propertyName, propertyValue) => { try { - await thing.writeProperty(propertyName, propertyValue) + await thing.writeProperty(propertyName, propertyValue); } catch (error) { - console.error(`Error: ${error}`) + console.error(`Error: ${error}`); } -} +}; describe("Client Tests", () => { before(async () => { try { - const WoT = await servient.start() - thing = await WoT.consume(modbusTd) - } catch(error) { - console.error(error) + const WoT = await servient.start(); + thing = await WoT.consume(modbusTd); + } catch (error) { + console.error(error); } - }) + }); after(async () => { - await servient.shutdown() - }) + await servient.shutdown(); + }); describe("lightSwitch property", () => { it("should return false when it is not turned on", async () => { - const value = await readProperty(thing, "lightSwitch") - expect(value).to.be.false - }) + const value = await readProperty(thing, "lightSwitch"); + expect(value).to.be.false; + }); it("should return true when it is turned on", async () => { - await writeProperty(thing, "lightSwitch", true) - const value = await readProperty(thing, "lightSwitch") - expect(value).to.be.true - }) - }) + await writeProperty(thing, "lightSwitch", true); + const value = await readProperty(thing, "lightSwitch"); + expect(value).to.be.true; + }); + }); describe("onTheMove property", () => { it("should return false when floorNumber is not changed recently", async () => { - const value = await readProperty(thing, "onTheMove") - expect(value).to.be.false - }) + const value = await readProperty(thing, "onTheMove"); + expect(value).to.be.false; + }); it("should return true when floorNumber is changed recently", async () => { - const floorNumberValue = await readProperty(thing, "floorNumber") - await writeProperty(thing, "floorNumber", 12) - const onTheMoveValue = await readProperty(thing, "onTheMove") - await writeProperty(thing, "floorNumber", floorNumberValue) - expect(onTheMoveValue).to.be.true - }) - }) + const floorNumberValue = await readProperty(thing, "floorNumber"); + await writeProperty(thing, "floorNumber", 12); + const onTheMoveValue = await readProperty(thing, "onTheMove"); + await writeProperty(thing, "floorNumber", floorNumberValue); + expect(onTheMoveValue).to.be.true; + }); + }); describe("floorNumber property", () => { it("should return 0 when the thing is newly started", async () => { - const value = await readProperty(thing, "floorNumber") - expect(value).to.be.equal(0) - }) + const value = await readProperty(thing, "floorNumber"); + expect(value).to.be.equal(0); + }); it("should return the recently set value when a new value is set", async () => { - const newValue = 12 - await writeProperty(thing, "floorNumber", newValue) - const value = await readProperty(thing, "floorNumber") - expect(value).to.be.equal(newValue) - }) + const newValue = 12; + await writeProperty(thing, "floorNumber", newValue); + const value = await readProperty(thing, "floorNumber"); + expect(value).to.be.equal(newValue); + }); it.skip("should not write a value when the value is below 0", async () => { - await expect(writeProperty(thing, "floorNumber", -1)).to.be.rejected - }) - - it.skip("should not write a value when the value is above 15", async() => { - await expect(writeProperty(thing, "floorNumber", 16)).to.be.rejected - }) - }) -}) \ No newline at end of file + await expect(writeProperty(thing, "floorNumber", -1)).to.be + .rejected; + }); + + it.skip("should not write a value when the value is above 15", async () => { + await expect(writeProperty(thing, "floorNumber", 16)).to.be + .rejected; + }); + }); +}); diff --git a/things/elevator/modbus/js/test/fixtures.js b/things/elevator/modbus/js/test/fixtures.js index 28b2cdf..055c75d 100644 --- a/things/elevator/modbus/js/test/fixtures.js +++ b/things/elevator/modbus/js/test/fixtures.js @@ -1,23 +1,42 @@ -const { getInitiateMain } = require("../../../../../util/dist/util") -const path = require("node:path") +/******************************************************************************** + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the W3C Software Notice and + * Document License (2015-05-13) which is available at + * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document. + * + * SPDX-License-Identifier: EPL-2.0 OR W3C-20150513 + ********************************************************************************/ -let thingProcess -let response -const port = 8502 +const { getInitiateMain } = require("../../../../../util/dist/util"); +const path = require("node:path"); -exports.mochaGlobalSetup = async function() { +let thingProcess; +let response; +const port = 8502; + +exports.mochaGlobalSetup = async function () { try { - response = await getInitiateMain('node', [path.join(__dirname, '..', 'main.js'), '-p', `${port}`, '-t']) - thingProcess = response.process - } - catch(error) { - console.log(error) - thingProcess = error.process + response = await getInitiateMain("node", [ + path.join(__dirname, "..", "main.js"), + "-p", + `${port}`, + "-t", + ]); + thingProcess = response.process; + } catch (error) { + console.log(error); + thingProcess = error.process; } -} +}; -exports.mochaGlobalTeardown = function() { +exports.mochaGlobalTeardown = function () { if (thingProcess) { - thingProcess.kill() + thingProcess.kill(); } -} \ No newline at end of file +}; diff --git a/things/elevator/modbus/js/test/td.test.js b/things/elevator/modbus/js/test/td.test.js index 12684f2..e6d15c1 100644 --- a/things/elevator/modbus/js/test/td.test.js +++ b/things/elevator/modbus/js/test/td.test.js @@ -1,36 +1,54 @@ -const chai = require('chai') -const fs = require('fs') -const path = require('path') -const { getTDValidate } = require("../../../../../util/dist/util") +/******************************************************************************** + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the W3C Software Notice and + * Document License (2015-05-13) which is available at + * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document. + * + * SPDX-License-Identifier: EPL-2.0 OR W3C-20150513 + ********************************************************************************/ -const expect = chai.expect +const chai = require("chai"); +const fs = require("fs"); +const path = require("path"); +const { getTDValidate } = require("../../../../../util/dist/util"); -describe('Elevator Modbus JS', () => { - let validate +const expect = chai.expect; - before(async () => { - const tdValidate = getTDValidate() - - try { - const response = await Promise.all([tdValidate]) - validate = response[0].validate - } - catch (error) { - console.log(error) - } - }) +describe("Elevator Modbus JS", () => { + let validate; - it('should have a valid TD', (done) => { - fs.readFile(path.join(__dirname, "../modbus-elevator.td.json"), 'utf-8', (err, data) => { - if (err) { - console.log(err) - done(err) - } + before(async () => { + const tdValidate = getTDValidate(); - const result = JSON.parse(data.toString()) - const valid = validate(result) - expect(valid).to.be.true - done() - }) - }) -}) + try { + const response = await Promise.all([tdValidate]); + validate = response[0].validate; + } catch (error) { + console.log(error); + } + }); + + it("should have a valid TD", (done) => { + fs.readFile( + path.join(__dirname, "../modbus-elevator.td.json"), + "utf-8", + (err, data) => { + if (err) { + console.log(err); + done(err); + } + + const result = JSON.parse(data.toString()); + const valid = validate(result); + expect(valid).to.be.true; + done(); + } + ); + }); +}); diff --git a/things/elevator/tm.test.js b/things/elevator/tm.test.js index e94b488..6142856 100644 --- a/things/elevator/tm.test.js +++ b/things/elevator/tm.test.js @@ -1,32 +1,35 @@ -const Ajv = require('ajv') -const chai = require('chai') -const https = require('https') +const Ajv = require("ajv"); +const chai = require("chai"); +const https = require("https"); -const ajv = new Ajv({ strict: false, allErrors: true, validateFormats: false }) +const ajv = new Ajv({ strict: false, allErrors: true, validateFormats: false }); -const expect = chai.expect +const expect = chai.expect; -describe('Elevator', () => { - let validate +describe("Elevator", () => { + let validate; - before((done) => { - https.get('https://raw.githubusercontent.com/w3c/wot-thing-description/main/validation/tm-json-schema-validation.json', function (response) { - const body = [] - response.on('data', (chunk) => { - body.push(chunk) - }) + before((done) => { + https.get( + "https://raw.githubusercontent.com/w3c/wot-thing-description/main/validation/tm-json-schema-validation.json", + function (response) { + const body = []; + response.on("data", (chunk) => { + body.push(chunk); + }); - response.on('end', () => { - const tmSchema = JSON.parse(Buffer.concat(body).toString()) - validate = ajv.compile(tmSchema) - done() - }) - }) - }) + response.on("end", () => { + const tmSchema = JSON.parse(Buffer.concat(body).toString()); + validate = ajv.compile(tmSchema); + done(); + }); + } + ); + }); - it('should have a valid TM', () => { - const elevatorTM = require('./elevator.tm.json') - const valid = validate(elevatorTM) - expect(valid).to.be.true - }) -}) + it("should have a valid TM", () => { + const elevatorTM = require("./elevator.tm.json"); + const valid = validate(elevatorTM); + expect(valid).to.be.true; + }); +}); diff --git a/tsconfig.eslint.json b/tsconfig.eslint.json new file mode 100644 index 0000000..c2151c5 --- /dev/null +++ b/tsconfig.eslint.json @@ -0,0 +1,7 @@ +{ + "extends": "./tsconfig.json", + "include": ["**/*.ts"], + "compilerOptions": { + "experimentalDecorators": true + } +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..b98f521 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,33 @@ +{ + "compilerOptions": { + "target": "es6", + "lib": ["dom"], + "skipLibCheck": false, + "module": "commonjs", + "outDir": "dist", + "strict": true, + "composite": true, + "incremental": true, + "sourceMap": true, + "noImplicitReturns": true, + "removeComments": true, + "noLib": false, + "preserveConstEnums": true, + "experimentalDecorators": true, + "declaration": true, + "esModuleInterop": true, + "moduleResolution": "node" /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */, + "resolveJsonModule": false, + "skipDefaultLibCheck": false, + "allowJs": false, + "strictNullChecks": true + }, + "references": [ + { "path": "./things/advanced-coffee-machine/http/ts" }, + { "path": "./things/data-schema-thing/http/ts" } + ], + "include": [ + /** specifically nothing here */ + ], + "exclude": [] +} diff --git a/util/tsconfig.json b/util/tsconfig.json index 31dc724..b2de206 100644 --- a/util/tsconfig.json +++ b/util/tsconfig.json @@ -8,5 +8,5 @@ "sourceMap": false, "esModuleInterop": true, "removeComments": false - }, -} \ No newline at end of file + } +} diff --git a/util/util.ts b/util/util.ts index affeb93..0018c3a 100644 --- a/util/util.ts +++ b/util/util.ts @@ -1,86 +1,103 @@ -import Ajv, { ValidateFunction } from 'ajv' -import * as https from 'https' -import { ChildProcess } from 'node:child_process' +/******************************************************************************** + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the W3C Software Notice and + * Document License (2015-05-13) which is available at + * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document. + * + * SPDX-License-Identifier: EPL-2.0 OR W3C-20150513 + ********************************************************************************/ -export type ThingStartResponse = { - process?: ChildProcess, - message: string -} +import Ajv, { ValidateFunction } from "ajv"; +import * as https from "https"; +import { ChildProcess } from "node:child_process"; + +export type ThingStartResponse = { + process?: ChildProcess; + message: string; +}; export type ValidateResponse = { - validate?: ValidateFunction, - message: string -} + validate?: ValidateFunction; + message: string; +}; -const spawn = require('node:child_process').spawn +const spawn = require("node:child_process").spawn; -export const getInitiateMain = (mainCmd: string, cmdArgs: string[]): Promise => { +export const getInitiateMain = ( + mainCmd: string, + cmdArgs: string[] +): Promise => { return new Promise((resolve, reject) => { - const thingProcess = spawn( - mainCmd, - cmdArgs, - ) + const thingProcess = spawn(mainCmd, cmdArgs); // Avoids unsettled promise in case the promise is not settled in a second. const timeout = setTimeout(() => { - reject({ - process: thingProcess, - message: 'Thing did not start as expected.' - }) - }, 1000) + reject({ + process: thingProcess, + message: "Thing did not start as expected.", + }); + }, 1000); - thingProcess.stdout!.on('data', (data: Buffer) => { - if(data.toString().includes('ThingIsReady')) { - clearTimeout(timeout) + thingProcess.stdout!.on("data", (data: Buffer) => { + if (data.toString().includes("ThingIsReady")) { + clearTimeout(timeout); resolve({ process: thingProcess, - message: 'Success' - }) - } - }) - thingProcess.stderr!.on('data', (data: Buffer) => { - reject({ - process: thingProcess, - message: `Error: ${data}` - }) - }) - thingProcess.on('error', (error: any) => { - reject({ - process: thingProcess, - message: `Error: ${error}` - }) - }) - thingProcess.on('close', () => { - reject({ - process: thingProcess, - message: 'Failed to initiate the main script.' - }) - }) - }) -} - + message: "Success", + }); + } + }); + thingProcess.stderr!.on("data", (data: Buffer) => { + reject({ + process: thingProcess, + message: `Error: ${data}`, + }); + }); + thingProcess.on("error", (error: any) => { + reject({ + process: thingProcess, + message: `Error: ${error}`, + }); + }); + thingProcess.on("close", () => { + reject({ + process: thingProcess, + message: "Failed to initiate the main script.", + }); + }); + }); +}; -const ajv = new Ajv({ strict: false, allErrors: true, validateFormats: false }) +const ajv = new Ajv({ strict: false, allErrors: true, validateFormats: false }); export const getTDValidate = async (): Promise => { - const tdSchema: any = await getTDJSONSchema + const tdSchema: any = await getTDJSONSchema; return Promise.resolve({ validate: ajv.compile(tdSchema), - message: 'Success' - }) -} + message: "Success", + }); +}; const getTDJSONSchema = new Promise((resolve, reject) => { - https.get('https://raw.githubusercontent.com/w3c/wot-thing-description/main/validation/td-json-schema-validation.json', function (response: any) { - const body: Buffer[] = [] - response.on('data', (chunk: Buffer) => { - body.push(chunk) - }) - - response.on('end', () => { - const tdSchema = JSON.parse(Buffer.concat(body).toString()) - resolve(tdSchema) - }) - }) -}) + https.get( + "https://raw.githubusercontent.com/w3c/wot-thing-description/main/validation/td-json-schema-validation.json", + function (response: any) { + const body: Buffer[] = []; + response.on("data", (chunk: Buffer) => { + body.push(chunk); + }); + + response.on("end", () => { + const tdSchema = JSON.parse(Buffer.concat(body).toString()); + resolve(tdSchema); + }); + } + ); +});