From 270dbec6f5cbaf2f09c5294804b47cdb42c605bb Mon Sep 17 00:00:00 2001 From: Hasan Eroglu Date: Fri, 20 Sep 2024 12:43:22 +0200 Subject: [PATCH 1/7] add config files Signed-off-by: Hasan Eroglu --- .editorconfig | 9 +++++ .eslintrc.js | 88 ++++++++++++++++++++++++++++++++++++++++++++ .prettierignore | 3 ++ .prettierrc.json | 7 ++++ license.template.txt | 15 ++++++++ tsconfig.eslint.json | 7 ++++ tsconfig.json | 33 +++++++++++++++++ 7 files changed, 162 insertions(+) create mode 100644 .editorconfig create mode 100644 .eslintrc.js create mode 100644 .prettierignore create mode 100644 .prettierrc.json create mode 100644 license.template.txt create mode 100644 tsconfig.eslint.json create mode 100644 tsconfig.json 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..4b5d69a --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,88 @@ +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 + } + } + ] +}; \ 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/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/tsconfig.eslint.json b/tsconfig.eslint.json new file mode 100644 index 0000000..487ab6c --- /dev/null +++ b/tsconfig.eslint.json @@ -0,0 +1,7 @@ +{ + "extends": "./tsconfig.json", + "include": ["**/*.ts"], + "compilerOptions": { + "experimentalDecorators": true + } +} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..af61b5c --- /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": [] +} \ No newline at end of file From a46b52c419b23613d55bab6ee4ac1bed6b9e1685 Mon Sep 17 00:00:00 2001 From: Hasan Eroglu Date: Wed, 25 Sep 2024 14:42:22 +0200 Subject: [PATCH 2/7] add eslint, editorconfig and prettier Signed-off-by: Hasan Eroglu --- .eslintrc.js | 6 +- .github/workflows/run-tests.yml | 83 +- README.md | 37 +- conf/grafana/dashboards/dashboard.json | 2 +- .../provisioning/dashboards/dashboard.yml | 2 +- .../provisioning/datasources/datasource.yml | 2 +- conf/prometheus/prometheus.yml | 10 +- docker-compose-infra.yml | 46 +- docker-compose-things.yml | 62 +- mashups/smart-home/README.md | 6 +- mashups/smart-home/mashup-logic.ts | 74 +- mashups/smart-home/package.json | 5 +- mashups/smart-home/things/presence-sensor.ts | 79 +- .../things/simple-coffee-machine.ts | 345 +- mashups/smart-home/things/smart-clock.ts | 135 +- mashups/smart-home/tsconfig.json | 26 +- package-lock.json | 2885 ++++++++++++++++- package.json | 18 +- .../advanced-coffee-machine.tm.json | 420 ++- .../http/ts/.mocharc.json | 6 +- .../http/ts/package.json | 4 +- .../http/ts/src/main.ts | 529 +-- .../http/ts/test/client.test.ts | 177 +- .../http/ts/test/fixtures.ts | 53 +- .../http/ts/test/td.test.ts | 92 +- .../http/ts/tsconfig.json | 20 +- things/advanced-coffee-machine/tm.test.js | 53 +- things/calculator/calculator.tm.json | 118 +- .../content-negotiation-coap-client.js | 544 ++-- ...ode-wot-content-negotiation-coap-client.js | 92 +- .../node-wot-simple-coap-client.js | 93 +- .../coap/client-tests/simple-coap-client.js | 391 +-- .../js/coap-content-negotiation-calculator.js | 690 ++-- .../coap/js/coap-simple-calculator.js | 499 +-- things/calculator/coap/js/package.json | 4 + things/calculator/coap/js/test/td.test.js | 140 +- .../content-negotiation-http-client.js | 468 ++- ...ode-wot-content-negotiation-http-client.js | 25 +- .../node-wot-simple-http-client.js | 50 +- .../http/client-tests/simple-http-client.js | 217 +- .../http-content-negotiation-calculator.js | 778 +++-- .../http/express/http-simple-calculator.js | 196 +- things/calculator/http/express/package.json | 4 +- .../calculator/http/express/test/td.test.js | 158 +- things/calculator/http/flask/test/td.test.js | 138 +- things/calculator/mqtt/js/main.js | 203 +- things/calculator/mqtt/js/package.json | 4 + things/calculator/mqtt/js/test/td.test.js | 147 +- things/calculator/tm.test.js | 53 +- .../data-schema-thing.tm.json | 16 +- .../data-schema-thing/http/ts/.mocharc.json | 6 +- things/data-schema-thing/http/ts/package.json | 4 +- things/data-schema-thing/http/ts/src/main.ts | 421 +-- .../http/ts/test/client.test.ts | 366 ++- .../http/ts/test/fixtures.ts | 53 +- .../data-schema-thing/http/ts/test/td.test.ts | 91 +- .../data-schema-thing/http/ts/tsconfig.json | 20 +- things/data-schema-thing/tm.test.js | 53 +- things/elevator/elevator.tm.json | 74 +- things/elevator/modbus/js/main.js | 354 +- .../modbus/js/modbus-elevator.td.json | 155 +- things/elevator/modbus/js/package.json | 4 + things/elevator/modbus/js/test/td.test.js | 149 +- things/elevator/tm.test.js | 53 +- util/util.ts | 163 +- 65 files changed, 7679 insertions(+), 4492 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 4b5d69a..93486d2 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -82,7 +82,11 @@ module.exports = { ], "env": { "mocha": true - } + }, + "rules": { + "no-unused-expressions": "off", + "@typescript-eslint/no-unused-expressions": "off" + } } ] }; \ No newline at end of file diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 0348605..fc90ef9 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -1,47 +1,46 @@ name: Run tests on: - push: - paths-ignore: - - "**.md" - - "**.png" - - "**.svg" - pull_request: - paths-ignore: - - "**.md" - - "**.png" - - "**.svg" + 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 + 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 diff --git a/README.md b/README.md index c42c075..d413cbf 100644 --- a/README.md +++ b/README.md @@ -101,25 +101,25 @@ 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 and cadvisor. +[docker-compose-infra.yml](./docker-compose-infra.yml) consists of the docker configuration of additional tools such as traefik, prometheus, grafana and cadvisor. 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 have a basic username and password pair. The services are accessible at: -- Traefik dashboard -> dashboard.localhost +- Traefik dashboard -> dashboard.localhost - Prometheus -> prometheus.localhost - Grafana -> grafana.localhost - cAdvisor -> cadvisor.localhost @@ -127,9 +127,9 @@ To be able to access additional tools, the user must have a basic username and p Hostname and ports can be changed from `.env` file in the root directory. Therefore the links for devices would change accordingly. A username and password should be generated for running the services. To do so: - 1. Choose a username, e.g. `myuser`, and run the following command in the command line: `echo $(htpasswd -nB USERNAMECHOICE) | sed -e s/\\$/\\$\\$/g` - 2. Enter the username and the generated password (hashed) in the `.env` file under `TRAEFIK_DASHBOARD_USER` and `TRAEFIK_DASHBOARD_PASS`, respectively. - 3. Use the username and the password you have types (not the hashed one) when logging in at any service but Portainer. +1. Choose a username, e.g. `myuser`, and run the following command in the command line: `echo $(htpasswd -nB USERNAMECHOICE) | sed -e s/\\$/\\$\\$/g` +2. Enter the username and the generated password (hashed) in the `.env` file under `TRAEFIK_DASHBOARD_USER` and `TRAEFIK_DASHBOARD_PASS`, respectively. +3. Use the username and the password you have types (not the hashed one) when logging in at any service but Portainer. ### Running separately @@ -141,8 +141,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/dashboard.json b/conf/grafana/dashboards/dashboard.json index 1f0e23f..93f3e88 100644 --- a/conf/grafana/dashboards/dashboard.json +++ b/conf/grafana/dashboards/dashboard.json @@ -139,4 +139,4 @@ "uid": "bdwzocnv714hsd", "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/grafana/provisioning/datasources/datasource.yml b/conf/grafana/provisioning/datasources/datasource.yml index f6e070d..2f7dfe2 100644 --- a/conf/grafana/provisioning/datasources/datasource.yml +++ b/conf/grafana/provisioning/datasources/datasource.yml @@ -7,4 +7,4 @@ datasources: type: prometheus access: proxy url: http://prometheus:${PROMETHEUS_PORT} - isDefault: true \ No newline at end of file + isDefault: true 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/docker-compose-infra.yml b/docker-compose-infra.yml index 003c246..8744e67 100644 --- a/docker-compose-infra.yml +++ b/docker-compose-infra.yml @@ -34,10 +34,10 @@ services: deploy: resources: limits: - cpus: '0.08' + cpus: "0.08" memory: 100M reservations: - cpus: '0.05' + cpus: "0.05" memory: 50M prometheus: image: prom/prometheus @@ -52,10 +52,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 @@ -75,10 +75,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 @@ -91,28 +91,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: @@ -124,10 +124,10 @@ services: deploy: resources: limits: - cpus: '0.08' + cpus: "0.08" memory: 50M reservations: - cpus: '0.05' + cpus: "0.05" memory: 50M volumes: prometheus_data: diff --git a/docker-compose-things.yml b/docker-compose-things.yml index c1933a3..4b3f577 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: @@ -49,10 +49,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-content-negotiation: labels: @@ -67,10 +67,10 @@ services: deploy: resources: limits: - cpus: '0.05' + cpus: "0.05" memory: 50M reservations: - cpus: '0.01' + cpus: "0.01" memory: 25M http-flask-calculator: labels: @@ -85,10 +85,10 @@ services: deploy: resources: limits: - cpus: '0.05' + cpus: "0.05" memory: 50M reservations: - cpus: '0.01' + cpus: "0.01" memory: 25M mqtt-calculator: build: @@ -97,10 +97,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: @@ -116,10 +116,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: @@ -134,10 +134,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: @@ -152,10 +152,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: @@ -164,13 +164,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: @@ -179,17 +179,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: @@ -200,19 +200,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 @@ -229,8 +229,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/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..34a8bc5 100644 --- a/mashups/smart-home/mashup-logic.ts +++ b/mashups/smart-home/mashup-logic.ts @@ -1,43 +1,61 @@ -// 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 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 + 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 + 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); - let morningCoffeeFlag = false + let morningCoffeeFlag = false; // We subscribe to the presence detection events presenceSensorThing.subscribeEvent("presenceDetected", async (eventData) => { @@ -45,13 +63,21 @@ let wotHelper = new Helpers(servient); // The emission of the event implies that a detection happened anyways console.log("Detected presence at,", await eventData.value(), "mm"); + type Time = { + hour: number, + minute: number + }; + // 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 + const currentTime: Time = await currentTimeData.value() as Time; // You need to always call the .value function // Optionally, we can log the current time console.log( - "Current time is " + currentTime.hour.toString().padStart(2, '0') + ":" + currentTime.minute.toString().padStart(2, '0') + "Current time is " + + currentTime.hour.toString().padStart(2, "0") + + ":" + + currentTime.minute.toString().padStart(2, "0"), ); // To avoid accidental brews, a flag is used to check whether a coffee was brewed before @@ -69,9 +95,9 @@ let wotHelper = new Helpers(servient); // we reset the morningCoffeeFlag every day at 1am setInterval(() => { - if (currentTime.hour == 1) { + if (currentTime.hour === 1) { morningCoffeeFlag = false; } }, 1000); }); -})(); \ No newline at end of file +})(); diff --git a/mashups/smart-home/package.json b/mashups/smart-home/package.json index a91f3a6..1523bcb 100644 --- a/mashups/smart-home/package.json +++ b/mashups/smart-home/package.json @@ -2,14 +2,15 @@ "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" + "start:all": "./runMashupThings.sh", + "lint": "eslint .", + "lint:fix": "eslint . --fix" }, "author": "Eclipse Thingweb (https://thingweb.io/)", "license": "EPL-2.0 OR W3C-20150513", diff --git a/mashups/smart-home/things/presence-sensor.ts b/mashups/smart-home/things/presence-sensor.ts index 119ba9a..fc6f80b 100644 --- a/mashups/smart-home/things/presence-sensor.ts +++ b/mashups/smart-home/things/presence-sensor.ts @@ -1,5 +1,5 @@ /******************************************************************************** - * Copyright (c) 2023 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. @@ -18,50 +18,51 @@ 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) => { - WoT.produce({ - title: "smart-home-presence-sensor", - description: "Thing that can detect presence of human nearby", - support: "https://github.com/eclipse-thingweb/node-wot/", - "@context": "https://www.w3.org/2022/wot/td/v1.1", - events: { - presenceDetected: { - title: "Presence Detected", - description: - "An event that is emitted when a person is detected in the room. It is mocked and emitted every 5 seconds", - data: { - type: "number", - title: "Distance", - minimum: 55, - maximum: 1200, - }, - }, + WoT.produce({ + title: "smart-home-presence-sensor", + description: "Thing that can detect presence of human nearby", + support: "https://github.com/eclipse-thingweb/node-wot/", + "@context": "https://www.w3.org/2022/wot/td/v1.1", + events: { + presenceDetected: { + title: "Presence Detected", + description: + "An event that is emitted when a person is detected in the room. It is mocked and emitted every 5 seconds", + data: { + type: "number", + title: "Distance", + minimum: 55, + maximum: 1200, }, - }) - .then((thing) => { - console.log("Produced " + thing.getThingDescription().title); + }, + }, + }) + .then((thing) => { + console.log("Produced " + thing.getThingDescription().title); - // expose the thing - thing.expose().then(() => { - console.info(thing.getThingDescription().title + " ready"); + // expose the thing + thing.expose().then(() => { + console.info(thing.getThingDescription().title + " ready"); - // mocking the detection with an event sent every 5 seconds, with a random distance - setInterval(() => { - const distance: number = Math.random() * (1200 - 55) + 55; - thing.emitEvent("presenceDetected", distance); - console.info("Emitted presence with distance ", distance); - }, 5000); - }); - }) - .catch((e) => { - console.log(e); - }); -}); \ No newline at end of file + // mocking the detection with an event sent every 5 seconds, with a random distance + setInterval(() => { + const distance: number = Math.random() * (1200 - 55) + 55; + thing.emitEvent("presenceDetected", distance); + console.info("Emitted presence with distance ", distance); + }, 5000); + }); + }) + .catch((e) => { + console.log(e); + }); +}); diff --git a/mashups/smart-home/things/simple-coffee-machine.ts b/mashups/smart-home/things/simple-coffee-machine.ts index 1180e34..7e37508 100644 --- a/mashups/smart-home/things/simple-coffee-machine.ts +++ b/mashups/smart-home/things/simple-coffee-machine.ts @@ -1,5 +1,5 @@ /******************************************************************************** - * Copyright (c) 2023 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. @@ -18,18 +18,18 @@ 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}`, - port: parseInt(httpPort), - }) + new HttpServer({ + baseUri: `http://${hostname}:${httpPort}`, + port: parseInt(httpPort), + }), ); let waterAmount = 1000; @@ -38,176 +38,179 @@ let milkAmount = 1000; // promisify timeout since it does not return a promise function timeout(ms: number) { - return new Promise((resolve) => setTimeout(resolve, ms)); + return new Promise((resolve) => setTimeout(resolve, ms)); } servient.start().then((WoT) => { - WoT.produce({ - title: "smart-home-simple-coffee-machine", - 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", + WoT.produce({ + title: "smart-home-simple-coffee-machine", + 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: { + resources: { + readOnly: true, + observable: true, + type: "object", properties: { - resources: { - readOnly: true, - observable: true, - type: "object", - properties: { - water: { - type: "integer", - minimum: 0, - maximum: 1000, - }, - beans: { - type: "integer", - minimum: 0, - maximum: 1000, - }, - milk: { - type: "integer", - minimum: 0, - maximum: 1000, - }, - }, - }, + water: { + type: "integer", + minimum: 0, + maximum: 1000, + }, + beans: { + type: "integer", + minimum: 0, + maximum: 1000, + }, + milk: { + type: "integer", + minimum: 0, + maximum: 1000, + }, }, - actions: { - brew: { - synchronous: true, - input: { - type: "string", - enum: ["espresso", "cappuccino", "americano"], - }, - }, - refill: { - synchronous: true, - input: { - type: "array", - items: { - type: "string", - enum: ["water", "beans", "milk"], - }, - }, - }, + }, + }, + actions: { + brew: { + synchronous: true, + input: { + type: "string", + enum: ["espresso", "cappuccino", "americano"], }, - events: { - resourceEmpty: { - data: { - type: "array", - items: { - type: "string", - enum: ["water", "beans", "milk"], - }, - }, - }, + }, + refill: { + synchronous: true, + input: { + type: "array", + items: { + type: "string", + enum: ["water", "beans", "milk"], + }, }, - }) - .then((thing) => { - console.log("Produced " + thing.getThingDescription().title); + }, + }, + events: { + resourceEmpty: { + data: { + type: "array", + items: { + type: "string", + enum: ["water", "beans", "milk"], + }, + }, + }, + }, + }) + .then((thing) => { + console.log("Produced " + thing.getThingDescription().title); - thing.setPropertyReadHandler("resources", async () => { - return { - water: waterAmount, - beans: beansAmount, - milk: milkAmount, - }; - }); + thing.setPropertyReadHandler("resources", async () => { + return { + water: waterAmount, + beans: beansAmount, + milk: milkAmount, + }; + }); - thing.setActionHandler("brew", async (params, options) => { - const coffeeType = await params.value(); - console.info("received coffee order of ", coffeeType); - if (coffeeType === "espresso") { - if (waterAmount <= 10 || beansAmount <= 10) { - throw new Error("Not enough water or beans"); - } else { - await timeout(1000); - waterAmount = waterAmount - 10; - beansAmount = beansAmount - 10; - thing.emitPropertyChange("resources"); - const resourceEvent: Array = []; - if (waterAmount <= 10) { - resourceEvent.push("water"); - } - if (beansAmount <= 10) { - resourceEvent.push("beans"); - } - if (resourceEvent.length > 0) { - thing.emitEvent("resourceEmpty", resourceEvent); - } - return undefined; - } - } else if (coffeeType === "cappuccino") { - if (waterAmount <= 20 || beansAmount <= 25 || milkAmount <= 15) { - throw new Error("Not enough water or beans"); - } else { - await timeout(2000); - waterAmount = waterAmount - 15; - beansAmount = beansAmount - 20; - milkAmount = milkAmount - 10; - thing.emitPropertyChange("resources"); - const resourceEvent: Array = []; - if (waterAmount <= 10) { - resourceEvent.push("water"); - } - if (beansAmount <= 10) { - resourceEvent.push("beans"); - } - if (milkAmount <= 10) { - resourceEvent.push("milk"); - } - if (resourceEvent.length > 0) { - thing.emitEvent("resourceEmpty", resourceEvent); - } - return undefined; - } - } else if (coffeeType === "americano") { - if (waterAmount <= 35 || beansAmount <= 10) { - throw new Error("Not enough water or beans"); - } else { - await timeout(2000); - waterAmount = waterAmount - 30; - beansAmount = beansAmount - 10; - thing.emitPropertyChange("resources"); - const resourceEvent: Array = []; - if (waterAmount <= 10) { - resourceEvent.push("water"); - } - if (beansAmount <= 10) { - resourceEvent.push("beans"); - } - if (resourceEvent.length > 0) { - thing.emitEvent("resourceEmpty", resourceEvent); - } - return undefined; - } - } else { - throw new Error("Wrong coffee input"); - } - }); + thing.setActionHandler("brew", async (params, options) => { + const coffeeType = await params.value(); + console.info("received coffee order of ", coffeeType); + if (coffeeType === "espresso") { + if (waterAmount <= 10 || beansAmount <= 10) { + throw new Error("Not enough water or beans"); + } else { + await timeout(1000); + waterAmount = waterAmount - 10; + beansAmount = beansAmount - 10; + thing.emitPropertyChange("resources"); + const resourceEvent: Array = []; + if (waterAmount <= 10) { + resourceEvent.push("water"); + } + if (beansAmount <= 10) { + resourceEvent.push("beans"); + } + if (resourceEvent.length > 0) { + thing.emitEvent("resourceEmpty", resourceEvent); + } + return undefined; + } + } else if (coffeeType === "cappuccino") { + if (waterAmount <= 20 || beansAmount <= 25 || milkAmount <= 15) { + throw new Error("Not enough water or beans"); + } else { + await timeout(2000); + waterAmount = waterAmount - 15; + beansAmount = beansAmount - 20; + milkAmount = milkAmount - 10; + thing.emitPropertyChange("resources"); + const resourceEvent: Array = []; + if (waterAmount <= 10) { + resourceEvent.push("water"); + } + if (beansAmount <= 10) { + resourceEvent.push("beans"); + } + if (milkAmount <= 10) { + resourceEvent.push("milk"); + } + if (resourceEvent.length > 0) { + thing.emitEvent("resourceEmpty", resourceEvent); + } + return undefined; + } + } else if (coffeeType === "americano") { + if (waterAmount <= 35 || beansAmount <= 10) { + throw new Error("Not enough water or beans"); + } else { + await timeout(2000); + waterAmount = waterAmount - 30; + beansAmount = beansAmount - 10; + thing.emitPropertyChange("resources"); + const resourceEvent: Array = []; + if (waterAmount <= 10) { + resourceEvent.push("water"); + } + if (beansAmount <= 10) { + resourceEvent.push("beans"); + } + if (resourceEvent.length > 0) { + thing.emitEvent("resourceEmpty", resourceEvent); + } + return undefined; + } + } else { + throw new Error("Wrong coffee input"); + } + }); - thing.setActionHandler("refill", async (params, options) => { - const selectedResource = (await params.value()) as Array<"water" | "beans" | "milk">; - console.info("received refill order of ", selectedResource); - if (selectedResource!.indexOf("water") !== -1) { - waterAmount = 1000; - } - if (selectedResource!.indexOf("beans") !== -1) { - beansAmount = 1000; - } - if (selectedResource!.indexOf("milk") !== -1) { - milkAmount = 1000; - } - thing.emitPropertyChange("resources"); - return undefined; - }); + thing.setActionHandler("refill", async (params, options) => { + const selectedResource = (await params.value()) as Array< + "water" | "beans" | "milk" + >; + console.info("received refill order of ", selectedResource); + if (selectedResource!.indexOf("water") !== -1) { + waterAmount = 1000; + } + if (selectedResource!.indexOf("beans") !== -1) { + beansAmount = 1000; + } + if (selectedResource!.indexOf("milk") !== -1) { + milkAmount = 1000; + } + thing.emitPropertyChange("resources"); + return undefined; + }); - // expose the thing - thing.expose().then(() => { - console.info(thing.getThingDescription().title + " ready"); - console.info("TD available at http://" + hostname + ":" + httpPort); - }); - }) - .catch((e) => { - console.log(e); - }); -}); \ No newline at end of file + // expose the thing + thing.expose().then(() => { + console.info(thing.getThingDescription().title + " ready"); + console.info("TD available at http://" + hostname + ":" + httpPort); + }); + }) + .catch((e) => { + console.log(e); + }); +}); diff --git a/mashups/smart-home/things/smart-clock.ts b/mashups/smart-home/things/smart-clock.ts index 6b11e08..07fe01a 100644 --- a/mashups/smart-home/things/smart-clock.ts +++ b/mashups/smart-home/things/smart-clock.ts @@ -1,5 +1,5 @@ /******************************************************************************** - * Copyright (c) 2023 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. @@ -17,88 +17,89 @@ 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({ - address: hostname, - port: parseInt(port) - } -)); + new CoapServer({ + address: hostname, + port: parseInt(port), + }), +); let minuteCounter = 0; let hourCounter = 5; async function timeCount(thing: WoT.ExposedThing) { - for (minuteCounter = 0; minuteCounter < 59; minuteCounter++) { - // if we have <60, we can get a 15:60. - await new Promise((resolve) => setTimeout(resolve, 1000)); // sleep - thing.emitPropertyChange("time"); - } - console.info({ - hour: hourCounter, - minute: minuteCounter, - }); + for (minuteCounter = 0; minuteCounter < 59; minuteCounter++) { + // if we have <60, we can get a 15:60. + await new Promise((resolve) => setTimeout(resolve, 1000)); // sleep + thing.emitPropertyChange("time"); + } + console.info({ + hour: hourCounter, + minute: minuteCounter, + }); - hourCounter++; - if (hourCounter === 24) { - hourCounter = 0; - } + hourCounter++; + if (hourCounter === 24) { + hourCounter = 0; + } } 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.", - support: "https://github.com/eclipse-thingweb/node-wot/", - "@context": "https://www.w3.org/2022/wot/td/v1.1", + 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.", + support: "https://github.com/eclipse-thingweb/node-wot/", + "@context": "https://www.w3.org/2022/wot/td/v1.1", + properties: { + time: { + readOnly: true, + observable: true, + type: "object", properties: { - time: { - readOnly: true, - observable: true, - type: "object", - properties: { - minute: { - type: "integer", - minimum: 0, - maximum: 59, - }, - hour: { - type: "integer", - minimum: 0, - maximum: 23, - }, - }, - }, + minute: { + type: "integer", + minimum: 0, + maximum: 59, + }, + hour: { + type: "integer", + minimum: 0, + maximum: 23, + }, }, - }) - .then(async (thing) => { - console.log("Produced " + thing.getThingDescription().title); + }, + }, + }) + .then(async (thing) => { + console.log("Produced " + thing.getThingDescription().title); - thing.setPropertyReadHandler("time", async () => { - return { - hour: hourCounter, - minute: minuteCounter, - }; - }); + thing.setPropertyReadHandler("time", async () => { + return { + hour: hourCounter, + minute: minuteCounter, + }; + }); - timeCount(thing); - setInterval(async () => { - timeCount(thing); - thing.emitPropertyChange("time"); - }, 61000); // if this is 60s, we never leave the for loop + timeCount(thing); + setInterval(async () => { + timeCount(thing); + thing.emitPropertyChange("time"); + }, 61000); // if this is 60s, we never leave the for loop - // expose the thing - thing.expose().then(() => { - console.info(thing.getThingDescription().title + " ready"); - }); - }) - .catch((e) => { - console.log(e); - }); -}); \ No newline at end of file + // expose the thing + thing.expose().then(() => { + console.info(thing.getThingDescription().title + " ready"); + }); + }) + .catch((e) => { + console.log(e); + }); +}); diff --git a/mashups/smart-home/tsconfig.json b/mashups/smart-home/tsconfig.json index c90f523..f5e4a37 100644 --- a/mashups/smart-home/tsconfig.json +++ b/mashups/smart-home/tsconfig.json @@ -1,14 +1,14 @@ { - "compilerOptions": { - "outDir": "dist", - "rootDir": "./", - "target": "ES2018", - "module": "commonjs", - "skipLibCheck": false, - "strict": true, - "sourceMap": false, - "esModuleInterop": true, - "removeComments": false - }, - "include": ["./mashup-logic.ts", "./things/**.ts"], -} \ No newline at end of file + "compilerOptions": { + "outDir": "dist", + "rootDir": "./", + "target": "ES2018", + "module": "commonjs", + "skipLibCheck": false, + "strict": true, + "sourceMap": false, + "esModuleInterop": true, + "removeComments": false + }, + "include": ["./mashup-logic.ts", "./things/**.ts"] +} diff --git a/package-lock.json b/package-lock.json index 041a605..419dd28 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,10 +17,21 @@ "@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", - "mocha": "^10.7.3" + "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": "^3.3.3" } }, "mashups/smart-home": { @@ -60,6 +71,163 @@ "node": ">=12" } }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "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", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.1.tgz", + "integrity": "sha512-m4DVN9ZqskZoLU5GlWZadwDnYo3vAEydiUayB9widCl9ffWx2IvPnp6n3on5rJmziJSw9Bv+Z3ChDVdMwXCY8Q==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "dev": true, + "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", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "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", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/@eslint/eslintrc/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@eslint/js": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", + "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", + "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", + "deprecated": "Use @eslint/config-array instead", + "dev": true, + "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", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@humanwhocodes/config-array/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "deprecated": "Use @eslint/object-schema instead", + "dev": true + }, "node_modules/@jridgewell/resolve-uri": { "version": "3.1.2", "dev": true, @@ -149,10 +317,51 @@ "web-streams-polyfill": "^3.0.1" } }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "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/@rtsao/scc": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", + "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", + "dev": true + }, "node_modules/@serialport/binding-mock": { "version": "10.2.2", "license": "MIT", @@ -493,6 +702,12 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "dev": true + }, "node_modules/@types/mime": { "version": "1.3.5", "dev": true, @@ -572,6 +787,209 @@ "@types/node": "*" } }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.6.0.tgz", + "integrity": "sha512-UOaz/wFowmoh2G6Mr9gw60B1mm0MzUtm6Ic8G2yM1Le6gyj5Loi/N+O5mocugRGY+8OeeKmkMmbxNqUCq3B4Sg==", + "dev": true, + "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", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.6.0.tgz", + "integrity": "sha512-eQcbCuA2Vmw45iGfcyG4y6rS7BhWfz9MQuk409WD47qMM+bKCGQWXxvoOs1DUp+T7UBMTtRTVT+kXr7Sh4O9Ow==", + "dev": true, + "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", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.6.0.tgz", + "integrity": "sha512-ZuoutoS5y9UOxKvpc/GkvF4cuEmpokda4wRg64JEia27wX+PysIE9q+lzDtlHHgblwUWwo5/Qn+/WyTUvDwBHw==", + "dev": true, + "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", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.6.0.tgz", + "integrity": "sha512-dtePl4gsuenXVwC7dVNlb4mGDcKjDT/Ropsk4za/ouMBPplCLyznIaR+W65mvCvsyS97dymoBRrioEXI7k0XIg==", + "dev": true, + "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", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.6.0.tgz", + "integrity": "sha512-rojqFZGd4MQxw33SrOy09qIDS8WEldM8JWtKQLAjf/X5mGSeEFh5ixQlxssMNyPslVIk9yzWqXCsV2eFhYrYUw==", + "dev": true, + "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", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.6.0.tgz", + "integrity": "sha512-MOVAzsKJIPIlLK239l5s06YXjNqpKTVhBVDnqUumQJja5+Y94V3+4VUFRA0G60y2jNnTVwRCkhyGQpavfsbq/g==", + "dev": true, + "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", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "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", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.6.0.tgz", + "integrity": "sha512-eNp9cWnYf36NaOVjkEUznf6fEgVy1TWpE0o52e4wtojjBx7D1UV2WAWGzR+8Y5lVFtpMLPwNbC67T83DWSph4A==", + "dev": true, + "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", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.6.0.tgz", + "integrity": "sha512-wapVFfZg9H0qOYh4grNVQiMklJGluQrOUiOhYRrQWhx7BY/+I1IYb8BczWNbbUpO+pqy0rDciv3lQH5E1bCLrg==", + "dev": true, + "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", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "dev": true + }, "node_modules/abort-controller": { "version": "3.0.0", "license": "MIT", @@ -608,6 +1026,15 @@ "node": ">=0.4.0" } }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, "node_modules/acorn-walk": { "version": "8.3.4", "dev": true, @@ -752,6 +1179,15 @@ "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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/ansi-styles": { "version": "4.3.0", "dev": true, @@ -788,27 +1224,156 @@ "dev": true, "license": "Python-2.0" }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz", + "integrity": "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==", + "dev": true, + "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/assertion-error": { - "version": "1.1.0", + "node_modules/array-includes": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.8.tgz", + "integrity": "sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==", "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": "*" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/asynckit": { - "version": "0.4.0", + "node_modules/array.prototype.findlastindex": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.5.tgz", + "integrity": "sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==", "dev": true, - "license": "MIT" - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "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", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz", + "integrity": "sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==", + "dev": true, + "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", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz", + "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==", + "dev": true, + "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", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz", + "integrity": "sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==", + "dev": true, + "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/asynckit": { + "version": "0.4.0", + "dev": true, + "license": "MIT" + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dev": true, + "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": [ @@ -955,6 +1520,29 @@ "version": "1.1.2", "license": "MIT" }, + "node_modules/builtin-modules": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", + "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/builtins": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/builtins/-/builtins-5.1.0.tgz", + "integrity": "sha512-SW9lzGTLvWTP1AY8xeAMZimqDrIaSdLQUcVr9DMef51niJ022Ri87SwRRKYm4A6iHfkPaiVUu/Duw2Wc4J7kKg==", + "dev": true, + "peer": true, + "dependencies": { + "semver": "^7.0.0" + } + }, "node_modules/bulk-write-stream": { "version": "2.0.1", "license": "MIT", @@ -991,6 +1579,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/camelcase": { "version": "6.3.0", "dev": true, @@ -1143,14 +1740,6 @@ "wrap-ansi": "^7.0.0" } }, - "node_modules/cliui/node_modules/ansi-regex": { - "version": "5.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/cliui/node_modules/is-fullwidth-code-point": { "version": "3.0.0", "dev": true, @@ -1172,17 +1761,6 @@ "node": ">=8" } }, - "node_modules/cliui/node_modules/strip-ansi": { - "version": "6.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/coap": { "version": "1.4.0", "license": "MIT", @@ -1273,6 +1851,12 @@ "typedarray": "^0.0.6" } }, + "node_modules/confbox": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.7.tgz", + "integrity": "sha512-uJcB/FKZtBMCJpK8MQji6bJHgu1tixKPxRLeGkNzBoOZzpnZUJm0jm2/sBDWcuBx1dYgxV4JU+g5hmNxCyAmdA==", + "dev": true + }, "node_modules/content-disposition": { "version": "0.5.4", "license": "MIT", @@ -1343,6 +1927,71 @@ "dev": true, "license": "MIT" }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "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", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz", + "integrity": "sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==", + "dev": true, + "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", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz", + "integrity": "sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==", + "dev": true, + "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", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz", + "integrity": "sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==", + "dev": true, + "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", @@ -1387,6 +2036,12 @@ "node": ">=6" } }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, "node_modules/define-data-property": { "version": "1.1.4", "license": "MIT", @@ -1402,6 +2057,23 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "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, @@ -1443,6 +2115,18 @@ "node": ">=6" } }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/dotenv": { "version": "16.4.5", "license": "BSD-2-Clause", @@ -1486,6 +2170,66 @@ "once": "^1.4.0" } }, + "node_modules/es-abstract": { + "version": "1.23.3", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz", + "integrity": "sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==", + "dev": true, + "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", @@ -1503,6 +2247,58 @@ "node": ">= 0.4" } }, + "node_modules/es-object-atoms": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", + "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz", + "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==", + "dev": true, + "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", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz", + "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", + "dev": true, + "dependencies": { + "hasown": "^2.0.0" + } + }, + "node_modules/es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "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, @@ -1526,28 +2322,636 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/etag": { - "version": "1.8.1", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/event-target-shim": { - "version": "5.0.1", - "license": "MIT", + "node_modules/eslint": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", + "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", + "dev": true, + "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": ">=6" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/events": { - "version": "3.3.0", - "license": "MIT", + "node_modules/eslint-compat-utils": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/eslint-compat-utils/-/eslint-compat-utils-0.5.1.tgz", + "integrity": "sha512-3z3vFexKIEnjHE3zCMRo6fn/e44U7T1khUjg+Hp0ZQMCigh28rALD0nPFBcGZuiLC5rLZa2ubQHDRln09JfU2Q==", + "dev": true, + "peer": true, + "dependencies": { + "semver": "^7.5.4" + }, "engines": { - "node": ">=0.8.x" + "node": ">=12" + }, + "peerDependencies": { + "eslint": ">=6.0.0" } }, - "node_modules/eventsource": { + "node_modules/eslint-config-prettier": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz", + "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==", + "dev": true, + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-config-standard": { + "version": "17.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-17.1.0.tgz", + "integrity": "sha512-IwHwmaBNtDK4zDHQukFDW5u/aTb8+meQWZvNFWkiGmbWjD6bqyuSSBxxXKkCftCUzc1zwCH2m/baCNDLGmuO5Q==", + "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" + } + ], + "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", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", + "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", + "dev": true, + "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", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-module-utils": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.11.0.tgz", + "integrity": "sha512-gbBE5Hitek/oG6MUVj6sFuzEjA/ClzNflVrLovHi/JgLdC7fiN5gLAY1WIPW1a0V5I999MnsrvVrCOGmmVqDBQ==", + "dev": true, + "dependencies": { + "debug": "^3.2.7" + }, + "engines": { + "node": ">=4" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/eslint-module-utils/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-es": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-3.0.1.tgz", + "integrity": "sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ==", + "dev": true, + "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", + "resolved": "https://registry.npmjs.org/eslint-plugin-es-x/-/eslint-plugin-es-x-7.8.0.tgz", + "integrity": "sha512-7Ds8+wAAoV3T+LAKeu39Y5BzXCrGKrcISfgKEqTS4BDN8SFEDQd0S43jiQ8vIa3wUKD07qitZdfzlenSi8/0qQ==", + "dev": true, + "funding": [ + "https://github.com/sponsors/ota-meshi", + "https://opencollective.com/eslint" + ], + "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", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.30.0.tgz", + "integrity": "sha512-/mHNE9jINJfiD2EKkg1BKyPyUk4zdnT54YgbOgfjSakWT5oyX/qQLVNTkehyfpcMxZXMy1zyonZ2v7hZTX43Yw==", + "dev": true, + "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", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/eslint-plugin-import/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-import/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eslint-plugin-import/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/eslint-plugin-import/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/eslint-plugin-n": { + "version": "16.6.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-16.6.2.tgz", + "integrity": "sha512-6TyDmZ1HXoFQXnhCTUjVFULReoBPOAjpuiKELMkeP40yffI/1ZRO+d9ug/VC6fqISo2WkuIBk3cvuRPALaWlOQ==", + "dev": true, + "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", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "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", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "peer": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/eslint-plugin-node": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz", + "integrity": "sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g==", + "dev": true, + "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", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/eslint-plugin-node/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/eslint-plugin-node/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/eslint-plugin-notice": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-notice/-/eslint-plugin-notice-1.0.0.tgz", + "integrity": "sha512-M3VLQMZzZpvfTZ/vy9FmClIKq5rLBbQpM0KgfLZPJPrVXpmJYeobmmb+lfJzHWdNm8PWwvw8KlafQWo2N9xx1Q==", + "dev": true, + "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", + "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-6.6.0.tgz", + "integrity": "sha512-57Zzfw8G6+Gq7axm2Pdo3gW/Rx3h9Yywgn61uE/3elTCOePEHVrn2i5CdfBwA1BLK0Q0WqctICIUSqXZW/VprQ==", + "dev": true, + "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", + "resolved": "https://registry.npmjs.org/eslint-plugin-unused-imports/-/eslint-plugin-unused-imports-4.1.4.tgz", + "integrity": "sha512-YptD6IzQjDardkl0POxnnRBhU1OEePMV0nd6siHaRBbd+lyh6NAhFEobiznKU7kTsSsDeSD62Pe7kAM1b7dAZQ==", + "dev": true, + "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", + "resolved": "https://registry.npmjs.org/eslint-plugin-workspaces/-/eslint-plugin-workspaces-0.10.1.tgz", + "integrity": "sha512-17p+ljQq2oYtN+cS6F52wRCV7xVh5fHiBOhQaYkSShRwRdiTvEkD6gvGysZjx08ckr9cozIFqI9SxdgTWDV9+g==", + "dev": true, + "dependencies": { + "find-workspaces": "^0.3.0" + } + }, + "node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "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", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "dev": true, + "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", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "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", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "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", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/eslint/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/eslint/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/eslint/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "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", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "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": { @@ -1631,6 +3035,34 @@ "version": "3.1.3", "license": "MIT" }, + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "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", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, "node_modules/fast-querystring": { "version": "1.1.2", "license": "MIT", @@ -1671,10 +3103,31 @@ "xtend": "^4.0.2" } }, + "node_modules/fastq": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, "node_modules/fastseries": { "version": "2.0.0", "license": "ISC" }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, "node_modules/fill-range": { "version": "7.1.1", "dev": true, @@ -1732,6 +3185,12 @@ "node": ">=14" } }, + "node_modules/find-root": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", + "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", + "dev": true + }, "node_modules/find-up": { "version": "5.0.0", "dev": true, @@ -1747,6 +3206,17 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/find-workspaces": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/find-workspaces/-/find-workspaces-0.3.1.tgz", + "integrity": "sha512-UDkGILGJSA1LN5Aa7McxCid4sqW3/e+UYsVwyxki3dDT0F8+ym0rAfnCkEfkL0rO7M+8/mvkim4t/s3IPHmg+w==", + "dev": true, + "dependencies": { + "fast-glob": "^3.3.2", + "pkg-types": "^1.0.3", + "yaml": "^2.3.4" + } + }, "node_modules/flat": { "version": "5.0.2", "dev": true, @@ -1755,6 +3225,35 @@ "flat": "cli.js" } }, + "node_modules/flat-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dev": true, + "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", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", + "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", + "dev": true + }, + "node_modules/for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dev": true, + "dependencies": { + "is-callable": "^1.1.3" + } + }, "node_modules/form-data": { "version": "4.0.0", "dev": true, @@ -1833,6 +3332,33 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/function.prototype.name": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", + "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", + "dev": true, + "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", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/get-caller-file": { "version": "2.0.5", "dev": true, @@ -1866,6 +3392,36 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-symbol-description": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz", + "integrity": "sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==", + "dev": true, + "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", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.8.1.tgz", + "integrity": "sha512-k9PN+cFBmaLWtVz29SkUoqU5O0slLuHJXt/2P+tMVFT+phsSGXGkp9t3rQIqdz0e+06EHNGs3oM6ZX1s2zHxRg==", + "dev": true, + "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, @@ -1895,6 +3451,37 @@ "node": ">= 6" } }, + "node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "dev": true, + "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", @@ -1905,6 +3492,21 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, + "node_modules/has-bigints": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/has-flag": { "version": "4.0.0", "dev": true, @@ -1943,6 +3545,21 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "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", @@ -2053,6 +3670,40 @@ ], "license": "BSD-3-Clause" }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "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", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, "node_modules/inflight": { "version": "1.0.6", "license": "ISC", @@ -2065,6 +3716,20 @@ "version": "2.0.4", "license": "ISC" }, + "node_modules/internal-slot": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz", + "integrity": "sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==", + "dev": true, + "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", @@ -2079,6 +3744,34 @@ "node": ">=8" } }, + "node_modules/is-array-buffer": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz", + "integrity": "sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==", + "dev": true, + "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-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dev": true, + "dependencies": { + "has-bigints": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-binary-path": { "version": "2.1.0", "dev": true, @@ -2090,6 +3783,95 @@ "node": ">=8" } }, + "node_modules/is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "dev": true, + "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", + "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.1.tgz", + "integrity": "sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==", + "dev": true, + "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", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz", + "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==", + "dev": true, + "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", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.1.tgz", + "integrity": "sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==", + "dev": true, + "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", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dev": true, + "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, @@ -2109,6 +3891,18 @@ "node": ">=0.10.0" } }, + "node_modules/is-negative-zero": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", + "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-number": { "version": "7.0.0", "dev": true, @@ -2117,6 +3911,30 @@ "node": ">=0.12.0" } }, + "node_modules/is-number-object": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "dev": true, + "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", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/is-plain-obj": { "version": "2.1.0", "dev": true, @@ -2125,6 +3943,82 @@ "node": ">=8" } }, + "node_modules/is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dev": true, + "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", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz", + "integrity": "sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dev": true, + "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", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dev": true, + "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", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz", + "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", + "dev": true, + "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, @@ -2136,10 +4030,28 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "dev": true, + "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", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, "node_modules/js-sdsl": { "version": "4.3.0", "license": "MIT", @@ -2159,6 +4071,12 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, "node_modules/json-placeholder-replacer": { "version": "1.0.37", "license": "MIT", @@ -2171,6 +4089,33 @@ "version": "1.0.0", "license": "MIT" }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "dependencies": { + "json-buffer": "3.0.1" + } + }, "node_modules/leven": { "version": "2.1.0", "license": "MIT", @@ -2178,6 +4123,19 @@ "node": ">=0.10.0" } }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "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, @@ -2192,6 +4150,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, "node_modules/log-symbols": { "version": "4.1.0", "dev": true, @@ -2244,11 +4214,39 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, "node_modules/methods": { "version": "1.1.2", "license": "MIT", "engines": { - "node": ">= 0.6" + "node": ">= 0.6" + } + }, + "node_modules/metric-lcs": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/metric-lcs/-/metric-lcs-0.1.2.tgz", + "integrity": "sha512-+TZ5dUDPKPJaU/rscTzxyN8ZkX7eAVLAiQU/e+YINleXPv03SCmJShaMT1If1liTH8OcmWXZs0CmzCBRBLcMpA==", + "dev": true + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" } }, "node_modules/mime": { @@ -2296,6 +4294,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/mlly": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.7.1.tgz", + "integrity": "sha512-rrVRZRELyQzrIUAVMHxP97kv+G786pHmOKzuFII8zDYahFBS7qnHh2AlYSl1GAHhaMPCz6/oHjVMcfFYgFYHgA==", + "dev": true, + "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, @@ -2881,6 +4891,12 @@ "multicast-dns": "cli.js" } }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, "node_modules/negotiator": { "version": "0.6.3", "license": "MIT", @@ -2977,6 +4993,82 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", + "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", + "dev": true, + "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", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", + "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", + "dev": true, + "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", + "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz", + "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==", + "dev": true, + "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", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.0.tgz", + "integrity": "sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==", + "dev": true, + "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", @@ -2994,6 +5086,23 @@ "wrappy": "1" } }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "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, @@ -3022,6 +5131,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/parseurl": { "version": "1.3.3", "license": "MIT", @@ -3044,10 +5165,31 @@ "node": ">=0.10.0" } }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, "node_modules/path-to-regexp": { "version": "0.1.10", "license": "MIT" }, + "node_modules/pathe": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", + "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", + "dev": true + }, "node_modules/pathval": { "version": "1.1.1", "dev": true, @@ -3067,6 +5209,17 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/pkg-types": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.2.0.tgz", + "integrity": "sha512-+ifYuSSqOQ8CqP4MbZA5hDpb97n3E8SVWdJe+Wms9kj745lmd3b7EZJiqvmLwAlmRfjrI7Hi5z3kdBJ93lFNPA==", + "dev": true, + "dependencies": { + "confbox": "^0.1.7", + "mlly": "^1.7.1", + "pathe": "^1.1.2" + } + }, "node_modules/popsicle": { "version": "12.1.2", "license": "MIT", @@ -3136,6 +5289,39 @@ "servie": "^4.0.0" } }, + "node_modules/possible-typed-array-names": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", + "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz", + "integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==", + "dev": true, + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, "node_modules/process": { "version": "0.11.10", "license": "MIT", @@ -3217,6 +5403,26 @@ "version": "2.2.0", "license": "MIT" }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "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/randombytes": { "version": "2.1.0", "dev": true, @@ -3272,6 +5478,36 @@ "version": "0.14.1", "license": "MIT" }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz", + "integrity": "sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==", + "dev": true, + "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", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, "node_modules/reinterval": { "version": "1.1.0", "license": "MIT" @@ -3295,6 +5531,42 @@ "version": "1.0.0", "license": "MIT" }, + "node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "dev": true, + "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", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "peer": true, + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, "node_modules/ret": { "version": "0.2.2", "license": "MIT", @@ -3318,6 +5590,88 @@ "version": "1.4.1", "license": "MIT" }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "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", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/rimraf/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "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", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, "node_modules/rxjs": { "version": "5.5.11", "license": "Apache-2.0", @@ -3328,10 +5682,51 @@ "npm": ">=2.0.0" } }, - "node_modules/safe-buffer": { - "version": "5.1.2", - "license": "MIT" - }, + "node_modules/safe-array-concat": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz", + "integrity": "sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==", + "dev": true, + "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", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true + }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "license": "MIT" + }, + "node_modules/safe-regex-test": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz", + "integrity": "sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==", + "dev": true, + "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", @@ -3483,10 +5878,46 @@ "node": ">= 0.4" } }, + "node_modules/set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "dev": true, + "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", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/side-channel": { "version": "1.0.6", "license": "MIT", @@ -3571,6 +6002,76 @@ ], "license": "MIT" }, + "node_modules/string.prototype.trim": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz", + "integrity": "sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==", + "dev": true, + "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", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz", + "integrity": "sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==", + "dev": true, + "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", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", + "dev": true, + "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", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/strip-json-comments": { "version": "3.1.1", "dev": true, @@ -3596,6 +6097,18 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/symbol-observable": { "version": "1.0.1", "license": "MIT", @@ -3603,6 +6116,12 @@ "node": ">=0.10.0" } }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, "node_modules/throwback": { "version": "4.1.0", "license": "MIT" @@ -3646,6 +6165,18 @@ "version": "0.0.3", "license": "MIT" }, + "node_modules/ts-api-utils": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", + "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", + "dev": true, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, "node_modules/ts-expect": { "version": "1.3.0", "license": "MIT" @@ -3700,10 +6231,34 @@ "node": ">=0.3.1" } }, + "node_modules/tsconfig-paths": { + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", + "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", + "dev": true, + "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", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/type-detect": { "version": "4.1.0", "dev": true, @@ -3712,6 +6267,18 @@ "node": ">=4" } }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/type-is": { "version": "1.6.18", "license": "MIT", @@ -3723,6 +6290,79 @@ "node": ">= 0.6" } }, + "node_modules/typed-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz", + "integrity": "sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==", + "dev": true, + "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", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz", + "integrity": "sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==", + "dev": true, + "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", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz", + "integrity": "sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==", + "dev": true, + "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", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.6.tgz", + "integrity": "sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==", + "dev": true, + "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" @@ -3739,6 +6379,27 @@ "node": ">=4.2.0" } }, + "node_modules/ufo": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.5.4.tgz", + "integrity": "sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ==", + "dev": true + }, + "node_modules/unbox-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "dev": true, + "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" @@ -3757,6 +6418,15 @@ "node": ">= 0.8" } }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, "node_modules/uritemplate": { "version": "0.3.4" }, @@ -3825,6 +6495,65 @@ "webidl-conversions": "^3.0.0" } }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dev": true, + "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", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz", + "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==", + "dev": true, + "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/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/worker-timers": { "version": "7.1.8", "license": "MIT", @@ -3889,14 +6618,6 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/wrap-ansi/node_modules/ansi-regex": { - "version": "5.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/wrap-ansi/node_modules/is-fullwidth-code-point": { "version": "3.0.0", "dev": true, @@ -3918,17 +6639,6 @@ "node": ">=8" } }, - "node_modules/wrap-ansi/node_modules/strip-ansi": { - "version": "6.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/wrappy": { "version": "1.0.2", "license": "ISC" @@ -3971,6 +6681,18 @@ "version": "4.0.0", "license": "ISC" }, + "node_modules/yaml": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.5.1.tgz", + "integrity": "sha512-bLQOjaX/ADgQ20isPJRvF0iRUHIxVhYvr53Of7wGcWlO2jvtUlH5m87DsmulFVxRpNLOnI4tB6p/oh8D7kpn9Q==", + "dev": true, + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/yargs": { "version": "16.2.0", "dev": true, @@ -4010,14 +6732,6 @@ "node": ">=10" } }, - "node_modules/yargs/node_modules/ansi-regex": { - "version": "5.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/yargs/node_modules/is-fullwidth-code-point": { "version": "3.0.0", "dev": true, @@ -4039,17 +6753,6 @@ "node": ">=8" } }, - "node_modules/yargs/node_modules/strip-ansi": { - "version": "6.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/yn": { "version": "3.1.1", "dev": true, diff --git a/package.json b/package.json index fc4c1ed..b510167 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,10 @@ "description": "Test Things", "scripts": { "setup": "./scripts/installDependencies.sh", - "test": "./scripts/runTests.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", @@ -23,9 +26,20 @@ "@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", - "mocha": "^10.7.3" + "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": "^3.3.3" } } diff --git a/things/advanced-coffee-machine/advanced-coffee-machine.tm.json b/things/advanced-coffee-machine/advanced-coffee-machine.tm.json index 9e434b4..25be585 100644 --- a/things/advanced-coffee-machine/advanced-coffee-machine.tm.json +++ b/things/advanced-coffee-machine/advanced-coffee-machine.tm.json @@ -1,232 +1,212 @@ { - "@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}}", - "properties": { - "allAvailableResources": { - "type": "object", - "description": "Current level of all available resources given as an integer percentage for each particular resource.\\nThe data is obtained from the machine's sensors but can be set manually via the availableResourceLevel property in case the sensors are broken.", - "readOnly": true, - "properties": { - "water": { - "type": "integer", - "minimum": 0, - "maximum": 100 - }, - "milk": { - "type": "integer", - "minimum": 0, - "maximum": 100 - }, - "chocolate": { - "type": "integer", - "minimum": 0, - "maximum": 100 - }, - "coffeeBeans": { - "type": "integer", - "minimum": 0, - "maximum": 100 - } - } + "@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}}", + "properties": { + "allAvailableResources": { + "type": "object", + "description": "Current level of all available resources given as an integer percentage for each particular resource.\\nThe data is obtained from the machine's sensors but can be set manually via the availableResourceLevel property in case the sensors are broken.", + "readOnly": true, + "properties": { + "water": { + "type": "integer", + "minimum": 0, + "maximum": 100 }, - "availableResourceLevel": { - "type": "number", - "description": "Current level of a particular resource. Requires resource id variable as uriVariables.\\nThe property can also be overridden, which also requires resource id as uriVariables.", - "uriVariables": { - "id": { - "type": "string", - "enum": [ - "water", - "milk", - "chocolate", - "coffeeBeans" - ] - } - } + "milk": { + "type": "integer", + "minimum": 0, + "maximum": 100 }, - "possibleDrinks": { - "type": "array", - "description": "The list of possible drinks in general. Doesn't depend on the available resources.", - "readOnly": true, - "items": { - "type": "string" - } + "chocolate": { + "type": "integer", + "minimum": 0, + "maximum": 100 }, - "servedCounter": { + "coffeeBeans": { + "type": "integer", + "minimum": 0, + "maximum": 100 + } + } + }, + "availableResourceLevel": { + "type": "number", + "description": "Current level of a particular resource. Requires resource id variable as uriVariables.\\nThe property can also be overridden, which also requires resource id as uriVariables.", + "uriVariables": { + "id": { + "type": "string", + "enum": ["water", "milk", "chocolate", "coffeeBeans"] + } + } + }, + "possibleDrinks": { + "type": "array", + "description": "The list of possible drinks in general. Doesn't depend on the available resources.", + "readOnly": true, + "items": { + "type": "string" + } + }, + "servedCounter": { + "type": "integer", + "description": "The total number of served beverages.", + "minimum": 0 + }, + "maintenanceNeeded": { + "type": "boolean", + "description": "Shows whether a maintenance is needed. The property is observable. Automatically set to true when the servedCounter property exceeds 1000.", + "observable": true + }, + "schedules": { + "type": "array", + "description": "The list of scheduled tasks.", + "readOnly": true, + "items": { + "type": "object", + "properties": { + "drinkId": { + "type": "string", + "description": "Defines what drink to make, drinkId is one of possibleDrinks property values, e.g. latte." + }, + "size": { + "type": "string", + "description": "Defines the size of a drink, s = small, m = medium, l = large.", + "enum": ["s", "m", "l"] + }, + "quantity": { "type": "integer", - "description": "The total number of served beverages.", - "minimum": 0 + "description": "Defines how many drinks to make, ranging from 1 to 5.", + "minimum": 1, + "maximum": 5 + }, + "time": { + "type": "string", + "description": "Defines the time of the scheduled task in 24h format, e.g. 10: 00 or 21: 00." + }, + "mode": { + "type": "string", + "description": "Defines the mode of the scheduled task, e.g. once or everyday. All the possible values are given in the enum field of this Thing Description.", + "enum": [ + "once", + "everyday", + "everyMo", + "everyTu", + "everyWe", + "everyTh", + "everyFr", + "everySat", + "everySun" + ] + } + } + } + } + }, + "actions": { + "makeDrink": { + "description": "Make a drink from available list of beverages. Accepts drink id, size and quantity as uriVariables.\\nBrews one medium americano if no uriVariables are specified.", + "uriVariables": { + "drinkId": { + "type": "string", + "description": "Defines what drink to make, drinkId is one of possibleDrinks property values, e.g. latte." }, - "maintenanceNeeded": { - "type": "boolean", - "description": "Shows whether a maintenance is needed. The property is observable. Automatically set to true when the servedCounter property exceeds 1000.", - "observable": true + "size": { + "type": "string", + "description": "Defines the size of a drink, s = small, m = medium, l = large.", + "enum": ["s", "m", "l"] }, - "schedules": { - "type": "array", - "description": "The list of scheduled tasks.", - "readOnly": true, - "items": { - "type": "object", - "properties": { - "drinkId": { - "type": "string", - "description": "Defines what drink to make, drinkId is one of possibleDrinks property values, e.g. latte." - }, - "size": { - "type": "string", - "description": "Defines the size of a drink, s = small, m = medium, l = large.", - "enum": [ - "s", - "m", - "l" - ] - }, - "quantity": { - "type": "integer", - "description": "Defines how many drinks to make, ranging from 1 to 5.", - "minimum": 1, - "maximum": 5 - }, - "time": { - "type": "string", - "description": "Defines the time of the scheduled task in 24h format, e.g. 10: 00 or 21: 00." - }, - "mode": { - "type": "string", - "description": "Defines the mode of the scheduled task, e.g. once or everyday. All the possible values are given in the enum field of this Thing Description.", - "enum": [ - "once", - "everyday", - "everyMo", - "everyTu", - "everyWe", - "everyTh", - "everyFr", - "everySat", - "everySun" - ] - } - } - } + "quantity": { + "type": "integer", + "description": "Defines how many drinks to make, ranging from 1 to 5.", + "minimum": 1, + "maximum": 5 } - }, - "actions": { - "makeDrink": { - "description": "Make a drink from available list of beverages. Accepts drink id, size and quantity as uriVariables.\\nBrews one medium americano if no uriVariables are specified.", - "uriVariables": { - "drinkId": { - "type": "string", - "description": "Defines what drink to make, drinkId is one of possibleDrinks property values, e.g. latte." - }, - "size": { - "type": "string", - "description": "Defines the size of a drink, s = small, m = medium, l = large.", - "enum": [ - "s", - "m", - "l" - ] - }, - "quantity": { - "type": "integer", - "description": "Defines how many drinks to make, ranging from 1 to 5.", - "minimum": 1, - "maximum": 5 - } - }, - "output": { - "type": "object", - "description": "Returns true/false and a message when all invoked promises are resolved (asynchronous).", - "properties": { - "result": { - "type": "boolean" - }, - "message": { - "type": "string" - } - } - } - }, - "setSchedule": { - "description": "Add a scheduled task to the schedules property. Accepts drink id, size, quantity, time and mode as body of a request.\\nAssumes one medium americano if not specified, but time and mode are mandatory fields.", - "input": { - "type": "object", - "properties": { - "drinkId": { - "type": "string", - "description": "Defines what drink to make, drinkId is one of possibleDrinks property values, e.g. latte." - }, - "size": { - "type": "string", - "description": "Defines the size of a drink, s = small, m = medium, l = large.", - "enum": [ - "s", - "m", - "l" - ] - }, - "quantity": { - "type": "integer", - "description": "Defines how many drinks to make, ranging from 1 to 5.", - "minimum": 1, - "maximum": 5 - }, - "time": { - "type": "string", - "description": "Defines the time of the scheduled task in 24h format, e.g. 10: 00 or 21: 00." - }, - "mode": { - "type": "string", - "description": "Defines the mode of the scheduled task, e.g. once or everyday. All the possible values are given in the enum field of this Thing Description.", - "enum": [ - "once", - "everyday", - "everyMo", - "everyTu", - "everyWe", - "everyTh", - "everyFr", - "everySat", - "everySun" - ] - } - }, - "required": [ - "time", - "mode" - ] - }, - "output": { - "type": "object", - "description": "Returns true/false and a message when all invoked promises are resolved (asynchronous).", - "properties": { - "result": { - "type": "boolean" - }, - "message": { - "type": "string" - } - } - } + }, + "output": { + "type": "object", + "description": "Returns true/false and a message when all invoked promises are resolved (asynchronous).", + "properties": { + "result": { + "type": "boolean" + }, + "message": { + "type": "string" + } } + } }, - "events": { - "outOfResource": { - "description": "Out of resource event. Emitted when the available resource level is not sufficient for a desired drink.", - "data": { - "type": "string" - } + "setSchedule": { + "description": "Add a scheduled task to the schedules property. Accepts drink id, size, quantity, time and mode as body of a request.\\nAssumes one medium americano if not specified, but time and mode are mandatory fields.", + "input": { + "type": "object", + "properties": { + "drinkId": { + "type": "string", + "description": "Defines what drink to make, drinkId is one of possibleDrinks property values, e.g. latte." + }, + "size": { + "type": "string", + "description": "Defines the size of a drink, s = small, m = medium, l = large.", + "enum": ["s", "m", "l"] + }, + "quantity": { + "type": "integer", + "description": "Defines how many drinks to make, ranging from 1 to 5.", + "minimum": 1, + "maximum": 5 + }, + "time": { + "type": "string", + "description": "Defines the time of the scheduled task in 24h format, e.g. 10: 00 or 21: 00." + }, + "mode": { + "type": "string", + "description": "Defines the mode of the scheduled task, e.g. once or everyday. All the possible values are given in the enum field of this Thing Description.", + "enum": [ + "once", + "everyday", + "everyMo", + "everyTu", + "everyWe", + "everyTh", + "everyFr", + "everySat", + "everySun" + ] + } + }, + "required": ["time", "mode"] + }, + "output": { + "type": "object", + "description": "Returns true/false and a message when all invoked promises are resolved (asynchronous).", + "properties": { + "result": { + "type": "boolean" + }, + "message": { + "type": "string" + } } + } + } + }, + "events": { + "outOfResource": { + "description": "Out of resource event. Emitted when the available resource level is not sufficient for a desired drink.", + "data": { + "type": "string" + } } -} \ 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..0b67134 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 + "spec": "./test/**.test.ts", + "require": ["ts-node/register", "./test/fixtures.ts"] +} diff --git a/things/advanced-coffee-machine/http/ts/package.json b/things/advanced-coffee-machine/http/ts/package.json index d302444..5398b69 100644 --- a/things/advanced-coffee-machine/http/ts/package.json +++ b/things/advanced-coffee-machine/http/ts/package.json @@ -6,7 +6,9 @@ "scripts": { "start": "node ./dist/main.js", "build": "tsc -b", - "test": "mocha" + "test": "mocha", + "lint": "eslint .", + "lint:fix": "eslint . --fix" }, "keywords": [ "wot", diff --git a/things/advanced-coffee-machine/http/ts/src/main.ts b/things/advanced-coffee-machine/http/ts/src/main.ts index 2c560b8..65bdafd 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,19 @@ // 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; @@ -38,234 +38,313 @@ let schedules: unknown[]; let servedCounter: number; function readFromSensor(sensorType: string): number { - // Actual implementation of reading data from a sensor can go here - // For the sake of example, let's just return a value - return 100; + // Actual implementation of reading data from a sensor can go here + // For the sake of example, let's just return a value + return 100; } function notify(subscribers: unknown, msg: string) { - // Actual implementation of notifying subscribers with a message can go here - console.log(msg); + // Actual implementation of notifying subscribers with a message can go here + console.log(msg); } - 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); + +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); + tmPath?.split(path.sep).join(path.win32.sep); +} + +let thingModel + +if (tmPath != null && tmPath !== "") { + thingModel = JSON.parse(fs.readFileSync(path.join(__dirname, tmPath)).toString()) } - -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 + PROTOCOL: "http", + THING_NAME: thingName, + HOSTNAME: hostname, + 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"); - - // 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 ("id" in uriVariables) { - const id = uriVariables.id; - 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) => { - // Check if uriVariables are provided - if (options && typeof options === "object" && "uriVariables" in options) { - const uriVariables = options.uriVariables as Record; - 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; - } - - // 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; - - // 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); - - // 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); - - return { result: true, message: `Your schedule has been set!` }; - } - - 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}`); - }) - }) - .catch((e: Error) => { - console.log(e); + +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}`, + ); + } + }); + + // 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 ("id" in uriVariables) { + const id = uriVariables.id; + 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) => { + // Check if uriVariables are provided + if ( + options && + typeof options === "object" && + "uriVariables" in options + ) { + const uriVariables = options.uriVariables as Record; + 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< + 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; + + // 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); + + // 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); + + return { result: true, message: `Your schedule has been set!` }; + } + + 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}`); }); + }) + .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 77057bb..c083f62 100644 --- a/things/advanced-coffee-machine/http/ts/test/client.test.ts +++ b/things/advanced-coffee-machine/http/ts/test/client.test.ts @@ -1,101 +1,108 @@ - -import chai from 'chai' -import chaiAsPromised from '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 + ********************************************************************************/import chai from "chai"; +import chaiAsPromised from "chai-as-promised"; import { Servient } from "@node-wot/core"; -import { HttpClientFactory } from "@node-wot/binding-http" +import { HttpClientFactory } from "@node-wot/binding-http"; -chai.use(chaiAsPromised) -const expect = chai.expect +chai.use(chaiAsPromised); +const expect = chai.expect; -let servient = new Servient() -servient.addClientFactory(new HttpClientFactory()) -const port = 3000 +const servient = new Servient(); +servient.addClientFactory(new HttpClientFactory()); +const port = 3000; -let thing: WoT.ConsumedThing +let thing: WoT.ConsumedThing; -const readProperty = async (thing: WoT.ConsumedThing, name: string): Promise => { +describe("Client Tests", () => { + before(async () => { try { - const res = await thing.readProperty(name) - const value = await res.value() - return value - } - catch (error) { - console.error(`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); } -} + }); -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) - } - }) - - it("should read allAvailableResources property", async () => { - const response = await thing.readProperty("allAvailableResources") - const value = await response.value() - expect(value).to.be.eql({ - water: 100, - milk: 100, - chocolate: 100, - coffeeBeans: 100 - }) - }) + it("should read allAvailableResources property", async () => { + const response = await thing.readProperty("allAvailableResources"); + const value = await response.value(); + expect(value).to.be.eql({ + water: 100, + milk: 100, + chocolate: 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) - }) + 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); + }); - it("should observe maintenanceNeeded", async () => { - await thing.observeProperty("maintenanceNeeded", async (data) => { - const value = await data.value() - expect(value).to.be.true - }) + it("should observe maintenanceNeeded", async () => { + await thing.observeProperty("maintenanceNeeded", async (data) => { + 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 - }) + 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; + }); - it("should schedule a task", async () => { - const schedule = { - drinkId: "espresso", - size: "m", - 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) - }) + it("should schedule a task", async () => { + const schedule = { + drinkId: "espresso", + size: "m", + 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); + }); - it("should subscribe to outOfResource event", async () => { - await thing.subscribeEvent("outOfResource", async (data) => { - const value = await data.value() - expect(value).to.be.not.null - }) + it("should subscribe to outOfResource event", async () => { + await thing.subscribeEvent("outOfResource", async (data) => { + const value = await data.value(); + expect(value).to.be.not.null; + }); - await thing.invokeAction("makeDrink", undefined, { - uriVariables: { drinkId: "latte", size: "l", quantity: 1000 }, - }); - }) -}) + 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 af7659c..c910838 100644 --- a/things/advanced-coffee-machine/http/ts/test/fixtures.ts +++ b/things/advanced-coffee-machine/http/ts/test/fixtures.ts @@ -1,24 +1,41 @@ -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 -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; +const port = 3000; export async function mochaGlobalSetup() { - try { - response = await getInitiateMain(path.join(__dirname, '..', 'dist', 'main.js'), port) - thingProcess = response.process - } - catch(error: any) { - console.log(error) - thingProcess = error.process - } + try { + response = await getInitiateMain( + path.join(__dirname, "..", "dist", "main.js"), + port, + ); + thingProcess = response.process; + } catch (error: unknown) { + console.log(error); + thingProcess = (error as ThingStartResponse).process; + } } export function mochaGlobalTeardown() { - if (thingProcess) { - thingProcess.kill() - } -} \ No newline at end of file + if (thingProcess) { + thingProcess.kill(); + } +} 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 a651471..8ef4960 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,61 @@ -import * as chai from 'chai' -import * as http from 'http' -import { getTDValidate } from '../../../../../util/util' -import { ValidateFunction } from 'ajv' +/******************************************************************************** + * 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"; -const port = 3000 -let validate: ValidateFunction | undefined +const expect = chai.expect; + +const port = 3000; +let validate: ValidateFunction | undefined; describe("TD Test", () => { before(async () => { - const tdValidate = getTDValidate() - + 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', () => { - 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) - } - }) - }) - }) -}) + 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: 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..396527d 100644 --- a/things/advanced-coffee-machine/http/ts/tsconfig.json +++ b/things/advanced-coffee-machine/http/ts/tsconfig.json @@ -1,14 +1,8 @@ { - "compilerOptions": { - "outDir": "dist", - "rootDir": "src", - "target": "ES2018", - "module": "commonjs", - "skipLibCheck": false, - "strict": true, - "sourceMap": false, - "esModuleInterop": true, - "removeComments": false - }, - "include": ["src/**/*"], -} \ No newline at end of file + "extends": "../../../..//tsconfig.json", + "compilerOptions": { + "outDir": "dist", + "rootDir": "src", + }, + "include": ["src/**/*"] +} diff --git a/things/advanced-coffee-machine/tm.test.js b/things/advanced-coffee-machine/tm.test.js index 51ef32e..2d33825 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) - }) + 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..108f07e 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}}" + "@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" }, - "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 + "output": { + "type": "number" }, - "subtract":{ - "input":{ - "type":"number" - }, - "output":{ - "type":"number" - }, - "idempotent":false, - "safe":false - } - }, - "events":{ - "update":{ - "data":{} - } - } - } \ No newline at end of file + "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 index 165c0c9..b7de798 100644 --- a/things/calculator/coap/client-tests/content-negotiation-coap-client.js +++ b/things/calculator/coap/client-tests/content-negotiation-coap-client.js @@ -1,17 +1,15 @@ -const coap = require('coap') -const cbor = require('cbor') -const hostname = 'localhost' -const portNumber = 5684 -const thingName = 'coap-calculator-content-negotiation' +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` - - + resultEndPoint = `/${thingName}/properties/result`, + lastChangeEndPoint = `/${thingName}/properties/lastChange`, + additionEndPoint = `/${thingName}/actions/add`, + subtractionEndPoint = `/${thingName}/actions/subtract`, + updateEndPoint = `/${thingName}/events/update`; /****************************************/ /****** Thing Description Endpoint ******/ @@ -19,69 +17,69 @@ const fullTDEndpoint = `/${thingName}`, // 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() + 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() + 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(); } /** @@ -89,200 +87,199 @@ function getResult(acceptType) { * 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 + 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.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(); + 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() + 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 + 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.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(); + 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(); + 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(); + 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 ************/ /****************************************/ @@ -292,65 +289,62 @@ function subtractNumber(acceptType, contentType, numberToSubtract) { * 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 + 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}`); + } }); + }); - 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(); + // 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) + //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 +runCalculatorInteractions(); 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 index 31e77a9..c517f13 100644 --- 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 @@ -6,47 +6,51 @@ 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 + .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); + }); 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 index 52e7266..9f8d8ef 100644 --- a/things/calculator/coap/client-tests/node-wot-simple-coap-client.js +++ b/things/calculator/coap/client-tests/node-wot-simple-coap-client.js @@ -7,48 +7,51 @@ 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 + .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); + }); diff --git a/things/calculator/coap/client-tests/simple-coap-client.js b/things/calculator/coap/client-tests/simple-coap-client.js index ea38e52..ea55d67 100644 --- a/things/calculator/coap/client-tests/simple-coap-client.js +++ b/things/calculator/coap/client-tests/simple-coap-client.js @@ -1,210 +1,219 @@ -const coap = require('coap') -const hostname = 'localhost' -const portNumber = 5683 -const thingName = 'coap-calculator-simple' +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` - + 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() + // 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 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()}`); - } - }) - + 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(); + // 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 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()}`); - } - }) - + 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(); + // 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(); + // 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(); + // 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 ************/ /****************************************/ @@ -215,52 +224,52 @@ function subtractNumber(numberToSubtract) { */ function observeUpdateEvent() { - const observeEventChange = coap.request({ - method: 'GET', - observe: 0, // Enable observation - host: hostname, - port: portNumber, - pathname: updateEndPoint + 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()}`, + ); + } }); + }); - 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(); + // 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) + 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 +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..06256ac 100644 --- a/things/calculator/coap/js/coap-content-negotiation-calculator.js +++ b/things/calculator/coap/js/coap-content-negotiation-calculator.js @@ -1,488 +1,502 @@ -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({ +/******************************************************************************** + * 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' - } - } -}) + 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 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] + 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) - - const newFormObs = JSON.parse(JSON.stringify(newFormRead)) - newFormObs['op'] = ['observeproperty', 'unobserveproperty'] - newFormObs['subprotocol'] = 'cov:observe' - thingDescription['properties'][key]['forms'].push(newFormObs) + 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'] = [] +// 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' + const newForm = JSON.parse(JSON.stringify(defaultForm)); + newForm.href = `actions/${key}`; + newForm["cov:method"] = "POST"; + newForm.op = "invokeaction"; - thingDescription['actions'][key]['forms'].push(newForm) + thingDescription.actions[key].forms.push(newForm); - const originalForm = thingDescription['actions'][key]['forms'][0] + 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 + * 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) + 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 + * 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) + 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) + 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) { + thingDescription.events[key].data.type = "number"; -//Adding headers to the Events -for (const key in thingDescription['events']) { + thingDescription.events[key].forms = []; - thingDescription['events'][key]['data']['type'] = "number" + 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'] = [] + thingDescription.events[key].forms.push(newForm); - 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) - - 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) + 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); } /*********************************************************/ -/************** 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('/') - const acceptHeaders = req.headers['Accept'] || [] - const reqContentType = req.headers['Content-Type'] || req.headers['Content-Format'] || [] +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() + 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) + 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 = 406 - res.end() - } - } - else { - res.code = 405 - res.end() + } else { + res.code = 405; + res.end(); } } } - if (segments[2] === 'properties') { - if (req.method === 'GET') { + 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); - //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 + // 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...') + console.log("Observing result property..."); - let oldResult = result + let oldResult = result; - //Todo: observation functionality should not happen inside a loop + // 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 + 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) + }, 1000); - res.on('finish', () => { + 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)) + 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(result) - res.end(cborData) - } - } } - //Last Change Endpoint - else if (segments[3] === 'lastChange') { - - //Start the observation of the property if observe attribute is set to true + // 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...') + console.log("Observing lastChange property..."); - let oldDate = lastChange + 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 + 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) + }, 1000); - res.on('finish', () => { + 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) + 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(); } - else { - res.code = 404 - res.end() - } - - } - else { - res.statusCode = 406 - res.end() + } else { + res.statusCode = 406; + res.end(); } - } - else { - res.code = 405 - res.end() + } else { + res.code = 405; + res.end(); } } - if (segments[2] === 'actions') { - if (req.method === 'POST') { + 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); - //Set the content-format header to the accepted header - res.setOption('Content-Format', acceptHeaders) + // Addition endpoint + if (segments[3] === "add") { + let numberToAdd; - //Addition endpoint - if (segments[3] === 'add') { - let numberToAdd - - if (reqContentType.includes('application/json')) { - numberToAdd = JSON.parse(req.payload.toString()) - } - else { + 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) + 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 + // Subtraction endpoint + else if (segments[3] === "subtract") { + let numberToSubtract; - if (reqContentType.includes('application/json')) { - numberToSubtract = JSON.parse(req.payload.toString()) - } - else { + 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) + 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 = 404 - res.end() - } + } else { + res.code = 406; + res.end(); } - else { - res.code = 406 - res.end() - } - } - else { - res.code = 415 - res.end() + } else { + res.code = 415; + 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) { if (supportedContentTypes.includes(acceptHeaders)) { - console.log('Observing update event...') + console.log("Observing update event..."); - let oldResult = result + let oldResult = result; const changeInterval = setInterval(() => { - //Set the content-format header to the accepted header - res.setOption('Content-Format', acceptHeaders) + // 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 + 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() + }, 1000); + + res.on("finish", () => { + clearInterval(changeInterval); + }); + } else { + res.statusCode = 406; + res.end(); } + } else { + res.code = 402; + res.end(); } - 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/coap-simple-calculator.js b/things/calculator/coap/js/coap-simple-calculator.js index f061fe9..1beb766 100644 --- a/things/calculator/coap/js/coap-simple-calculator.js +++ b/things/calculator/coap/js/coap-simple-calculator.js @@ -1,302 +1,303 @@ -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' - } - } -}) +/******************************************************************************** + * 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", + }, + }, +}); 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 defaultForm = { - "href": "", - "contentType": "application/json", - "op": [] -} + href: "", + contentType: "application/json", + op: [], +}; -//add properties forms -for (const key in thingDescription['properties']) { +// add properties forms +for (const key in thingDescription.properties) { + 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']) { - - thingDescription['actions'][key]['forms'] = [] +// add actions forms +for (const key in thingDescription.actions) { + 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) { + 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); + 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('/') - - if (segments[1] !== thingName) { - 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() - } - } +let result = 0; +let lastChange = new Date().toISOString(); + +server.on("request", (req, res) => { + const segments = req.url.split("/"); + + if (segments[1] !== thingName) { + 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 (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") - - //Checking for the observe option - if (req.headers.Observe === 0) { - console.log('Observing the result property...') - res.statusCode = 205 - - let oldResult = result - const changeInterval = setInterval(() => { - if (oldResult !== result) { - res.write(`${JSON.stringify(result)}`) - oldResult = result - } - }, 1000) - - 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 - else { - res.end(JSON.stringify(result)) - } - } - else if (segments[3] === 'lastChange') { - - //setting the content type header to json - res.setOption('Content-Format', "application/json") - - //Checking for the observe option - if (req.headers.Observe === 0) { - console.log('Observing the lastChange property...') - res.statusCode = 205 - - let oldDate = lastChange - const changeInterval = setInterval(() => { - if (oldDate !== lastChange) { - res.write(`${JSON.stringify(lastChange)}`) - oldDate = lastChange - } - }, 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 - else { - res.end(JSON.stringify(lastChange)) - } - } - else { - res.code = 404 - res.end() + } + + 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"); + + // Checking for the observe option + if (req.headers.Observe === 0) { + console.log("Observing the result property..."); + res.statusCode = 205; + + let oldResult = result; + const changeInterval = setInterval(() => { + if (oldResult !== result) { + res.write(`${JSON.stringify(result)}`); + oldResult = result; } + }, 1000); + + 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 else { - res.code = 405 - res.end() + res.end(JSON.stringify(result)); } - } - - - 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 - try { - inputNumber = JSON.parse(req.payload.toString()) - } catch (err) { - res.code = 400 - res.end() - } - - if (typeof inputNumber !== "number") { - res.code = 400 - res.end() - } else { - 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") - - let inputNumber - try { - inputNumber = JSON.parse(req.payload.toString()) - } catch (err) { - res.code = 400 - res.end() - } - - if (typeof inputNumber !== "number") { - res.code = 400 - res.end() - } else { - result -= inputNumber - lastChange = new Date() - res.end(JSON.stringify(result)) - } - } - else { - res.code = 404 - res.end() + } else if (segments[3] === "lastChange") { + // setting the content type header to json + res.setOption("Content-Format", "application/json"); + + // Checking for the observe option + if (req.headers.Observe === 0) { + console.log("Observing the lastChange property..."); + res.statusCode = 205; + + let oldDate = lastChange; + const changeInterval = setInterval(() => { + if (oldDate !== lastChange) { + res.write(`${JSON.stringify(lastChange)}`); + oldDate = lastChange; } + }, 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 else { - res.code = 405 - res.end() + res.end(JSON.stringify(lastChange)); } + } else { + res.code = 404; + 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"); + + let inputNumber; + try { + inputNumber = JSON.parse(req.payload.toString()); + } catch (err) { + console.log(err) + res.code = 400; + res.end(); + } - 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 - - //setting the content type header to json - res.setOption('Content-Format', "application/json") - - let oldResult = result - const changeInterval = setInterval(() => { - if (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() - } + if (typeof inputNumber !== "number") { + res.code = 400; + res.end(); + } else { + result += inputNumber; + lastChange = new Date(); + res.end(JSON.stringify(result)); } - else { - res.code = 404 - res.end() + } else if (segments[3] === "subtract") { + // setting the content type header to json + res.setOption("Content-Format", "application/json"); + + let inputNumber; + try { + inputNumber = JSON.parse(req.payload.toString()); + } catch (err) { + console.log(err) + res.code = 400; + res.end(); } + + if (typeof inputNumber !== "number") { + res.code = 400; + res.end(); + } else { + result -= inputNumber; + lastChange = new Date(); + res.end(JSON.stringify(result)); + } + } else { + res.code = 404; + res.end(); + } + } else { + res.code = 405; + res.end(); + } + } + + 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; + + // setting the content type header to json + res.setOption("Content-Format", "application/json"); + + let oldResult = result; + const changeInterval = setInterval(() => { + if (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(); + } + } 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..fa2f2cd 100644 --- a/things/calculator/coap/js/package.json +++ b/things/calculator/coap/js/package.json @@ -9,6 +9,10 @@ "iot", "coap" ], + "scripts": { + "lint": "eslint .", + "lint:fix": "eslint . --fix" + }, "author": "Eclipse Thingweb (https://thingweb.io/)", "license": "EPL-2.0 OR W3C-20150513", "dependencies": { diff --git a/things/calculator/coap/js/test/td.test.js b/things/calculator/coap/js/test/td.test.js index 54bbd3c..b205a51 100644 --- a/things/calculator/coap/js/test/td.test.js +++ b/things/calculator/coap/js/test/td.test.js @@ -1,79 +1,97 @@ -const Ajv = require('ajv') -const chai = require('chai') -const https = require('https') -const coap = require('coap') -const path = require('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 + ********************************************************************************/ -const spawn = require('child_process').spawn +const Ajv = require("ajv"); +const chai = require("chai"); +const https = require("https"); +const coap = require("coap"); +const path = require("path"); -const ajv = new Ajv({ strict: false, allErrors: true, validateFormats: false }) +const spawn = require("child_process").spawn; -const expect = chai.expect -const port = 5683 -let thingProcess +const ajv = new Ajv({ strict: false, allErrors: true, validateFormats: false }); -describe('Calculator CoAP JS', () => { - let validate +const expect = chai.expect; +const port = 5683; +let thingProcess; + +describe("Calculator CoAP JS", () => { + let validate; before(async () => { - const initiateMain = new Promise(async (resolve, reject) => { + const initiateMain = new Promise((resolve, reject) => { thingProcess = spawn( - 'node', - ['coap-simple-calculator.js', '-p', `${port}`], - { cwd: path.join(__dirname, '..') } - ) - thingProcess.stdout.on('data', (data) => { - if (data.toString().trim() === 'ThingIsReady') { - resolve('Success') + "node", + ["coap-simple-calculator.js", "-p", `${port}`], + { cwd: path.join(__dirname, "..") }, + ); + thingProcess.stdout.on("data", (data) => { + if (data.toString().trim() === "ThingIsReady") { + resolve("Success"); } - }) - thingProcess.stderr.on('data', (data) => { - reject(`Error: ${data}`) - }) - thingProcess.on('error', (error) => { - reject(`Error: ${error}`) - }) - thingProcess.on('close', () => { - reject('Failed to initiate the main script.') - }) - }) + }); + thingProcess.stderr.on("data", (data) => { + reject(new Error(`Error: ${data}`)); + }); + thingProcess.on("error", (error) => { + reject(new Error(`Error: ${error}`)); + }); + thingProcess.on("close", () => { + reject(new Error("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) - }) + 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() - }) + thingProcess.kill(); + }); - it('should have a valid TD', (done) => { - const req = coap.request(`coap://localhost:${port}/coap-calculator-simple`) + it("should have a valid TD", (done) => { + const req = coap.request(`coap://localhost:${port}/coap-calculator-simple`); - req.on('response', (res) => { - const valid = validate(JSON.parse(res.payload.toString())) - 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(); + }); +}); diff --git a/things/calculator/http/client-tests/content-negotiation-http-client.js b/things/calculator/http/client-tests/content-negotiation-http-client.js index b953e86..11ec216 100644 --- a/things/calculator/http/client-tests/content-negotiation-http-client.js +++ b/things/calculator/http/client-tests/content-negotiation-http-client.js @@ -4,202 +4,194 @@ * Requests as well as responses can be sent and received in JSON and CBOR formats. */ -const cbor = require('cbor') -const EventSource = require('eventsource') +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" - + 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 + * 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 - } + 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 + * @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 - } + 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, + }, + }); - 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) + 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 + * @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 - } + 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) + 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 { 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 - } + 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 { - throw new Error(await res.text()); + const buffer = await res.arrayBuffer(); + const decodedData = cbor.decode(buffer); + return decodedData; } + } else { + throw new Error(await res.text()); + } } /** @@ -210,104 +202,108 @@ async function addNumber(number, contentType, acceptType) { * @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 - } + 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 { - throw new Error(await res.text()); + 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. -*/ + * Create an EventSource for the update endpoint. + */ function listenToUpdateEvent(acceptType) { + const updateEventSource = new EventSource(url + updateEndPoint, { + headers: { + Accept: 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) -} + 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); - } - + 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 +runCalculatorInteractions(); 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 index 04331d5..f2bf1c4 100644 --- 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 @@ -4,18 +4,21 @@ 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"); +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 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()); + 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 @@ -31,8 +34,6 @@ servient.start().then(async (WoT) => { // 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()); @@ -41,5 +42,7 @@ servient.start().then(async (WoT) => { // //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 + }) + .catch((err) => { + console.error(err); + }); 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 index 68d9c8b..34890e1 100644 --- a/things/calculator/http/client-tests/node-wot-simple-http-client.js +++ b/things/calculator/http/client-tests/node-wot-simple-http-client.js @@ -1,38 +1,42 @@ -const { Servient } = require("@node-wot/core") -const { HttpClientFactory } = require("@node-wot/binding-http") +const { Servient } = require("@node-wot/core"); +const { HttpClientFactory } = require("@node-wot/binding-http"); -const servient = new Servient() -servient.addClientFactory(new HttpClientFactory(null)) +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") +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) + 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) + 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"]); - }) + 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 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()) - - + 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 + }) + .catch((err) => { + console.error(err); + }); diff --git a/things/calculator/http/client-tests/simple-http-client.js b/things/calculator/http/client-tests/simple-http-client.js index eaae894..83be242 100644 --- a/things/calculator/http/client-tests/simple-http-client.js +++ b/things/calculator/http/client-tests/simple-http-client.js @@ -3,36 +3,35 @@ * This client is mostly used for testing the overall basic functionality of the http thing. */ -const EventSource = require('eventsource') +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" - + 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 + * Return the Full TD * @returns TD - JSON object */ async function getFullTD() { - const res = await fetch(url) + const res = await fetch(url); - return res.json() + return res.json(); } /** - * Fetch current calculator result + * Fetch current calculator result * @returns result - Number */ async function getCurrentResult() { - const res = await fetch(url + resultEndPoint) + const res = await fetch(url + resultEndPoint); - return res.json() + return res.json(); } /** @@ -40,31 +39,31 @@ async function getCurrentResult() { * Uncomment to test the SSE functionality. */ function listenToResult() { - const resultEventSource = new EventSource(url + resultEndPointObserve); + const resultEventSource = new EventSource(url + resultEndPointObserve); - resultEventSource.onmessage = (e) => { - console.log('Result SSE:', JSON.parse(e.data)); - }; + resultEventSource.onmessage = (e) => { + console.log("Result SSE:", JSON.parse(e.data)); + }; - resultEventSource.onerror = (error) => { - console.error('Error with Result SSE:', error); - }; + 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) + //Closing the event source after 6 seconds + setTimeout(() => { + resultEventSource.close(); + console.log("- Closing Result Property SSE"); + }, 6000); } /** - * Fetches when the latest change was made + * Fetches when the latest change was made * @returns lastChange - String */ async function getLatestChange() { - const res = await fetch(url + lastChangeEndPoint) + const res = await fetch(url + lastChangeEndPoint); - return res.json() + return res.json(); } /** @@ -72,38 +71,40 @@ async function getLatestChange() { * 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) + 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 + * @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() + const res = await fetch(url + additionEndPoint, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: number, + }); + + return res.json(); } /** @@ -112,71 +113,69 @@ async function addNumber(number) { * @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() + 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) + // 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); - } + 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() +runCalculatorInteractions(); diff --git a/things/calculator/http/express/http-content-negotiation-calculator.js b/things/calculator/http/express/http-content-negotiation-calculator.js index e51644d..9205d38 100644 --- a/things/calculator/http/express/http-content-negotiation-calculator.js +++ b/things/calculator/http/express/http-content-negotiation-calculator.js @@ -1,402 +1,458 @@ -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({ +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: 'http', + 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' + LAST_CHANGE_OBSERVABLE: true, +}); + +const defaultForm = { + href: "", + contentType: "application/json", + response: { + contentType: "application/json", }, - 'op': '', - 'htv:methodName': '', - 'htv:headers': [ + op: "", + "htv:methodName": "", + "htv:headers": [ { - '@type': 'htv:RequestHeader', - 'fieldValue': 'application/json', - '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]['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) + "@type": "htv:RequestHeader", + fieldValue: "application/json", + 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].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); } - }) + }); } -//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] - - 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) +// 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]; + + 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)) { + 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); + newForm.response.contentType = type; + newForm["htv:headers"][0].fieldValue = type; + thingDescription.actions[key].forms.push(newForm); } - }) + }); } - }) + }); } -//Adding headers to the Events - -for (const key in thingDescription['events']) { +// Adding headers to the Events - thingDescription['events'][key]['data']['type'] = "object" +for (const key in thingDescription.events) { + 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]['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); } - /******************************************/ -/************** 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() + 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() + 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'); + res.status(405).json("Method Not Allowed"); } } if (endpoint === additionEndPoint || endpoint === subtractionEndPoint) { - if (method === 'POST') { - next() + if (method === "POST") { + next(); } else { - res.status(405).json('Method Not Allowed'); + 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') + 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() + 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'); + 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 + 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() + 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) + 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 +// 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') + 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 + 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` + 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 + res.statusCode = 200; + res.write(message); + oldResult = result; } + }, 1000); - }, 1000) - - - res.on('finish', () => { - clearInterval(changeInterval) - }) + res.on("finish", () => { + clearInterval(changeInterval); + }); 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) + 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 +// 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') + 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 + 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` + 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 + res.statusCode = 200; + res.write(message); + oldLastChange = lastChange; } + }, 1000); - }, 1000) - - - res.on('finish', () => { - clearInterval(changeInterval) - }) + res.on("finish", () => { + clearInterval(changeInterval); + }); res.on("close", () => { console.log("Client stopped listening to lastChange property"); - }) -}) - + }); +}); // /*****************************************************/ // /*************** Actions Endpoints *******************/ @@ -404,96 +460,99 @@ 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 + 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') { + // 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'); + res.status(400).json("Bad Request"); } - } - else { + } else { try { - bodyInput = JSON.parse(req.body) + bodyInput = JSON.parse(req.body); } catch (err) { - res.status(400).json('Bad Request'); + res.status(400).json("Bad Request"); } } - /**Check if given input is a number, if not return an error message, + /** 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') + 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() + 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) + const cborData = cbor.encode(result); console.log("cbor encoded: ", cborData); - res.setHeader('Content-Type', 'application/cbor') - res.send(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 + 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') { + // 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'); + res.status(400).json("Bad Request"); } - } - else { + } else { try { - bodyInput = JSON.parse(req.body) + bodyInput = JSON.parse(req.body); } catch (err) { - res.status(400).json('Bad Request'); + res.status(400).json("Bad Request"); } } - /**Check if given input is a valid number, if not return an error message, + /** 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') + 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) + 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 +560,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') + 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 + 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` + 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 + res.statusCode = 200; + res.write(message); + oldResult = result; } - }, 1000) + }, 1000); - - res.on('finish', () => { - clearInterval(changeInterval) - }) + 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 84763e2..4d35b5b 100644 --- a/things/calculator/http/express/http-simple-calculator.js +++ b/things/calculator/http/express/http-simple-calculator.js @@ -1,4 +1,18 @@ -const express = require("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"); @@ -13,16 +27,25 @@ const hostname = process.env.HOSTNAME ?? "localhost"; let portNumber = process.env.PORT ?? 3000; const thingName = "http-express-calculator-simple"; -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 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 }, @@ -54,65 +77,65 @@ placeholderReplacer.addVariableMap({ HOSTNAME: hostname, PORT_NUMBER: portNumber, RESULT_OBSERVABLE: true, - LAST_CHANGE_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']) { - - thingDescription['properties'][key]['forms'] = [] - - 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" - - thingDescription['properties'][key]['forms'].push(newFormRead) - thingDescription['properties'][key]['forms'].push(newFormObs) + href: "", + contentType: "application/json", + op: [], +}; + +// add properties forms +for (const key in thingDescription.properties) { + thingDescription.properties[key].forms = []; + + 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"; + + thingDescription.properties[key].forms.push(newFormRead); + thingDescription.properties[key].forms.push(newFormObs); } -//add actions forms -for (const key in thingDescription['actions']) { - - thingDescription['actions'][key]['forms'] = [] +// add actions forms +for (const key in thingDescription.actions) { + 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']) { - - thingDescription['events'][key]['data']['type'] = "object" +// add events forms +for (const key in thingDescription.events) { + 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); } @@ -123,47 +146,51 @@ let result = 0; let lastChange = new Date().toISOString(); /******************************************/ -/************** 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") + res.status(404).json("Not Found"); + } else { + next(); } - 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() + 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'); + res.status(405).json("Method Not Allowed"); } } if (endpoint === additionEndPoint || endpoint === subtractionEndPoint) { - if (method === 'POST') { - next() + if (method === "POST") { + next(); } else { - res.status(405).json('Method Not Allowed'); + res.status(405).json("Method Not Allowed"); } } - -}) - +}); /******************************************/ -/*************** Endpoints ****************/ +/** ************* Endpoints ****************/ /******************************************/ app.get(TDEndPoint, (req, res) => { @@ -181,11 +208,10 @@ app.get(resultEndPointObserve, (req, res) => { res.setHeader("connection", "keep-alive"); res.setHeader("Content-Type", "text/event-stream"); - console.log("Client is listening to result property") + console.log("Client is listening to result property"); let oldResult = result; const changeInterval = setInterval(() => { - if (oldResult !== result) { res.write(`data: ${JSON.stringify(result)}\n\n`); oldResult = result; @@ -198,7 +224,7 @@ app.get(resultEndPointObserve, (req, res) => { res.on("close", () => { console.log("Client stopped listening to result property"); - }) + }); }); app.get(lastChangeEndPoint, (req, res) => { @@ -216,7 +242,6 @@ app.get(lastChangeEndPointObserve, (req, res) => { let oldLastChange = lastChange; const changeInterval = setInterval(() => { - if (oldLastChange !== lastChange) { res.write(`data: ${JSON.stringify(lastChange)}\n\n`); oldLastChange = lastChange; @@ -229,13 +254,12 @@ app.get(lastChangeEndPointObserve, (req, res) => { res.on("close", () => { console.log("Client stopped listening to lastChange property"); - }) + }); }); app.post(additionEndPoint, reqParser, (req, res) => { - try { - const bodyInput = JSON.parse(req.body) + const bodyInput = JSON.parse(req.body); if (typeof bodyInput !== "number") { res.status(400).json("Input should be a valid number"); @@ -247,12 +271,11 @@ app.post(additionEndPoint, reqParser, (req, res) => { } 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) + const bodyInput = JSON.parse(req.body); if (typeof bodyInput !== "number") { res.status(400).json("Input should be a valid number"); @@ -283,7 +306,6 @@ app.get(updateEndPoint, (req, res) => { * interpreted correctly by the client, which could create empty values. */ const changeInterval = setInterval(() => { - if (oldResult !== result) { res.write(`data: ${result}\n\n`); oldResult = result; @@ -296,7 +318,7 @@ app.get(updateEndPoint, (req, res) => { res.on("close", () => { console.log("Client stopped listening to update event"); - }) + }); }); app.listen(portNumber, () => { diff --git a/things/calculator/http/express/package.json b/things/calculator/http/express/package.json index a3593c4..7027397 100644 --- a/things/calculator/http/express/package.json +++ b/things/calculator/http/express/package.json @@ -10,7 +10,9 @@ "http" ], "scripts": { - "test": "node simple-calculator.js ; pid = $! ; result=$(mocha --exit *.test.js)) & kill $pid" + "test": "node simple-calculator.js ; pid = $! ; result=$(mocha --exit *.test.js)) & kill $pid", + "lint": "eslint .", + "lint:fix": "eslint . --fix" }, "author": "Eclipse Thingweb (https://thingweb.io/)", "license": "EPL-2.0 OR W3C-20150513", diff --git a/things/calculator/http/express/test/td.test.js b/things/calculator/http/express/test/td.test.js index 009cf04..f8ce405 100644 --- a/things/calculator/http/express/test/td.test.js +++ b/things/calculator/http/express/test/td.test.js @@ -1,87 +1,107 @@ -const Ajv = require('ajv') -const chai = require('chai') -const http = require('http') -const https = require('https') -const path = require('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 + ********************************************************************************/ + 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 = 3000 -let thingProcess +const expect = chai.expect; +const port = 3000; +let thingProcess; -describe('Calculator HTTP JS', () => { - let validate +describe("Calculator HTTP JS", () => { + let validate; before(async () => { const initiateMain = new Promise(async (resolve, reject) => { thingProcess = spawn( - 'node', - ['http-simple-calculator.js', '-p', `${port}`], - { cwd: path.join(__dirname, '..') } - ) - thingProcess.stdout.on('data', (data) => { - if (data.toString().includes('ThingIsReady')) { - resolve('Success') + "node", + ["http-simple-calculator.js", "-p", `${port}`], + { cwd: path.join(__dirname, "..") }, + ); + thingProcess.stdout.on("data", (data) => { + if (data.toString().includes("ThingIsReady")) { + resolve("Success"); } - }) - thingProcess.stderr.on('data', (data) => { - reject(`Error: ${data}`) - }) - thingProcess.on('error', (error) => { - reject(`Error: ${error}`) - }) - thingProcess.on('close', () => { - reject('Failed to initiate the main script.') - }) - }) + }); + thingProcess.stderr.on("data", (data) => { + reject(`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) - }) + 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() - }) + thingProcess.kill(); + }); - it('should have a valid TD', (done) => { - http.get(`http://localhost:${port}/http-express-calculator-simple`, function (response) { - const body = [] - response.on('data', (chunk) => { - body.push(chunk) - }) + it("should have a valid TD", (done) => { + http.get( + `http://localhost:${port}/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) - } - }) - }) - }) -}) + 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..bed60c7 100644 --- a/things/calculator/http/flask/test/td.test.js +++ b/things/calculator/http/flask/test/td.test.js @@ -1,86 +1,90 @@ -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 = 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.') - }) - }) + }); + 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) - }) + 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() - }) + thingProcess.kill(); + }); - it('should have a valid TD', (done) => { + 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) - }) + 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/main.js b/things/calculator/mqtt/js/main.js index 9d86cd0..1acb034 100644 --- a/things/calculator/mqtt/js/main.js +++ b/things/calculator/mqtt/js/main.js @@ -1,44 +1,60 @@ -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 brokerURI = process.env.BROKER_URI ?? 'test.mosquitto.org' -let portNumber = process.env.PORT ?? 1883 - -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 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 brokerURI = process.env.BROKER_URI ?? "test.mosquitto.org"; +let portNumber = process.env.PORT ?? 1883; + +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 thingName = 'mqtt-calculator' -const PROPERTIES = 'properties' -const ACTIONS = 'actions' -const EVENTS = 'events' +const thingName = "mqtt-calculator"; +const PROPERTIES = "properties"; +const ACTIONS = "actions"; +const EVENTS = "events"; -const broker = mqtt.connect(`mqtt://${brokerURI}`, { port: portNumber }) +const broker = mqtt.connect(`mqtt://${brokerURI}`, { port: portNumber }); -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: 'mqtt', + PROTOCOL: "mqtt", THING_NAME: thingName, PROPERTIES, ACTIONS, @@ -46,113 +62,110 @@ placeholderReplacer.addVariableMap({ HOSTNAME: brokerURI, PORT_NUMBER: portNumber, RESULT_OBSERVABLE: true, - LAST_CHANGE_OBSERVABLE: true -}) -const thingDescription = placeholderReplacer.replace(thingModel) -thingDescription['@type'] = 'Thing' + LAST_CHANGE_OBSERVABLE: true, +}); +const thingDescription = placeholderReplacer.replace(thingModel); +thingDescription["@type"] = "Thing"; const defaultForm = { - "href": "", - "contentType": "application/json", - "op": [] -} + href: "", + contentType: "application/json", + op: [], +}; -//add properties forms -for (const key in thingDescription['properties']) { +// add properties forms +for (const key in thingDescription.properties) { + 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']) { - - thingDescription['actions'][key]['forms'] = [] +// add actions forms +for (const key in thingDescription.actions) { + 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']) { - - thingDescription['events'][key]['data']['type'] = "string" +// add events forms +for (const key in thingDescription.events) { + 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); } +broker.on("connect", () => { + console.log(`Connected to broker via port ${portNumber}`); +}); -broker.on('connect', () => { - console.log(`Connected to broker via port ${portNumber}`) -}) +let result = 0; +let lastChange = ""; -let result = 0 -let lastChange = '' - -broker.on('message', (topic, payload, packet) => { - console.log(`Messaged to the topic ${topic}`) - const segments = topic.split('/') +broker.on("message", (topic, payload, packet) => { + console.log(`Messaged to the topic ${topic}`); + const segments = topic.split("/"); if (segments[0] !== thingName) { - return + return; } if (segments[1] === PROPERTIES) { - if (segments.length === 3 && segments[2] === 'result') { - console.log(`Result is : ${result}`) + if (segments.length === 3 && segments[2] === "result") { + console.log(`Result is : ${result}`); } - if (segments.length === 3 && segments[2] === 'lastChange') { - console.log(`Last change : ${lastChange}`) + if (segments.length === 3 && segments[2] === "lastChange") { + console.log(`Last change : ${lastChange}`); } } if (segments[1] === ACTIONS) { - if (segments.length === 3 && segments[2] === 'add') { - const parsedValue = parseInt(payload.toString()) + if (segments.length === 3 && segments[2] === "add") { + const parsedValue = parseInt(payload.toString()); if (isNaN(parsedValue)) { - return + return; } else { - result += parsedValue - lastChange = (new Date()).toLocaleTimeString() + result += parsedValue; + lastChange = new Date().toLocaleTimeString(); } } - if (segments.length === 3 && segments[2] === 'subtract') { - const parsedValue = parseInt(payload.toString()) + if (segments.length === 3 && segments[2] === "subtract") { + const parsedValue = parseInt(payload.toString()); if (isNaN(parsedValue)) { - } else { - result -= parsedValue - lastChange = (new Date()).toLocaleTimeString() + result -= parsedValue; + lastChange = new Date().toLocaleTimeString(); } } } -}) +}); setInterval(() => { - broker.publish(`${thingName}/${EVENTS}/update`, 'Updated the thing!') -}, 500) - -broker.subscribe(`${thingName}/${PROPERTIES}/result`) -broker.subscribe(`${thingName}/${PROPERTIES}/lastChange`) -broker.subscribe(`${thingName}/${ACTIONS}/add`) -broker.subscribe(`${thingName}/${ACTIONS}/subtract`) -broker.publish(`${thingName}`, JSON.stringify(thingDescription), { retain: true }) -console.log('ThingIsReady') + broker.publish(`${thingName}/${EVENTS}/update`, "Updated the thing!"); +}, 500); + +broker.subscribe(`${thingName}/${PROPERTIES}/result`); +broker.subscribe(`${thingName}/${PROPERTIES}/lastChange`); +broker.subscribe(`${thingName}/${ACTIONS}/add`); +broker.subscribe(`${thingName}/${ACTIONS}/subtract`); +broker.publish(`${thingName}`, JSON.stringify(thingDescription), { + retain: true, +}); +console.log("ThingIsReady"); diff --git a/things/calculator/mqtt/js/package.json b/things/calculator/mqtt/js/package.json index b015ba1..6867a04 100644 --- a/things/calculator/mqtt/js/package.json +++ b/things/calculator/mqtt/js/package.json @@ -9,6 +9,10 @@ "iot", "mqtt" ], + "scripts": { + "lint": "eslint .", + "lint:fix": "eslint . --fix" + }, "author": "Eclipse Thingweb (https://thingweb.io/)", "license": "EPL-2.0 OR W3C-20150513", "dependencies": { diff --git a/things/calculator/mqtt/js/test/td.test.js b/things/calculator/mqtt/js/test/td.test.js index b69deb8..4a24e4f 100644 --- a/things/calculator/mqtt/js/test/td.test.js +++ b/things/calculator/mqtt/js/test/td.test.js @@ -1,85 +1,100 @@ -const Ajv = require('ajv') -const chai = require('chai') -const https = require('https') -const mqtt = require('mqtt') -const path = require('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 + ********************************************************************************/ + const Ajv = require("ajv"); +const chai = require("chai"); +const https = require("https"); +const mqtt = require("mqtt"); +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 hostname = 'test.mosquitto.org' -const port = 1883 -let thingProcess +const expect = chai.expect; +const hostname = "test.mosquitto.org"; +const port = 1883; +let thingProcess; -describe('Calculator MQTT JS', () => { - let validate +describe("Calculator MQTT JS", () => { + let validate; before(async () => { const initiateMain = new Promise(async (resolve, reject) => { - thingProcess = spawn( - 'node', - ['main.js', '-p', `${port}`], - { cwd: path.join(__dirname, '..') } - ) - thingProcess.stdout.on('data', (data) => { - if (data.toString().trim() === 'ThingIsReady') { - resolve('Success') + thingProcess = spawn("node", ["main.js", "-p", `${port}`], { + cwd: path.join(__dirname, ".."), + }); + thingProcess.stdout.on("data", (data) => { + if (data.toString().trim() === "ThingIsReady") { + resolve("Success"); } - }) - thingProcess.stderr.on('data', (data) => { - reject(`Error: ${data}`) - }) - thingProcess.on('error', (error) => { - reject(`Error: ${error}`) - }) - thingProcess.on('close', () => { - reject('Failed to initiate the main script.') - }) - }) + }); + thingProcess.stderr.on("data", (data) => { + reject(`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) - }) + 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() - }) + thingProcess.kill(); + }); - it('should have a valid TD', (done) => { - const broker = mqtt.connect(`mqtt://${hostname}`, { port }) - broker.subscribe('mqtt-calculator') + it("should have a valid TD", (done) => { + const broker = mqtt.connect(`mqtt://${hostname}`, { port }); + broker.subscribe("mqtt-calculator"); - let valid = false + let valid = false; - broker.on('message', (topic, payload, packet) => { - valid = validate(JSON.parse(payload.toString())) - broker.end() - }) + broker.on("message", (topic, payload, packet) => { + valid = validate(JSON.parse(payload.toString())); + broker.end(); + }); - broker.on('close', () => { - expect(valid).to.be.true - done() - }) - }) -}) + 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..5b2e255 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) - }) + 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..2f5c7c9 100644 --- a/things/data-schema-thing/data-schema-thing.tm.json +++ b/things/data-schema-thing/data-schema-thing.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" - } + "@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}}", + "@type": "tm:ThingModel", + "title": "{{THING_NAME}}", "description": "Test Thing", "base": "{{PROTOCOL}}://{{HOSTNAME}}:{{PORT_NUMBER}}/{{THING_NAME}}", "properties": { diff --git a/things/data-schema-thing/http/ts/.mocharc.json b/things/data-schema-thing/http/ts/.mocharc.json index b0ed45c..0b67134 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 + "spec": "./test/**.test.ts", + "require": ["ts-node/register", "./test/fixtures.ts"] +} diff --git a/things/data-schema-thing/http/ts/package.json b/things/data-schema-thing/http/ts/package.json index 1c19240..dc45bd0 100644 --- a/things/data-schema-thing/http/ts/package.json +++ b/things/data-schema-thing/http/ts/package.json @@ -5,7 +5,9 @@ "main": "main.js", "scripts": { "build": "tsc -b", - "test": "mocha" + "test": "mocha", + "lint": "eslint .", + "lint:fix": "eslint . --fix" }, "keywords": [ "wot", diff --git a/things/data-schema-thing/http/ts/src/main.ts b/things/data-schema-thing/http/ts/src/main.ts index 621ef59..c787b9b 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,73 +13,79 @@ * 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 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-data-schema-thing"; - function checkPropertyWrite(expected: string, actual: unknown) { - const output = "Property " + expected + " written with " + actual; - if (expected === actual) { - console.info("PASS: " + output); - } else { - throw new Error("FAIL: " + output); - } + const output = "Property " + expected + " written with " + actual; + if (expected === actual) { + console.info("PASS: " + output); + } else { + throw new Error("FAIL: " + output); + } } -function checkActionInvocation(name: string, expected: string, actual: unknown) { - const output = "Action " + name + " invoked with " + actual; - if (expected === actual) { - console.info("PASS: " + output); - } else { - throw new Error("FAIL: " + output); - } +function checkActionInvocation( + name: string, + expected: string, + actual: unknown, +) { + const output = "Action " + name + " invoked with " + actual; + if (expected === actual) { + console.info("PASS: " + output); + } else { + throw new Error("FAIL: " + output); + } } -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); - } - - 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({ - PROTOCOL: "http", - THING_NAME: thingName, - HOSTNAME: hostname, - PORT_NUMBER: portNumber - }); - const thingDescription = placeholderReplacer.replace(thingModel); - thingDescription["@type"] = "Thing"; + }, +}); +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); +} + +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, +}); + +let thingDescription = placeholderReplacer.replace(thingModel); +thingDescription = { + ...thingDescription, + '@type': 'Thing' +} // init property values let bool = false; @@ -90,146 +95,166 @@ let string = "unset"; let array: unknown[] = [2, "unset"]; let object: Record = { id: 123, name: "abc" }; -let servient = new WotCore.Servient(); -servient.addServer(new HttpServer({ +const servient = new 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); - bool = 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); - } - int = 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); - num = 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); - string = 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); - } - array = 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); - } - object = localObject as Record; - thing.emitEvent("on-object", object); - }) - .setPropertyReadHandler("object", async () => object); - - // set action handlers - thing - .setActionHandler("void-void", async (parameters) => { - checkActionInvocation("void-void", "undefined", typeof (await parameters.value())); - return undefined; - }) - .setActionHandler("void-int", async (parameters) => { - checkActionInvocation("void-int", "undefined", typeof (await parameters.value())); - return 0; - }) - .setActionHandler("int-void", async (parameters) => { - const localParameters = await parameters.value(); - 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(); - if (localParameters === Math.floor(localParameters as number)) { - checkActionInvocation("int-int", "integer", "integer"); - } else { - checkActionInvocation("int-int", "integer", typeof localParameters); - } - return (localParameters as number) + 1; - }) - .setActionHandler("int-string", async (parameters) => { - const localParameters = await parameters.value(); - const inputtype = typeof localParameters; - if (localParameters === Math.floor(localParameters as number)) { - checkActionInvocation("int-string", "integer", "integer"); - } else { - checkActionInvocation("int-string", "integer", typeof localParameters); - } - - if (inputtype === "number") { - // eslint-disable-next-line no-new-wrappers - return 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"); - } - }) - .setActionHandler("void-obj", async (parameters) => { - checkActionInvocation("void-complex", "undefined", typeof (await parameters.value())); - return { prop1: 123, prop2: "abc" }; - }) - .setActionHandler("obj-void", async (parameters) => { - checkActionInvocation("complex-void", "object", typeof (await parameters.value())); - return undefined; - }); - - // expose the thing - thing.expose().then(async () => { - console.info(thing.getThingDescription().title + " ready"); - console.log("ThingIsReady"); - }); - }) - .catch((e: Error) => { - console.log(e) - }) + 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); + bool = 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); + } + int = 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); + num = 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); + string = 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); + } + array = 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); + } + object = localObject as Record; + thing.emitEvent("on-object", object); + }) + .setPropertyReadHandler("object", async () => object); + + // set action handlers + thing + .setActionHandler("void-void", async (parameters) => { + checkActionInvocation( + "void-void", + "undefined", + typeof (await parameters.value()), + ); + return undefined; + }) + .setActionHandler("void-int", async (parameters) => { + checkActionInvocation( + "void-int", + "undefined", + typeof (await parameters.value()), + ); + return 0; + }) + .setActionHandler("int-void", async (parameters) => { + const localParameters = await parameters.value(); + 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(); + if (localParameters === Math.floor(localParameters as number)) { + checkActionInvocation("int-int", "integer", "integer"); + } else { + checkActionInvocation("int-int", "integer", typeof localParameters); + } + return (localParameters as number) + 1; + }) + .setActionHandler("int-string", async (parameters) => { + const localParameters = await parameters.value(); + const inputtype = typeof localParameters; + if (localParameters === Math.floor(localParameters as number)) { + checkActionInvocation("int-string", "integer", "integer"); + } else { + checkActionInvocation( + "int-string", + "integer", + typeof localParameters, + ); + } + + if (inputtype === "number") { + // eslint-disable-next-line no-new-wrappers + return 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"); + } + }) + .setActionHandler("void-obj", async (parameters) => { + checkActionInvocation( + "void-complex", + "undefined", + typeof (await parameters.value()), + ); + return { prop1: 123, prop2: "abc" }; + }) + .setActionHandler("obj-void", async (parameters) => { + checkActionInvocation( + "complex-void", + "object", + typeof (await parameters.value()), + ); + return undefined; + }); + + // expose the thing + thing.expose().then(async () => { + console.info(thing.getThingDescription().title + " ready"); + console.log("ThingIsReady"); + }); }) - \ No newline at end of file + .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 63d6a0f..5ffe299 100644 --- a/things/data-schema-thing/http/ts/test/client.test.ts +++ b/things/data-schema-thing/http/ts/test/client.test.ts @@ -1,184 +1,200 @@ - -import chai from 'chai' -import chaiAsPromised from '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 + ********************************************************************************/ + import chai from "chai"; +import chaiAsPromised from "chai-as-promised"; import { Servient } from "@node-wot/core"; -import { HttpClientFactory } from "@node-wot/binding-http" +import { HttpClientFactory } from "@node-wot/binding-http"; + +chai.use(chaiAsPromised); +const expect = chai.expect; -chai.use(chaiAsPromised) -const expect = chai.expect +const servient = new Servient(); +servient.addClientFactory(new HttpClientFactory({ baseUri: "localhost:3000" })); +const port = 3000; -let servient = new Servient() -servient.addClientFactory(new HttpClientFactory({ baseUri: 'localhost:3000' })) -const port = 3000 +let thing: WoT.ConsumedThing; -let thing: WoT.ConsumedThing +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 readProperty = async (thing: WoT.ConsumedThing, name: string): Promise => { +describe("Client Tests", () => { + before(async () => { try { - const res = await thing.readProperty(name) - const value = await res.value() - return value - } - catch (error) { - console.error(`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); } -} + }); -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) - } - }) - - describe("bool property", () => { - it("should read property bool", async () => { - 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 - }) - - it("should write value false", async () => { - 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 - }) - }) - - describe("int property", () => { - it("should read property int", async () => { - 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) - }) - - it("should fail to write value 3.1415", async () => { - 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 - }) - }) - - describe("num property", () => { - it("should read property num", async () => { - 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) - }) - - 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) - }) - - it("should fail to write string value 'Pi'", async () => { - 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') - }) - - 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) - }) - - it("should fail to write value 13", async () => { - 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 - }) - }) - - describe("array property", () => { - it("should read property array", async () => { - 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) - }) - - it("should fail to write object value", async () => { - 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 - }) - }) - - describe("object property", () => { - it("should read property object", async () => { - 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) - }) - - it("should fail to write value null", async () => { - 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 - }) - }) -}) + describe("bool property", () => { + it("should read property bool", async () => { + 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; + }); + + it("should write value false", async () => { + 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; + }); + }); + + describe("int property", () => { + it("should read property int", async () => { + 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); + }); + + it("should fail to write value 3.1415", async () => { + 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; + }); + }); + + describe("num property", () => { + it("should read property num", async () => { + 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); + }); + + 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); + }); + + it("should fail to write string value 'Pi'", async () => { + 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"); + }); + + 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); + }); + + it("should fail to write value 13", async () => { + 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; + }); + }); + + describe("array property", () => { + it("should read property array", async () => { + 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); + }); + + it("should fail to write object value", async () => { + 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; + }); + }); + + describe("object property", () => { + it("should read property object", async () => { + 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); + }); + + it("should fail to write value null", async () => { + 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; + }); + }); +}); diff --git a/things/data-schema-thing/http/ts/test/fixtures.ts b/things/data-schema-thing/http/ts/test/fixtures.ts index 796f067..9c4122b 100644 --- a/things/data-schema-thing/http/ts/test/fixtures.ts +++ b/things/data-schema-thing/http/ts/test/fixtures.ts @@ -1,25 +1,40 @@ -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 + ********************************************************************************/ + import { ChildProcess } from "child_process"; +import { getInitiateMain, ThingStartResponse } from "../../../../../util/util"; +import path from "path"; -let thingProcess: ChildProcess | undefined -let response: ThingStartResponse -const port = 3000 +let thingProcess: ChildProcess | undefined; +let response: ThingStartResponse; +const port = 3000; export async function mochaGlobalSetup() { - try { - response = await getInitiateMain(path.join(__dirname, '..', 'dist', 'main.js'), port) - } - catch(error) { - console.log(error) - } - finally { - thingProcess = response.process - } + try { + response = await getInitiateMain( + path.join(__dirname, "..", "dist", "main.js"), + port, + ); + } catch (error) { + console.log(error); + } finally { + thingProcess = response.process; + } } export function mochaGlobalTeardown() { - if (thingProcess) { - thingProcess.kill() - } -} \ No newline at end of file + if (thingProcess) { + thingProcess.kill(); + } +} 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 bf7e44f..e358873 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,60 @@ -import * as chai from 'chai' -import * as http from 'http' -import { getTDValidate } from '../../../../../util/util' -import { ValidateFunction } from 'ajv' +/******************************************************************************** + * 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 * as chai from "chai"; +import * as http from "http"; +import { getTDValidate } from "../../../../../util/util"; +import { ValidateFunction } from "ajv"; -const expect = chai.expect +const expect = chai.expect; -const port = 3000 -let validate: ValidateFunction | undefined +const port = 3000; +let validate: ValidateFunction | undefined; describe("TD Test", () => { before(async () => { - const tdValidate = getTDValidate() - + 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', () => { - 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) - } - }) - }) - }) -}) + 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", () => { + 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..8bc0fe4 100644 --- a/things/data-schema-thing/http/ts/tsconfig.json +++ b/things/data-schema-thing/http/ts/tsconfig.json @@ -1,14 +1,8 @@ { - "compilerOptions": { - "outDir": "dist", - "rootDir": "src", - "target": "ES2018", - "module": "commonjs", - "skipLibCheck": false, - "strict": true, - "sourceMap": false, - "esModuleInterop": true, - "removeComments": false - }, - "include": ["src/**/*"], -} \ No newline at end of file + "extends": "../../../..//tsconfig.json", + "compilerOptions": { + "outDir": "dist", + "rootDir": "src", + }, + "include": ["src/**/*"] +} \ No newline at end of file diff --git a/things/data-schema-thing/tm.test.js b/things/data-schema-thing/tm.test.js index 316cec0..dbca9e6 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) - }) + 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..d614784 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" - } + "@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" - } + "@type": "tm:ThingModel", + "title": "{{THING_NAME}}", + "description": "Elevator Thing", + "securityDefinitions": { + "nosec_sc": { + "scheme": "nosec" + } }, - "security":[ - "nosec_sc" - ], + "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 - } + "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 +} diff --git a/things/elevator/modbus/js/main.js b/things/elevator/modbus/js/main.js index c3f8647..945e74d 100644 --- a/things/elevator/modbus/js/main.js +++ b/things/elevator/modbus/js/main.js @@ -1,195 +1,231 @@ -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" +/******************************************************************************** + * 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 } } = parseArgs({ - options: { - port: { - type: 'string', - short: 'p' - } - } -}) - +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 }, +} = 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: '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) - -const lightSwitchForms = [{ - "href": `?address=1&quantity=1`, - "op": "readproperty", + 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); + +const lightSwitchForms = [ + { + href: `?address=1&quantity=1`, + op: "readproperty", "modbus:entity": "Coil", "modbus:function": "readCoil", - "contentType": "application/octet-stream" -}, { - "href": `?address=1&quantity=1`, - "op": "writeproperty", + contentType: "application/octet-stream", + }, + { + href: `?address=1&quantity=1`, + op: "writeproperty", "modbus:entity": "Coil", "modbus:function": "writeSingleCoil", - "contentType": "application/octet-stream" -}] - + contentType: "application/octet-stream", + }, +]; -thingDescription['properties']['lightSwitch']['forms'] = lightSwitchForms +thingDescription.properties.lightSwitch.forms = lightSwitchForms; -const onTheMovePollingTime = 1000 +const onTheMovePollingTime = 1000; -const onTheMoveForms = [{ - "href": `?address=1&quantity=1`, - "op": [ - "readproperty", - "observeproperty" - ], +const onTheMoveForms = [ + { + href: `?address=1&quantity=1`, + op: ["readproperty", "observeproperty"], "modbus:entity": "DiscreteInput", "modbus:function": "readDiscreteInput", "modbus:pollingTime": onTheMovePollingTime, - "contentType": "application/octet-stream" -}] - -discreteInputs[0] = 0 -let onTheMoveIsPolled = false -thingDescription['properties']['onTheMove']['forms'] = onTheMoveForms - -const floorNumberForms = [{ - "href": `?address=1&quantity=1`, - "op": "readproperty", + contentType: "application/octet-stream", + }, +]; + +discreteInputs[0] = 0; +let onTheMoveIsPolled = false; +thingDescription.properties.onTheMove.forms = onTheMoveForms; + +const floorNumberForms = [ + { + href: `?address=1&quantity=1`, + op: "readproperty", "modbus:entity": "HoldingRegister", "modbus:function": "readHoldingRegister", - "contentType": "application/octet-stream" -}, { - "href": `?address=1&quantity=1`, - "op": "writeproperty", + contentType: "application/octet-stream", + }, + { + href: `?address=1&quantity=1`, + op: "writeproperty", "modbus:entity": "HoldingRegister", "modbus:function": "writeSingleHoldingRegister", - "contentType": "application/octet-stream" -}] + contentType: "application/octet-stream", + }, +]; -const minFloorNumber = 0 -const maxFloorNumber = 15 -thingDescription['properties']['floorNumber']['forms'] = floorNumberForms +const minFloorNumber = 0; +const maxFloorNumber = 15; +thingDescription.properties.floorNumber.forms = floorNumberForms; -fs.writeFile(`${thingName}.td.json`, JSON.stringify(thingDescription, 4, 4), 'utf-8', function(){}) +fs.writeFile( + `${thingName}.td.json`, + JSON.stringify(thingDescription, 4, 4), + "utf-8", + function () {}, +); const vector = { - getDiscreteInput: function(addr, unitID) { - if (thingUnitID === unitID) { - console.log(`Reading discrete input @${addr}`) - if (addr === 1) { - if (onTheMoveIsPolled) { - console.log(`Polling onTheMove too frequently. You should poll it every ${onTheMovePollingTime} ms.`) - return - } - - onTheMoveIsPolled = true - setTimeout(function() { - onTheMoveIsPolled = false - }, onTheMovePollingTime) - - return discreteInputs[addr - 1]; - } - } - }, - getHoldingRegister: function(addr, unitID, callback) { - if (thingUnitID === unitID) { - setTimeout(function() { - console.log(`Reading holding register @${addr}`) - callback(null, holdingRegisters[addr - 1]) - }, 10) - } - }, - getCoil: function(addr, unitID) { - if (thingUnitID === unitID) { - return new Promise(function(resolve) { - console.log(`Reading coil @${addr}`) - resolve(coils[addr - 1]) - }) - } - }, - setRegister: function(addr, value, unitID) { - if (thingUnitID === unitID) { - console.log(`Setting register @${addr} to ${value}`) - // trying to change floor number - if (addr === 1) { - // elevator is on the move - if (discreteInputs[0]) { - console.log("Elevator is on the move, cannot change the floor number") - } else { - if (value < minFloorNumber) { - console.log(`Floor number should not be under ${minFloorNumber}`) - return -1 - } - - if (value > maxFloorNumber) { - console.log(`Floor number should not be above ${maxFloorNumber}`) - return -1 - } - - console.log(`Changing the floor number to ${value}`) - - holdingRegisters[addr - 1] = value - // simulating elevator movement - discreteInputs[0] = 1 - // elevator completes its movement in 5 seconds - setTimeout(() => { - discreteInputs[0] = 0 - }, 5000) - } - } + getDiscreteInput: function (addr, unitID) { + if (thingUnitID === unitID) { + console.log(`Reading discrete input @${addr}`); + if (addr === 1) { + if (onTheMoveIsPolled) { + console.log( + `Polling onTheMove too frequently. You should poll it every ${onTheMovePollingTime} ms.`, + ); + return; } - return - }, - setCoil: function(addr, value, unitID) { - if (thingUnitID === unitID) { - console.log(`Setting coil @${addr} to ${value}`) - coils[addr - 1] = value + onTheMoveIsPolled = true; + setTimeout(function () { + onTheMoveIsPolled = false; + }, onTheMovePollingTime); + + return discreteInputs[addr - 1]; + } + } + }, + getHoldingRegister: function (addr, unitID, callback) { + if (thingUnitID === unitID) { + setTimeout(function () { + console.log(`Reading holding register @${addr}`); + callback(null, holdingRegisters[addr - 1]); + }, 10); + } + }, + getCoil: function (addr, unitID) { + if (thingUnitID === unitID) { + return new Promise(function (resolve) { + console.log(`Reading coil @${addr}`); + resolve(coils[addr - 1]); + }); + } + }, + setRegister: function (addr, value, unitID) { + if (thingUnitID === unitID) { + console.log(`Setting register @${addr} to ${value}`); + // trying to change floor number + if (addr === 1) { + // elevator is on the move + if (discreteInputs[0]) { + console.log( + "Elevator is on the move, cannot change the floor number", + ); + } else { + if (value < minFloorNumber) { + console.log(`Floor number should not be under ${minFloorNumber}`); + return -1; + } + + if (value > maxFloorNumber) { + console.log(`Floor number should not be above ${maxFloorNumber}`); + return -1; + } + + console.log(`Changing the floor number to ${value}`); + + holdingRegisters[addr - 1] = value; + // simulating elevator movement + discreteInputs[0] = 1; + // elevator completes its movement in 5 seconds + setTimeout(() => { + discreteInputs[0] = 0; + }, 5000); } - - return + } + } + + + }, + setCoil: function (addr, value, unitID) { + if (thingUnitID === unitID) { + console.log(`Setting coil @${addr} to ${value}`); + coils[addr - 1] = value; } + + + }, }; // 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){ - // Handle socket error if needed, can be ignored - console.error(err) +serverTCP.on("socketError", function (err) { + // Handle socket error if needed, can be ignored + console.error(err); }); diff --git a/things/elevator/modbus/js/modbus-elevator.td.json b/things/elevator/modbus/js/modbus-elevator.td.json index f0b420b..b28b75e 100644 --- a/things/elevator/modbus/js/modbus-elevator.td.json +++ b/things/elevator/modbus/js/modbus-elevator.td.json @@ -1,88 +1,83 @@ { - "@context": [ - "https://www.w3.org/2019/wot/td/v1", - "https://www.w3.org/2022/wot/td/v1.1", + "@context": [ + "https://www.w3.org/2019/wot/td/v1", + "https://www.w3.org/2022/wot/td/v1.1", + { + "@language": "en" + } + ], + "@type": "Thing", + "title": "modbus-elevator", + "description": "Elevator Thing", + "securityDefinitions": { + "nosec_sc": { + "scheme": "nosec" + } + }, + "security": ["nosec_sc"], + "base": "modbus+tcp://0.0.0.0:8502/1", + "properties": { + "lightSwitch": { + "type": "boolean", + "readOnly": false, + "writeOnly": false, + "observable": false, + "forms": [ { - "@language": "en" + "href": "?address=1&quantity=1", + "op": "readproperty", + "modbus:entity": "Coil", + "modbus:function": "readCoil", + "contentType": "application/octet-stream" + }, + { + "href": "?address=1&quantity=1", + "op": "writeproperty", + "modbus:entity": "Coil", + "modbus:function": "writeSingleCoil", + "contentType": "application/octet-stream" } - ], - "@type": "Thing", - "title": "modbus-elevator", - "description": "Elevator Thing", - "securityDefinitions": { - "nosec_sc": { - "scheme": "nosec" + ] + }, + "onTheMove": { + "type": "boolean", + "readOnly": true, + "writeOnly": false, + "observable": true, + "forms": [ + { + "href": "?address=1&quantity=1", + "op": ["readproperty", "observeproperty"], + "modbus:entity": "DiscreteInput", + "modbus:function": "readDiscreteInput", + "modbus:pollingTime": 1000, + "contentType": "application/octet-stream" } + ] }, - "security": [ - "nosec_sc" - ], - "base": "modbus+tcp://0.0.0.0:8502/1", - "properties": { - "lightSwitch": { - "type": "boolean", - "readOnly": false, - "writeOnly": false, - "observable": false, - "forms": [ - { - "href": "?address=1&quantity=1", - "op": "readproperty", - "modbus:entity": "Coil", - "modbus:function": "readCoil", - "contentType": "application/octet-stream" - }, - { - "href": "?address=1&quantity=1", - "op": "writeproperty", - "modbus:entity": "Coil", - "modbus:function": "writeSingleCoil", - "contentType": "application/octet-stream" - } - ] - }, - "onTheMove": { - "type": "boolean", - "readOnly": true, - "writeOnly": false, - "observable": true, - "forms": [ - { - "href": "?address=1&quantity=1", - "op": [ - "readproperty", - "observeproperty" - ], - "modbus:entity": "DiscreteInput", - "modbus:function": "readDiscreteInput", - "modbus:pollingTime": 1000, - "contentType": "application/octet-stream" - } - ] + "floorNumber": { + "type": "integer", + "minimum": 0, + "maximum": 15, + "readOnly": false, + "writeOnly": false, + "observable": false, + "forms": [ + { + "href": "?address=1&quantity=1", + "op": "readproperty", + "modbus:entity": "HoldingRegister", + "modbus:function": "readHoldingRegister", + "contentType": "application/octet-stream" }, - "floorNumber": { - "type": "integer", - "minimum": 0, - "maximum": 15, - "readOnly": false, - "writeOnly": false, - "observable": false, - "forms": [ - { - "href": "?address=1&quantity=1", - "op": "readproperty", - "modbus:entity": "HoldingRegister", - "modbus:function": "readHoldingRegister", - "contentType": "application/octet-stream" - }, - { - "href": "?address=1&quantity=1", - "op": "writeproperty", - "modbus:entity": "HoldingRegister", - "modbus:function": "writeSingleHoldingRegister", - "contentType": "application/octet-stream" - } - ] + { + "href": "?address=1&quantity=1", + "op": "writeproperty", + "modbus:entity": "HoldingRegister", + "modbus:function": "writeSingleHoldingRegister", + "contentType": "application/octet-stream" } + ] } -} \ No newline at end of file + } +} diff --git a/things/elevator/modbus/js/package.json b/things/elevator/modbus/js/package.json index a8caf99..a5a44b9 100644 --- a/things/elevator/modbus/js/package.json +++ b/things/elevator/modbus/js/package.json @@ -3,6 +3,10 @@ "version": "1.0.0", "description": "Modbus Elevator", "main": "main.js", + "scripts": { + "lint": "eslint .", + "lint:fix": "eslint . --fix" + }, "keywords": [ "wot", "thing", diff --git a/things/elevator/modbus/js/test/td.test.js b/things/elevator/modbus/js/test/td.test.js index 9a8cec9..3d0d34d 100644 --- a/things/elevator/modbus/js/test/td.test.js +++ b/things/elevator/modbus/js/test/td.test.js @@ -1,82 +1,101 @@ -const Ajv = require('ajv') -const chai = require('chai') -const http = require('http') -const https = require('https') -const fs = require('fs') -const path = require('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 + ********************************************************************************/ -const spawn = require('child_process').spawn +const Ajv = require("ajv"); +const chai = require("chai"); +const https = require("https"); +const fs = require("fs"); +const path = require("path"); -const ajv = new Ajv({ strict: false, allErrors: true, validateFormats: false }) +const spawn = require("child_process").spawn; -const expect = chai.expect -const port = "8502" -let thingProcess +const ajv = new Ajv({ strict: false, allErrors: true, validateFormats: false }); -describe('Elevator Modbus JS', () => { - let validate +const expect = chai.expect; +const port = "8502"; +let thingProcess; + +describe("Elevator Modbus JS", () => { + let validate; before(async () => { const initiateMain = new Promise(async (resolve, reject) => { - thingProcess = spawn( - 'node', - ['main.js', '-p', `${port}`], - { cwd: path.join(__dirname, '..') } - ) - thingProcess.stdout.on('data', (data) => { - if (data.toString().includes('ThingIsReady')) { - resolve('Success') + thingProcess = spawn("node", ["main.js", "-p", `${port}`], { + cwd: path.join(__dirname, ".."), + }); + thingProcess.stdout.on("data", (data) => { + if (data.toString().includes("ThingIsReady")) { + resolve("Success"); } - }) - thingProcess.stderr.on('data', (data) => { - reject(`Error: ${data}`) - }) - thingProcess.on('error', (error) => { - reject(`Error: ${error}`) - }) - thingProcess.on('close', () => { - reject('Failed to initiate the main script.') - }) - }) + }); + thingProcess.stderr.on("data", (data) => { + reject(new Error(`Error: ${data}`)); + }); + thingProcess.on("error", (error) => { + reject(new Error(`Error: ${error}`)); + }); + thingProcess.on("close", () => { + reject(new Error("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) - }) + 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() - }) + thingProcess.kill(); + }); - 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) - } + 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() - }) - }) -}) + 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..fae21ae 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) - }) + 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/util/util.ts b/util/util.ts index 8592a2f..e98144b 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 ValidateResponse = { - validate?: ValidateFunction, - message: string -} +export type ThingStartResponse = { + process?: ChildProcess; + message: string; +}; -const spawn = require('node:child_process').spawn +export type ValidateResponse = { + validate?: ValidateFunction; + message: string; +}; -export const getInitiateMain = (thingPath: string, port: number): Promise => { - return new Promise((resolve, reject) => { - const thingProcess = spawn( - 'node', - [thingPath, '-p', `${port}`], - ) +const spawn = require("node:child_process").spawn; - // 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) +export const getInitiateMain = ( + thingPath: string, + port: number, +): Promise => { + return new Promise((resolve, reject) => { + const thingProcess = spawn("node", [thingPath, "-p", `${port}`]); - 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.' - }) - }) - }) -} + // 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); + 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.", + }); + }); + }); +}; -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' - }) -} + return Promise.resolve({ + validate: ajv.compile(tdSchema), + 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); + }); + }, + ); +}); From fc49b85cd031ee73e7477fc7be2b9a09ec50e3ac Mon Sep 17 00:00:00 2001 From: Hasan Eroglu Date: Fri, 27 Sep 2024 17:17:37 +0200 Subject: [PATCH 3/7] fix eslint issues and run prettier Signed-off-by: Hasan Eroglu --- .eslintrc.js | 124 +-- .github/workflows/{run-tests.yml => ci.yml} | 29 +- conf/grafana/dashboards/dashboard.json | 272 +++--- mashups/smart-home/mashup-logic.ts | 125 +-- mashups/smart-home/package.json | 54 +- mashups/smart-home/things/presence-sensor.ts | 70 +- .../things/simple-coffee-machine.ts | 342 +++---- mashups/smart-home/things/smart-clock.ts | 124 +-- mashups/smart-home/tsconfig.json | 24 +- package.json | 86 +- .../advanced-coffee-machine.tm.json | 398 ++++----- .../http/ts/.mocharc.json | 4 +- .../http/ts/package.json | 60 +- .../http/ts/src/main.ts | 595 +++++++------ .../http/ts/test/client.test.ts | 135 +-- .../http/ts/test/fixtures.ts | 26 +- .../http/ts/test/td.test.ts | 67 +- .../http/ts/tsconfig.json | 12 +- things/advanced-coffee-machine/tm.test.js | 44 +- things/calculator/calculator.tm.json | 112 +-- .../content-negotiation-coap-client.js | 547 ++++++------ ...ode-wot-content-negotiation-coap-client.js | 100 ++- .../node-wot-simple-coap-client.js | 96 +- .../coap/client-tests/simple-coap-client.js | 396 +++++---- .../js/coap-content-negotiation-calculator.js | 787 +++++++++-------- .../coap/js/coap-simple-calculator.js | 412 ++++----- things/calculator/coap/js/package.json | 44 +- things/calculator/coap/js/test/td.test.js | 116 +-- .../content-negotiation-http-client.js | 454 +++++----- ...ode-wot-content-negotiation-http-client.js | 84 +- .../node-wot-simple-http-client.js | 68 +- .../http/client-tests/simple-http-client.js | 204 ++--- .../http-content-negotiation-calculator.js | 834 +++++++++--------- .../http/express/http-simple-calculator.js | 398 +++++---- things/calculator/http/express/package.json | 56 +- .../calculator/http/express/test/td.test.js | 144 +-- things/calculator/http/flask/test/td.test.js | 136 +-- things/calculator/mqtt/js/main.js | 149 ++-- things/calculator/mqtt/js/package.json | 42 +- things/calculator/mqtt/js/test/td.test.js | 120 +-- things/calculator/tm.test.js | 44 +- .../data-schema-thing.tm.json | 270 +++--- .../data-schema-thing/http/ts/.mocharc.json | 4 +- things/data-schema-thing/http/ts/package.json | 58 +- things/data-schema-thing/http/ts/src/main.ts | 418 +++++---- .../http/ts/test/client.test.ts | 336 +++---- .../http/ts/test/fixtures.ts | 28 +- .../data-schema-thing/http/ts/test/td.test.ts | 69 +- .../data-schema-thing/http/ts/tsconfig.json | 14 +- things/data-schema-thing/tm.test.js | 44 +- things/elevator/elevator.tm.json | 74 +- things/elevator/modbus/js/main.js | 284 +++--- .../modbus/js/modbus-elevator.td.json | 148 ++-- things/elevator/modbus/js/package.json | 46 +- things/elevator/modbus/js/test/td.test.js | 126 +-- things/elevator/tm.test.js | 44 +- tsconfig.eslint.json | 2 +- tsconfig.json | 2 +- util/util.ts | 118 +-- 59 files changed, 5132 insertions(+), 4887 deletions(-) rename .github/workflows/{run-tests.yml => ci.yml} (63%) diff --git a/.eslintrc.js b/.eslintrc.js index 93486d2..e562b25 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,10 +1,6 @@ module.exports = { root: true, - extends: [ - 'eslint:recommended', - "standard", - "prettier" - ], + extends: ["eslint:recommended", "standard", "prettier"], plugins: ["unused-imports", "workspaces", "notice"], env: { es6: true, @@ -15,7 +11,8 @@ module.exports = { "notice/notice": [ "error", { - mustMatch: "Copyright \\(c\\) [0-9]{0,4} Contributors to the Eclipse Foundation", + mustMatch: + "Copyright \\(c\\) [0-9]{0,4} Contributors to the Eclipse Foundation", templateFile: __dirname + "/license.template.txt", onNonMatchingHeader: "replace", }, @@ -31,62 +28,67 @@ module.exports = { }, ], }, - 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", - }, + 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", ], - "@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 - }, + 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 + { + files: ["**/*.test.js", "**/*.test.ts"], + env: { + mocha: true, + }, + rules: { + "no-unused-expressions": "off", + "@typescript-eslint/no-unused-expressions": "off", + }, }, - "rules": { - "no-unused-expressions": "off", - "@typescript-eslint/no-unused-expressions": "off" - } - } - ] -}; \ No newline at end of file + ], +}; diff --git a/.github/workflows/run-tests.yml b/.github/workflows/ci.yml similarity index 63% rename from .github/workflows/run-tests.yml rename to .github/workflows/ci.yml index fc90ef9..23b4d1d 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/ci.yml @@ -1,4 +1,4 @@ -name: Run tests +name: CI Test Things on: push: @@ -44,3 +44,30 @@ jobs: - 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/conf/grafana/dashboards/dashboard.json b/conf/grafana/dashboards/dashboard.json index 93f3e88..aac09dd 100644 --- a/conf/grafana/dashboards/dashboard.json +++ b/conf/grafana/dashboards/dashboard.json @@ -1,142 +1,142 @@ { - "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, - "id": 1, - "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 + "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, + "id": 1, + "links": [], + "panels": [ + { + "datasource": { + "default": true, + "type": "prometheus", + "uid": "adwznwwq8yupse" }, - "insertNulls": false, - "lineInterpolation": "smooth", - "lineWidth": 2, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" + "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": [] }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 0 }, - "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 + "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": "HTTP Express Calculator GET Requests", + "type": "timeseries" } - ], - "title": "HTTP Express Calculator GET Requests", - "type": "timeseries" - } - ], - "refresh": "", - "schemaVersion": 39, - "tags": [], - "templating": { - "list": [] - }, - "time": { - "from": "now-1h", - "to": "now" - }, - "timepicker": {}, - "timezone": "browser", - "title": "HTTP Services", - "uid": "bdwzocnv714hsd", - "version": 1, - "weekStart": "" + ], + "refresh": "", + "schemaVersion": 39, + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-1h", + "to": "now" + }, + "timepicker": {}, + "timezone": "browser", + "title": "HTTP Services", + "uid": "bdwzocnv714hsd", + "version": 1, + "weekStart": "" } diff --git a/mashups/smart-home/mashup-logic.ts b/mashups/smart-home/mashup-logic.ts index 34a8bc5..3ebae1c 100644 --- a/mashups/smart-home/mashup-logic.ts +++ b/mashups/smart-home/mashup-logic.ts @@ -12,7 +12,7 @@ * * 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"; @@ -30,74 +30,77 @@ servient.addClientFactory(new MqttClientFactory()); 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"); - type Time = { - hour: number, - minute: number - }; + type Time = { + hour: number; + minute: number; + }; - // 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 + // 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 - // Optionally, we can log the current time - console.log( - "Current time is " + - currentTime.hour.toString().padStart(2, "0") + - ":" + - currentTime.minute.toString().padStart(2, "0"), - ); + // Optionally, we can log the current time + console.log( + "Current time is " + + currentTime.hour.toString().padStart(2, "0") + + ":" + + currentTime.minute.toString().padStart(2, "0"), + ); - // 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; - } - } + // 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); - }); + // 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 1523bcb..23f220d 100644 --- a/mashups/smart-home/package.json +++ b/mashups/smart-home/package.json @@ -1,29 +1,29 @@ { - "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" - }, - "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" + }, + "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 fc6f80b..6818904 100644 --- a/mashups/smart-home/things/presence-sensor.ts +++ b/mashups/smart-home/things/presence-sensor.ts @@ -24,45 +24,45 @@ 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"; + process.env.PRESENCE_SENSOR_BROKER_URI ?? "mqtt://test.mosquitto.org"; servient.addServer(new MqttBrokerServer({ uri: brokerUri })); servient.start().then((WoT) => { - WoT.produce({ - title: "smart-home-presence-sensor", - description: "Thing that can detect presence of human nearby", - support: "https://github.com/eclipse-thingweb/node-wot/", - "@context": "https://www.w3.org/2022/wot/td/v1.1", - events: { - presenceDetected: { - title: "Presence Detected", - description: - "An event that is emitted when a person is detected in the room. It is mocked and emitted every 5 seconds", - data: { - type: "number", - title: "Distance", - minimum: 55, - maximum: 1200, + WoT.produce({ + title: "smart-home-presence-sensor", + description: "Thing that can detect presence of human nearby", + support: "https://github.com/eclipse-thingweb/node-wot/", + "@context": "https://www.w3.org/2022/wot/td/v1.1", + events: { + presenceDetected: { + title: "Presence Detected", + description: + "An event that is emitted when a person is detected in the room. It is mocked and emitted every 5 seconds", + data: { + type: "number", + title: "Distance", + minimum: 55, + maximum: 1200, + }, + }, }, - }, - }, - }) - .then((thing) => { - console.log("Produced " + thing.getThingDescription().title); + }) + .then((thing) => { + console.log("Produced " + thing.getThingDescription().title); - // expose the thing - thing.expose().then(() => { - console.info(thing.getThingDescription().title + " ready"); + // expose the thing + thing.expose().then(() => { + console.info(thing.getThingDescription().title + " ready"); - // mocking the detection with an event sent every 5 seconds, with a random distance - setInterval(() => { - const distance: number = Math.random() * (1200 - 55) + 55; - thing.emitEvent("presenceDetected", distance); - console.info("Emitted presence with distance ", distance); - }, 5000); - }); - }) - .catch((e) => { - console.log(e); - }); + // mocking the detection with an event sent every 5 seconds, with a random distance + setInterval(() => { + const distance: number = Math.random() * (1200 - 55) + 55; + thing.emitEvent("presenceDetected", distance); + console.info("Emitted presence with distance ", distance); + }, 5000); + }); + }) + .catch((e) => { + console.log(e); + }); }); diff --git a/mashups/smart-home/things/simple-coffee-machine.ts b/mashups/smart-home/things/simple-coffee-machine.ts index 7e37508..b880bf4 100644 --- a/mashups/smart-home/things/simple-coffee-machine.ts +++ b/mashups/smart-home/things/simple-coffee-machine.ts @@ -26,10 +26,10 @@ const servient = new Servient(); 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}`, - port: parseInt(httpPort), - }), + new HttpServer({ + baseUri: `http://${hostname}:${httpPort}`, + port: parseInt(httpPort), + }), ); let waterAmount = 1000; @@ -38,179 +38,185 @@ let milkAmount = 1000; // promisify timeout since it does not return a promise function timeout(ms: number) { - return new Promise((resolve) => setTimeout(resolve, ms)); + return new Promise((resolve) => setTimeout(resolve, ms)); } servient.start().then((WoT) => { - WoT.produce({ - title: "smart-home-simple-coffee-machine", - 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: { - resources: { - readOnly: true, - observable: true, - type: "object", + WoT.produce({ + title: "smart-home-simple-coffee-machine", + 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: { - water: { - type: "integer", - minimum: 0, - maximum: 1000, - }, - beans: { - type: "integer", - minimum: 0, - maximum: 1000, - }, - milk: { - type: "integer", - minimum: 0, - maximum: 1000, - }, + resources: { + readOnly: true, + observable: true, + type: "object", + properties: { + water: { + type: "integer", + minimum: 0, + maximum: 1000, + }, + beans: { + type: "integer", + minimum: 0, + maximum: 1000, + }, + milk: { + type: "integer", + minimum: 0, + maximum: 1000, + }, + }, + }, }, - }, - }, - actions: { - brew: { - synchronous: true, - input: { - type: "string", - enum: ["espresso", "cappuccino", "americano"], + actions: { + brew: { + synchronous: true, + input: { + type: "string", + enum: ["espresso", "cappuccino", "americano"], + }, + }, + refill: { + synchronous: true, + input: { + type: "array", + items: { + type: "string", + enum: ["water", "beans", "milk"], + }, + }, + }, }, - }, - refill: { - synchronous: true, - input: { - type: "array", - items: { - type: "string", - enum: ["water", "beans", "milk"], - }, + events: { + resourceEmpty: { + data: { + type: "array", + items: { + type: "string", + enum: ["water", "beans", "milk"], + }, + }, + }, }, - }, - }, - events: { - resourceEmpty: { - data: { - type: "array", - items: { - type: "string", - enum: ["water", "beans", "milk"], - }, - }, - }, - }, - }) - .then((thing) => { - console.log("Produced " + thing.getThingDescription().title); + }) + .then((thing) => { + console.log("Produced " + thing.getThingDescription().title); - thing.setPropertyReadHandler("resources", async () => { - return { - water: waterAmount, - beans: beansAmount, - milk: milkAmount, - }; - }); + thing.setPropertyReadHandler("resources", async () => { + return { + water: waterAmount, + beans: beansAmount, + milk: milkAmount, + }; + }); - thing.setActionHandler("brew", async (params, options) => { - const coffeeType = await params.value(); - console.info("received coffee order of ", coffeeType); - if (coffeeType === "espresso") { - if (waterAmount <= 10 || beansAmount <= 10) { - throw new Error("Not enough water or beans"); - } else { - await timeout(1000); - waterAmount = waterAmount - 10; - beansAmount = beansAmount - 10; - thing.emitPropertyChange("resources"); - const resourceEvent: Array = []; - if (waterAmount <= 10) { - resourceEvent.push("water"); - } - if (beansAmount <= 10) { - resourceEvent.push("beans"); - } - if (resourceEvent.length > 0) { - thing.emitEvent("resourceEmpty", resourceEvent); - } - return undefined; - } - } else if (coffeeType === "cappuccino") { - if (waterAmount <= 20 || beansAmount <= 25 || milkAmount <= 15) { - throw new Error("Not enough water or beans"); - } else { - await timeout(2000); - waterAmount = waterAmount - 15; - beansAmount = beansAmount - 20; - milkAmount = milkAmount - 10; - thing.emitPropertyChange("resources"); - const resourceEvent: Array = []; - if (waterAmount <= 10) { - resourceEvent.push("water"); - } - if (beansAmount <= 10) { - resourceEvent.push("beans"); - } - if (milkAmount <= 10) { - resourceEvent.push("milk"); - } - if (resourceEvent.length > 0) { - thing.emitEvent("resourceEmpty", resourceEvent); - } - return undefined; - } - } else if (coffeeType === "americano") { - if (waterAmount <= 35 || beansAmount <= 10) { - throw new Error("Not enough water or beans"); - } else { - await timeout(2000); - waterAmount = waterAmount - 30; - beansAmount = beansAmount - 10; - thing.emitPropertyChange("resources"); - const resourceEvent: Array = []; - if (waterAmount <= 10) { - resourceEvent.push("water"); - } - if (beansAmount <= 10) { - resourceEvent.push("beans"); - } - if (resourceEvent.length > 0) { - thing.emitEvent("resourceEmpty", resourceEvent); - } - return undefined; - } - } else { - throw new Error("Wrong coffee input"); - } - }); + thing.setActionHandler("brew", async (params, options) => { + const coffeeType = await params.value(); + console.info("received coffee order of ", coffeeType); + if (coffeeType === "espresso") { + if (waterAmount <= 10 || beansAmount <= 10) { + throw new Error("Not enough water or beans"); + } else { + await timeout(1000); + waterAmount = waterAmount - 10; + beansAmount = beansAmount - 10; + thing.emitPropertyChange("resources"); + const resourceEvent: Array = []; + if (waterAmount <= 10) { + resourceEvent.push("water"); + } + if (beansAmount <= 10) { + resourceEvent.push("beans"); + } + if (resourceEvent.length > 0) { + thing.emitEvent("resourceEmpty", resourceEvent); + } + return undefined; + } + } else if (coffeeType === "cappuccino") { + if ( + waterAmount <= 20 || + beansAmount <= 25 || + milkAmount <= 15 + ) { + throw new Error("Not enough water or beans"); + } else { + await timeout(2000); + waterAmount = waterAmount - 15; + beansAmount = beansAmount - 20; + milkAmount = milkAmount - 10; + thing.emitPropertyChange("resources"); + const resourceEvent: Array = []; + if (waterAmount <= 10) { + resourceEvent.push("water"); + } + if (beansAmount <= 10) { + resourceEvent.push("beans"); + } + if (milkAmount <= 10) { + resourceEvent.push("milk"); + } + if (resourceEvent.length > 0) { + thing.emitEvent("resourceEmpty", resourceEvent); + } + return undefined; + } + } else if (coffeeType === "americano") { + if (waterAmount <= 35 || beansAmount <= 10) { + throw new Error("Not enough water or beans"); + } else { + await timeout(2000); + waterAmount = waterAmount - 30; + beansAmount = beansAmount - 10; + thing.emitPropertyChange("resources"); + const resourceEvent: Array = []; + if (waterAmount <= 10) { + resourceEvent.push("water"); + } + if (beansAmount <= 10) { + resourceEvent.push("beans"); + } + if (resourceEvent.length > 0) { + thing.emitEvent("resourceEmpty", resourceEvent); + } + return undefined; + } + } else { + throw new Error("Wrong coffee input"); + } + }); - thing.setActionHandler("refill", async (params, options) => { - const selectedResource = (await params.value()) as Array< - "water" | "beans" | "milk" - >; - console.info("received refill order of ", selectedResource); - if (selectedResource!.indexOf("water") !== -1) { - waterAmount = 1000; - } - if (selectedResource!.indexOf("beans") !== -1) { - beansAmount = 1000; - } - if (selectedResource!.indexOf("milk") !== -1) { - milkAmount = 1000; - } - thing.emitPropertyChange("resources"); - return undefined; - }); + thing.setActionHandler("refill", async (params, options) => { + const selectedResource = (await params.value()) as Array< + "water" | "beans" | "milk" + >; + console.info("received refill order of ", selectedResource); + if (selectedResource!.indexOf("water") !== -1) { + waterAmount = 1000; + } + if (selectedResource!.indexOf("beans") !== -1) { + beansAmount = 1000; + } + if (selectedResource!.indexOf("milk") !== -1) { + milkAmount = 1000; + } + thing.emitPropertyChange("resources"); + return undefined; + }); - // expose the thing - thing.expose().then(() => { - console.info(thing.getThingDescription().title + " ready"); - console.info("TD available at http://" + hostname + ":" + httpPort); - }); - }) - .catch((e) => { - console.log(e); - }); + // expose the thing + thing.expose().then(() => { + console.info(thing.getThingDescription().title + " ready"); + console.info( + "TD available at http://" + hostname + ":" + httpPort, + ); + }); + }) + .catch((e) => { + console.log(e); + }); }); diff --git a/mashups/smart-home/things/smart-clock.ts b/mashups/smart-home/things/smart-clock.ts index 07fe01a..b94f6a3 100644 --- a/mashups/smart-home/things/smart-clock.ts +++ b/mashups/smart-home/things/smart-clock.ts @@ -25,81 +25,81 @@ const servient = new Servient(); const hostname = process.env.SMART_CLOCK_HOSTNAME ?? "localhost"; const port = process.env.SMART_CLOCK_PORT ?? "5686"; servient.addServer( - new CoapServer({ - address: hostname, - port: parseInt(port), - }), + new CoapServer({ + address: hostname, + port: parseInt(port), + }), ); let minuteCounter = 0; let hourCounter = 5; async function timeCount(thing: WoT.ExposedThing) { - for (minuteCounter = 0; minuteCounter < 59; minuteCounter++) { - // if we have <60, we can get a 15:60. - await new Promise((resolve) => setTimeout(resolve, 1000)); // sleep - thing.emitPropertyChange("time"); - } - console.info({ - hour: hourCounter, - minute: minuteCounter, - }); + for (minuteCounter = 0; minuteCounter < 59; minuteCounter++) { + // if we have <60, we can get a 15:60. + await new Promise((resolve) => setTimeout(resolve, 1000)); // sleep + thing.emitPropertyChange("time"); + } + console.info({ + hour: hourCounter, + minute: minuteCounter, + }); - hourCounter++; - if (hourCounter === 24) { - hourCounter = 0; - } + hourCounter++; + if (hourCounter === 24) { + hourCounter = 0; + } } 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.", - support: "https://github.com/eclipse-thingweb/node-wot/", - "@context": "https://www.w3.org/2022/wot/td/v1.1", - properties: { - time: { - readOnly: true, - observable: true, - type: "object", + 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.", + support: "https://github.com/eclipse-thingweb/node-wot/", + "@context": "https://www.w3.org/2022/wot/td/v1.1", properties: { - minute: { - type: "integer", - minimum: 0, - maximum: 59, - }, - hour: { - type: "integer", - minimum: 0, - maximum: 23, - }, + time: { + readOnly: true, + observable: true, + type: "object", + properties: { + minute: { + type: "integer", + minimum: 0, + maximum: 59, + }, + hour: { + type: "integer", + minimum: 0, + maximum: 23, + }, + }, + }, }, - }, - }, - }) - .then(async (thing) => { - console.log("Produced " + thing.getThingDescription().title); + }) + .then(async (thing) => { + console.log("Produced " + thing.getThingDescription().title); - thing.setPropertyReadHandler("time", async () => { - return { - hour: hourCounter, - minute: minuteCounter, - }; - }); + thing.setPropertyReadHandler("time", async () => { + return { + hour: hourCounter, + minute: minuteCounter, + }; + }); - timeCount(thing); - setInterval(async () => { - timeCount(thing); - thing.emitPropertyChange("time"); - }, 61000); // if this is 60s, we never leave the for loop + timeCount(thing); + setInterval(async () => { + timeCount(thing); + thing.emitPropertyChange("time"); + }, 61000); // if this is 60s, we never leave the for loop - // expose the thing - thing.expose().then(() => { - console.info(thing.getThingDescription().title + " ready"); - }); - }) - .catch((e) => { - console.log(e); - }); + // expose the thing + thing.expose().then(() => { + console.info(thing.getThingDescription().title + " ready"); + }); + }) + .catch((e) => { + console.log(e); + }); }); diff --git a/mashups/smart-home/tsconfig.json b/mashups/smart-home/tsconfig.json index f5e4a37..4150c64 100644 --- a/mashups/smart-home/tsconfig.json +++ b/mashups/smart-home/tsconfig.json @@ -1,14 +1,14 @@ { - "compilerOptions": { - "outDir": "dist", - "rootDir": "./", - "target": "ES2018", - "module": "commonjs", - "skipLibCheck": false, - "strict": true, - "sourceMap": false, - "esModuleInterop": true, - "removeComments": false - }, - "include": ["./mashup-logic.ts", "./things/**.ts"] + "compilerOptions": { + "outDir": "dist", + "rootDir": "./", + "target": "ES2018", + "module": "commonjs", + "skipLibCheck": false, + "strict": true, + "sourceMap": false, + "esModuleInterop": true, + "removeComments": false + }, + "include": ["./mashup-logic.ts", "./things/**.ts"] } diff --git a/package.json b/package.json index b510167..5bc8fb0 100644 --- a/package.json +++ b/package.json @@ -1,45 +1,45 @@ { - "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": "^3.3.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": "^3.3.3" + } } diff --git a/things/advanced-coffee-machine/advanced-coffee-machine.tm.json b/things/advanced-coffee-machine/advanced-coffee-machine.tm.json index 25be585..7891bb5 100644 --- a/things/advanced-coffee-machine/advanced-coffee-machine.tm.json +++ b/things/advanced-coffee-machine/advanced-coffee-machine.tm.json @@ -1,212 +1,212 @@ { - "@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}}", - "properties": { - "allAvailableResources": { - "type": "object", - "description": "Current level of all available resources given as an integer percentage for each particular resource.\\nThe data is obtained from the machine's sensors but can be set manually via the availableResourceLevel property in case the sensors are broken.", - "readOnly": true, - "properties": { - "water": { - "type": "integer", - "minimum": 0, - "maximum": 100 + "@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}}", + "properties": { + "allAvailableResources": { + "type": "object", + "description": "Current level of all available resources given as an integer percentage for each particular resource.\\nThe data is obtained from the machine's sensors but can be set manually via the availableResourceLevel property in case the sensors are broken.", + "readOnly": true, + "properties": { + "water": { + "type": "integer", + "minimum": 0, + "maximum": 100 + }, + "milk": { + "type": "integer", + "minimum": 0, + "maximum": 100 + }, + "chocolate": { + "type": "integer", + "minimum": 0, + "maximum": 100 + }, + "coffeeBeans": { + "type": "integer", + "minimum": 0, + "maximum": 100 + } + } }, - "milk": { - "type": "integer", - "minimum": 0, - "maximum": 100 + "availableResourceLevel": { + "type": "number", + "description": "Current level of a particular resource. Requires resource id variable as uriVariables.\\nThe property can also be overridden, which also requires resource id as uriVariables.", + "uriVariables": { + "id": { + "type": "string", + "enum": ["water", "milk", "chocolate", "coffeeBeans"] + } + } }, - "chocolate": { - "type": "integer", - "minimum": 0, - "maximum": 100 + "possibleDrinks": { + "type": "array", + "description": "The list of possible drinks in general. Doesn't depend on the available resources.", + "readOnly": true, + "items": { + "type": "string" + } }, - "coffeeBeans": { - "type": "integer", - "minimum": 0, - "maximum": 100 - } - } - }, - "availableResourceLevel": { - "type": "number", - "description": "Current level of a particular resource. Requires resource id variable as uriVariables.\\nThe property can also be overridden, which also requires resource id as uriVariables.", - "uriVariables": { - "id": { - "type": "string", - "enum": ["water", "milk", "chocolate", "coffeeBeans"] - } - } - }, - "possibleDrinks": { - "type": "array", - "description": "The list of possible drinks in general. Doesn't depend on the available resources.", - "readOnly": true, - "items": { - "type": "string" - } - }, - "servedCounter": { - "type": "integer", - "description": "The total number of served beverages.", - "minimum": 0 - }, - "maintenanceNeeded": { - "type": "boolean", - "description": "Shows whether a maintenance is needed. The property is observable. Automatically set to true when the servedCounter property exceeds 1000.", - "observable": true - }, - "schedules": { - "type": "array", - "description": "The list of scheduled tasks.", - "readOnly": true, - "items": { - "type": "object", - "properties": { - "drinkId": { - "type": "string", - "description": "Defines what drink to make, drinkId is one of possibleDrinks property values, e.g. latte." - }, - "size": { - "type": "string", - "description": "Defines the size of a drink, s = small, m = medium, l = large.", - "enum": ["s", "m", "l"] - }, - "quantity": { + "servedCounter": { "type": "integer", - "description": "Defines how many drinks to make, ranging from 1 to 5.", - "minimum": 1, - "maximum": 5 - }, - "time": { - "type": "string", - "description": "Defines the time of the scheduled task in 24h format, e.g. 10: 00 or 21: 00." - }, - "mode": { - "type": "string", - "description": "Defines the mode of the scheduled task, e.g. once or everyday. All the possible values are given in the enum field of this Thing Description.", - "enum": [ - "once", - "everyday", - "everyMo", - "everyTu", - "everyWe", - "everyTh", - "everyFr", - "everySat", - "everySun" - ] - } - } - } - } - }, - "actions": { - "makeDrink": { - "description": "Make a drink from available list of beverages. Accepts drink id, size and quantity as uriVariables.\\nBrews one medium americano if no uriVariables are specified.", - "uriVariables": { - "drinkId": { - "type": "string", - "description": "Defines what drink to make, drinkId is one of possibleDrinks property values, e.g. latte." + "description": "The total number of served beverages.", + "minimum": 0 }, - "size": { - "type": "string", - "description": "Defines the size of a drink, s = small, m = medium, l = large.", - "enum": ["s", "m", "l"] + "maintenanceNeeded": { + "type": "boolean", + "description": "Shows whether a maintenance is needed. The property is observable. Automatically set to true when the servedCounter property exceeds 1000.", + "observable": true }, - "quantity": { - "type": "integer", - "description": "Defines how many drinks to make, ranging from 1 to 5.", - "minimum": 1, - "maximum": 5 - } - }, - "output": { - "type": "object", - "description": "Returns true/false and a message when all invoked promises are resolved (asynchronous).", - "properties": { - "result": { - "type": "boolean" - }, - "message": { - "type": "string" - } + "schedules": { + "type": "array", + "description": "The list of scheduled tasks.", + "readOnly": true, + "items": { + "type": "object", + "properties": { + "drinkId": { + "type": "string", + "description": "Defines what drink to make, drinkId is one of possibleDrinks property values, e.g. latte." + }, + "size": { + "type": "string", + "description": "Defines the size of a drink, s = small, m = medium, l = large.", + "enum": ["s", "m", "l"] + }, + "quantity": { + "type": "integer", + "description": "Defines how many drinks to make, ranging from 1 to 5.", + "minimum": 1, + "maximum": 5 + }, + "time": { + "type": "string", + "description": "Defines the time of the scheduled task in 24h format, e.g. 10: 00 or 21: 00." + }, + "mode": { + "type": "string", + "description": "Defines the mode of the scheduled task, e.g. once or everyday. All the possible values are given in the enum field of this Thing Description.", + "enum": [ + "once", + "everyday", + "everyMo", + "everyTu", + "everyWe", + "everyTh", + "everyFr", + "everySat", + "everySun" + ] + } + } + } } - } }, - "setSchedule": { - "description": "Add a scheduled task to the schedules property. Accepts drink id, size, quantity, time and mode as body of a request.\\nAssumes one medium americano if not specified, but time and mode are mandatory fields.", - "input": { - "type": "object", - "properties": { - "drinkId": { - "type": "string", - "description": "Defines what drink to make, drinkId is one of possibleDrinks property values, e.g. latte." - }, - "size": { - "type": "string", - "description": "Defines the size of a drink, s = small, m = medium, l = large.", - "enum": ["s", "m", "l"] - }, - "quantity": { - "type": "integer", - "description": "Defines how many drinks to make, ranging from 1 to 5.", - "minimum": 1, - "maximum": 5 - }, - "time": { - "type": "string", - "description": "Defines the time of the scheduled task in 24h format, e.g. 10: 00 or 21: 00." - }, - "mode": { - "type": "string", - "description": "Defines the mode of the scheduled task, e.g. once or everyday. All the possible values are given in the enum field of this Thing Description.", - "enum": [ - "once", - "everyday", - "everyMo", - "everyTu", - "everyWe", - "everyTh", - "everyFr", - "everySat", - "everySun" - ] - } + "actions": { + "makeDrink": { + "description": "Make a drink from available list of beverages. Accepts drink id, size and quantity as uriVariables.\\nBrews one medium americano if no uriVariables are specified.", + "uriVariables": { + "drinkId": { + "type": "string", + "description": "Defines what drink to make, drinkId is one of possibleDrinks property values, e.g. latte." + }, + "size": { + "type": "string", + "description": "Defines the size of a drink, s = small, m = medium, l = large.", + "enum": ["s", "m", "l"] + }, + "quantity": { + "type": "integer", + "description": "Defines how many drinks to make, ranging from 1 to 5.", + "minimum": 1, + "maximum": 5 + } + }, + "output": { + "type": "object", + "description": "Returns true/false and a message when all invoked promises are resolved (asynchronous).", + "properties": { + "result": { + "type": "boolean" + }, + "message": { + "type": "string" + } + } + } }, - "required": ["time", "mode"] - }, - "output": { - "type": "object", - "description": "Returns true/false and a message when all invoked promises are resolved (asynchronous).", - "properties": { - "result": { - "type": "boolean" - }, - "message": { - "type": "string" - } + "setSchedule": { + "description": "Add a scheduled task to the schedules property. Accepts drink id, size, quantity, time and mode as body of a request.\\nAssumes one medium americano if not specified, but time and mode are mandatory fields.", + "input": { + "type": "object", + "properties": { + "drinkId": { + "type": "string", + "description": "Defines what drink to make, drinkId is one of possibleDrinks property values, e.g. latte." + }, + "size": { + "type": "string", + "description": "Defines the size of a drink, s = small, m = medium, l = large.", + "enum": ["s", "m", "l"] + }, + "quantity": { + "type": "integer", + "description": "Defines how many drinks to make, ranging from 1 to 5.", + "minimum": 1, + "maximum": 5 + }, + "time": { + "type": "string", + "description": "Defines the time of the scheduled task in 24h format, e.g. 10: 00 or 21: 00." + }, + "mode": { + "type": "string", + "description": "Defines the mode of the scheduled task, e.g. once or everyday. All the possible values are given in the enum field of this Thing Description.", + "enum": [ + "once", + "everyday", + "everyMo", + "everyTu", + "everyWe", + "everyTh", + "everyFr", + "everySat", + "everySun" + ] + } + }, + "required": ["time", "mode"] + }, + "output": { + "type": "object", + "description": "Returns true/false and a message when all invoked promises are resolved (asynchronous).", + "properties": { + "result": { + "type": "boolean" + }, + "message": { + "type": "string" + } + } + } + } + }, + "events": { + "outOfResource": { + "description": "Out of resource event. Emitted when the available resource level is not sufficient for a desired drink.", + "data": { + "type": "string" + } } - } - } - }, - "events": { - "outOfResource": { - "description": "Out of resource event. Emitted when the available resource level is not sufficient for a desired drink.", - "data": { - "type": "string" - } } - } } diff --git a/things/advanced-coffee-machine/http/ts/.mocharc.json b/things/advanced-coffee-machine/http/ts/.mocharc.json index 0b67134..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"] + "spec": "./test/**.test.ts", + "require": ["ts-node/register", "./test/fixtures.ts"] } diff --git a/things/advanced-coffee-machine/http/ts/package.json b/things/advanced-coffee-machine/http/ts/package.json index 5398b69..6e1c311 100644 --- a/things/advanced-coffee-machine/http/ts/package.json +++ b/things/advanced-coffee-machine/http/ts/package.json @@ -1,32 +1,32 @@ { - "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" - }, - "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" + }, + "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 65bdafd..93ba5ff 100644 --- a/things/advanced-coffee-machine/http/ts/src/main.ts +++ b/things/advanced-coffee-machine/http/ts/src/main.ts @@ -18,17 +18,20 @@ // An accompanying tutorial is available at http://www.thingweb.io/smart-coffee-machine.html. 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 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 != null && process.env.PORT !== "" ? parseInt(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; @@ -38,313 +41,343 @@ let schedules: unknown[]; let servedCounter: number; function readFromSensor(sensorType: string): number { - // Actual implementation of reading data from a sensor can go here - // For the sake of example, let's just return a value - return 100; + // Actual implementation of reading data from a sensor can go here + // For the sake of example, let's just return a value + return 100; } function notify(subscribers: unknown, msg: string) { - // Actual implementation of notifying subscribers with a message can go here - console.log(msg); + // Actual implementation of notifying subscribers with a message can go here + console.log(msg); } const { - values: { port }, + values: { port }, } = parseArgs({ - options: { - port: { - type: "string", - short: "p", + options: { + port: { + type: "string", + short: "p", + }, }, - }, }); if (port != null && !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); } -let thingModel +let thingModel; if (tmPath != null && tmPath !== "") { - thingModel = JSON.parse(fs.readFileSync(path.join(__dirname, tmPath)).toString()) + 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, + PROTOCOL: "http", + THING_NAME: thingName, + HOSTNAME: hostname, + PORT_NUMBER: portNumber, }); let thingDescription = placeholderReplacer.replace(thingModel); thingDescription = { - ...thingDescription, - '@type': 'Thing' -} + ...thingDescription, + "@type": "Thing", +}; const servient = new Servient(); servient.addServer( - new HttpServer({ - baseUri: `http://${hostname}:${portNumber}`, - port: portNumber, - }), + 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}`, - ); - } - }); - - // 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 ("id" in uriVariables) { - const id = uriVariables.id; - 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) => { - // Check if uriVariables are provided - if ( - options && - typeof options === "object" && - "uriVariables" in options - ) { - const uriVariables = options.uriVariables as Record; - 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< - 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`, + .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"), }; - } - } - - // 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!` }; - }); - - // 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); - - // 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); - - return { result: true, message: `Your schedule has been set!` }; - } - - 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}`); + 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"); + + // 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< + string, + string + >; + if ("id" in uriVariables) { + const id = uriVariables.id; + 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) => { + // Check if uriVariables are provided + 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< + 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; + + // 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< + 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; + + // Now add a new schedule + schedules.push(paramsp); + + return { + result: true, + message: `Your schedule has been set!`, + }; + } + + 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}`); + }); + }) + .catch((e: Error) => { + console.log(e); }); - }) - .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 c083f62..ae86698 100644 --- a/things/advanced-coffee-machine/http/ts/test/client.test.ts +++ b/things/advanced-coffee-machine/http/ts/test/client.test.ts @@ -11,7 +11,7 @@ * 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 chai from "chai"; import chaiAsPromised from "chai-as-promised"; import { Servient } from "@node-wot/core"; @@ -27,82 +27,85 @@ const port = 3000; 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); - } - }); - - it("should read allAvailableResources property", async () => { - const response = await thing.readProperty("allAvailableResources"); - const value = await response.value(); - expect(value).to.be.eql({ - water: 100, - milk: 100, - chocolate: 100, - coffeeBeans: 100, + 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); + } }); - }); - 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" }, + it("should read allAvailableResources property", async () => { + const response = await thing.readProperty("allAvailableResources"); + const value = await response.value(); + expect(value).to.be.eql({ + water: 100, + milk: 100, + chocolate: 100, + coffeeBeans: 100, + }); }); - 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; + 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 servedCounter = 1001; - await thing.writeProperty("servedCounter", servedCounter); - }); + it("should observe maintenanceNeeded", async () => { + await thing.observeProperty("maintenanceNeeded", async (data) => { + const value = await data.value(); + expect(value).to.be.true; + }); - it("should make 3 cups of latte", async () => { - const makeCoffee = await thing.invokeAction("makeDrink", undefined, { - uriVariables: { drinkId: "latte", size: "l", quantity: 3 }, + const servedCounter = 1001; + await thing.writeProperty("servedCounter", servedCounter); }); - const makeCoffeeValue = (await makeCoffee?.value()) as Record; - expect(makeCoffeeValue.result).to.be.not.null; - }); - it("should schedule a task", async () => { - const schedule = { - drinkId: "espresso", - size: "m", - 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); - }); + 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< + string, + unknown + >; + expect(makeCoffeeValue.result).to.be.not.null; + }); - it("should subscribe to outOfResource event", async () => { - await thing.subscribeEvent("outOfResource", async (data) => { - const value = await data.value(); - expect(value).to.be.not.null; + it("should schedule a task", async () => { + const schedule = { + drinkId: "espresso", + size: "m", + 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); }); - await thing.invokeAction("makeDrink", undefined, { - uriVariables: { drinkId: "latte", size: "l", quantity: 1000 }, + it("should subscribe to outOfResource event", async () => { + await thing.subscribeEvent("outOfResource", async (data) => { + 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 c910838..3e1af5f 100644 --- a/things/advanced-coffee-machine/http/ts/test/fixtures.ts +++ b/things/advanced-coffee-machine/http/ts/test/fixtures.ts @@ -22,20 +22,20 @@ let response: ThingStartResponse; const port = 3000; export async function mochaGlobalSetup() { - try { - response = await getInitiateMain( - path.join(__dirname, "..", "dist", "main.js"), - port, - ); - thingProcess = response.process; - } catch (error: unknown) { - console.log(error); - thingProcess = (error as ThingStartResponse).process; - } + try { + response = await getInitiateMain( + path.join(__dirname, "..", "dist", "main.js"), + port, + ); + thingProcess = response.process; + } catch (error: unknown) { + console.log(error); + thingProcess = (error as ThingStartResponse).process; + } } export function mochaGlobalTeardown() { - if (thingProcess) { - thingProcess.kill(); - } + if (thingProcess) { + thingProcess.kill(); + } } 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 8ef4960..749f0b3 100644 --- a/things/advanced-coffee-machine/http/ts/test/td.test.ts +++ b/things/advanced-coffee-machine/http/ts/test/td.test.ts @@ -24,38 +24,43 @@ const port = 3000; let validate: ValidateFunction | undefined; describe("TD Test", () => { - before(async () => { - const tdValidate = getTDValidate(); + before(async () => { + const tdValidate = getTDValidate(); - try { - const response = await Promise.all([tdValidate]); - validate = response[0].validate; - } catch (error) { - console.log(error); - } - }); + 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: http.IncomingMessage) { - const body: Buffer[] = []; - response.on("data", (chunk: Buffer) => { - body.push(chunk); - }); + 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); - } - }); - }, - ); - }); + 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 396527d..93aaf4a 100644 --- a/things/advanced-coffee-machine/http/ts/tsconfig.json +++ b/things/advanced-coffee-machine/http/ts/tsconfig.json @@ -1,8 +1,8 @@ { - "extends": "../../../..//tsconfig.json", - "compilerOptions": { - "outDir": "dist", - "rootDir": "src", - }, - "include": ["src/**/*"] + "extends": "../../../..//tsconfig.json", + "compilerOptions": { + "outDir": "dist", + "rootDir": "src" + }, + "include": ["src/**/*"] } diff --git a/things/advanced-coffee-machine/tm.test.js b/things/advanced-coffee-machine/tm.test.js index 2d33825..67889b9 100644 --- a/things/advanced-coffee-machine/tm.test.js +++ b/things/advanced-coffee-machine/tm.test.js @@ -7,29 +7,29 @@ const ajv = new Ajv({ strict: false, allErrors: true, validateFormats: false }); const expect = chai.expect; describe("Advanced Coffee Machine", () => { - let validate; + 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 108f07e..f16156e 100644 --- a/things/calculator/calculator.tm.json +++ b/things/calculator/calculator.tm.json @@ -1,61 +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}}" + "@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" + } }, - "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 + "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}}" + } }, - "subtract": { - "input": { - "type": "number" - }, - "output": { - "type": "number" - }, - "idempotent": false, - "safe": false - } - }, - "events": { - "update": { - "data": {} + "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 index b7de798..9aeadd6 100644 --- a/things/calculator/coap/client-tests/content-negotiation-coap-client.js +++ b/things/calculator/coap/client-tests/content-negotiation-coap-client.js @@ -5,11 +5,11 @@ 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`; + resultEndPoint = `/${thingName}/properties/result`, + lastChangeEndPoint = `/${thingName}/properties/lastChange`, + additionEndPoint = `/${thingName}/actions/add`, + subtractionEndPoint = `/${thingName}/actions/subtract`, + updateEndPoint = `/${thingName}/events/update`; /****************************************/ /****** Thing Description Endpoint ******/ @@ -17,36 +17,39 @@ const fullTDEndpoint = `/${thingName}`, // 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(); + 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(); } /****************************************/ @@ -55,31 +58,34 @@ function getFullTD(acceptType) { // 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(); + 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(); } /** @@ -87,38 +93,41 @@ function getResult(acceptType) { * 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}`); - } + 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(); + observeResult.end(); } /****************************************/ @@ -127,30 +136,33 @@ function observeResultProperty(acceptType) { // 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(); + 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(); } /** @@ -158,38 +170,41 @@ function getLastChange(acceptType) { * 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}`); - } + const observeLastChange = coap.request({ + method: "GET", + observe: true, + host: hostname, + port: portNumber, + pathname: lastChangeEndPoint, + headers: { + Accept: acceptType, + }, }); - }); - observeLastChange.end(); + 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(); } /****************************************/ @@ -198,42 +213,42 @@ function observeLastChangeProperty(acceptType) { // 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(); + 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(); } /****************************************/ @@ -242,42 +257,42 @@ function addNumber(acceptType, contentType, numberToAdd) { // 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(); + 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(); } /****************************************/ @@ -289,62 +304,62 @@ function subtractNumber(acceptType, contentType, numberToSubtract) { * 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}`); - } + 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(); + // 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); + //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/client-tests/node-wot-content-negotiation-coap-client.js b/things/calculator/coap/client-tests/node-wot-content-negotiation-coap-client.js index c517f13..3984c3f 100644 --- 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 @@ -6,51 +6,55 @@ 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); - }); + .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); + }); 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 index 9f8d8ef..8ebf38c 100644 --- a/things/calculator/coap/client-tests/node-wot-simple-coap-client.js +++ b/things/calculator/coap/client-tests/node-wot-simple-coap-client.js @@ -7,51 +7,51 @@ 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); - }); + .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); + }); diff --git a/things/calculator/coap/client-tests/simple-coap-client.js b/things/calculator/coap/client-tests/simple-coap-client.js index ea55d67..23b6577 100644 --- a/things/calculator/coap/client-tests/simple-coap-client.js +++ b/things/calculator/coap/client-tests/simple-coap-client.js @@ -4,35 +4,38 @@ 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`; + 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(); + // 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(); } // /****************************************/ @@ -40,25 +43,25 @@ function getThingDescription() { // /****************************************/ 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 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(); } /** @@ -67,31 +70,31 @@ function getResult() { */ 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()}`, - ); - } + 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(); + // Start observing + observeResult.end(); } /****************************************/ @@ -99,24 +102,24 @@ function observeResultProperty() { /****************************************/ 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 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(); } /** @@ -125,31 +128,31 @@ function getLastChange() { */ 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()}`, - ); - } + 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(); + // Start observing + observeLastChange.end(); } /****************************************/ @@ -157,30 +160,30 @@ function observeLastChangeProperty() { /****************************************/ 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(); + // 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(); } /****************************************/ @@ -188,30 +191,33 @@ function addNumber(numberToAdd) { /****************************************/ 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(); + // 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(); } /****************************************/ @@ -224,52 +230,52 @@ function subtractNumber(numberToSubtract) { */ 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()}`, - ); - } + 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(); + // 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); + 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 06256ac..0fa4869 100644 --- a/things/calculator/coap/js/coap-content-negotiation-calculator.js +++ b/things/calculator/coap/js/coap-content-negotiation-calculator.js @@ -12,7 +12,7 @@ * * SPDX-License-Identifier: EPL-2.0 OR W3C-20150513 ********************************************************************************/ - const { parseArgs } = require("node:util"); +const { parseArgs } = require("node:util"); const coap = require("coap"); const fs = require("fs"); const path = require("path"); @@ -26,36 +26,36 @@ let portNumber = Number(process.env.PORT ?? 5684); const thingName = "coap-calculator-content-negotiation"; 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: "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, }); /*****************************************/ @@ -67,155 +67,171 @@ thingDescription["@type"] = "Thing"; 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: { + 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); - - const newFormObs = JSON.parse(JSON.stringify(newFormRead)); - newFormObs.op = ["observeproperty", "unobserveproperty"]; - newFormObs.subprotocol = "cov:observe"; - thingDescription.properties[key].forms.push(newFormObs); + if (!Object.hasOwn(thingDescription.properties, key)) { + continue; + } + + 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); + 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) { - thingDescription.events[key].data.type = "number"; + if (!Object.hasOwn(thingDescription.events, key)) { + continue; + } + + 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 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); } /*********************************************************/ @@ -225,278 +241,293 @@ 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; + 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(); - } - } - } - - 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; + } else { + if (!segments[2]) { + if (req.method === "GET") { if ( - acceptHeaders.includes("application/json") || - acceptHeaders.includes("application/*") || - acceptHeaders === "*/*" + acceptHeaders.includes("application/json") || + acceptHeaders.includes("application/td+json") || + acceptHeaders.includes("application/*") || + acceptHeaders === "*/*" ) { - res.write(JSON.stringify(result)); - oldResult = result; + 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 { - const cborData = cbor.encode(result); - res.write(cborData); - oldResult = result; + 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); + 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); + } + } + } + // 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 { - const cborData = cbor.encode(lastChange); - res.write(cborData); - oldDate = lastChange; + 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); + res.statusCode = 406; + res.end(); } - } } else { - res.code = 404; - res.end(); + 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(); + 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 { - 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); - } + res.code = 415; + res.end(); } - } else { - res.code = 404; - res.end(); - } } else { - res.code = 406; - res.end(); + 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); - }); + 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.statusCode = 406; - res.end(); + 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"); + 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 1beb766..b462a75 100644 --- a/things/calculator/coap/js/coap-simple-calculator.js +++ b/things/calculator/coap/js/coap-simple-calculator.js @@ -12,7 +12,7 @@ * * SPDX-License-Identifier: EPL-2.0 OR W3C-20150513 ********************************************************************************/ - const { parseArgs } = require("node:util"); +const { parseArgs } = require("node:util"); const coap = require("coap"); const fs = require("fs"); const path = require("path"); @@ -25,36 +25,36 @@ let portNumber = Number(process.env.PORT ?? 5683); const thingName = "coap-calculator-simple"; 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: "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, }); /*****************************************/ @@ -65,60 +65,72 @@ const thingDescription = placeholderReplacer.replace(thingModel); thingDescription["@type"] = "Thing"; const defaultForm = { - href: "", - contentType: "application/json", - op: [], + href: "", + contentType: "application/json", + op: [], }; // add properties forms for (const key in thingDescription.properties) { - thingDescription.properties[key].forms = []; + if (!Object.hasOwn(thingDescription.properties, key)) { + continue; + } + + 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) { - thingDescription.actions[key].forms = []; + if (!Object.hasOwn(thingDescription.actions, key)) { + continue; + } + + 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) { - thingDescription.events[key].data.type = "number"; + if (!Object.hasOwn(thingDescription.events, key)) { + continue; + } + + 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 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); + console.log(err); } /*********************************************************/ @@ -129,175 +141,177 @@ let result = 0; let lastChange = new Date().toISOString(); server.on("request", (req, res) => { - const segments = req.url.split("/"); - - if (segments[1] !== thingName) { - 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 (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"); - - // Checking for the observe option - if (req.headers.Observe === 0) { - console.log("Observing the result property..."); - res.statusCode = 205; - - let oldResult = result; - const changeInterval = setInterval(() => { - if (oldResult !== result) { - res.write(`${JSON.stringify(result)}`); - oldResult = result; - } - }, 1000); - - 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 - else { - res.end(JSON.stringify(result)); - } - } else if (segments[3] === "lastChange") { - // setting the content type header to json - res.setOption("Content-Format", "application/json"); - - // Checking for the observe option - if (req.headers.Observe === 0) { - console.log("Observing the lastChange property..."); - res.statusCode = 205; - - let oldDate = lastChange; - const changeInterval = setInterval(() => { - if (oldDate !== lastChange) { - res.write(`${JSON.stringify(lastChange)}`); - oldDate = lastChange; - } - }, 1000); + const segments = req.url.split("/"); - 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 - else { - res.end(JSON.stringify(lastChange)); - } - } else { + if (segments[1] !== thingName) { res.code = 404; 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"); - - let inputNumber; - try { - inputNumber = JSON.parse(req.payload.toString()); - } catch (err) { - console.log(err) - res.code = 400; - res.end(); + 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 (typeof inputNumber !== "number") { - res.code = 400; - res.end(); + 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"); + + // Checking for the observe option + if (req.headers.Observe === 0) { + console.log("Observing the result property..."); + res.statusCode = 205; + + let oldResult = result; + const changeInterval = setInterval(() => { + if (oldResult !== result) { + res.write(`${JSON.stringify(result)}`); + oldResult = result; + } + }, 1000); + + 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 + else { + res.end(JSON.stringify(result)); + } + } else if (segments[3] === "lastChange") { + // setting the content type header to json + res.setOption("Content-Format", "application/json"); + + // Checking for the observe option + if (req.headers.Observe === 0) { + console.log("Observing the lastChange property..."); + res.statusCode = 205; + + let oldDate = lastChange; + const changeInterval = setInterval(() => { + if (oldDate !== lastChange) { + res.write(`${JSON.stringify(lastChange)}`); + oldDate = lastChange; + } + }, 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 + else { + res.end(JSON.stringify(lastChange)); + } + } else { + res.code = 404; + res.end(); + } } else { - 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"); - - let inputNumber; - try { - inputNumber = JSON.parse(req.payload.toString()); - } catch (err) { - console.log(err) - res.code = 400; - res.end(); + res.code = 405; + res.end(); } + } - if (typeof inputNumber !== "number") { - res.code = 400; - 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"); + + let inputNumber; + try { + inputNumber = JSON.parse(req.payload.toString()); + } catch (err) { + console.log(err); + res.code = 400; + res.end(); + } + + if (typeof inputNumber !== "number") { + res.code = 400; + res.end(); + } else { + 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"); + + let inputNumber; + try { + inputNumber = JSON.parse(req.payload.toString()); + } catch (err) { + console.log(err); + res.code = 400; + res.end(); + } + + if (typeof inputNumber !== "number") { + res.code = 400; + res.end(); + } else { + result -= inputNumber; + lastChange = new Date(); + res.end(JSON.stringify(result)); + } + } else { + res.code = 404; + res.end(); + } } else { - result -= inputNumber; - lastChange = new Date(); - res.end(JSON.stringify(result)); + res.code = 405; + res.end(); } - } else { - res.code = 404; - res.end(); - } - } else { - res.code = 405; - res.end(); } - } - - 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; - - // setting the content type header to json - res.setOption("Content-Format", "application/json"); - - let oldResult = result; - const changeInterval = setInterval(() => { - if (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(); - } - } else { - res.code = 404; - res.end(); + + 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; + + // setting the content type header to json + res.setOption("Content-Format", "application/json"); + + let oldResult = result; + const changeInterval = setInterval(() => { + if (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(); + } + } else { + res.code = 404; + res.end(); + } } - } }); server.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/coap/js/package.json b/things/calculator/coap/js/package.json index fa2f2cd..1ebd44f 100644 --- a/things/calculator/coap/js/package.json +++ b/things/calculator/coap/js/package.json @@ -1,24 +1,24 @@ { - "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" - }, - "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" + }, + "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/td.test.js b/things/calculator/coap/js/test/td.test.js index b205a51..1783c79 100644 --- a/things/calculator/coap/js/test/td.test.js +++ b/things/calculator/coap/js/test/td.test.js @@ -28,70 +28,74 @@ const port = 5683; let thingProcess; describe("Calculator CoAP JS", () => { - let validate; + let validate; - before(async () => { - const initiateMain = new Promise((resolve, reject) => { - thingProcess = spawn( - "node", - ["coap-simple-calculator.js", "-p", `${port}`], - { cwd: path.join(__dirname, "..") }, - ); - thingProcess.stdout.on("data", (data) => { - if (data.toString().trim() === "ThingIsReady") { - resolve("Success"); - } - }); - thingProcess.stderr.on("data", (data) => { - reject(new Error(`Error: ${data}`)); - }); - thingProcess.on("error", (error) => { - reject(new Error(`Error: ${error}`)); - }); - thingProcess.on("close", () => { - reject(new Error("Failed to initiate the main script.")); - }); - }); + before(async () => { + const initiateMain = new Promise((resolve, reject) => { + thingProcess = spawn( + "node", + ["coap-simple-calculator.js", "-p", `${port}`], + { cwd: path.join(__dirname, "..") }, + ); + thingProcess.stdout.on("data", (data) => { + if (data.toString().trim() === "ThingIsReady") { + resolve("Success"); + } + }); + thingProcess.stderr.on("data", (data) => { + reject(new Error(`Error: ${data}`)); + }); + thingProcess.on("error", (error) => { + reject(new Error(`Error: ${error}`)); + }); + thingProcess.on("close", () => { + reject(new Error("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) => { + const req = coap.request( + `coap://localhost:${port}/coap-calculator-simple`, + ); - it("should have a valid TD", (done) => { - const req = coap.request(`coap://localhost:${port}/coap-calculator-simple`); + req.on("response", (res) => { + const valid = validate(JSON.parse(res.payload.toString())); + 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(); - }); }); diff --git a/things/calculator/http/client-tests/content-negotiation-http-client.js b/things/calculator/http/client-tests/content-negotiation-http-client.js index 11ec216..b6e24a6 100644 --- a/things/calculator/http/client-tests/content-negotiation-http-client.js +++ b/things/calculator/http/client-tests/content-negotiation-http-client.js @@ -8,13 +8,13 @@ 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"; + 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 @@ -22,22 +22,22 @@ const url = "http://localhost:3000/http-express-calculator-content-negotiation", * @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; - } + 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; + } } /** @@ -46,55 +46,55 @@ async function getFullTD(acceptType) { * @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; - } + 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); + 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); } /** @@ -103,58 +103,58 @@ function listenToResultProperty(acceptType) { * @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; - } + 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); - }; + const lastChangeEventSource = new EventSource( + url + lastChangeEndPointObserve, + { + headers: { + Accept: acceptType, + }, + }, + ); - //Closing the event source after 6 seconds - setTimeout(() => { - lastChangeEventSource.close(); - console.log("- Closing lastChange Property SSE"); - }, 6000); + 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); } /** @@ -165,33 +165,33 @@ function listenToLastChangeProperty(acceptType) { * @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(); + 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 { - const buffer = await res.arrayBuffer(); - const decodedData = cbor.decode(buffer); - return decodedData; + throw new Error(await res.text()); } - } else { - throw new Error(await res.text()); - } } /** @@ -202,108 +202,114 @@ async function addNumber(number, contentType, acceptType) { * @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(); + 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 { - const buffer = await res.arrayBuffer(); - const decodedData = cbor.decode(buffer); - return decodedData; + throw new Error(await res.text()); } - } 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); + 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); - } + 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/client-tests/node-wot-content-negotiation-http-client.js b/things/calculator/http/client-tests/node-wot-content-negotiation-http-client.js index f2bf1c4..94ad5ef 100644 --- 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 @@ -5,44 +5,46 @@ 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); - }); + .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); + }); 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 index 34890e1..7c5051d 100644 --- a/things/calculator/http/client-tests/node-wot-simple-http-client.js +++ b/things/calculator/http/client-tests/node-wot-simple-http-client.js @@ -5,38 +5,38 @@ 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"]); + .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); }); - - //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); - }); diff --git a/things/calculator/http/client-tests/simple-http-client.js b/things/calculator/http/client-tests/simple-http-client.js index 83be242..f274b91 100644 --- a/things/calculator/http/client-tests/simple-http-client.js +++ b/things/calculator/http/client-tests/simple-http-client.js @@ -6,22 +6,22 @@ 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"; + 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); + const res = await fetch(url); - return res.json(); + return res.json(); } /** @@ -29,9 +29,9 @@ async function getFullTD() { * @returns result - Number */ async function getCurrentResult() { - const res = await fetch(url + resultEndPoint); + const res = await fetch(url + resultEndPoint); - return res.json(); + return res.json(); } /** @@ -39,21 +39,21 @@ async function getCurrentResult() { * Uncomment to test the SSE functionality. */ function listenToResult() { - const resultEventSource = new EventSource(url + resultEndPointObserve); + const resultEventSource = new EventSource(url + resultEndPointObserve); - resultEventSource.onmessage = (e) => { - console.log("Result SSE:", JSON.parse(e.data)); - }; + resultEventSource.onmessage = (e) => { + console.log("Result SSE:", JSON.parse(e.data)); + }; - resultEventSource.onerror = (error) => { - console.error("Error with Result SSE:", error); - }; + 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); + //Closing the event source after 6 seconds + setTimeout(() => { + resultEventSource.close(); + console.log("- Closing Result Property SSE"); + }, 6000); } /** @@ -61,9 +61,9 @@ function listenToResult() { * @returns lastChange - String */ async function getLatestChange() { - const res = await fetch(url + lastChangeEndPoint); + const res = await fetch(url + lastChangeEndPoint); - return res.json(); + return res.json(); } /** @@ -71,23 +71,23 @@ async function getLatestChange() { * 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); + 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); } /** @@ -96,15 +96,15 @@ function listenToLastChange() { * @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(); + const res = await fetch(url + additionEndPoint, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: number, + }); + + return res.json(); } /** @@ -113,69 +113,69 @@ async function addNumber(number) { * @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(); + 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); + // 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); - } + 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.js b/things/calculator/http/express/http-content-negotiation-calculator.js index 9205d38..3fe24ab 100644 --- a/things/calculator/http/express/http-content-negotiation-calculator.js +++ b/things/calculator/http/express/http-content-negotiation-calculator.js @@ -12,7 +12,7 @@ * * SPDX-License-Identifier: EPL-2.0 OR W3C-20150513 ********************************************************************************/ - const express = require("express"); +const express = require("express"); const fs = require("fs"); const path = require("path"); const bodyParser = require("body-parser"); @@ -29,76 +29,76 @@ 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 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, + TDEndPoint, + resultEndPoint, + resultEndPointObserve, + lastChangeEndPoint, + lastChangeEndPointObserve, + additionEndPoint, + subtractionEndPoint, + updateEndPoint, ]; let result = 0; let lastChange = new Date().toISOString(); 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 defaultForm = { - href: "", - contentType: "application/json", - response: { + href: "", contentType: "application/json", - }, - op: "", - "htv:methodName": "", - "htv:headers": [ - { - "@type": "htv:RequestHeader", - fieldValue: "application/json", - fieldName: "Accept", + response: { + contentType: "application/json", }, - ], + op: "", + "htv:methodName": "", + "htv:headers": [ + { + "@type": "htv:RequestHeader", + fieldValue: "application/json", + fieldName: "Accept", + }, + ], }; const thingDescription = placeholderReplacer.replace(thingModel); @@ -108,127 +108,137 @@ const supportedContentTypes = ["application/json", "application/cbor"]; // Adding headers to the Properties for (const key in thingDescription.properties) { - thingDescription.properties[key].forms = []; + if (!Object.hasOwn(thingDescription.properties, key)) { + continue; + } - const newFormRead = JSON.parse(JSON.stringify(defaultForm)); - newFormRead.href = `properties/${key}`; - newFormRead["htv:methodName"] = "GET"; - newFormRead.op = "readproperty"; - thingDescription.properties[key].forms.push(newFormRead); + thingDescription.properties[key].forms = []; - 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 newFormRead = JSON.parse(JSON.stringify(defaultForm)); + newFormRead.href = `properties/${key}`; + newFormRead["htv:methodName"] = "GET"; + newFormRead.op = "readproperty"; + thingDescription.properties[key].forms.push(newFormRead); - const originalForm = thingDescription.properties[key].forms[0]; + 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.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); - } - }); + 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); + } + }); } // Adding headers to the Actions for (const key in thingDescription.actions) { - thingDescription.actions[key].forms = []; + if (!Object.hasOwn(thingDescription.actions, key)) { + continue; + } - const newForm = JSON.parse(JSON.stringify(defaultForm)); - newForm.href = `actions/${key}`; - newForm["htv:methodName"] = "POST"; - newForm.op = "invokeaction"; + thingDescription.actions[key].forms = []; - thingDescription.actions[key].forms.push(newForm); + const newForm = JSON.parse(JSON.stringify(defaultForm)); + newForm.href = `actions/${key}`; + newForm["htv:methodName"] = "POST"; + newForm.op = "invokeaction"; - const originalForm = thingDescription.actions[key].forms[0]; + thingDescription.actions[key].forms.push(newForm); - 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); + const originalForm = thingDescription.actions[key].forms[0]; - supportedContentTypes.forEach((type) => { + supportedContentTypes.forEach((type) => { if ( - !thingDescription.actions[key].forms[0].response.contentType.includes(type) + !thingDescription.actions[key].forms[0].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); + 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 for (const key in thingDescription.events) { - thingDescription.events[key].data.type = "object"; + if (!Object.hasOwn(thingDescription.events, key)) { + continue; + } - thingDescription.events[key].forms = []; + thingDescription.events[key].data.type = "object"; - 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 = []; - thingDescription.events[key].forms.push(newForm); + const newForm = JSON.parse(JSON.stringify(defaultForm)); + newForm.href = `events/${key}`; + newForm["htv:methodName"] = "GET"; + newForm.op = "subscribeevent"; + newForm.subprotocol = "sse"; - const originalForm = thingDescription.events[key].forms[0]; + 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); - } - }); + 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].fieldValue = type; + thingDescription.events[key].forms.push(newForm); + } + }); } // 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); } /******************************************/ @@ -237,86 +247,82 @@ try { // 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 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"); + 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(); + next(); } - } else { - next(); - } }); // Use body-parser middleware to parse the request body @@ -330,128 +336,128 @@ app.use(bodyParser.raw({ type: "application/cbor" })); // Get full thing app.get(TDEndPoint, (req, res) => { - const jsonData = JSON.stringify(thingDescription); - res.setHeader("Content-Type", "application/json"); - res.send(jsonData); -}); - -// 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); + const jsonData = JSON.stringify(thingDescription); 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; +// Get current result +app.get(resultEndPoint, (req, res) => { + const acceptHeader = req.get("Accept"); - if ( + // JSON media type is utilized as the default for the wild card + 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 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); +}); - res.on("finish", () => { - clearInterval(changeInterval); - }); +// 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("close", () => { - console.log("Client stopped listening to result property"); - }); + res.on("finish", () => { + clearInterval(changeInterval); + }); + + 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; + const acceptHeader = req.get("Accept"); - if ( + 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 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); +}); + +// 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("finish", () => { - clearInterval(changeInterval); - }); + res.on("finish", () => { + clearInterval(changeInterval); + }); - res.on("close", () => { - console.log("Client stopped listening to lastChange property"); - }); + res.on("close", () => { + console.log("Client stopped listening to lastChange property"); + }); }); // /*****************************************************/ @@ -460,98 +466,102 @@ 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"); + 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"); + } } - } - - /** 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); + + /** 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 { - 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); + 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"); + 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); + + /** 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 { - result -= bodyInput; - lastChange = new Date(); - const cborData = cbor.encode(result); - res.setHeader("Content-Type", "application/cbor"); - res.send(cborData); + 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); + } } - } }); // /*****************************************************/ @@ -560,42 +570,42 @@ 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); + 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); - }); + res.on("finish", () => { + clearInterval(changeInterval); + }); - res.on("close", () => { - console.log("Client stopped listening to update event"); - }); + res.on("close", () => { + console.log("Client stopped listening to update event"); + }); }); /************************************************/ @@ -603,6 +613,6 @@ app.get(updateEndPoint, (req, res) => { /************************************************/ 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/http-simple-calculator.js b/things/calculator/http/express/http-simple-calculator.js index 4d35b5b..9a12e7d 100644 --- a/things/calculator/http/express/http-simple-calculator.js +++ b/things/calculator/http/express/http-simple-calculator.js @@ -12,7 +12,7 @@ * * SPDX-License-Identifier: EPL-2.0 OR W3C-20150513 ********************************************************************************/ - const express = require("express"); +const express = require("express"); const fs = require("fs"); const path = require("path"); const bodyParser = require("body-parser"); @@ -28,116 +28,128 @@ let portNumber = process.env.PORT ?? 3000; const thingName = "http-express-calculator-simple"; 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 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, + 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: [], + href: "", + contentType: "application/json", + op: [], }; // add properties forms for (const key in thingDescription.properties) { - thingDescription.properties[key].forms = []; + if (!Object.hasOwn(thingDescription.properties, key)) { + continue; + } + + 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) { - thingDescription.actions[key].forms = []; + if (!Object.hasOwn(thingDescription.actions, key)) { + continue; + } - const newForm = JSON.parse(JSON.stringify(defaultForm)); - newForm.href = `actions/${key}`; - newForm.op = ["invokeaction"]; + thingDescription.actions[key].forms = []; - thingDescription.actions[key].forms.push(newForm); + const newForm = JSON.parse(JSON.stringify(defaultForm)); + newForm.href = `actions/${key}`; + newForm.op = ["invokeaction"]; + + thingDescription.actions[key].forms.push(newForm); } // add events forms for (const key in thingDescription.events) { - thingDescription.events[key].data.type = "object"; + if (!Object.hasOwn(thingDescription.events, key)) { + continue; + } + + 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 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: "*/*" }); @@ -151,42 +163,42 @@ let lastChange = new Date().toISOString(); // 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 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"); + } } - } }); /******************************************/ @@ -194,134 +206,136 @@ app.use((req, res, next) => { /******************************************/ app.get(TDEndPoint, (req, res) => { - res.json(thingDescription); + res.json(thingDescription); }); app.get(resultEndPoint, (req, res) => { - res.json(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"); - let oldResult = result; - - const changeInterval = setInterval(() => { - if (oldResult !== result) { - res.write(`data: ${JSON.stringify(result)}\n\n`); - oldResult = result; - } - }, 1000); - - res.on("finish", () => { - clearInterval(changeInterval); - }); - - res.on("close", () => { - console.log("Client stopped listening to result 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 result property"); + let oldResult = result; + + const changeInterval = setInterval(() => { + if (oldResult !== result) { + res.write(`data: ${JSON.stringify(result)}\n\n`); + oldResult = result; + } + }, 1000); + + res.on("finish", () => { + clearInterval(changeInterval); + }); + + res.on("close", () => { + console.log("Client stopped listening to result property"); + }); }); app.get(lastChangeEndPoint, (req, res) => { - res.json(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"); - let oldLastChange = lastChange; - - const changeInterval = setInterval(() => { - if (oldLastChange !== lastChange) { - res.write(`data: ${JSON.stringify(lastChange)}\n\n`); - oldLastChange = lastChange; - } - }, 1000); - - res.on("finish", () => { - clearInterval(changeInterval); - }); - - res.on("close", () => { - 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"); + let oldLastChange = lastChange; + + const changeInterval = setInterval(() => { + if (oldLastChange !== lastChange) { + res.write(`data: ${JSON.stringify(lastChange)}\n\n`); + oldLastChange = lastChange; + } + }, 1000); + + res.on("finish", () => { + clearInterval(changeInterval); + }); + + res.on("close", () => { + 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 { - result += bodyInput; - lastChange = new Date(); - 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 { + result += bodyInput; + lastChange = new Date(); + 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 { - result -= bodyInput; - lastChange = new Date(); - 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 { + result -= bodyInput; + lastChange = new Date(); + 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"); - - /** - * 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) { - res.write(`data: ${result}\n\n`); - oldResult = result; - } - }, 1000); - - res.on("finish", () => { - clearInterval(changeInterval); - }); - - res.on("close", () => { - console.log("Client stopped listening to update event"); - }); + 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"); + + /** + * 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) { + res.write(`data: ${result}\n\n`); + oldResult = result; + } + }, 1000); + + res.on("finish", () => { + clearInterval(changeInterval); + }); + + res.on("close", () => { + console.log("Client stopped listening to update event"); + }); }); 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 7027397..acf2484 100644 --- a/things/calculator/http/express/package.json +++ b/things/calculator/http/express/package.json @@ -1,30 +1,30 @@ { - "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" - }, - "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" - }, - "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" + }, + "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" + }, + "devDependencies": { + "@types/express": "^4.17.17" + } } diff --git a/things/calculator/http/express/test/td.test.js b/things/calculator/http/express/test/td.test.js index f8ce405..d83e5b5 100644 --- a/things/calculator/http/express/test/td.test.js +++ b/things/calculator/http/express/test/td.test.js @@ -12,7 +12,7 @@ * * SPDX-License-Identifier: EPL-2.0 OR W3C-20150513 ********************************************************************************/ - const Ajv = require("ajv"); +const Ajv = require("ajv"); const chai = require("chai"); const http = require("http"); const https = require("https"); @@ -27,81 +27,85 @@ const port = 3000; let thingProcess; describe("Calculator HTTP JS", () => { - let validate; + let validate; - before(async () => { - const initiateMain = new Promise(async (resolve, reject) => { - thingProcess = spawn( - "node", - ["http-simple-calculator.js", "-p", `${port}`], - { cwd: path.join(__dirname, "..") }, - ); - thingProcess.stdout.on("data", (data) => { - if (data.toString().includes("ThingIsReady")) { - resolve("Success"); - } - }); - thingProcess.stderr.on("data", (data) => { - reject(`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((resolve, reject) => { + thingProcess = spawn( + "node", + ["http-simple-calculator.js", "-p", `${port}`], + { cwd: path.join(__dirname, "..") }, + ); + thingProcess.stdout.on("data", (data) => { + if (data.toString().includes("ThingIsReady")) { + resolve("Success"); + } + }); + thingProcess.stderr.on("data", (data) => { + reject(new Error(`Error: ${data}`)); + }); + thingProcess.on("error", (error) => { + reject(new Error(`Error: ${error}`)); + }); + thingProcess.on("close", () => { + reject(new Error("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) => { - http.get( - `http://localhost:${port}/http-express-calculator-simple`, - function (response) { - const body = []; - response.on("data", (chunk) => { - body.push(chunk); - }); + it("should have a valid TD", (done) => { + http.get( + `http://localhost:${port}/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); - } - }); - }, - ); - }); + 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 bed60c7..7e4a114 100644 --- a/things/calculator/http/flask/test/td.test.js +++ b/things/calculator/http/flask/test/td.test.js @@ -13,78 +13,82 @@ const port = 5000; let thingProcess; describe("Calculator HTTP Flask", () => { - let validate; + 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/main.js b/things/calculator/mqtt/js/main.js index 1acb034..1953e9d 100644 --- a/things/calculator/mqtt/js/main.js +++ b/things/calculator/mqtt/js/main.js @@ -12,7 +12,7 @@ * * SPDX-License-Identifier: EPL-2.0 OR W3C-20150513 ********************************************************************************/ - const mqtt = require("mqtt"); +const mqtt = require("mqtt"); const { parseArgs } = require("node:util"); const fs = require("fs"); const path = require("path"); @@ -23,18 +23,18 @@ const brokerURI = process.env.BROKER_URI ?? "test.mosquitto.org"; let portNumber = process.env.PORT ?? 1883; 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 thingName = "mqtt-calculator"; @@ -47,118 +47,127 @@ 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); + 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: "mqtt", - THING_NAME: thingName, - PROPERTIES, - ACTIONS, - EVENTS, - HOSTNAME: brokerURI, - PORT_NUMBER: portNumber, - RESULT_OBSERVABLE: true, - LAST_CHANGE_OBSERVABLE: true, + 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: [], + href: "", + contentType: "application/json", + op: [], }; // add properties forms for (const key in thingDescription.properties) { - thingDescription.properties[key].forms = []; + if (!Object.hasOwn(thingDescription.properties, key)) { + continue; + } + + 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) { - thingDescription.actions[key].forms = []; + if (!Object.hasOwn(thingDescription.actions, key)) { + continue; + } - const newForm = JSON.parse(JSON.stringify(defaultForm)); - newForm.href = `actions/${key}`; - newForm.op = ["invokeaction"]; + thingDescription.actions[key].forms = []; - thingDescription.actions[key].forms.push(newForm); + const newForm = JSON.parse(JSON.stringify(defaultForm)); + newForm.href = `actions/${key}`; + newForm.op = ["invokeaction"]; + + thingDescription.actions[key].forms.push(newForm); } // add events forms for (const key in thingDescription.events) { - thingDescription.events[key].data.type = "string"; + if (!Object.hasOwn(thingDescription.events, key)) { + continue; + } + + 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); } broker.on("connect", () => { - console.log(`Connected to broker via port ${portNumber}`); + console.log(`Connected to broker via port ${portNumber}`); }); let result = 0; let lastChange = ""; broker.on("message", (topic, payload, packet) => { - console.log(`Messaged to the topic ${topic}`); - const segments = topic.split("/"); + console.log(`Messaged to the topic ${topic}`); + 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}`); + if (segments[0] !== thingName) { + return; } - if (segments.length === 3 && segments[2] === "lastChange") { - console.log(`Last change : ${lastChange}`); + if (segments[1] === PROPERTIES) { + if (segments.length === 3 && segments[2] === "result") { + console.log(`Result is : ${result}`); + } + + if (segments.length === 3 && segments[2] === "lastChange") { + console.log(`Last change : ${lastChange}`); + } } - } - if (segments[1] === ACTIONS) { - if (segments.length === 3 && segments[2] === "add") { - const parsedValue = parseInt(payload.toString()); + if (segments[1] === ACTIONS) { + if (segments.length === 3 && segments[2] === "add") { + const parsedValue = parseInt(payload.toString()); - if (isNaN(parsedValue)) { - return; - } else { - result += parsedValue; - lastChange = new Date().toLocaleTimeString(); - } - } + if (!isNaN(parsedValue)) { + result += parsedValue; + lastChange = new Date().toLocaleTimeString(); + } + } - if (segments.length === 3 && segments[2] === "subtract") { - const parsedValue = parseInt(payload.toString()); + if (segments.length === 3 && segments[2] === "subtract") { + const parsedValue = parseInt(payload.toString()); - if (isNaN(parsedValue)) { - } else { - result -= parsedValue; - lastChange = new Date().toLocaleTimeString(); - } + if (!isNaN(parsedValue)) { + result -= parsedValue; + lastChange = new Date().toLocaleTimeString(); + } + } } - } }); setInterval(() => { - broker.publish(`${thingName}/${EVENTS}/update`, "Updated the thing!"); + broker.publish(`${thingName}/${EVENTS}/update`, "Updated the thing!"); }, 500); broker.subscribe(`${thingName}/${PROPERTIES}/result`); @@ -166,6 +175,6 @@ broker.subscribe(`${thingName}/${PROPERTIES}/lastChange`); broker.subscribe(`${thingName}/${ACTIONS}/add`); broker.subscribe(`${thingName}/${ACTIONS}/subtract`); broker.publish(`${thingName}`, JSON.stringify(thingDescription), { - retain: true, + retain: true, }); console.log("ThingIsReady"); diff --git a/things/calculator/mqtt/js/package.json b/things/calculator/mqtt/js/package.json index 6867a04..2f9c0a7 100644 --- a/things/calculator/mqtt/js/package.json +++ b/things/calculator/mqtt/js/package.json @@ -1,23 +1,23 @@ { - "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" - }, - "author": "Eclipse Thingweb (https://thingweb.io/)", - "license": "EPL-2.0 OR W3C-20150513", - "dependencies": { - "json-placeholder-replacer": "^1.0.35", - "dotenv": "^16.3.1", - "mqtt": "^4.3.7" - } + "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" + }, + "author": "Eclipse Thingweb (https://thingweb.io/)", + "license": "EPL-2.0 OR W3C-20150513", + "dependencies": { + "json-placeholder-replacer": "^1.0.35", + "dotenv": "^16.3.1", + "mqtt": "^4.3.7" + } } diff --git a/things/calculator/mqtt/js/test/td.test.js b/things/calculator/mqtt/js/test/td.test.js index 4a24e4f..276b738 100644 --- a/things/calculator/mqtt/js/test/td.test.js +++ b/things/calculator/mqtt/js/test/td.test.js @@ -12,7 +12,7 @@ * * SPDX-License-Identifier: EPL-2.0 OR W3C-20150513 ********************************************************************************/ - const Ajv = require("ajv"); +const Ajv = require("ajv"); const chai = require("chai"); const https = require("https"); const mqtt = require("mqtt"); @@ -28,73 +28,75 @@ const port = 1883; let thingProcess; describe("Calculator MQTT JS", () => { - let validate; + let validate; - before(async () => { - const initiateMain = new Promise(async (resolve, reject) => { - thingProcess = spawn("node", ["main.js", "-p", `${port}`], { - cwd: path.join(__dirname, ".."), - }); - thingProcess.stdout.on("data", (data) => { - if (data.toString().trim() === "ThingIsReady") { - resolve("Success"); - } - }); - thingProcess.stderr.on("data", (data) => { - reject(`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((resolve, reject) => { + thingProcess = spawn("node", ["main.js", "-p", `${port}`], { + cwd: path.join(__dirname, ".."), + }); + thingProcess.stdout.on("data", (data) => { + if (data.toString().trim() === "ThingIsReady") { + resolve("Success"); + } + }); + thingProcess.stderr.on("data", (data) => { + reject(new Error(`Error: ${data}`)); + }); + thingProcess.on("error", (error) => { + reject(new Error(`Error: ${error}`)); + }); + thingProcess.on("close", () => { + reject(new Error("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) => { - const broker = mqtt.connect(`mqtt://${hostname}`, { port }); - broker.subscribe("mqtt-calculator"); + it("should have a valid TD", (done) => { + const broker = mqtt.connect(`mqtt://${hostname}`, { port }); + broker.subscribe("mqtt-calculator"); - let valid = false; + let valid = false; - broker.on("message", (topic, payload, packet) => { - valid = validate(JSON.parse(payload.toString())); - broker.end(); - }); + broker.on("message", (topic, payload, packet) => { + valid = validate(JSON.parse(payload.toString())); + broker.end(); + }); - broker.on("close", () => { - expect(valid).to.be.true; - done(); + broker.on("close", () => { + expect(valid).to.be.true; + done(); + }); }); - }); }); diff --git a/things/calculator/tm.test.js b/things/calculator/tm.test.js index 5b2e255..e2fb610 100644 --- a/things/calculator/tm.test.js +++ b/things/calculator/tm.test.js @@ -7,29 +7,29 @@ const ajv = new Ajv({ strict: false, allErrors: true, validateFormats: false }); const expect = chai.expect; describe("Calculator", () => { - let validate; + 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 2f5c7c9..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 0b67134..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"] + "spec": "./test/**.test.ts", + "require": ["ts-node/register", "./test/fixtures.ts"] } diff --git a/things/data-schema-thing/http/ts/package.json b/things/data-schema-thing/http/ts/package.json index dc45bd0..b03b29c 100644 --- a/things/data-schema-thing/http/ts/package.json +++ b/things/data-schema-thing/http/ts/package.json @@ -1,31 +1,31 @@ { - "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" - }, - "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" + }, + "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 c787b9b..be677be 100644 --- a/things/data-schema-thing/http/ts/src/main.ts +++ b/things/data-schema-thing/http/ts/src/main.ts @@ -13,79 +13,86 @@ * SPDX-License-Identifier: EPL-2.0 OR W3C-20150513 ********************************************************************************/ -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() +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 != null && process.env.PORT !== "" ? parseInt(process.env.PORT) : 3000; +let portNumber = + process.env.PORT != null && process.env.PORT !== "" + ? parseInt(process.env.PORT) + : 3000; const thingName = "http-data-schema-thing"; function checkPropertyWrite(expected: string, actual: unknown) { - const output = "Property " + expected + " written with " + actual; - if (expected === actual) { - console.info("PASS: " + output); - } else { - throw new Error("FAIL: " + output); - } + const output = "Property " + expected + " written with " + actual; + if (expected === actual) { + console.info("PASS: " + output); + } else { + throw new Error("FAIL: " + output); + } } function checkActionInvocation( - name: string, - expected: string, - actual: unknown, + name: string, + expected: string, + actual: unknown, ) { - const output = "Action " + name + " invoked with " + actual; - if (expected === actual) { - console.info("PASS: " + output); - } else { - throw new Error("FAIL: " + output); - } + const output = "Action " + name + " invoked with " + actual; + if (expected === actual) { + console.info("PASS: " + output); + } else { + throw new Error("FAIL: " + output); + } } -const { values: { port } } = parseArgs({ - options: { - port: { - type: "string", - short: "p", +const { + values: { port }, +} = parseArgs({ + options: { + port: { + type: "string", + short: "p", + }, }, - }, }); if (port != null && !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); } -let thingModel +let thingModel; if (tmPath != null && tmPath !== "") { - thingModel = JSON.parse(fs.readFileSync(path.join(__dirname, tmPath)).toString()); -} + 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, + PROTOCOL: "http", + THING_NAME: thingName, + HOSTNAME: hostname, + PORT_NUMBER: portNumber, }); let thingDescription = placeholderReplacer.replace(thingModel); thingDescription = { - ...thingDescription, - '@type': 'Thing' -} + ...thingDescription, + "@type": "Thing", +}; // init property values let bool = false; @@ -97,164 +104,185 @@ let object: Record = { id: 123, name: "abc" }; const servient = new Servient(); servient.addServer( - new HttpServer({ - baseUri: `http://${hostname}:${portNumber}`, - port: portNumber, - }), + 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); + 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); - bool = 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); - } - int = 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); - num = 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); - string = 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); - } - array = 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); - } - object = localObject as Record; - thing.emitEvent("on-object", object); - }) - .setPropertyReadHandler("object", async () => object); + // set property read/write handlers + thing + .setPropertyWriteHandler("bool", async (value) => { + const localBool = await value.value(); + checkPropertyWrite("boolean", typeof localBool); + bool = 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); + } + int = 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); + num = 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); + string = 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); + } + array = 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); + } + object = localObject as Record; + thing.emitEvent("on-object", object); + }) + .setPropertyReadHandler("object", async () => object); - // set action handlers - thing - .setActionHandler("void-void", async (parameters) => { - checkActionInvocation( - "void-void", - "undefined", - typeof (await parameters.value()), - ); - return undefined; - }) - .setActionHandler("void-int", async (parameters) => { - checkActionInvocation( - "void-int", - "undefined", - typeof (await parameters.value()), - ); - return 0; - }) - .setActionHandler("int-void", async (parameters) => { - const localParameters = await parameters.value(); - 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(); - if (localParameters === Math.floor(localParameters as number)) { - checkActionInvocation("int-int", "integer", "integer"); - } else { - checkActionInvocation("int-int", "integer", typeof localParameters); - } - return (localParameters as number) + 1; - }) - .setActionHandler("int-string", async (parameters) => { - const localParameters = await parameters.value(); - const inputtype = typeof localParameters; - if (localParameters === Math.floor(localParameters as number)) { - checkActionInvocation("int-string", "integer", "integer"); - } else { - checkActionInvocation( - "int-string", - "integer", - typeof localParameters, - ); - } + // set action handlers + thing + .setActionHandler("void-void", async (parameters) => { + checkActionInvocation( + "void-void", + "undefined", + typeof (await parameters.value()), + ); + return undefined; + }) + .setActionHandler("void-int", async (parameters) => { + checkActionInvocation( + "void-int", + "undefined", + typeof (await parameters.value()), + ); + return 0; + }) + .setActionHandler("int-void", async (parameters) => { + const localParameters = await parameters.value(); + 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(); + if ( + localParameters === + Math.floor(localParameters as number) + ) { + checkActionInvocation("int-int", "integer", "integer"); + } else { + checkActionInvocation( + "int-int", + "integer", + typeof localParameters, + ); + } + return (localParameters as number) + 1; + }) + .setActionHandler("int-string", async (parameters) => { + const localParameters = await parameters.value(); + const inputtype = typeof localParameters; + if ( + localParameters === + Math.floor(localParameters as number) + ) { + checkActionInvocation( + "int-string", + "integer", + "integer", + ); + } else { + checkActionInvocation( + "int-string", + "integer", + typeof localParameters, + ); + } - if (inputtype === "number") { - // eslint-disable-next-line no-new-wrappers - return 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"); - } - }) - .setActionHandler("void-obj", async (parameters) => { - checkActionInvocation( - "void-complex", - "undefined", - typeof (await parameters.value()), - ); - return { prop1: 123, prop2: "abc" }; + if (inputtype === "number") { + // eslint-disable-next-line no-new-wrappers + return 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"); + } + }) + .setActionHandler("void-obj", async (parameters) => { + checkActionInvocation( + "void-complex", + "undefined", + typeof (await parameters.value()), + ); + return { prop1: 123, prop2: "abc" }; + }) + .setActionHandler("obj-void", async (parameters) => { + checkActionInvocation( + "complex-void", + "object", + typeof (await parameters.value()), + ); + return undefined; + }); + + // expose the thing + thing.expose().then(async () => { + console.info(thing.getThingDescription().title + " ready"); + console.log("ThingIsReady"); + }); }) - .setActionHandler("obj-void", async (parameters) => { - checkActionInvocation( - "complex-void", - "object", - typeof (await parameters.value()), - ); - return undefined; + .catch((e: Error) => { + console.log(e); }); - - // 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 5ffe299..265a1f5 100644 --- a/things/data-schema-thing/http/ts/test/client.test.ts +++ b/things/data-schema-thing/http/ts/test/client.test.ts @@ -12,7 +12,7 @@ * * SPDX-License-Identifier: EPL-2.0 OR W3C-20150513 ********************************************************************************/ - import chai from "chai"; +import chai from "chai"; import chaiAsPromised from "chai-as-promised"; import { Servient } from "@node-wot/core"; @@ -28,173 +28,179 @@ const port = 3000; let thing: WoT.ConsumedThing; 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}`); - } -}; - -describe("Client Tests", () => { - before(async () => { + thing: WoT.ConsumedThing, + name: string, +): Promise => { 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); + const res = await thing.readProperty(name); + const value = await res.value(); + return value; } catch (error) { - console.error(error); + console.error(`Error: ${error}`); + return null; } - }); - - describe("bool property", () => { - it("should read property bool", async () => { - 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; - }); - - it("should write value false", async () => { - 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; - }); - }); - - describe("int property", () => { - it("should read property int", async () => { - 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); - }); - - it("should fail to write value 3.1415", async () => { - 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; - }); - }); - - describe("num property", () => { - it("should read property num", async () => { - 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); - }); - - 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); - }); - - it("should fail to write string value 'Pi'", async () => { - 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"); - }); - - 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); - }); - - it("should fail to write value 13", async () => { - 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; - }); - }); - - describe("array property", () => { - it("should read property array", async () => { - 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); - }); - - it("should fail to write object value", async () => { - 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; - }); - }); - - describe("object property", () => { - it("should read property object", async () => { - 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); - }); - - it("should fail to write value null", async () => { - 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; +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); + } + }); + + describe("bool property", () => { + it("should read property bool", async () => { + 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; + }); + + it("should write value false", async () => { + 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; + }); + }); + + describe("int property", () => { + it("should read property int", async () => { + 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); + }); + + it("should fail to write value 3.1415", async () => { + 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; + }); + }); + + describe("num property", () => { + it("should read property num", async () => { + 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); + }); + + 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); + }); + + it("should fail to write string value 'Pi'", async () => { + 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"); + }); + + 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); + }); + + it("should fail to write value 13", async () => { + 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; + }); + }); + + describe("array property", () => { + it("should read property array", async () => { + 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); + }); + + it("should fail to write object value", async () => { + 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; + }); + }); + + describe("object property", () => { + it("should read property object", async () => { + 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); + }); + + it("should fail to write value null", async () => { + 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; + }); }); - }); }); diff --git a/things/data-schema-thing/http/ts/test/fixtures.ts b/things/data-schema-thing/http/ts/test/fixtures.ts index 9c4122b..9740f64 100644 --- a/things/data-schema-thing/http/ts/test/fixtures.ts +++ b/things/data-schema-thing/http/ts/test/fixtures.ts @@ -12,7 +12,7 @@ * * SPDX-License-Identifier: EPL-2.0 OR W3C-20150513 ********************************************************************************/ - import { ChildProcess } from "child_process"; +import { ChildProcess } from "child_process"; import { getInitiateMain, ThingStartResponse } from "../../../../../util/util"; import path from "path"; @@ -21,20 +21,20 @@ let response: ThingStartResponse; const port = 3000; export async function mochaGlobalSetup() { - try { - response = await getInitiateMain( - path.join(__dirname, "..", "dist", "main.js"), - port, - ); - } catch (error) { - console.log(error); - } finally { - thingProcess = response.process; - } + try { + response = await getInitiateMain( + path.join(__dirname, "..", "dist", "main.js"), + port, + ); + } catch (error) { + console.log(error); + } finally { + thingProcess = response.process; + } } export function mochaGlobalTeardown() { - if (thingProcess) { - thingProcess.kill(); - } + if (thingProcess) { + thingProcess.kill(); + } } 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 e358873..d79cb2f 100644 --- a/things/data-schema-thing/http/ts/test/td.test.ts +++ b/things/data-schema-thing/http/ts/test/td.test.ts @@ -12,7 +12,7 @@ * * SPDX-License-Identifier: EPL-2.0 OR W3C-20150513 ********************************************************************************/ - import * as chai from "chai"; +import * as chai from "chai"; import * as http from "http"; import { getTDValidate } from "../../../../../util/util"; import { ValidateFunction } from "ajv"; @@ -23,38 +23,43 @@ const port = 3000; let validate: ValidateFunction | undefined; describe("TD Test", () => { - before(async () => { - const tdValidate = getTDValidate(); + before(async () => { + const tdValidate = getTDValidate(); - try { - const response = await Promise.all([tdValidate]); - validate = response[0].validate; - } catch (error) { - console.log(error); - } - }); + 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); - }); + 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); - } - }); - }, - ); - }); + 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 8bc0fe4..93aaf4a 100644 --- a/things/data-schema-thing/http/ts/tsconfig.json +++ b/things/data-schema-thing/http/ts/tsconfig.json @@ -1,8 +1,8 @@ { - "extends": "../../../..//tsconfig.json", - "compilerOptions": { - "outDir": "dist", - "rootDir": "src", - }, - "include": ["src/**/*"] -} \ No newline at end of file + "extends": "../../../..//tsconfig.json", + "compilerOptions": { + "outDir": "dist", + "rootDir": "src" + }, + "include": ["src/**/*"] +} diff --git a/things/data-schema-thing/tm.test.js b/things/data-schema-thing/tm.test.js index dbca9e6..3d95e70 100644 --- a/things/data-schema-thing/tm.test.js +++ b/things/data-schema-thing/tm.test.js @@ -7,29 +7,29 @@ const ajv = new Ajv({ strict: false, allErrors: true, validateFormats: false }); const expect = chai.expect; describe("Test Thing", () => { - let validate; + 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 d614784..43e6554 100644 --- a/things/elevator/elevator.tm.json +++ b/things/elevator/elevator.tm.json @@ -1,41 +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 + "@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" + } }, - "floorNumber": { - "type": "integer", - "minimum": 0, - "maximum": 15, - "readOnly": false, - "writeOnly": false, - "observable": false + "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/main.js b/things/elevator/modbus/js/main.js index 945e74d..e066d1b 100644 --- a/things/elevator/modbus/js/main.js +++ b/things/elevator/modbus/js/main.js @@ -12,7 +12,7 @@ * * SPDX-License-Identifier: EPL-2.0 OR W3C-20150513 ********************************************************************************/ - const { ServerTCP } = require("modbus-serial"); +const { ServerTCP } = require("modbus-serial"); const fs = require("fs"); const path = require("path"); const { JsonPlaceholderReplacer } = require("json-placeholder-replacer"); @@ -22,43 +22,43 @@ 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"; + ? 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 }, + 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: "modbus+tcp", - THING_NAME: thingName, - HOSTNAME: hostname, - PORT_NUMBER: portNumber, - UNIT_ID: thingUnitID, + PROTOCOL: "modbus+tcp", + THING_NAME: thingName, + HOSTNAME: hostname, + PORT_NUMBER: portNumber, + UNIT_ID: thingUnitID, }); const thingDescription = placeholderReplacer.replace(thingModel); thingDescription["@type"] = "Thing"; @@ -68,20 +68,20 @@ const discreteInputs = new Array(9999); const holdingRegisters = new Array(9999); const lightSwitchForms = [ - { - href: `?address=1&quantity=1`, - op: "readproperty", - "modbus:entity": "Coil", - "modbus:function": "readCoil", - contentType: "application/octet-stream", - }, - { - href: `?address=1&quantity=1`, - op: "writeproperty", - "modbus:entity": "Coil", - "modbus:function": "writeSingleCoil", - contentType: "application/octet-stream", - }, + { + href: `?address=1&quantity=1`, + op: "readproperty", + "modbus:entity": "Coil", + "modbus:function": "readCoil", + contentType: "application/octet-stream", + }, + { + href: `?address=1&quantity=1`, + op: "writeproperty", + "modbus:entity": "Coil", + "modbus:function": "writeSingleCoil", + contentType: "application/octet-stream", + }, ]; thingDescription.properties.lightSwitch.forms = lightSwitchForms; @@ -89,14 +89,14 @@ thingDescription.properties.lightSwitch.forms = lightSwitchForms; const onTheMovePollingTime = 1000; const onTheMoveForms = [ - { - href: `?address=1&quantity=1`, - op: ["readproperty", "observeproperty"], - "modbus:entity": "DiscreteInput", - "modbus:function": "readDiscreteInput", - "modbus:pollingTime": onTheMovePollingTime, - contentType: "application/octet-stream", - }, + { + href: `?address=1&quantity=1`, + op: ["readproperty", "observeproperty"], + "modbus:entity": "DiscreteInput", + "modbus:function": "readDiscreteInput", + "modbus:pollingTime": onTheMovePollingTime, + contentType: "application/octet-stream", + }, ]; discreteInputs[0] = 0; @@ -104,20 +104,20 @@ let onTheMoveIsPolled = false; thingDescription.properties.onTheMove.forms = onTheMoveForms; const floorNumberForms = [ - { - href: `?address=1&quantity=1`, - op: "readproperty", - "modbus:entity": "HoldingRegister", - "modbus:function": "readHoldingRegister", - contentType: "application/octet-stream", - }, - { - href: `?address=1&quantity=1`, - op: "writeproperty", - "modbus:entity": "HoldingRegister", - "modbus:function": "writeSingleHoldingRegister", - contentType: "application/octet-stream", - }, + { + href: `?address=1&quantity=1`, + op: "readproperty", + "modbus:entity": "HoldingRegister", + "modbus:function": "readHoldingRegister", + contentType: "application/octet-stream", + }, + { + href: `?address=1&quantity=1`, + op: "writeproperty", + "modbus:entity": "HoldingRegister", + "modbus:function": "writeSingleHoldingRegister", + contentType: "application/octet-stream", + }, ]; const minFloorNumber = 0; @@ -125,93 +125,93 @@ const maxFloorNumber = 15; thingDescription.properties.floorNumber.forms = floorNumberForms; fs.writeFile( - `${thingName}.td.json`, - JSON.stringify(thingDescription, 4, 4), - "utf-8", - function () {}, + `${thingName}.td.json`, + JSON.stringify(thingDescription, 4, 4), + "utf-8", + function () {}, ); const vector = { - getDiscreteInput: function (addr, unitID) { - if (thingUnitID === unitID) { - console.log(`Reading discrete input @${addr}`); - if (addr === 1) { - if (onTheMoveIsPolled) { - console.log( - `Polling onTheMove too frequently. You should poll it every ${onTheMovePollingTime} ms.`, - ); - return; + getDiscreteInput: function (addr, unitID) { + if (thingUnitID === unitID) { + console.log(`Reading discrete input @${addr}`); + if (addr === 1) { + if (onTheMoveIsPolled) { + console.log( + `Polling onTheMove too frequently. You should poll it every ${onTheMovePollingTime} ms.`, + ); + return; + } + + onTheMoveIsPolled = true; + setTimeout(function () { + onTheMoveIsPolled = false; + }, onTheMovePollingTime); + + return discreteInputs[addr - 1]; + } } - - onTheMoveIsPolled = true; - setTimeout(function () { - onTheMoveIsPolled = false; - }, onTheMovePollingTime); - - return discreteInputs[addr - 1]; - } - } - }, - getHoldingRegister: function (addr, unitID, callback) { - if (thingUnitID === unitID) { - setTimeout(function () { - console.log(`Reading holding register @${addr}`); - callback(null, holdingRegisters[addr - 1]); - }, 10); - } - }, - getCoil: function (addr, unitID) { - if (thingUnitID === unitID) { - return new Promise(function (resolve) { - console.log(`Reading coil @${addr}`); - resolve(coils[addr - 1]); - }); - } - }, - setRegister: function (addr, value, unitID) { - if (thingUnitID === unitID) { - console.log(`Setting register @${addr} to ${value}`); - // trying to change floor number - if (addr === 1) { - // elevator is on the move - if (discreteInputs[0]) { - console.log( - "Elevator is on the move, cannot change the floor number", - ); - } else { - if (value < minFloorNumber) { - console.log(`Floor number should not be under ${minFloorNumber}`); - return -1; - } - - if (value > maxFloorNumber) { - console.log(`Floor number should not be above ${maxFloorNumber}`); - return -1; - } - - console.log(`Changing the floor number to ${value}`); - - holdingRegisters[addr - 1] = value; - // simulating elevator movement - discreteInputs[0] = 1; - // elevator completes its movement in 5 seconds - setTimeout(() => { - discreteInputs[0] = 0; - }, 5000); + }, + getHoldingRegister: function (addr, unitID, callback) { + if (thingUnitID === unitID) { + setTimeout(function () { + console.log(`Reading holding register @${addr}`); + callback(null, holdingRegisters[addr - 1]); + }, 10); } - } - } - - - }, - setCoil: function (addr, value, unitID) { - if (thingUnitID === unitID) { - console.log(`Setting coil @${addr} to ${value}`); - coils[addr - 1] = value; - } - - - }, + }, + getCoil: function (addr, unitID) { + if (thingUnitID === unitID) { + return new Promise(function (resolve) { + console.log(`Reading coil @${addr}`); + resolve(coils[addr - 1]); + }); + } + }, + setRegister: function (addr, value, unitID) { + if (thingUnitID === unitID) { + console.log(`Setting register @${addr} to ${value}`); + // trying to change floor number + if (addr === 1) { + // elevator is on the move + if (discreteInputs[0]) { + console.log( + "Elevator is on the move, cannot change the floor number", + ); + } else { + if (value < minFloorNumber) { + console.log( + `Floor number should not be under ${minFloorNumber}`, + ); + return -1; + } + + if (value > maxFloorNumber) { + console.log( + `Floor number should not be above ${maxFloorNumber}`, + ); + return -1; + } + + console.log(`Changing the floor number to ${value}`); + + holdingRegisters[addr - 1] = value; + // simulating elevator movement + discreteInputs[0] = 1; + // elevator completes its movement in 5 seconds + setTimeout(() => { + discreteInputs[0] = 0; + }, 5000); + } + } + } + }, + setCoil: function (addr, value, unitID) { + if (thingUnitID === unitID) { + console.log(`Setting coil @${addr} to ${value}`); + coils[addr - 1] = value; + } + }, }; // set the server to answer for modbus requests @@ -219,13 +219,13 @@ console.log(`Started listening to on port ${portNumber}`); console.log("ThingIsReady"); const serverTCP = new ServerTCP(vector, { - host: hostname, - port: portNumber, - debug: true, - unitID: thingUnitID, + host: hostname, + port: portNumber, + debug: true, + unitID: thingUnitID, }); serverTCP.on("socketError", function (err) { - // Handle socket error if needed, can be ignored - console.error(err); + // Handle socket error if needed, can be ignored + console.error(err); }); diff --git a/things/elevator/modbus/js/modbus-elevator.td.json b/things/elevator/modbus/js/modbus-elevator.td.json index b28b75e..fe79d29 100644 --- a/things/elevator/modbus/js/modbus-elevator.td.json +++ b/things/elevator/modbus/js/modbus-elevator.td.json @@ -1,83 +1,83 @@ { - "@context": [ - "https://www.w3.org/2019/wot/td/v1", - "https://www.w3.org/2022/wot/td/v1.1", - { - "@language": "en" - } - ], - "@type": "Thing", - "title": "modbus-elevator", - "description": "Elevator Thing", - "securityDefinitions": { - "nosec_sc": { - "scheme": "nosec" - } - }, - "security": ["nosec_sc"], - "base": "modbus+tcp://0.0.0.0:8502/1", - "properties": { - "lightSwitch": { - "type": "boolean", - "readOnly": false, - "writeOnly": false, - "observable": false, - "forms": [ + "@context": [ + "https://www.w3.org/2019/wot/td/v1", + "https://www.w3.org/2022/wot/td/v1.1", { - "href": "?address=1&quantity=1", - "op": "readproperty", - "modbus:entity": "Coil", - "modbus:function": "readCoil", - "contentType": "application/octet-stream" - }, - { - "href": "?address=1&quantity=1", - "op": "writeproperty", - "modbus:entity": "Coil", - "modbus:function": "writeSingleCoil", - "contentType": "application/octet-stream" + "@language": "en" } - ] - }, - "onTheMove": { - "type": "boolean", - "readOnly": true, - "writeOnly": false, - "observable": true, - "forms": [ - { - "href": "?address=1&quantity=1", - "op": ["readproperty", "observeproperty"], - "modbus:entity": "DiscreteInput", - "modbus:function": "readDiscreteInput", - "modbus:pollingTime": 1000, - "contentType": "application/octet-stream" + ], + "@type": "Thing", + "title": "modbus-elevator", + "description": "Elevator Thing", + "securityDefinitions": { + "nosec_sc": { + "scheme": "nosec" } - ] }, - "floorNumber": { - "type": "integer", - "minimum": 0, - "maximum": 15, - "readOnly": false, - "writeOnly": false, - "observable": false, - "forms": [ - { - "href": "?address=1&quantity=1", - "op": "readproperty", - "modbus:entity": "HoldingRegister", - "modbus:function": "readHoldingRegister", - "contentType": "application/octet-stream" + "security": ["nosec_sc"], + "base": "modbus+tcp://0.0.0.0:8502/1", + "properties": { + "lightSwitch": { + "type": "boolean", + "readOnly": false, + "writeOnly": false, + "observable": false, + "forms": [ + { + "href": "?address=1&quantity=1", + "op": "readproperty", + "modbus:entity": "Coil", + "modbus:function": "readCoil", + "contentType": "application/octet-stream" + }, + { + "href": "?address=1&quantity=1", + "op": "writeproperty", + "modbus:entity": "Coil", + "modbus:function": "writeSingleCoil", + "contentType": "application/octet-stream" + } + ] }, - { - "href": "?address=1&quantity=1", - "op": "writeproperty", - "modbus:entity": "HoldingRegister", - "modbus:function": "writeSingleHoldingRegister", - "contentType": "application/octet-stream" + "onTheMove": { + "type": "boolean", + "readOnly": true, + "writeOnly": false, + "observable": true, + "forms": [ + { + "href": "?address=1&quantity=1", + "op": ["readproperty", "observeproperty"], + "modbus:entity": "DiscreteInput", + "modbus:function": "readDiscreteInput", + "modbus:pollingTime": 1000, + "contentType": "application/octet-stream" + } + ] + }, + "floorNumber": { + "type": "integer", + "minimum": 0, + "maximum": 15, + "readOnly": false, + "writeOnly": false, + "observable": false, + "forms": [ + { + "href": "?address=1&quantity=1", + "op": "readproperty", + "modbus:entity": "HoldingRegister", + "modbus:function": "readHoldingRegister", + "contentType": "application/octet-stream" + }, + { + "href": "?address=1&quantity=1", + "op": "writeproperty", + "modbus:entity": "HoldingRegister", + "modbus:function": "writeSingleHoldingRegister", + "contentType": "application/octet-stream" + } + ] } - ] } - } } diff --git a/things/elevator/modbus/js/package.json b/things/elevator/modbus/js/package.json index a5a44b9..0799871 100644 --- a/things/elevator/modbus/js/package.json +++ b/things/elevator/modbus/js/package.json @@ -1,25 +1,25 @@ { - "name": "modbus-elevator", - "version": "1.0.0", - "description": "Modbus Elevator", - "main": "main.js", - "scripts": { - "lint": "eslint .", - "lint:fix": "eslint . --fix" - }, - "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" - } + "name": "modbus-elevator", + "version": "1.0.0", + "description": "Modbus Elevator", + "main": "main.js", + "scripts": { + "lint": "eslint .", + "lint:fix": "eslint . --fix" + }, + "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" + } } diff --git a/things/elevator/modbus/js/test/td.test.js b/things/elevator/modbus/js/test/td.test.js index 3d0d34d..af0b7d5 100644 --- a/things/elevator/modbus/js/test/td.test.js +++ b/things/elevator/modbus/js/test/td.test.js @@ -28,74 +28,76 @@ const port = "8502"; let thingProcess; describe("Elevator Modbus JS", () => { - let validate; + let validate; - before(async () => { - const initiateMain = new Promise(async (resolve, reject) => { - thingProcess = spawn("node", ["main.js", "-p", `${port}`], { - cwd: path.join(__dirname, ".."), - }); - thingProcess.stdout.on("data", (data) => { - if (data.toString().includes("ThingIsReady")) { - resolve("Success"); - } - }); - thingProcess.stderr.on("data", (data) => { - reject(new Error(`Error: ${data}`)); - }); - thingProcess.on("error", (error) => { - reject(new Error(`Error: ${error}`)); - }); - thingProcess.on("close", () => { - reject(new Error("Failed to initiate the main script.")); - }); - }); + before(async () => { + const initiateMain = new Promise((resolve, reject) => { + thingProcess = spawn("node", ["main.js", "-p", `${port}`], { + cwd: path.join(__dirname, ".."), + }); + thingProcess.stdout.on("data", (data) => { + if (data.toString().includes("ThingIsReady")) { + resolve("Success"); + } + }); + thingProcess.stderr.on("data", (data) => { + reject(new Error(`Error: ${data}`)); + }); + thingProcess.on("error", (error) => { + reject(new Error(`Error: ${error}`)); + }); + thingProcess.on("close", () => { + reject(new Error("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) => { - fs.readFile( - path.join(__dirname, "../modbus-elevator.td.json"), - "utf-8", - (err, data) => { - if (err) { - console.log(err); - done(err); - } + 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(); - }, - ); - }); + 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 fae21ae..a4aa841 100644 --- a/things/elevator/tm.test.js +++ b/things/elevator/tm.test.js @@ -7,29 +7,29 @@ const ajv = new Ajv({ strict: false, allErrors: true, validateFormats: false }); const expect = chai.expect; describe("Elevator", () => { - let validate; + 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 index 487ab6c..c2151c5 100644 --- a/tsconfig.eslint.json +++ b/tsconfig.eslint.json @@ -4,4 +4,4 @@ "compilerOptions": { "experimentalDecorators": true } -} \ No newline at end of file +} diff --git a/tsconfig.json b/tsconfig.json index af61b5c..b98f521 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -30,4 +30,4 @@ /** specifically nothing here */ ], "exclude": [] -} \ No newline at end of file +} diff --git a/util/util.ts b/util/util.ts index e98144b..edefa64 100644 --- a/util/util.ts +++ b/util/util.ts @@ -18,86 +18,86 @@ import * as https from "https"; import { ChildProcess } from "node:child_process"; export type ThingStartResponse = { - process?: ChildProcess; - message: string; + process?: ChildProcess; + message: string; }; export type ValidateResponse = { - validate?: ValidateFunction; - message: string; + validate?: ValidateFunction; + message: string; }; const spawn = require("node:child_process").spawn; export const getInitiateMain = ( - thingPath: string, - port: number, + thingPath: string, + port: number, ): Promise => { - return new Promise((resolve, reject) => { - const thingProcess = spawn("node", [thingPath, "-p", `${port}`]); + return new Promise((resolve, reject) => { + const thingProcess = spawn("node", [thingPath, "-p", `${port}`]); - // 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); + // 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); - thingProcess.stdout!.on("data", (data: Buffer) => { - if (data.toString().includes("ThingIsReady")) { - clearTimeout(timeout); - resolve({ - process: thingProcess, - message: "Success", + 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.", + }); }); - } - }); - 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 }); export const getTDValidate = async (): Promise => { - const tdSchema: any = await getTDJSONSchema; + const tdSchema: any = await getTDJSONSchema; - return Promise.resolve({ - validate: ajv.compile(tdSchema), - message: "Success", - }); + return Promise.resolve({ + validate: ajv.compile(tdSchema), + 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); - }); + 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); - }); - }, - ); + response.on("end", () => { + const tdSchema = JSON.parse(Buffer.concat(body).toString()); + resolve(tdSchema); + }); + }, + ); }); From 52e7921a774423c86d410b406ac25968165c81f9 Mon Sep 17 00:00:00 2001 From: Hasan Eroglu Date: Fri, 27 Sep 2024 17:54:17 +0200 Subject: [PATCH 4/7] fix lint issues and run prettier Signed-off-by: Hasan Eroglu --- .gitignore | 2 +- README.md | 8 +- .../http-data-schema-thing-dashboard.json | 3642 ++-- ...p-express-calculator-simple-dashboard.json | 1980 +- .../dashboards/mqtt-calculator-dashboard.json | 1134 +- conf/loki/loki-config.yaml | 1 - conf/promtail/promtail-config.yaml | 1 - docker-compose-things.yml | 10 +- package-lock.json | 15602 ++++++++-------- .../http/ts/test/client.test.ts | 14 +- .../http/ts/test/fixtures.ts | 15 +- .../http/ts/test/td.test.ts | 2 +- things/calculator/coap/js/.mocharc.json | 2 +- things/calculator/coap/js/test/client.test.js | 368 +- things/calculator/coap/js/test/fixtures.js | 83 +- things/calculator/coap/js/test/td.test.js | 147 +- things/calculator/http/express/.mocharc.json | 2 +- ...ent-negotiation-calculator-thing.td.jsonld | 45 +- .../http-content-negotiation-calculator.js | 33 +- .../http/express/http-simple-calculator.js | 489 +- things/calculator/http/express/package.json | 4 +- .../http/express/test/client.test.js | 370 +- .../calculator/http/express/test/fixtures.js | 83 +- .../calculator/http/express/test/td.test.js | 143 +- things/calculator/mqtt/js/.mocharc.json | 2 +- things/calculator/mqtt/js/main.js | 273 +- things/calculator/mqtt/js/package.json | 8 +- things/calculator/mqtt/js/test/client.test.js | 151 +- things/calculator/mqtt/js/test/fixtures.js | 50 +- things/calculator/mqtt/js/test/td.test.js | 70 +- things/data-schema-thing/http/ts/package.json | 4 +- things/data-schema-thing/http/ts/src/main.ts | 519 +- .../http/ts/test/client.test.ts | 14 +- .../http/ts/test/fixtures.ts | 16 +- .../data-schema-thing/http/ts/test/td.test.ts | 2 +- things/elevator/modbus/js/.mocharc.json | 2 +- things/elevator/modbus/js/main.js | 286 +- .../modbus/js/modbus-elevator.td.json | 6 +- things/elevator/modbus/js/package.json | 47 +- things/elevator/modbus/js/test/client.test.js | 143 +- things/elevator/modbus/js/test/fixtures.js | 51 +- things/elevator/modbus/js/test/td.test.js | 46 +- util/tsconfig.json | 4 +- util/util.ts | 10 +- 44 files changed, 12918 insertions(+), 12966 deletions(-) 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/README.md b/README.md index 0f114dc..2a8c1d7 100644 --- a/README.md +++ b/README.md @@ -101,7 +101,7 @@ 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: @@ -124,6 +124,7 @@ To be able to access additional tools, the user must login through GitHub. For G - 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/). 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/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/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-things.yml b/docker-compose-things.yml index b2f9285..4bf184f 100644 --- a/docker-compose-things.yml +++ b/docker-compose-things.yml @@ -59,11 +59,11 @@ services: 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`) diff --git a/package-lock.json b/package-lock.json index 64d20aa..a6b1b21 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,8179 +1,7431 @@ { - "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": "^3.3.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/@eslint-community/eslint-utils": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", - "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", - "dev": true, - "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", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.1.tgz", - "integrity": "sha512-m4DVN9ZqskZoLU5GlWZadwDnYo3vAEydiUayB9widCl9ffWx2IvPnp6n3on5rJmziJSw9Bv+Z3ChDVdMwXCY8Q==", - "dev": true, - "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" - } - }, - "node_modules/@eslint/eslintrc": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", - "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", - "dev": true, - "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", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "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", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "node_modules/@eslint/eslintrc/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/@eslint/js": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", - "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", - "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", - "deprecated": "Use @eslint/config-array instead", - "dev": true, - "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", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@humanwhocodes/config-array/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true, - "engines": { - "node": ">=12.22" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@humanwhocodes/object-schema": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", - "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", - "deprecated": "Use @eslint/object-schema instead", - "dev": true - }, - "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/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "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/@rtsao/scc": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", - "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", - "dev": true - }, - "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", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/json5": { - "version": "0.0.29", - "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", - "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", - "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/@typescript-eslint/eslint-plugin": { - "version": "8.6.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.6.0.tgz", - "integrity": "sha512-UOaz/wFowmoh2G6Mr9gw60B1mm0MzUtm6Ic8G2yM1Le6gyj5Loi/N+O5mocugRGY+8OeeKmkMmbxNqUCq3B4Sg==", - "dev": true, - "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", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.6.0.tgz", - "integrity": "sha512-eQcbCuA2Vmw45iGfcyG4y6rS7BhWfz9MQuk409WD47qMM+bKCGQWXxvoOs1DUp+T7UBMTtRTVT+kXr7Sh4O9Ow==", - "dev": true, - "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", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.6.0.tgz", - "integrity": "sha512-ZuoutoS5y9UOxKvpc/GkvF4cuEmpokda4wRg64JEia27wX+PysIE9q+lzDtlHHgblwUWwo5/Qn+/WyTUvDwBHw==", - "dev": true, - "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", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.6.0.tgz", - "integrity": "sha512-dtePl4gsuenXVwC7dVNlb4mGDcKjDT/Ropsk4za/ouMBPplCLyznIaR+W65mvCvsyS97dymoBRrioEXI7k0XIg==", - "dev": true, - "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", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.6.0.tgz", - "integrity": "sha512-rojqFZGd4MQxw33SrOy09qIDS8WEldM8JWtKQLAjf/X5mGSeEFh5ixQlxssMNyPslVIk9yzWqXCsV2eFhYrYUw==", - "dev": true, - "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", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.6.0.tgz", - "integrity": "sha512-MOVAzsKJIPIlLK239l5s06YXjNqpKTVhBVDnqUumQJja5+Y94V3+4VUFRA0G60y2jNnTVwRCkhyGQpavfsbq/g==", - "dev": true, - "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", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "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", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.6.0.tgz", - "integrity": "sha512-eNp9cWnYf36NaOVjkEUznf6fEgVy1TWpE0o52e4wtojjBx7D1UV2WAWGzR+8Y5lVFtpMLPwNbC67T83DWSph4A==", - "dev": true, - "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", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.6.0.tgz", - "integrity": "sha512-wapVFfZg9H0qOYh4grNVQiMklJGluQrOUiOhYRrQWhx7BY/+I1IYb8BczWNbbUpO+pqy0rDciv3lQH5E1bCLrg==", - "dev": true, - "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", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", - "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", - "dev": true - }, - "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-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.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==", - "dev": 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", - "dev": true, - "license": "Python-2.0" - }, - "node_modules/array-buffer-byte-length": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz", - "integrity": "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==", - "dev": true, - "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", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.8.tgz", - "integrity": "sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==", - "dev": true, - "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", - "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.5.tgz", - "integrity": "sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==", - "dev": true, - "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", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz", - "integrity": "sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==", - "dev": true, - "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", - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz", - "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==", - "dev": true, - "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", - "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz", - "integrity": "sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==", - "dev": true, - "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", - "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", - "dev": true, - "license": "MIT" - }, - "node_modules/available-typed-arrays": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", - "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", - "dev": true, - "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", - "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", - "license": "MIT" - }, - "node_modules/builtin-modules": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", - "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", - "dev": true, - "peer": true, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/builtins": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/builtins/-/builtins-5.1.0.tgz", - "integrity": "sha512-SW9lzGTLvWTP1AY8xeAMZimqDrIaSdLQUcVr9DMef51niJ022Ri87SwRRKYm4A6iHfkPaiVUu/Duw2Wc4J7kKg==", - "dev": true, - "peer": true, - "dependencies": { - "semver": "^7.0.0" - } - }, - "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/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "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/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", - "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/confbox": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.7.tgz", - "integrity": "sha512-uJcB/FKZtBMCJpK8MQji6bJHgu1tixKPxRLeGkNzBoOZzpnZUJm0jm2/sBDWcuBx1dYgxV4JU+g5hmNxCyAmdA==", - "dev": 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", - "dev": true, - "license": "MIT" - }, - "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "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", - "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz", - "integrity": "sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==", - "dev": true, - "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", - "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz", - "integrity": "sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==", - "dev": true, - "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", - "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz", - "integrity": "sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==", - "dev": true, - "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", - "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-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true - }, - "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/define-properties": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", - "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", - "dev": true, - "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", - "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/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "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-abstract": { - "version": "1.23.3", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz", - "integrity": "sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==", - "dev": true, - "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", - "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/es-object-atoms": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", - "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", - "dev": true, - "dependencies": { - "es-errors": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-set-tostringtag": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz", - "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==", - "dev": true, - "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", - "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz", - "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", - "dev": true, - "dependencies": { - "hasown": "^2.0.0" - } - }, - "node_modules/es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "dev": true, - "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", - "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/eslint": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", - "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", - "dev": true, - "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", - "resolved": "https://registry.npmjs.org/eslint-compat-utils/-/eslint-compat-utils-0.5.1.tgz", - "integrity": "sha512-3z3vFexKIEnjHE3zCMRo6fn/e44U7T1khUjg+Hp0ZQMCigh28rALD0nPFBcGZuiLC5rLZa2ubQHDRln09JfU2Q==", - "dev": true, - "peer": true, - "dependencies": { - "semver": "^7.5.4" - }, - "engines": { - "node": ">=12" - }, - "peerDependencies": { - "eslint": ">=6.0.0" - } - }, - "node_modules/eslint-config-prettier": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz", - "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==", - "dev": true, - "bin": { - "eslint-config-prettier": "bin/cli.js" - }, - "peerDependencies": { - "eslint": ">=7.0.0" - } - }, - "node_modules/eslint-config-standard": { - "version": "17.1.0", - "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-17.1.0.tgz", - "integrity": "sha512-IwHwmaBNtDK4zDHQukFDW5u/aTb8+meQWZvNFWkiGmbWjD6bqyuSSBxxXKkCftCUzc1zwCH2m/baCNDLGmuO5Q==", - "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" - } - ], - "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", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", - "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", - "dev": true, - "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", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/eslint-module-utils": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.11.0.tgz", - "integrity": "sha512-gbBE5Hitek/oG6MUVj6sFuzEjA/ClzNflVrLovHi/JgLdC7fiN5gLAY1WIPW1a0V5I999MnsrvVrCOGmmVqDBQ==", - "dev": true, - "dependencies": { - "debug": "^3.2.7" - }, - "engines": { - "node": ">=4" - }, - "peerDependenciesMeta": { - "eslint": { - "optional": true - } - } - }, - "node_modules/eslint-module-utils/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/eslint-plugin-es": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-3.0.1.tgz", - "integrity": "sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ==", - "dev": true, - "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", - "resolved": "https://registry.npmjs.org/eslint-plugin-es-x/-/eslint-plugin-es-x-7.8.0.tgz", - "integrity": "sha512-7Ds8+wAAoV3T+LAKeu39Y5BzXCrGKrcISfgKEqTS4BDN8SFEDQd0S43jiQ8vIa3wUKD07qitZdfzlenSi8/0qQ==", - "dev": true, - "funding": [ - "https://github.com/sponsors/ota-meshi", - "https://opencollective.com/eslint" - ], - "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", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.30.0.tgz", - "integrity": "sha512-/mHNE9jINJfiD2EKkg1BKyPyUk4zdnT54YgbOgfjSakWT5oyX/qQLVNTkehyfpcMxZXMy1zyonZ2v7hZTX43Yw==", - "dev": true, - "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", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/eslint-plugin-import/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/eslint-plugin-import/node_modules/doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", - "dev": true, - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/eslint-plugin-import/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/eslint-plugin-import/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/eslint-plugin-n": { - "version": "16.6.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-16.6.2.tgz", - "integrity": "sha512-6TyDmZ1HXoFQXnhCTUjVFULReoBPOAjpuiKELMkeP40yffI/1ZRO+d9ug/VC6fqISo2WkuIBk3cvuRPALaWlOQ==", - "dev": true, - "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", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "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", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "peer": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/eslint-plugin-node": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz", - "integrity": "sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g==", - "dev": true, - "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", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/eslint-plugin-node/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/eslint-plugin-node/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/eslint-plugin-notice": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-notice/-/eslint-plugin-notice-1.0.0.tgz", - "integrity": "sha512-M3VLQMZzZpvfTZ/vy9FmClIKq5rLBbQpM0KgfLZPJPrVXpmJYeobmmb+lfJzHWdNm8PWwvw8KlafQWo2N9xx1Q==", - "dev": true, - "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", - "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-6.6.0.tgz", - "integrity": "sha512-57Zzfw8G6+Gq7axm2Pdo3gW/Rx3h9Yywgn61uE/3elTCOePEHVrn2i5CdfBwA1BLK0Q0WqctICIUSqXZW/VprQ==", - "dev": true, - "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", - "resolved": "https://registry.npmjs.org/eslint-plugin-unused-imports/-/eslint-plugin-unused-imports-4.1.4.tgz", - "integrity": "sha512-YptD6IzQjDardkl0POxnnRBhU1OEePMV0nd6siHaRBbd+lyh6NAhFEobiznKU7kTsSsDeSD62Pe7kAM1b7dAZQ==", - "dev": true, - "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", - "resolved": "https://registry.npmjs.org/eslint-plugin-workspaces/-/eslint-plugin-workspaces-0.10.1.tgz", - "integrity": "sha512-17p+ljQq2oYtN+cS6F52wRCV7xVh5fHiBOhQaYkSShRwRdiTvEkD6gvGysZjx08ckr9cozIFqI9SxdgTWDV9+g==", - "dev": true, - "dependencies": { - "find-workspaces": "^0.3.0" - } - }, - "node_modules/eslint-scope": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", - "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", - "dev": true, - "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", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", - "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", - "dev": true, - "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", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, - "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", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "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", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/eslint/node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/eslint/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "node_modules/eslint/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/espree": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", - "dev": true, - "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", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", - "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", - "dev": true, - "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "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", - "license": "MIT" - }, - "node_modules/fast-glob": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", - "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", - "dev": true, - "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", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true - }, - "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/fastq": { - "version": "1.17.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", - "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", - "dev": true, - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/fastseries": { - "version": "2.0.0", - "license": "ISC" - }, - "node_modules/file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "dev": true, - "dependencies": { - "flat-cache": "^3.0.4" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "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-root": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", - "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", - "dev": true - }, - "node_modules/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/find-workspaces": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/find-workspaces/-/find-workspaces-0.3.1.tgz", - "integrity": "sha512-UDkGILGJSA1LN5Aa7McxCid4sqW3/e+UYsVwyxki3dDT0F8+ym0rAfnCkEfkL0rO7M+8/mvkim4t/s3IPHmg+w==", - "dev": true, - "dependencies": { - "fast-glob": "^3.3.2", - "pkg-types": "^1.0.3", - "yaml": "^2.3.4" - } - }, - "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/flat-cache": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", - "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", - "dev": true, - "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", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", - "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", - "dev": true - }, - "node_modules/for-each": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", - "dev": true, - "dependencies": { - "is-callable": "^1.1.3" - } - }, - "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/function.prototype.name": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", - "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", - "dev": true, - "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", - "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", - "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "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/get-symbol-description": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz", - "integrity": "sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==", - "dev": true, - "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", - "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.8.1.tgz", - "integrity": "sha512-k9PN+cFBmaLWtVz29SkUoqU5O0slLuHJXt/2P+tMVFT+phsSGXGkp9t3rQIqdz0e+06EHNGs3oM6ZX1s2zHxRg==", - "dev": true, - "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", - "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/globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", - "dev": true, - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/globalthis": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", - "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", - "dev": true, - "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", - "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/graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true - }, - "node_modules/has-bigints": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", - "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", - "dev": true, - "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-tostringtag": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", - "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "dev": true, - "dependencies": { - "has-symbols": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "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" - } - ], - "license": "BSD-3-Clause" - }, - "node_modules/ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, - "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", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true, - "engines": { - "node": ">=0.8.19" - } - }, - "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", - "license": "ISC" - }, - "node_modules/internal-slot": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz", - "integrity": "sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==", - "dev": true, - "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", - "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-array-buffer": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz", - "integrity": "sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==", - "dev": true, - "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-bigint": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", - "dev": true, - "dependencies": { - "has-bigints": "^1.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "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-boolean-object": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", - "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", - "dev": true, - "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", - "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.1.tgz", - "integrity": "sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==", - "dev": true, - "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", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-core-module": { - "version": "2.15.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz", - "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==", - "dev": true, - "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", - "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.1.tgz", - "integrity": "sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==", - "dev": true, - "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", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", - "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", - "dev": true, - "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", - "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-negative-zero": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", - "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "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-number-object": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", - "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", - "dev": true, - "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", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "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-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", - "dev": true, - "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", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz", - "integrity": "sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.7" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-string": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", - "dev": true, - "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", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", - "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", - "dev": true, - "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", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz", - "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", - "dev": true, - "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", - "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/is-weakref": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", - "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", - "dev": true, - "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", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true - }, - "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-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true - }, - "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", - "license": "MIT" - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true - }, - "node_modules/json5": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", - "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", - "dev": true, - "dependencies": { - "minimist": "^1.2.0" - }, - "bin": { - "json5": "lib/cli.js" - } - }, - "node_modules/keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "dev": true, - "dependencies": { - "json-buffer": "3.0.1" - } - }, - "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/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.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/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true - }, - "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/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/metric-lcs": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/metric-lcs/-/metric-lcs-0.1.2.tgz", - "integrity": "sha512-+TZ5dUDPKPJaU/rscTzxyN8ZkX7eAVLAiQU/e+YINleXPv03SCmJShaMT1If1liTH8OcmWXZs0CmzCBRBLcMpA==", - "dev": true - }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "dev": true, - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.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/mlly": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.7.1.tgz", - "integrity": "sha512-rrVRZRELyQzrIUAVMHxP97kv+G786pHmOKzuFII8zDYahFBS7qnHh2AlYSl1GAHhaMPCz6/oHjVMcfFYgFYHgA==", - "dev": true, - "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", - "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/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": 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", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.assign": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", - "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", - "dev": true, - "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", - "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", - "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", - "dev": true, - "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", - "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz", - "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==", - "dev": true, - "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", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.0.tgz", - "integrity": "sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==", - "dev": true, - "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", - "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/optionator": { - "version": "0.9.4", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", - "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", - "dev": true, - "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", - "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/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "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-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true - }, - "node_modules/path-to-regexp": { - "version": "0.1.10", - "license": "MIT" - }, - "node_modules/pathe": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", - "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", - "dev": true - }, - "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/pkg-types": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.2.0.tgz", - "integrity": "sha512-+ifYuSSqOQ8CqP4MbZA5hDpb97n3E8SVWdJe+Wms9kj745lmd3b7EZJiqvmLwAlmRfjrI7Hi5z3kdBJ93lFNPA==", - "dev": true, - "dependencies": { - "confbox": "^0.1.7", - "mlly": "^1.7.1", - "pathe": "^1.1.2" - } - }, - "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/possible-typed-array-names": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", - "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", - "dev": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/prettier": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz", - "integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==", - "dev": true, - "bin": { - "prettier": "bin/prettier.cjs" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" - } - }, - "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", - "license": "MIT" - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, - "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/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", - "license": "MIT" - }, - "node_modules/regexp.prototype.flags": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz", - "integrity": "sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==", - "dev": true, - "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", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - } - }, - "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", - "license": "MIT" - }, - "node_modules/resolve": { - "version": "1.22.8", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", - "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", - "dev": true, - "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", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/resolve-pkg-maps": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", - "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", - "dev": true, - "peer": true, - "funding": { - "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" - } - }, - "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", - "license": "MIT" - }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", - "dev": true, - "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", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/rimraf/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "dev": true, - "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", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "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-array-concat": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz", - "integrity": "sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==", - "dev": true, - "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", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true - }, - "node_modules/safe-buffer": { - "version": "5.1.2", - "license": "MIT" - }, - "node_modules/safe-regex-test": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz", - "integrity": "sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==", - "dev": true, - "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", - "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/set-function-name": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", - "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", - "dev": true, - "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", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "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" - } - ], - "license": "MIT" - }, - "node_modules/string.prototype.trim": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz", - "integrity": "sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==", - "dev": true, - "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", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz", - "integrity": "sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==", - "dev": true, - "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", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", - "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", - "dev": true, - "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", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "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/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "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/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "dev": true - }, - "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", - "license": "MIT" - }, - "node_modules/ts-api-utils": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", - "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", - "dev": true, - "engines": { - "node": ">=16" - }, - "peerDependencies": { - "typescript": ">=4.2.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/tsconfig-paths": { - "version": "3.15.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", - "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", - "dev": true, - "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", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/type-detect": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.1.0.tgz", - "integrity": "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "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/typed-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz", - "integrity": "sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==", - "dev": true, - "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", - "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz", - "integrity": "sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==", - "dev": true, - "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", - "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz", - "integrity": "sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==", - "dev": true, - "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", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.6.tgz", - "integrity": "sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==", - "dev": true, - "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", - "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/ufo": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.5.4.tgz", - "integrity": "sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ==", - "dev": true - }, - "node_modules/unbox-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", - "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", - "dev": true, - "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", - "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/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "dependencies": { - "punycode": "^2.1.0" - } - }, - "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/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/which-boxed-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", - "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", - "dev": true, - "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", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz", - "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==", - "dev": true, - "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/word-wrap": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", - "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "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/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", - "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": "^3.3.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", + "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": "3.3.3", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "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", - "license": "ISC" - }, - "node_modules/yaml": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.5.1.tgz", - "integrity": "sha512-bLQOjaX/ADgQ20isPJRvF0iRUHIxVhYvr53Of7wGcWlO2jvtUlH5m87DsmulFVxRpNLOnI4tB6p/oh8D7kpn9Q==", - "dev": true, - "bin": { - "yaml": "bin.mjs" - }, - "engines": { - "node": ">= 14" - } - }, - "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/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", - "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/things/advanced-coffee-machine/http/ts/test/client.test.ts b/things/advanced-coffee-machine/http/ts/test/client.test.ts index 1b380fa..f5a92d6 100644 --- a/things/advanced-coffee-machine/http/ts/test/client.test.ts +++ b/things/advanced-coffee-machine/http/ts/test/client.test.ts @@ -14,15 +14,15 @@ ********************************************************************************/ 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; -let servient = new Servient() -servient.addClientFactory(new HttpClientFactory()) +const servient = new Servient(); +servient.addClientFactory(new HttpClientFactory()); let thing: WoT.ConsumedThing; diff --git a/things/advanced-coffee-machine/http/ts/test/fixtures.ts b/things/advanced-coffee-machine/http/ts/test/fixtures.ts index 8632a30..0579ccc 100644 --- a/things/advanced-coffee-machine/http/ts/test/fixtures.ts +++ b/things/advanced-coffee-machine/http/ts/test/fixtures.ts @@ -23,12 +23,15 @@ 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; } } 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 cc66d20..1bc55fc 100644 --- a/things/advanced-coffee-machine/http/ts/test/td.test.ts +++ b/things/advanced-coffee-machine/http/ts/test/td.test.ts @@ -17,7 +17,7 @@ import * as chai from "chai"; import * as http from "http"; import { getTDValidate } from "../../../../../util/util"; import { ValidateFunction } from "ajv"; -import { port } from './fixtures' +import { port } from "./fixtures"; const expect = chai.expect; 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/js/test/client.test.js b/things/calculator/coap/js/test/client.test.js index d5d2e21..ec7e9e6 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..5690372 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 5b14aa5..266a338 100644 --- a/things/calculator/coap/js/test/td.test.js +++ b/things/calculator/coap/js/test/td.test.js @@ -13,86 +13,87 @@ * SPDX-License-Identifier: EPL-2.0 OR W3C-20150513 ********************************************************************************/ -const chai = require('chai') -const coap = require('coap') -const cbor = require('cbor') -const { getTDValidate } = require('../../../../../util/dist/util') -const { simplePort, contentNegotiationPort } = require('./fixtures') +const chai = require("chai"); +const coap = require("coap"); +const cbor = require("cbor"); +const { getTDValidate } = require("../../../../../util/dist/util"); +const { simplePort, contentNegotiationPort } = require("./fixtures"); -const expect = chai.expect +const expect = chai.expect; describe("Calculator CoAP JS", () => { let validate; - before(async () => { - const tdValidate = getTDValidate() - - try { - const response = await Promise.all([tdValidate]) - validate = response[0].validate - } - catch (error) { - console.log(error) - } - }) + before(async () => { + const tdValidate = getTDValidate(); - 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() - }) - }) + try { + const response = await Promise.all([tdValidate]); + validate = response[0].validate; + } catch (error) { + console.log(error); + } + }); - 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() - }) + describe("Calculator Simple", () => { + it("should have a valid TD", (done) => { + const req = coap.request( + `coap://localhost:${simplePort}/coap-calculator-simple`, + ); - req.end() - }) + req.on("response", (res) => { + const valid = validate(JSON.parse(res.payload.toString())); + expect(valid).to.be.true; + done(); + }); - 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(); + }); + }); - 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/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/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 fd056a3..ecf45c7 100644 --- a/things/calculator/http/express/http-content-negotiation-calculator.js +++ b/things/calculator/http/express/http-content-negotiation-calculator.js @@ -84,23 +84,22 @@ placeholderReplacer.addVariableMap({ 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 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"; diff --git a/things/calculator/http/express/http-simple-calculator.js b/things/calculator/http/express/http-simple-calculator.js index 67f498b..65929ed 100644 --- a/things/calculator/http/express/http-simple-calculator.js +++ b/things/calculator/http/express/http-simple-calculator.js @@ -20,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 })); @@ -30,19 +30,21 @@ 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 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`; @@ -173,59 +175,136 @@ 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' }}) -} + lastChange = value; + logger.info({ + message: `${lastChange}`, + labels: { + affordance: "property", + affordanceName: "lastChange", + messageType: "updateProperty", + }, + }); +}; -let result -setResult(0) +let result; +setResult(0); -let lastChange -setLastChange(new Date().toISOString()) +let lastChange; +setLastChange(new Date().toISOString()); -let updateSubscriptionCount = 0 -logger.info({ message: `${updateSubscriptionCount}`, labels: { affordance: 'event', affordanceName: 'update', messageType: 'subscriptionCount' }}) +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' }}) -} + 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' }}) +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' }}) -} + 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' }}) +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 ****************/ @@ -280,8 +359,15 @@ app.get(TDEndPoint, (req, res) => { }); 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) => { @@ -291,32 +377,60 @@ app.get(resultEndPointObserve, (req, res) => { 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() - }) + 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) => { @@ -326,68 +440,135 @@ app.get(lastChangeEndPointObserve, (req, res) => { 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); + 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.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); + 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); + 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) => { @@ -397,32 +578,54 @@ app.get(updateEndPoint, (req, res) => { 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() - }) + 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, () => { diff --git a/things/calculator/http/express/package.json b/things/calculator/http/express/package.json index 9d3e7b1..e4f65a7 100644 --- a/things/calculator/http/express/package.json +++ b/things/calculator/http/express/package.json @@ -23,8 +23,8 @@ "eventsource": "^2.0.2", "express": "^4.18.2", "json-placeholder-replacer": "^1.0.35", - "winston": "^3.14.2", - "winston-loki": "^6.1.2" + "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..e152499 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..dc52f4c 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 740de56..80d8fe8 100644 --- a/things/calculator/http/express/test/td.test.js +++ b/things/calculator/http/express/test/td.test.js @@ -13,77 +13,86 @@ * SPDX-License-Identifier: EPL-2.0 OR W3C-20150513 ********************************************************************************/ -const chai = require('chai') -const http = require('http') -const { getTDValidate } = require('../../../../../util/dist/util') -const { simplePort, contentNegotiationPort } = require('./fixtures') +const chai = require("chai"); +const http = require("http"); +const { getTDValidate } = require("../../../../../util/dist/util"); +const { simplePort, contentNegotiationPort } = require("./fixtures"); -const expect = chai.expect +const expect = chai.expect; describe("Calculator HTTP JS", () => { let validate; - before(async () => { - const tdValidate = getTDValidate() - - try { - const response = await Promise.all([tdValidate]) - validate = response[0].validate - } - catch (error) { - console.log(error) - } - }) + before(async () => { + const tdValidate = getTDValidate(); - 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' + 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/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 c7960c0..747a89e 100644 --- a/things/calculator/mqtt/js/main.js +++ b/things/calculator/mqtt/js/main.js @@ -13,15 +13,15 @@ * 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 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 { 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; @@ -46,19 +46,21 @@ 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 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; @@ -137,90 +139,185 @@ for (const key in thingDescription.events) { 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' }}) -} + lastChange = value; + logger.info({ + message: `${lastChange}`, + labels: { + affordance: "property", + affordanceName: "lastChange", + messageType: "updateProperty", + }, + }); +}; -let result -setResult(0) +let result; +setResult(0); -let lastChange -setLastChange(new Date().toISOString()) +let lastChange; +setLastChange(new Date().toISOString()); -broker.on('message', (topic, payload, packet) => { - const segments = topic.split('/') +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' }}) + 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] === '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.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 }, + ); + } + } } - } - - 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)) { - 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 }) - } - } - } -}) +}); setInterval(() => { 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/package.json b/things/calculator/mqtt/js/package.json index 75f3afe..b20a48e 100644 --- a/things/calculator/mqtt/js/package.json +++ b/things/calculator/mqtt/js/package.json @@ -16,10 +16,10 @@ "author": "Eclipse Thingweb (https://thingweb.io/)", "license": "EPL-2.0 OR W3C-20150513", "dependencies": { - "dotenv": "^16.3.1", + "dotenv": "^16.3.1", "json-placeholder-replacer": "^1.0.35", - "mqtt": "^4.3.8", - "winston": "^3.14.2", - "winston-loki": "^6.1.2" + "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..7903491 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 67f8644..ea6b69c 100644 --- a/things/calculator/mqtt/js/test/td.test.js +++ b/things/calculator/mqtt/js/test/td.test.js @@ -13,44 +13,42 @@ * 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 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' +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() - }) - }) -}) + 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/data-schema-thing/http/ts/package.json b/things/data-schema-thing/http/ts/package.json index 13fc3f2..d8b0e50 100644 --- a/things/data-schema-thing/http/ts/package.json +++ b/things/data-schema-thing/http/ts/package.json @@ -23,8 +23,8 @@ "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" + "winston": "^3.14.2", + "winston-loki": "^6.1.2" }, "devDependencies": { "ts-node": "^10.9.2", diff --git a/things/data-schema-thing/http/ts/src/main.ts b/things/data-schema-thing/http/ts/src/main.ts index a4a670e..c536145 100644 --- a/things/data-schema-thing/http/ts/src/main.ts +++ b/things/data-schema-thing/http/ts/src/main.ts @@ -20,12 +20,11 @@ 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 { createLogger, transports, format } = require('winston') -const LokiTransport = require('winston-loki') - const hostname = process.env.HOSTNAME ?? "localhost"; let portNumber = process.env.PORT != null && process.env.PORT !== "" @@ -33,21 +32,21 @@ let portNumber = : 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; @@ -118,8 +117,15 @@ 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); @@ -127,17 +133,31 @@ let int: number; const setInt = (value: number) => { int = value; - logger.info({ message: `${int}`, labels: { affordance: 'property', affordanceName: 'int', messageType: 'updateProperty' }}); -} + logger.info({ + message: `${int}`, + labels: { + affordance: "property", + affordanceName: "int", + messageType: "updateProperty", + }, + }); +}; -setInt(42) +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); @@ -145,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"); @@ -154,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"]); @@ -163,8 +197,15 @@ 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" }); @@ -181,146 +222,300 @@ servient.start().then((WoT) => { .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; + // 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; + }); // expose the thing thing.expose().then(async () => { 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 b9b31c1..7ac461b 100644 --- a/things/data-schema-thing/http/ts/test/client.test.ts +++ b/things/data-schema-thing/http/ts/test/client.test.ts @@ -18,10 +18,10 @@ import chaiAsPromised from "chai-as-promised"; import { Servient } from "@node-wot/core"; import { HttpClientFactory } from "@node-wot/binding-http"; -import { port } from './fixtures'; +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()); @@ -53,12 +53,12 @@ describe("Client Tests", () => { } 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"); diff --git a/things/data-schema-thing/http/ts/test/fixtures.ts b/things/data-schema-thing/http/ts/test/fixtures.ts index 7944ff5..15e158b 100644 --- a/things/data-schema-thing/http/ts/test/fixtures.ts +++ b/things/data-schema-thing/http/ts/test/fixtures.ts @@ -23,13 +23,15 @@ 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; } } 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 a38880a..6fdad69 100644 --- a/things/data-schema-thing/http/ts/test/td.test.ts +++ b/things/data-schema-thing/http/ts/test/td.test.ts @@ -17,7 +17,7 @@ import * as chai from "chai"; import * as http from "http"; import { getTDValidate } from "../../../../../util/util"; import { ValidateFunction } from "ajv"; -import { port } from './fixtures' +import { port } from "./fixtures"; const expect = chai.expect; 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 e817716..0214ea9 100644 --- a/things/elevator/modbus/js/main.js +++ b/things/elevator/modbus/js/main.js @@ -18,7 +18,6 @@ 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"; @@ -31,20 +30,22 @@ const hostname = process.env.HOSTNAME let portNumber = process.env.PORT ?? "8502"; const thingUnitID = 1; -const { values: { port, isTestRun } } = parseArgs({ +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); } @@ -68,101 +69,113 @@ placeholderReplacer.addVariableMap({ 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" -}] +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 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" -}] +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 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) { 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) { @@ -172,91 +185,109 @@ const vector = { 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) { 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) { 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) { 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); } } } @@ -265,14 +296,17 @@ const vector = { 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; } }, }; diff --git a/things/elevator/modbus/js/modbus-elevator.td.json b/things/elevator/modbus/js/modbus-elevator.td.json index 6d1299d..e3bd3c6 100644 --- a/things/elevator/modbus/js/modbus-elevator.td.json +++ b/things/elevator/modbus/js/modbus-elevator.td.json @@ -14,7 +14,9 @@ "scheme": "nosec" } }, - "security": ["nosec_sc"], + "security": [ + "nosec_sc" + ], "base": "modbus+tcp://0.0.0.0:8502/1", "properties": { "lightSwitch": { @@ -83,4 +85,4 @@ ] } } -} +} \ No newline at end of file diff --git a/things/elevator/modbus/js/package.json b/things/elevator/modbus/js/package.json index a5a44b9..e850848 100644 --- a/things/elevator/modbus/js/package.json +++ b/things/elevator/modbus/js/package.json @@ -1,25 +1,26 @@ { - "name": "modbus-elevator", - "version": "1.0.0", - "description": "Modbus Elevator", - "main": "main.js", - "scripts": { - "lint": "eslint .", - "lint:fix": "eslint . --fix" - }, - "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" - } + "name": "modbus-elevator", + "version": "1.0.0", + "description": "Modbus Elevator", + "main": "main.js", + "scripts": { + "lint": "eslint .", + "lint:fix": "eslint . --fix" + }, + "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 0f54b62..c0f2e84 100644 --- a/things/elevator/modbus/js/test/td.test.js +++ b/things/elevator/modbus/js/test/td.test.js @@ -13,34 +13,36 @@ * SPDX-License-Identifier: EPL-2.0 OR W3C-20150513 ********************************************************************************/ -const chai = require('chai') -const fs = require('fs') -const path = require('path') -const { getTDValidate } = require("../../../../../util/dist/util") +const chai = require("chai"); +const fs = require("fs"); +const path = require("path"); +const { getTDValidate } = require("../../../../../util/dist/util"); -const expect = chai.expect +const expect = chai.expect; describe("Elevator Modbus JS", () => { let validate; - before(async () => { - const tdValidate = getTDValidate() - - try { - const response = await Promise.all([tdValidate]) - validate = response[0].validate - } - catch (error) { - console.log(error) - } - }) + before(async () => { + const tdValidate = getTDValidate(); - 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) - } + 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); 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 6090806..2e9f050 100644 --- a/util/util.ts +++ b/util/util.ts @@ -29,12 +29,12 @@ export type ValidateResponse = { 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(() => { From 3468cbf06560753fa96e4cc43cd022e96c783acd Mon Sep 17 00:00:00 2001 From: Hasan Eroglu Date: Fri, 27 Sep 2024 18:12:25 +0200 Subject: [PATCH 5/7] run prettier Signed-off-by: Hasan Eroglu --- mashups/smart-home/package.json | 3 ++- .../http/ts/package.json | 3 ++- .../coap/{ => js}/client-tests/README.md | 0 .../content-negotiation-coap-client.js | 0 ...ode-wot-content-negotiation-coap-client.js | 0 .../node-wot-simple-coap-client.js | 0 .../client-tests/simple-coap-client.js | 27 ++++++++++++++----- things/calculator/coap/js/package.json | 3 ++- .../http/{ => express}/client-tests/README.md | 0 .../content-negotiation-http-client.js | 0 ...ode-wot-content-negotiation-http-client.js | 0 .../node-wot-simple-http-client.js | 0 .../client-tests/simple-http-client.js | 0 things/calculator/http/express/package.json | 3 ++- .../mqtt/js/mqtt-calculator.td.json | 27 +++++-------------- things/calculator/mqtt/js/package.json | 3 ++- .../modbus/js/modbus-elevator.td.json | 11 +++----- things/elevator/modbus/js/package.json | 3 ++- 18 files changed, 43 insertions(+), 40 deletions(-) rename things/calculator/coap/{ => js}/client-tests/README.md (100%) rename things/calculator/coap/{ => js}/client-tests/content-negotiation-coap-client.js (100%) rename things/calculator/coap/{ => js}/client-tests/node-wot-content-negotiation-coap-client.js (100%) rename things/calculator/coap/{ => js}/client-tests/node-wot-simple-coap-client.js (100%) rename things/calculator/coap/{ => js}/client-tests/simple-coap-client.js (87%) rename things/calculator/http/{ => express}/client-tests/README.md (100%) rename things/calculator/http/{ => express}/client-tests/content-negotiation-http-client.js (100%) rename things/calculator/http/{ => express}/client-tests/node-wot-content-negotiation-http-client.js (100%) rename things/calculator/http/{ => express}/client-tests/node-wot-simple-http-client.js (100%) rename things/calculator/http/{ => express}/client-tests/simple-http-client.js (100%) diff --git a/mashups/smart-home/package.json b/mashups/smart-home/package.json index 23f220d..9802cd9 100644 --- a/mashups/smart-home/package.json +++ b/mashups/smart-home/package.json @@ -10,7 +10,8 @@ "start:smart-clock": "node ./dist/things/smart-clock.js", "start:all": "./runMashupThings.sh", "lint": "eslint .", - "lint:fix": "eslint . --fix" + "lint:fix": "eslint . --fix", + "format": "prettier --write \"**/*.ts\" \"**/*.json\"" }, "author": "Eclipse Thingweb (https://thingweb.io/)", "license": "EPL-2.0 OR W3C-20150513", diff --git a/things/advanced-coffee-machine/http/ts/package.json b/things/advanced-coffee-machine/http/ts/package.json index 6e1c311..8dff49f 100644 --- a/things/advanced-coffee-machine/http/ts/package.json +++ b/things/advanced-coffee-machine/http/ts/package.json @@ -8,7 +8,8 @@ "build": "tsc -b", "test": "mocha", "lint": "eslint .", - "lint:fix": "eslint . --fix" + "lint:fix": "eslint . --fix", + "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\" \"**/*.json\"" }, "keywords": [ "wot", 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/client-tests/content-negotiation-coap-client.js b/things/calculator/coap/js/client-tests/content-negotiation-coap-client.js similarity index 100% rename from things/calculator/coap/client-tests/content-negotiation-coap-client.js rename to things/calculator/coap/js/client-tests/content-negotiation-coap-client.js diff --git a/things/calculator/coap/client-tests/node-wot-content-negotiation-coap-client.js b/things/calculator/coap/js/client-tests/node-wot-content-negotiation-coap-client.js similarity index 100% rename from things/calculator/coap/client-tests/node-wot-content-negotiation-coap-client.js rename to things/calculator/coap/js/client-tests/node-wot-content-negotiation-coap-client.js diff --git a/things/calculator/coap/client-tests/node-wot-simple-coap-client.js b/things/calculator/coap/js/client-tests/node-wot-simple-coap-client.js similarity index 100% rename from things/calculator/coap/client-tests/node-wot-simple-coap-client.js rename to things/calculator/coap/js/client-tests/node-wot-simple-coap-client.js diff --git a/things/calculator/coap/client-tests/simple-coap-client.js b/things/calculator/coap/js/client-tests/simple-coap-client.js similarity index 87% rename from things/calculator/coap/client-tests/simple-coap-client.js rename to things/calculator/coap/js/client-tests/simple-coap-client.js index 23b6577..2e51a26 100644 --- a/things/calculator/coap/client-tests/simple-coap-client.js +++ b/things/calculator/coap/js/client-tests/simple-coap-client.js @@ -1,14 +1,29 @@ +/******************************************************************************** + * 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}`, - resultEndPoint = `/${thingName}/properties/result`, - lastChangeEndPoint = `/${thingName}/properties/lastChange`, - additionEndPoint = `/${thingName}/actions/add`, - subtractionEndPoint = `/${thingName}/actions/subtract`, - updateEndPoint = `/${thingName}/events/update`; +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 ******/ diff --git a/things/calculator/coap/js/package.json b/things/calculator/coap/js/package.json index 1ebd44f..8aac562 100644 --- a/things/calculator/coap/js/package.json +++ b/things/calculator/coap/js/package.json @@ -11,7 +11,8 @@ ], "scripts": { "lint": "eslint .", - "lint:fix": "eslint . --fix" + "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", 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/client-tests/content-negotiation-http-client.js b/things/calculator/http/express/client-tests/content-negotiation-http-client.js similarity index 100% rename from things/calculator/http/client-tests/content-negotiation-http-client.js rename to things/calculator/http/express/client-tests/content-negotiation-http-client.js diff --git a/things/calculator/http/client-tests/node-wot-content-negotiation-http-client.js b/things/calculator/http/express/client-tests/node-wot-content-negotiation-http-client.js similarity index 100% rename from things/calculator/http/client-tests/node-wot-content-negotiation-http-client.js rename to things/calculator/http/express/client-tests/node-wot-content-negotiation-http-client.js diff --git a/things/calculator/http/client-tests/node-wot-simple-http-client.js b/things/calculator/http/express/client-tests/node-wot-simple-http-client.js similarity index 100% rename from things/calculator/http/client-tests/node-wot-simple-http-client.js rename to things/calculator/http/express/client-tests/node-wot-simple-http-client.js diff --git a/things/calculator/http/client-tests/simple-http-client.js b/things/calculator/http/express/client-tests/simple-http-client.js similarity index 100% rename from things/calculator/http/client-tests/simple-http-client.js rename to things/calculator/http/express/client-tests/simple-http-client.js diff --git a/things/calculator/http/express/package.json b/things/calculator/http/express/package.json index e4f65a7..d33934a 100644 --- a/things/calculator/http/express/package.json +++ b/things/calculator/http/express/package.json @@ -12,7 +12,8 @@ "scripts": { "test": "node simple-calculator.js ; pid = $! ; result=$(mocha --exit *.test.js)) & kill $pid", "lint": "eslint .", - "lint:fix": "eslint . --fix" + "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", 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 b20a48e..bf2f167 100644 --- a/things/calculator/mqtt/js/package.json +++ b/things/calculator/mqtt/js/package.json @@ -11,7 +11,8 @@ ], "scripts": { "lint": "eslint .", - "lint:fix": "eslint . --fix" + "lint:fix": "eslint . --fix", + "format": "prettier --write \"**/*.js\" \"test/**/*.js\" \"**/*.json\"" }, "author": "Eclipse Thingweb (https://thingweb.io/)", "license": "EPL-2.0 OR W3C-20150513", 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 e850848..9e58152 100644 --- a/things/elevator/modbus/js/package.json +++ b/things/elevator/modbus/js/package.json @@ -5,7 +5,8 @@ "main": "main.js", "scripts": { "lint": "eslint .", - "lint:fix": "eslint . --fix" + "lint:fix": "eslint . --fix", + "format": "prettier --write \"**/*.js\" \"test/**/*.js\" \"**/*.json\"" }, "keywords": [ "wot", From c03e13ad4581c51efd646dbb478c699544684a3d Mon Sep 17 00:00:00 2001 From: Hasan Eroglu Date: Fri, 27 Sep 2024 18:21:47 +0200 Subject: [PATCH 6/7] fix eslint issues and run prettier Signed-off-by: Hasan Eroglu --- .github/workflows/ci.yml | 2 +- .../content-negotiation-coap-client.js | 49 ++++++++++++------- ...ode-wot-content-negotiation-coap-client.js | 29 ++++++++--- .../node-wot-simple-coap-client.js | 28 ++++++++--- .../js/client-tests/simple-coap-client.js | 14 +++--- .../content-negotiation-http-client.js | 41 ++++++++++------ ...ode-wot-content-negotiation-http-client.js | 27 +++++++--- .../node-wot-simple-http-client.js | 37 ++++++++++---- .../client-tests/simple-http-client.js | 40 +++++++++------ 9 files changed, 181 insertions(+), 86 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 23b4d1d..c510638 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -68,6 +68,6 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - - uses: actionsx/prettier@v2 + - uses: actionsx/prettier@v3 with: args: --check . 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 index 9aeadd6..83d9c94 100644 --- a/things/calculator/coap/js/client-tests/content-negotiation-coap-client.js +++ b/things/calculator/coap/js/client-tests/content-negotiation-coap-client.js @@ -1,18 +1,33 @@ +/******************************************************************************** + * 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}`, - resultEndPoint = `/${thingName}/properties/result`, - lastChangeEndPoint = `/${thingName}/properties/lastChange`, - additionEndPoint = `/${thingName}/actions/add`, - subtractionEndPoint = `/${thingName}/actions/subtract`, - updateEndPoint = `/${thingName}/events/update`; +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 ******/ +/** **** Thing Description Endpoint ******/ /****************************************/ // GET request to retrieve thing description @@ -28,7 +43,7 @@ function getFullTD(acceptType) { }); getThingDescription.on("response", (res) => { - //TODO: Fix the problem with block wise transfer to be able to parse the response accordingly + // 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" || @@ -53,7 +68,7 @@ function getFullTD(acceptType) { } /****************************************/ -/*********** Result Endpoint ************/ +/** ********* Result Endpoint ************/ /****************************************/ // GET request to retrieve a property (result) @@ -131,7 +146,7 @@ function observeResultProperty(acceptType) { } /****************************************/ -/********** lastChange Endpoint *********/ +/** ******** lastChange Endpoint *********/ /****************************************/ // GET request to retrieve a property (lastChange) @@ -208,7 +223,7 @@ function observeLastChangeProperty(acceptType) { } /****************************************/ -/*********** Addition Endpoint **********/ +/** ********* Addition Endpoint **********/ /****************************************/ // POST request to perform the addition action @@ -252,7 +267,7 @@ function addNumber(acceptType, contentType, numberToAdd) { } /****************************************/ -/********** Subtraction Endpoint ********/ +/** ******** Subtraction Endpoint ********/ /****************************************/ // POST request to perform the subtract action @@ -296,7 +311,7 @@ function subtractNumber(acceptType, contentType, numberToSubtract) { } /****************************************/ -/*********** Update Endpoint ************/ +/** ********* Update Endpoint ************/ /****************************************/ /** @@ -339,16 +354,16 @@ function observeUpdateEvent(acceptType) { observeUpdate.end(); } -//Test the main functionality of the content-negotiation-calculator-thing +// Test the main functionality of the content-negotiation-calculator-thing function runCalculatorInteractions() { - //Main GET and POST requests + // 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 + // Observation of properties and events after 1 second setTimeout(() => { console.log("\n-------- Start observation --------\n"); observeResultProperty("application/json"); @@ -356,7 +371,7 @@ function runCalculatorInteractions() { observeUpdateEvent("application/json"); }, 1000); - //Update the property result after 2.5 seconds to test the observation + // Update the property result after 2.5 seconds to test the observation setTimeout(() => { addNumber("application/cbor", "application/json", 1); }, 2500); 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 index 3984c3f..ebc42f3 100644 --- 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 @@ -1,3 +1,18 @@ +/******************************************************************************** + * 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"); @@ -17,18 +32,18 @@ servient console.log(td); // read property result - let result = await thing.readProperty("result", { formIndex: 2 }); + const result = await thing.readProperty("result", { formIndex: 2 }); console.log("result: ", await result.value()); // read property lastChange - let lastChange = await thing.readProperty("lastChange", { + const lastChange = await thing.readProperty("lastChange", { formIndex: 2, }); console.log("lastChange: ", await lastChange.value()); console.log("\n ---------- \n"); - //Observe properties + // Observe properties thing.observeProperty("result", async (data) => { console.log("Result observe:", await data.value()); }); @@ -41,11 +56,11 @@ servient console.log("Update event:", await data.value()); }); - //Invoke addition action - let add = await thing.invokeAction("add", 3, { formIndex: 1 }); + // Invoke addition action + const 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, { + // Invoke subtraction action + const subtract = await thing.invokeAction("subtract", 1, { formIndex: 3, }); console.log("Subtraction value:", await subtract.value()); 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 index 8ebf38c..4828452 100644 --- 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 @@ -1,4 +1,18 @@ -// example-client.js +/******************************************************************************** + * 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"); @@ -18,11 +32,11 @@ servient console.log(td); // read property result - let result = await thing.readProperty("result"); + const result = await thing.readProperty("result"); console.log("result: ", await result.value()); // read property lastChange - let lastChange = await thing.readProperty("lastChange"); + const lastChange = await thing.readProperty("lastChange"); console.log("lastChange: ", await lastChange.value()); console.log("\n------------\n"); @@ -40,11 +54,11 @@ servient console.log("Update event:", await data.value()); }); - //Invoke addition action - let addition = await thing.invokeAction("add", 2); + // Invoke addition action + const addition = await thing.invokeAction("add", 2); console.log("Addition result: ", await addition.value()); - //Invoke addition subtraction - let subtraction = await thing.invokeAction("subtract", 3); + // Invoke addition subtraction + const subtraction = await thing.invokeAction("subtract", 3); console.log("Subtraction result: ", await subtraction.value()); console.log("\n------------\n"); diff --git a/things/calculator/coap/js/client-tests/simple-coap-client.js b/things/calculator/coap/js/client-tests/simple-coap-client.js index 2e51a26..48bba53 100644 --- a/things/calculator/coap/js/client-tests/simple-coap-client.js +++ b/things/calculator/coap/js/client-tests/simple-coap-client.js @@ -26,7 +26,7 @@ const subtractionEndPoint = `/${thingName}/actions/subtract`; const updateEndPoint = `/${thingName}/events/update`; /****************************************/ -/****** Thing Description Endpoint ******/ +/** **** Thing Description Endpoint ******/ /****************************************/ function getThingDescription() { @@ -113,7 +113,7 @@ function observeResultProperty() { } /****************************************/ -/********** lastChange Endpoint *********/ +/** ******** lastChange Endpoint *********/ /****************************************/ function getLastChange() { @@ -171,7 +171,7 @@ function observeLastChangeProperty() { } /****************************************/ -/*********** Addition Endpoint **********/ +/** ********* Addition Endpoint **********/ /****************************************/ function addNumber(numberToAdd) { @@ -202,7 +202,7 @@ function addNumber(numberToAdd) { } /****************************************/ -/********** Subtraction Endpoint ********/ +/** ******** Subtraction Endpoint ********/ /****************************************/ function subtractNumber(numberToSubtract) { @@ -236,7 +236,7 @@ function subtractNumber(numberToSubtract) { } /****************************************/ -/*********** Update Endpoint ************/ +/** ********* Update Endpoint ************/ /****************************************/ /** @@ -279,7 +279,7 @@ function runCalculatorInteractions() { addNumber(3); subtractNumber(2); - //Start the observation of properties and events after 1 second + // Start the observation of properties and events after 1 second setTimeout(() => { console.log("\n-------- Start observation --------\n"); observeResultProperty(); @@ -287,7 +287,7 @@ function runCalculatorInteractions() { observeUpdateEvent(); }, 1000); - //Update the property result after 2.5 seconds to test the observation + // Update the property result after 2.5 seconds to test the observation setTimeout(() => { addNumber(1); }, 2500); 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 index b6e24a6..8d7a0a8 100644 --- a/things/calculator/http/express/client-tests/content-negotiation-http-client.js +++ b/things/calculator/http/express/client-tests/content-negotiation-http-client.js @@ -1,20 +1,29 @@ -/** - * @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. - */ +/******************************************************************************** + * 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", - resultEndPoint = "/properties/result", - resultEndPointObserve = `${resultEndPoint}/observe`, - lastChangeEndPoint = "/properties/lastChange", - lastChangeEndPointObserve = `${lastChangeEndPoint}/observe`, - additionEndPoint = "/actions/add", - subtractionEndPoint = "/actions/subtract", - updateEndPoint = "/events/update"; +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 @@ -90,7 +99,7 @@ function listenToResultProperty(acceptType) { console.error("Error with Result property SSE:", error); }; - //Closing the event source after 6 seconds + // Closing the event source after 6 seconds setTimeout(() => { resultEventSource.close(); console.log("- Closing Result Property SSE"); @@ -150,7 +159,7 @@ function listenToLastChangeProperty(acceptType) { console.error("Error with lastChange property SSE:", error); }; - //Closing the event source after 6 seconds + // Closing the event source after 6 seconds setTimeout(() => { lastChangeEventSource.close(); console.log("- Closing lastChange Property SSE"); @@ -257,7 +266,7 @@ function listenToUpdateEvent(acceptType) { console.error("Error with Update event SSE:", error); }; - //Closing the event source after 6 seconds + // Closing the event source after 6 seconds setTimeout(() => { updateEventSource.close(); console.log("- Closing Update Event SSE"); 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 index 94ad5ef..5e7f603 100644 --- 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 @@ -1,3 +1,18 @@ +/******************************************************************************** + * 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"); @@ -11,19 +26,19 @@ servient "http://localhost:3001/http-express-calculator-content-negotiation", ); - let thing = await WoT.consume(td); + const thing = await WoT.consume(td); console.log(td); - let result = await thing.readProperty("result", { formIndex: 0 }); + const result = await thing.readProperty("result", { formIndex: 0 }); console.log("Result property: ", await result.value()); - let lastChange = await thing.readProperty("lastChange", { + 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 + // 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()); @@ -36,7 +51,7 @@ servient // let subtraction2 = await thing.invokeAction("subtract", 5, {formIndex: 3}) // console.log(await subtraction2.value()); - //Update event property + // Update event property // thing.subscribeEvent("update", async (data) => { // console.log("Update event:", await data.value()); // }) 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 index 7c5051d..db6b135 100644 --- 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 @@ -1,3 +1,18 @@ +/******************************************************************************** + * 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"); @@ -11,29 +26,31 @@ servient "http://localhost:3000/http-express-calculator-simple", ); - let thing = await WoT.consume(td); + const thing = await WoT.consume(td); console.log(td); - //Property endpoints - let result = await (await thing.readProperty("result")).value(); + // Property endpoints + const result = await (await thing.readProperty("result")).value(); console.log("Read result:", result); - let lastChange = await (await thing.readProperty("lastChange")).value(); + const lastChange = await ( + await thing.readProperty("lastChange") + ).value(); console.log("Read lastChange:", lastChange); - //Update event observation + // Update event observation thing.subscribeEvent("update", async (data) => { - console.log("Update event:", (await data.value())["data"]); + console.log("Update event:", (await data.value()).data); }); - //Action endpoints - let additionResult = await thing.invokeAction("add", 3); + // Action endpoints + const additionResult = await thing.invokeAction("add", 3); console.log("Addition result: ", await additionResult.value()); - let subtractionResult = await thing.invokeAction("subtract", 3); + 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) + // 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()); }); }) diff --git a/things/calculator/http/express/client-tests/simple-http-client.js b/things/calculator/http/express/client-tests/simple-http-client.js index f274b91..faeaaa6 100644 --- a/things/calculator/http/express/client-tests/simple-http-client.js +++ b/things/calculator/http/express/client-tests/simple-http-client.js @@ -1,18 +1,28 @@ -/** - * @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. - */ +/******************************************************************************** + * 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", - resultEndPoint = "/properties/result", - resultEndPointObserve = `${resultEndPoint}/observe`, - lastChangeEndPoint = "/properties/lastChange", - lastChangeEndPointObserve = `${lastChangeEndPoint}/observe`, - additionEndPoint = "/actions/add", - subtractionEndPoint = "/actions/subtract", - updateEndPoint = "/events/update"; +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 @@ -49,7 +59,7 @@ function listenToResult() { console.error("Error with Result SSE:", error); }; - //Closing the event source after 6 seconds + // Closing the event source after 6 seconds setTimeout(() => { resultEventSource.close(); console.log("- Closing Result Property SSE"); @@ -83,7 +93,7 @@ function listenToLastChange() { console.error("Error with lastChange SSE:", error); }; - //Closing the event source after 6 seconds + // Closing the event source after 6 seconds setTimeout(() => { lastChangeEventSource.close(); console.log("- Closing lastChange Property SSE"); @@ -139,7 +149,7 @@ function listenToUpdateEvent() { console.error("Error with Update SSE:", error); }; - //Closing the event source after 6 seconds + // Closing the event source after 6 seconds setTimeout(() => { updateEventSource.close(); console.log("- Closing Update Event SSE"); From 41ed9c8f185207e704ede860d3ac79560ede6d86 Mon Sep 17 00:00:00 2001 From: Hasan Eroglu Date: Fri, 27 Sep 2024 18:23:54 +0200 Subject: [PATCH 7/7] format with prettier v2 Signed-off-by: Hasan Eroglu --- .github/workflows/ci.yml | 2 +- mashups/smart-home/mashup-logic.ts | 10 ++--- .../things/simple-coffee-machine.ts | 4 +- mashups/smart-home/things/smart-clock.ts | 2 +- package-lock.json | 11 ++--- package.json | 2 +- .../http/ts/src/main.ts | 26 ++++++------ .../http/ts/test/client.test.ts | 2 +- .../http/ts/test/td.test.ts | 4 +- things/advanced-coffee-machine/tm.test.js | 2 +- .../content-negotiation-coap-client.js | 26 ++++++------ ...ode-wot-content-negotiation-coap-client.js | 2 +- .../node-wot-simple-coap-client.js | 2 +- .../js/client-tests/simple-coap-client.js | 42 +++++++++++++------ .../js/coap-content-negotiation-calculator.js | 14 +++---- .../coap/js/coap-simple-calculator.js | 4 +- things/calculator/coap/js/test/client.test.js | 28 ++++++------- things/calculator/coap/js/test/fixtures.js | 2 +- things/calculator/coap/js/test/td.test.js | 2 +- .../content-negotiation-http-client.js | 14 +++---- ...ode-wot-content-negotiation-http-client.js | 2 +- .../node-wot-simple-http-client.js | 2 +- .../client-tests/simple-http-client.js | 4 +- .../http-content-negotiation-calculator.js | 8 ++-- .../http/express/http-simple-calculator.js | 2 +- .../http/express/test/client.test.js | 28 ++++++------- .../calculator/http/express/test/fixtures.js | 2 +- .../calculator/http/express/test/td.test.js | 8 ++-- things/calculator/http/flask/test/td.test.js | 8 ++-- things/calculator/mqtt/js/main.js | 10 ++--- things/calculator/mqtt/js/test/client.test.js | 6 +-- things/calculator/tm.test.js | 2 +- things/data-schema-thing/http/ts/src/main.ts | 22 +++++----- .../http/ts/test/client.test.ts | 4 +- .../data-schema-thing/http/ts/test/td.test.ts | 4 +- things/data-schema-thing/tm.test.js | 2 +- things/elevator/modbus/js/main.js | 20 ++++----- things/elevator/modbus/js/test/td.test.js | 2 +- things/elevator/tm.test.js | 2 +- util/util.ts | 4 +- 40 files changed, 180 insertions(+), 163 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c510638..23b4d1d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -68,6 +68,6 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - - uses: actionsx/prettier@v3 + - uses: actionsx/prettier@v2 with: args: --check . diff --git a/mashups/smart-home/mashup-logic.ts b/mashups/smart-home/mashup-logic.ts index 3ebae1c..51469f0 100644 --- a/mashups/smart-home/mashup-logic.ts +++ b/mashups/smart-home/mashup-logic.ts @@ -40,14 +40,14 @@ const wotHelper = new Helpers(servient); // we will fetch the TDs of the devices const coffeeMachineTD = (await wotHelper.fetch( - coffeeMachineURL, + 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`, + `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`, + `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 @@ -79,7 +79,7 @@ const wotHelper = new Helpers(servient); "Current time is " + currentTime.hour.toString().padStart(2, "0") + ":" + - currentTime.minute.toString().padStart(2, "0"), + currentTime.minute.toString().padStart(2, "0") ); // To avoid accidental brews, a flag is used to check whether a coffee was brewed before @@ -101,6 +101,6 @@ const wotHelper = new Helpers(servient); morningCoffeeFlag = false; } }, 1000); - }, + } ); })(); diff --git a/mashups/smart-home/things/simple-coffee-machine.ts b/mashups/smart-home/things/simple-coffee-machine.ts index b880bf4..18a36d8 100644 --- a/mashups/smart-home/things/simple-coffee-machine.ts +++ b/mashups/smart-home/things/simple-coffee-machine.ts @@ -29,7 +29,7 @@ servient.addServer( new HttpServer({ baseUri: `http://${hostname}:${httpPort}`, port: parseInt(httpPort), - }), + }) ); let waterAmount = 1000; @@ -212,7 +212,7 @@ servient.start().then((WoT) => { thing.expose().then(() => { console.info(thing.getThingDescription().title + " ready"); console.info( - "TD available at http://" + hostname + ":" + httpPort, + "TD available at http://" + hostname + ":" + httpPort ); }); }) diff --git a/mashups/smart-home/things/smart-clock.ts b/mashups/smart-home/things/smart-clock.ts index b94f6a3..9688d83 100644 --- a/mashups/smart-home/things/smart-clock.ts +++ b/mashups/smart-home/things/smart-clock.ts @@ -28,7 +28,7 @@ servient.addServer( new CoapServer({ address: hostname, port: parseInt(port), - }), + }) ); let minuteCounter = 0; diff --git a/package-lock.json b/package-lock.json index a6b1b21..0f3d29f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -31,7 +31,7 @@ "eslint-plugin-unused-imports": "^4.1.4", "eslint-plugin-workspaces": "^0.10.1", "mocha": "^10.7.3", - "prettier": "^3.3.3" + "prettier": "^2.3.2" } }, "mashups/smart-home": { @@ -5557,14 +5557,15 @@ } }, "node_modules/prettier": { - "version": "3.3.3", + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", "dev": true, - "license": "MIT", "bin": { - "prettier": "bin/prettier.cjs" + "prettier": "bin-prettier.js" }, "engines": { - "node": ">=14" + "node": ">=10.13.0" }, "funding": { "url": "https://github.com/prettier/prettier?sponsor=1" diff --git a/package.json b/package.json index 5bc8fb0..74182c3 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,6 @@ "eslint-plugin-unused-imports": "^4.1.4", "eslint-plugin-workspaces": "^0.10.1", "mocha": "^10.7.3", - "prettier": "^3.3.3" + "prettier": "^2.3.2" } } diff --git a/things/advanced-coffee-machine/http/ts/src/main.ts b/things/advanced-coffee-machine/http/ts/src/main.ts index 93ba5ff..848f16e 100644 --- a/things/advanced-coffee-machine/http/ts/src/main.ts +++ b/things/advanced-coffee-machine/http/ts/src/main.ts @@ -76,7 +76,7 @@ let thingModel; if (tmPath != null && tmPath !== "") { thingModel = JSON.parse( - fs.readFileSync(path.join(__dirname, tmPath)).toString(), + fs.readFileSync(path.join(__dirname, tmPath)).toString() ); } @@ -99,7 +99,7 @@ servient.addServer( new HttpServer({ baseUri: `http://${hostname}:${portNumber}`, port: portNumber, - }), + }) ); servient @@ -126,15 +126,15 @@ servient thing.setPropertyReadHandler( "allAvailableResources", - async () => allAvailableResources, + async () => allAvailableResources ); thing.setPropertyReadHandler( "possibleDrinks", - async () => possibleDrinks, + async () => possibleDrinks ); thing.setPropertyReadHandler( "maintenanceNeeded", - async () => maintenanceNeeded, + async () => maintenanceNeeded ); thing.setPropertyReadHandler("schedules", async () => schedules); @@ -150,7 +150,7 @@ servient // (the notify function here simply logs a message to the console) notify( "admin@coffeeMachine.com", - `maintenanceNeeded property has changed, new value is: ${maintenanceNeeded}`, + `maintenanceNeeded property has changed, new value is: ${maintenanceNeeded}` ); } }); @@ -181,7 +181,7 @@ servient } } throw Error("Please specify id variable as uriVariables."); - }, + } ); // Override a read handler for availableResourceLevel property, @@ -205,7 +205,7 @@ servient } } throw Error("Please specify id variable as uriVariables."); - }, + } ); // Set up a handler for makeDrink action @@ -291,22 +291,22 @@ servient newResources.water -= Math.ceil( quantity * sizeQuantifiers[size] * - drinkRecipes[drinkId].water, + drinkRecipes[drinkId].water ); newResources.milk -= Math.ceil( quantity * sizeQuantifiers[size] * - drinkRecipes[drinkId].milk, + drinkRecipes[drinkId].milk ); newResources.chocolate -= Math.ceil( quantity * sizeQuantifiers[size] * - drinkRecipes[drinkId].chocolate, + drinkRecipes[drinkId].chocolate ); newResources.coffeeBeans -= Math.ceil( quantity * sizeQuantifiers[size] * - drinkRecipes[drinkId].coffeeBeans, + drinkRecipes[drinkId].coffeeBeans ); // Check if the amount of available resources is sufficient to make a drink @@ -314,7 +314,7 @@ servient if (newResources[resource] <= 0) { thing.emitEvent( "outOfResource", - `Low level of ${resource}: ${newResources[resource]}%`, + `Low level of ${resource}: ${newResources[resource]}%` ); return { result: false, 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 f5a92d6..b7e2510 100644 --- a/things/advanced-coffee-machine/http/ts/test/client.test.ts +++ b/things/advanced-coffee-machine/http/ts/test/client.test.ts @@ -31,7 +31,7 @@ describe("Client Tests", () => { try { const WoT = await servient.start(); const td: WoT.ThingDescription = await WoT.requestThingDescription( - `http://localhost:${port}/http-advanced-coffee-machine`, + `http://localhost:${port}/http-advanced-coffee-machine` ); thing = await WoT.consume(td); } catch (error) { 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 1bc55fc..8ab2f23 100644 --- a/things/advanced-coffee-machine/http/ts/test/td.test.ts +++ b/things/advanced-coffee-machine/http/ts/test/td.test.ts @@ -47,7 +47,7 @@ describe("TD Test", () => { response.on("end", () => { try { const result = JSON.parse( - Buffer.concat(body).toString(), + Buffer.concat(body).toString() ); const valid = validate && result !== "" @@ -60,7 +60,7 @@ describe("TD Test", () => { done(error); } }); - }, + } ); }); }); diff --git a/things/advanced-coffee-machine/tm.test.js b/things/advanced-coffee-machine/tm.test.js index 67889b9..34ffc5b 100644 --- a/things/advanced-coffee-machine/tm.test.js +++ b/things/advanced-coffee-machine/tm.test.js @@ -23,7 +23,7 @@ describe("Advanced Coffee Machine", () => { validate = ajv.compile(tmSchema); done(); }); - }, + } ); }); 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 index 83d9c94..9922afe 100644 --- a/things/calculator/coap/js/client-tests/content-negotiation-coap-client.js +++ b/things/calculator/coap/js/client-tests/content-negotiation-coap-client.js @@ -51,13 +51,13 @@ function getFullTD(acceptType) { ) { console.log( "Thing Description (json):\n", - JSON.parse(res.payload.toString()), + JSON.parse(res.payload.toString()) ); } else { const decodedData = cbor.decode(res.payload); console.log( "Thing Description (cbor):\n", - JSON.parse(decodedData), + JSON.parse(decodedData) ); } } else { @@ -90,7 +90,7 @@ function getResult(acceptType) { if (contentType.includes("application/json")) { console.log( "Result (json): ", - JSON.parse(res.payload.toString()), + JSON.parse(res.payload.toString()) ); } else { const decodedData = cbor.decode(res.payload); @@ -127,13 +127,13 @@ function observeResultProperty(acceptType) { if (contentType.includes("application/json")) { console.log( "Observe result property (json): ", - JSON.parse(res.payload.toString()), + JSON.parse(res.payload.toString()) ); } else { const decodedData = cbor.decode(res.payload); console.log( "Observe result property (cbor): ", - decodedData, + decodedData ); } } else { @@ -167,7 +167,7 @@ function getLastChange(acceptType) { if (contentType.includes("application/json")) { console.log( "Last Change (json): ", - JSON.parse(res.payload.toString()), + JSON.parse(res.payload.toString()) ); } else { const decodedData = cbor.decode(res.payload); @@ -204,13 +204,13 @@ function observeLastChangeProperty(acceptType) { if (contentType.includes("application/json")) { console.log( "Observe lastChange property (json): ", - JSON.parse(res.payload.toString()), + JSON.parse(res.payload.toString()) ); } else { const decodedData = cbor.decode(res.payload); console.log( "Observe lastChange property (cbor): ", - decodedData, + decodedData ); } } else { @@ -243,7 +243,7 @@ function addNumber(acceptType, contentType, numberToAdd) { addNumberReq.write( contentType === "application/json" ? JSON.stringify(numberToAdd) - : cbor.encode(numberToAdd), + : cbor.encode(numberToAdd) ); addNumberReq.on("response", (res) => { @@ -253,7 +253,7 @@ function addNumber(acceptType, contentType, numberToAdd) { if (contentType.includes("application/json")) { console.log( "Addition result (json): ", - JSON.parse(res.payload.toString()), + JSON.parse(res.payload.toString()) ); } else { const decodedData = cbor.decode(res.payload); @@ -287,7 +287,7 @@ function subtractNumber(acceptType, contentType, numberToSubtract) { subtractNumberReq.write( contentType === "application/json" ? JSON.stringify(numberToSubtract) - : cbor.encode(numberToSubtract), + : cbor.encode(numberToSubtract) ); subtractNumberReq.on("response", (res) => { @@ -297,7 +297,7 @@ function subtractNumber(acceptType, contentType, numberToSubtract) { if (contentType.includes("application/json")) { console.log( "Subtraction result (json): ", - JSON.parse(res.payload.toString()), + JSON.parse(res.payload.toString()) ); } else { const decodedData = cbor.decode(res.payload); @@ -338,7 +338,7 @@ function observeUpdateEvent(acceptType) { if (contentType.includes("application/json")) { console.log( "Observe update event (json): ", - JSON.parse(res.payload.toString()), + JSON.parse(res.payload.toString()) ); } else { const decodedData = cbor.decode(res.payload); 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 index ebc42f3..544f70c 100644 --- 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 @@ -25,7 +25,7 @@ servient .then(async (WoT) => { try { const td = await WoT.requestThingDescription( - "coap://localhost:5684/coap-calculator-content-negotiation", + "coap://localhost:5684/coap-calculator-content-negotiation" ); const thing = await WoT.consume(td); 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 index 4828452..298033c 100644 --- 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 @@ -25,7 +25,7 @@ servient .then(async (WoT) => { try { const td = await WoT.requestThingDescription( - "coap://localhost:5683/coap-calculator-simple", + "coap://localhost:5683/coap-calculator-simple" ); const thing = await WoT.consume(td); diff --git a/things/calculator/coap/js/client-tests/simple-coap-client.js b/things/calculator/coap/js/client-tests/simple-coap-client.js index 48bba53..9f44867 100644 --- a/things/calculator/coap/js/client-tests/simple-coap-client.js +++ b/things/calculator/coap/js/client-tests/simple-coap-client.js @@ -42,11 +42,13 @@ function getThingDescription() { if (res.code === "2.05") { console.log( "Thing Description: \n", - JSON.parse(res.payload.toString()), + JSON.parse(res.payload.toString()) ); } else { console.error( - `Failed to get Thing Description: ${res.code} - ${res.payload.toString()}`, + `Failed to get Thing Description: ${ + res.code + } - ${res.payload.toString()}` ); } }); @@ -71,7 +73,9 @@ function getResult() { console.log("Result:", JSON.parse(res.payload.toString())); } else { console.error( - `Failed to get Property "result": ${res.code} - ${res.payload.toString()}`, + `Failed to get Property "result": ${ + res.code + } - ${res.payload.toString()}` ); } }); @@ -98,11 +102,13 @@ function observeResultProperty() { if (res.code === "2.05") { console.log( "Observe result property:", - JSON.parse(res.payload.toString()), + JSON.parse(res.payload.toString()) ); } else { console.error( - `Failed to observe Event "update": ${res.code} - ${res.payload.toString()}`, + `Failed to observe Event "update": ${ + res.code + } - ${res.payload.toString()}` ); } }); @@ -129,7 +135,9 @@ function getLastChange() { console.log("Last Change:", JSON.parse(res.payload.toString())); } else { console.error( - `Failed to get Property "lastChange": ${res.code} - ${res.payload.toString()}`, + `Failed to get Property "lastChange": ${ + res.code + } - ${res.payload.toString()}` ); } }); @@ -156,11 +164,13 @@ function observeLastChangeProperty() { if (res.code === "2.05") { console.log( "Observe lastChange property:", - JSON.parse(res.payload.toString()), + JSON.parse(res.payload.toString()) ); } else { console.error( - `Failed to observe Event "update": ${res.code} - ${res.payload.toString()}`, + `Failed to observe Event "update": ${ + res.code + } - ${res.payload.toString()}` ); } }); @@ -194,7 +204,9 @@ function addNumber(numberToAdd) { console.log("Addition result:", JSON.parse(res.payload.toString())); } else { console.error( - `Failed to call the Action "add": ${res.code} - ${res.payload.toString()}`, + `Failed to call the Action "add": ${ + res.code + } - ${res.payload.toString()}` ); } }); @@ -224,11 +236,13 @@ function subtractNumber(numberToSubtract) { if (res.code === "2.05") { console.log( "Subtraction result:", - JSON.parse(res.payload.toString()), + JSON.parse(res.payload.toString()) ); } else { console.error( - `Failed to call the Action "subtract": ${res.code} - ${res.payload.toString()}`, + `Failed to call the Action "subtract": ${ + res.code + } - ${res.payload.toString()}` ); } }); @@ -258,11 +272,13 @@ function observeUpdateEvent() { if (res.code === "2.05") { console.log( "Observe update event:", - JSON.parse(res.payload.toString()), + JSON.parse(res.payload.toString()) ); } else { console.error( - `Failed to observe Event "update": ${res.code} - ${res.payload.toString()}`, + `Failed to observe Event "update": ${ + res.code + } - ${res.payload.toString()}` ); } }); diff --git a/things/calculator/coap/js/coap-content-negotiation-calculator.js b/things/calculator/coap/js/coap-content-negotiation-calculator.js index 0fa4869..7e34847 100644 --- a/things/calculator/coap/js/coap-content-negotiation-calculator.js +++ b/things/calculator/coap/js/coap-content-negotiation-calculator.js @@ -228,7 +228,7 @@ for (const key in thingDescription.events) { try { fs.writeFileSync( "coap-content-negotiation-calculator-thing.td.jsonld", - JSON.stringify(thingDescription, null, 2), + JSON.stringify(thingDescription, null, 2) ); } catch (err) { console.log(err); @@ -262,7 +262,7 @@ server.on("request", (req, res) => { res.end(JSON.stringify(thingDescription)); } else if (acceptHeaders.includes("application/cbor")) { const cborData = cbor.encode( - JSON.stringify(thingDescription), + JSON.stringify(thingDescription) ); res.setOption("Content-Format", "application/cbor"); res.end(cborData); @@ -297,7 +297,7 @@ server.on("request", (req, res) => { res.statusCode = 205; if ( acceptHeaders.includes( - "application/json", + "application/json" ) || acceptHeaders.includes("application/*") || acceptHeaders === "*/*" @@ -314,7 +314,7 @@ server.on("request", (req, res) => { res.on("finish", () => { console.log( - "Result property observation has been closed", + "Result property observation has been closed" ); clearInterval(changeInterval); }); @@ -345,7 +345,7 @@ server.on("request", (req, res) => { res.statusCode = 205; if ( acceptHeaders.includes( - "application/json", + "application/json" ) || acceptHeaders.includes("application/*") || acceptHeaders === "*/*" @@ -362,7 +362,7 @@ server.on("request", (req, res) => { res.on("finish", () => { console.log( - "lastChange property observation has been closed", + "lastChange property observation has been closed" ); clearInterval(changeInterval); }); @@ -435,7 +435,7 @@ server.on("request", (req, res) => { if (reqContentType.includes("application/json")) { numberToSubtract = JSON.parse( - req.payload.toString(), + req.payload.toString() ); } else { numberToSubtract = cbor.decode(req.payload); diff --git a/things/calculator/coap/js/coap-simple-calculator.js b/things/calculator/coap/js/coap-simple-calculator.js index b462a75..fbfadd7 100644 --- a/things/calculator/coap/js/coap-simple-calculator.js +++ b/things/calculator/coap/js/coap-simple-calculator.js @@ -127,7 +127,7 @@ for (const key in thingDescription.events) { try { fs.writeFileSync( "coap-simple-calculator-thing.td.jsonld", - JSON.stringify(thingDescription, null, 2), + JSON.stringify(thingDescription, null, 2) ); } catch (err) { console.log(err); @@ -206,7 +206,7 @@ server.on("request", (req, res) => { res.on("finish", () => { console.log( - "Client stopped the lastChange observation", + "Client stopped the lastChange observation" ); clearInterval(changeInterval); }); diff --git a/things/calculator/coap/js/test/client.test.js b/things/calculator/coap/js/test/client.test.js index ec7e9e6..f71b54b 100644 --- a/things/calculator/coap/js/test/client.test.js +++ b/things/calculator/coap/js/test/client.test.js @@ -54,7 +54,7 @@ describe("Client Tests", () => { before(async () => { try { const td = await WoT.requestThingDescription( - `coap://localhost:${simplePort}/coap-calculator-simple`, + `coap://localhost:${simplePort}/coap-calculator-simple` ); thing = await WoT.consume(td); } catch (error) { @@ -82,7 +82,7 @@ describe("Client Tests", () => { await thing.invokeAction("subtract", valueToSubtract); const newResultValue = await readProperty(thing, "result"); expect(newResultValue).to.be.equal( - resultValue - valueToSubtract, + resultValue - valueToSubtract ); }); }); @@ -98,7 +98,7 @@ describe("Client Tests", () => { "lastChange", async (response) => { value = await response.value(); - }, + } ); setTimeout(async () => { @@ -124,11 +124,11 @@ describe("Client Tests", () => { const valueToSubtract = 3; const response = await thing.invokeAction( "subtract", - valueToSubtract, + valueToSubtract ); const actionResultValue = await response.value(); expect(actionResultValue).to.be.equal( - resultValue - valueToSubtract, + resultValue - valueToSubtract ); }); }); @@ -146,9 +146,9 @@ describe("Client Tests", () => { "update", async (response) => { await expect( - response.value, + response.value ).to.have.eventually.be.equal(resultValue + valueToAdd); - }, + } ); await subscription.stop(); @@ -162,7 +162,7 @@ describe("Client Tests", () => { before(async () => { try { const td = await WoT.requestThingDescription( - `coap://localhost:${contentNegotiationPort}/coap-calculator-content-negotiation`, + `coap://localhost:${contentNegotiationPort}/coap-calculator-content-negotiation` ); thing = await WoT.consume(td); } catch (error) { @@ -190,7 +190,7 @@ describe("Client Tests", () => { await thing.invokeAction("subtract", valueToSubtract); const newResultValue = await readProperty(thing, "result"); expect(newResultValue).to.be.equal( - resultValue - valueToSubtract, + resultValue - valueToSubtract ); }); }); @@ -206,7 +206,7 @@ describe("Client Tests", () => { "lastChange", async (response) => { value = await response.value(); - }, + } ); setTimeout(async () => { @@ -232,11 +232,11 @@ describe("Client Tests", () => { const valueToSubtract = 3; const response = await thing.invokeAction( "subtract", - valueToSubtract, + valueToSubtract ); const actionResultValue = await response.value(); expect(actionResultValue).to.be.equal( - resultValue - valueToSubtract, + resultValue - valueToSubtract ); }); }); @@ -254,9 +254,9 @@ describe("Client Tests", () => { "update", async (response) => { await expect( - response.value, + 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 5690372..07f564f 100644 --- a/things/calculator/coap/js/test/fixtures.js +++ b/things/calculator/coap/js/test/fixtures.js @@ -40,7 +40,7 @@ const mochaGlobalSetup = async function () { path.join( __dirname, "..", - "coap-content-negotiation-calculator.js", + "coap-content-negotiation-calculator.js" ), "-p", `${contentNegotiationPort}`, diff --git a/things/calculator/coap/js/test/td.test.js b/things/calculator/coap/js/test/td.test.js index 266a338..91eb852 100644 --- a/things/calculator/coap/js/test/td.test.js +++ b/things/calculator/coap/js/test/td.test.js @@ -38,7 +38,7 @@ describe("Calculator CoAP JS", () => { describe("Calculator Simple", () => { it("should have a valid TD", (done) => { const req = coap.request( - `coap://localhost:${simplePort}/coap-calculator-simple`, + `coap://localhost:${simplePort}/coap-calculator-simple` ); req.on("response", (res) => { 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 index 8d7a0a8..a18a19e 100644 --- a/things/calculator/http/express/client-tests/content-negotiation-http-client.js +++ b/things/calculator/http/express/client-tests/content-negotiation-http-client.js @@ -140,7 +140,7 @@ function listenToLastChangeProperty(acceptType) { headers: { Accept: acceptType, }, - }, + } ); lastChangeEventSource.onmessage = (e) => { @@ -282,20 +282,20 @@ async function runCalculatorInteractions() { console.log("Full thing: \n", await getFullTD("application/cbor")); console.log( "Current number: ", - await getCurrentResult("application/json"), + 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"), + await addNumber(5, "application/cbor", "application/json") ); console.log( "Result of the subtraction is: ", - await subtractNumber(3, "application/json", "application/cbor"), + await subtractNumber(3, "application/json", "application/cbor") ); console.log( "Current number: ", - await getCurrentResult("application/cbor"), + await getCurrentResult("application/cbor") ); console.log("Last Change: ", await getLatestChange("application/json")); @@ -303,7 +303,7 @@ async function runCalculatorInteractions() { * Start listening to the update event, result property and lastChange property. */ console.log( - "\n-------- Start listening to properties and events --------\n", + "\n-------- Start listening to properties and events --------\n" ); listenToResultProperty("application/cbor"); listenToUpdateEvent("application/json"); @@ -313,7 +313,7 @@ async function runCalculatorInteractions() { console.log( "Adding 1 to test observation: ", await addNumber(1, "application/cbor", "application/json"), - "\n", + "\n" ); }, 2000); } catch (err) { 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 index 5e7f603..4754ea1 100644 --- 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 @@ -23,7 +23,7 @@ servient .start() .then(async (WoT) => { const td = await WoT.requestThingDescription( - "http://localhost:3001/http-express-calculator-content-negotiation", + "http://localhost:3001/http-express-calculator-content-negotiation" ); const thing = await WoT.consume(td); 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 index db6b135..9d79700 100644 --- 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 @@ -23,7 +23,7 @@ servient .start() .then(async (WoT) => { const td = await WoT.requestThingDescription( - "http://localhost:3000/http-express-calculator-simple", + "http://localhost:3000/http-express-calculator-simple" ); const thing = await WoT.consume(td); diff --git a/things/calculator/http/express/client-tests/simple-http-client.js b/things/calculator/http/express/client-tests/simple-http-client.js index faeaaa6..a0617c4 100644 --- a/things/calculator/http/express/client-tests/simple-http-client.js +++ b/things/calculator/http/express/client-tests/simple-http-client.js @@ -82,7 +82,7 @@ async function getLatestChange() { */ function listenToLastChange() { const lastChangeEventSource = new EventSource( - url + lastChangeEndPointObserve, + url + lastChangeEndPointObserve ); lastChangeEventSource.onmessage = (e) => { @@ -174,7 +174,7 @@ async function runCalculatorInteractions() { * Start listening to the update event, result property and lastChange property. */ console.log( - "\n-------- Start listening to properties and events --------\n", + "\n-------- Start listening to properties and events --------\n" ); listenToUpdateEvent(); listenToLastChange(); diff --git a/things/calculator/http/express/http-content-negotiation-calculator.js b/things/calculator/http/express/http-content-negotiation-calculator.js index ecf45c7..ab8c8a4 100644 --- a/things/calculator/http/express/http-content-negotiation-calculator.js +++ b/things/calculator/http/express/http-content-negotiation-calculator.js @@ -131,7 +131,7 @@ for (const key in thingDescription.properties) { supportedContentTypes.forEach((type) => { if ( !thingDescription.properties[key].forms[0].contentType.includes( - type, + type ) ) { const newFormRead = JSON.parse(JSON.stringify(originalForm)); @@ -235,7 +235,7 @@ for (const key in thingDescription.events) { try { fs.writeFileSync( "http-content-negotiation-calculator-thing.td.jsonld", - JSON.stringify(thingDescription, null, 2), + JSON.stringify(thingDescription, null, 2) ); } catch (err) { console.log(err); @@ -291,7 +291,7 @@ app.use((req, res, next) => { if (acceptHeader === undefined) { res.status(406).json( - "Not Acceptable: Supported formats are application/json, and application/cbor", + "Not Acceptable: Supported formats are application/json, and application/cbor" ); } else if ( acceptHeader.includes("*/*") || @@ -304,7 +304,7 @@ app.use((req, res, next) => { next(); } else { res.status(406).json( - "Not Acceptable: Supported formats are application/json, and application/cbor", + "Not Acceptable: Supported formats are application/json, and application/cbor" ); } }); diff --git a/things/calculator/http/express/http-simple-calculator.js b/things/calculator/http/express/http-simple-calculator.js index 65929ed..ce895de 100644 --- a/things/calculator/http/express/http-simple-calculator.js +++ b/things/calculator/http/express/http-simple-calculator.js @@ -165,7 +165,7 @@ for (const key in thingDescription.events) { try { fs.writeFileSync( "http-simple-calculator-thing.td.jsonld", - JSON.stringify(thingDescription, null, 2), + JSON.stringify(thingDescription, null, 2) ); } catch (err) { console.log(err); diff --git a/things/calculator/http/express/test/client.test.js b/things/calculator/http/express/test/client.test.js index e152499..e545655 100644 --- a/things/calculator/http/express/test/client.test.js +++ b/things/calculator/http/express/test/client.test.js @@ -55,7 +55,7 @@ describe("Client Tests", () => { before(async () => { try { const td = await WoT.requestThingDescription( - `http://localhost:${simplePort}/http-express-calculator-simple`, + `http://localhost:${simplePort}/http-express-calculator-simple` ); thing = await WoT.consume(td); } catch (error) { @@ -83,7 +83,7 @@ describe("Client Tests", () => { await thing.invokeAction("subtract", valueToSubtract); const newResultValue = await readProperty(thing, "result"); expect(newResultValue).to.be.equal( - resultValue - valueToSubtract, + resultValue - valueToSubtract ); }); }); @@ -99,7 +99,7 @@ describe("Client Tests", () => { "lastChange", async (response) => { value = await response.value(); - }, + } ); setTimeout(async () => { @@ -125,11 +125,11 @@ describe("Client Tests", () => { const valueToSubtract = 3; const response = await thing.invokeAction( "subtract", - valueToSubtract, + valueToSubtract ); const actionResultValue = await response.value(); expect(actionResultValue).to.be.equal( - resultValue - valueToSubtract, + resultValue - valueToSubtract ); }); }); @@ -147,9 +147,9 @@ describe("Client Tests", () => { "update", async (response) => { await expect( - response.value, + response.value ).to.have.eventually.be.equal(resultValue + valueToAdd); - }, + } ); await subscription.stop(); @@ -163,7 +163,7 @@ describe("Client Tests", () => { before(async () => { try { const td = await WoT.requestThingDescription( - `http://localhost:${contentNegotiationPort}/http-express-calculator-content-negotiation`, + `http://localhost:${contentNegotiationPort}/http-express-calculator-content-negotiation` ); thing = await WoT.consume(td); } catch (error) { @@ -191,7 +191,7 @@ describe("Client Tests", () => { await thing.invokeAction("subtract", valueToSubtract); const newResultValue = await readProperty(thing, "result"); expect(newResultValue).to.be.equal( - resultValue - valueToSubtract, + resultValue - valueToSubtract ); }); }); @@ -207,7 +207,7 @@ describe("Client Tests", () => { "lastChange", async (response) => { value = await response.value(); - }, + } ); setTimeout(async () => { @@ -233,11 +233,11 @@ describe("Client Tests", () => { const valueToSubtract = 3; const response = await thing.invokeAction( "subtract", - valueToSubtract, + valueToSubtract ); const actionResultValue = await response.value(); expect(actionResultValue).to.be.equal( - resultValue - valueToSubtract, + resultValue - valueToSubtract ); }); }); @@ -255,9 +255,9 @@ describe("Client Tests", () => { "update", async (response) => { await expect( - response.value, + 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 dc52f4c..eefa5a3 100644 --- a/things/calculator/http/express/test/fixtures.js +++ b/things/calculator/http/express/test/fixtures.js @@ -40,7 +40,7 @@ const mochaGlobalSetup = async function () { path.join( __dirname, "..", - "http-content-negotiation-calculator.js", + "http-content-negotiation-calculator.js" ), "-p", `${contentNegotiationPort}`, diff --git a/things/calculator/http/express/test/td.test.js b/things/calculator/http/express/test/td.test.js index 80d8fe8..3a863f6 100644 --- a/things/calculator/http/express/test/td.test.js +++ b/things/calculator/http/express/test/td.test.js @@ -47,7 +47,7 @@ describe("Calculator HTTP JS", () => { response.on("end", () => { try { const result = JSON.parse( - Buffer.concat(body).toString(), + Buffer.concat(body).toString() ); const valid = validate(result); expect(valid).to.be.true; @@ -56,7 +56,7 @@ describe("Calculator HTTP JS", () => { console.log(error); } }); - }, + } ); }); }); @@ -82,7 +82,7 @@ describe("Calculator HTTP JS", () => { response.on("end", () => { try { const result = JSON.parse( - Buffer.concat(body).toString(), + Buffer.concat(body).toString() ); const valid = validate(result); expect(valid).to.be.true; @@ -91,7 +91,7 @@ describe("Calculator HTTP JS", () => { console.log(error); } }); - }, + } ); }); }); diff --git a/things/calculator/http/flask/test/td.test.js b/things/calculator/http/flask/test/td.test.js index 7e4a114..aba3bd5 100644 --- a/things/calculator/http/flask/test/td.test.js +++ b/things/calculator/http/flask/test/td.test.js @@ -47,12 +47,12 @@ describe("Calculator HTTP Flask", () => { response.on("end", () => { const tdSchema = JSON.parse( - Buffer.concat(body).toString(), + Buffer.concat(body).toString() ); validate = ajv.compile(tdSchema); resolve("Success"); }); - }, + } ); }); @@ -81,13 +81,13 @@ describe("Calculator HTTP Flask", () => { response.on("end", () => { const result = JSON.parse( - Buffer.concat(body).toString(), + Buffer.concat(body).toString() ); const valid = validate(result); expect(valid).to.be.true; done(); }); - }, + } ); }, 1000); }); diff --git a/things/calculator/mqtt/js/main.js b/things/calculator/mqtt/js/main.js index 747a89e..1b62993 100644 --- a/things/calculator/mqtt/js/main.js +++ b/things/calculator/mqtt/js/main.js @@ -143,7 +143,7 @@ fs.writeFile( `${thingName}.td.json`, JSON.stringify(thingDescription, 4, 4), "utf-8", - function () {}, + function () {} ); broker.on("connect", () => { @@ -256,12 +256,12 @@ broker.on("message", (topic, payload, packet) => { broker.publish( `${thingName}/${PROPERTIES}/result`, `${result}`, - { retain: true }, + { retain: true } ); broker.publish( `${thingName}/${PROPERTIES}/lastChange`, `${lastChange}`, - { retain: true }, + { retain: true } ); } } @@ -301,12 +301,12 @@ broker.on("message", (topic, payload, packet) => { broker.publish( `${thingName}/${PROPERTIES}/result`, `${result}`, - { retain: true }, + { retain: true } ); broker.publish( `${thingName}/${PROPERTIES}/lastChange`, `${lastChange}`, - { retain: true }, + { retain: true } ); } } diff --git a/things/calculator/mqtt/js/test/client.test.js b/things/calculator/mqtt/js/test/client.test.js index 7903491..b74e0e7 100644 --- a/things/calculator/mqtt/js/test/client.test.js +++ b/things/calculator/mqtt/js/test/client.test.js @@ -95,11 +95,11 @@ describe.skip("Client Tests", () => { const valueToSubtract = 3; const response = await thing.invokeAction( "subtract", - valueToSubtract, + valueToSubtract ); const actionResultValue = await response.value(); expect(actionResultValue).to.be.equal( - resultValue - valueToSubtract, + resultValue - valueToSubtract ); }); }); @@ -113,7 +113,7 @@ describe.skip("Client Tests", () => { console.log(value); expect(value).to.be.equal("Updated the thing!"); subscription.stop(); - }, + } ); }); }); diff --git a/things/calculator/tm.test.js b/things/calculator/tm.test.js index e2fb610..48f9c5a 100644 --- a/things/calculator/tm.test.js +++ b/things/calculator/tm.test.js @@ -23,7 +23,7 @@ describe("Calculator", () => { validate = ajv.compile(tmSchema); done(); }); - }, + } ); }); diff --git a/things/data-schema-thing/http/ts/src/main.ts b/things/data-schema-thing/http/ts/src/main.ts index c536145..81acff7 100644 --- a/things/data-schema-thing/http/ts/src/main.ts +++ b/things/data-schema-thing/http/ts/src/main.ts @@ -60,7 +60,7 @@ function checkPropertyWrite(expected: string, actual: unknown) { function checkActionInvocation( name: string, expected: string, - actual: unknown, + actual: unknown ) { const output = "Action " + name + " invoked with " + actual; if (expected === actual) { @@ -94,7 +94,7 @@ if (process.platform === "win32") { let thingModel; if (tmPath != null && tmPath !== "") { thingModel = JSON.parse( - fs.readFileSync(path.join(__dirname, tmPath)).toString(), + fs.readFileSync(path.join(__dirname, tmPath)).toString() ); } @@ -214,7 +214,7 @@ servient.addServer( new HttpServer({ baseUri: `http://${hostname}:${portNumber}`, port: portNumber, - }), + }) ); servient.start().then((WoT) => { @@ -293,7 +293,7 @@ servient.start().then((WoT) => { checkActionInvocation( "void-void", "undefined", - typeof (await parameters.value()), + typeof (await parameters.value()) ); return undefined; }) @@ -310,7 +310,7 @@ servient.start().then((WoT) => { checkActionInvocation( "void-int", "undefined", - typeof (await parameters.value()), + typeof (await parameters.value()) ); logger.info({ message: `${value}`, @@ -351,7 +351,7 @@ servient.start().then((WoT) => { checkActionInvocation( "int-void", "integer", - typeof parameters, + typeof parameters ); } return undefined; @@ -384,7 +384,7 @@ servient.start().then((WoT) => { checkActionInvocation( "int-int", "integer", - typeof localParameters, + typeof localParameters ); } const value = (localParameters as number) + 1; @@ -426,13 +426,13 @@ servient.start().then((WoT) => { checkActionInvocation( "int-string", "integer", - "integer", + "integer" ); } else { checkActionInvocation( "int-string", "integer", - typeof localParameters, + typeof localParameters ); } @@ -476,7 +476,7 @@ servient.start().then((WoT) => { checkActionInvocation( "void-complex", "undefined", - typeof (await parameters.value()), + typeof (await parameters.value()) ); const value = { prop1: 123, prop2: "abc" }; logger.info({ @@ -512,7 +512,7 @@ servient.start().then((WoT) => { checkActionInvocation( "complex-void", "object", - typeof (await parameters.value()), + typeof (await parameters.value()) ); return undefined; }); 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 7ac461b..9064c89 100644 --- a/things/data-schema-thing/http/ts/test/client.test.ts +++ b/things/data-schema-thing/http/ts/test/client.test.ts @@ -30,7 +30,7 @@ let thing: WoT.ConsumedThing; const readProperty = async ( thing: WoT.ConsumedThing, - name: string, + name: string ): Promise => { try { const res = await thing.readProperty(name); @@ -47,7 +47,7 @@ describe("Client Tests", () => { try { const WoT = await servient.start(); const td: WoT.ThingDescription = await WoT.requestThingDescription( - `http://localhost:${port}/http-data-schema-thing`, + `http://localhost:${port}/http-data-schema-thing` ); thing = await WoT.consume(td); } catch (error) { 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 6fdad69..30c5557 100644 --- a/things/data-schema-thing/http/ts/test/td.test.ts +++ b/things/data-schema-thing/http/ts/test/td.test.ts @@ -47,7 +47,7 @@ describe("TD Test", () => { response.on("end", () => { try { const result = JSON.parse( - Buffer.concat(body).toString(), + Buffer.concat(body).toString() ); const valid = validate && result !== "" @@ -60,7 +60,7 @@ describe("TD Test", () => { done(error); } }); - }, + } ); }); }); diff --git a/things/data-schema-thing/tm.test.js b/things/data-schema-thing/tm.test.js index 3d95e70..1efa70c 100644 --- a/things/data-schema-thing/tm.test.js +++ b/things/data-schema-thing/tm.test.js @@ -23,7 +23,7 @@ describe("Test Thing", () => { validate = ajv.compile(tmSchema); done(); }); - }, + } ); }); diff --git a/things/elevator/modbus/js/main.js b/things/elevator/modbus/js/main.js index 0214ea9..602f691 100644 --- a/things/elevator/modbus/js/main.js +++ b/things/elevator/modbus/js/main.js @@ -147,7 +147,7 @@ fs.writeFile( `${thingName}.td.json`, JSON.stringify(thingDescription, 4, 4), "utf-8", - function () {}, + function () {} ); const coilMemoryRange = [1, 9999]; @@ -174,13 +174,13 @@ const vector = { console.log(`Reading discrete input @${addr}`); const normalizedAddress = getNormalizedAddress( addr, - discreteInputMemoryRange, + discreteInputMemoryRange ); if (normalizedAddress === onTheMoveAddress) { if (onTheMoveIsPolled) { console.log( - `Polling onTheMove too frequently. You should poll it every ${onTheMovePollingTime} ms.`, + `Polling onTheMove too frequently. You should poll it every ${onTheMovePollingTime} ms.` ); return; } @@ -213,7 +213,7 @@ const vector = { const normalizedAddress = getNormalizedAddress( addr, - holdingRegisterMemoryRange, + holdingRegisterMemoryRange ); setTimeout(function () { @@ -232,7 +232,7 @@ const vector = { console.log(`Reading coil @${addr}`); const normalizedAddress = getNormalizedAddress( addr, - coilMemoryRange, + coilMemoryRange ); resolve(coils[normalizedAddress]); }); @@ -248,7 +248,7 @@ const vector = { console.log(`Setting register @${addr} to ${value}`); const normalizedAddress = getNormalizedAddress( addr, - holdingRegisterMemoryRange, + holdingRegisterMemoryRange ); // trying to change floor number holdingRegisters[normalizedAddress] = value; @@ -261,20 +261,20 @@ const vector = { // elevator is on the move if (discreteInputs[onTheMoveAddress] && !isTestRun) { console.log( - "Elevator is on the move, cannot change the floor number", + "Elevator is on the move, cannot change the floor number" ); } else { const floorNumberValue = getFloorNumberValue(); if (floorNumberValue < minFloorNumber) { console.log( - `Floor number should not be under ${minFloorNumber}`, + `Floor number should not be under ${minFloorNumber}` ); return -1; } if (floorNumberValue > maxFloorNumber) { console.log( - `Floor number should not be above ${maxFloorNumber}`, + `Floor number should not be above ${maxFloorNumber}` ); return -1; } @@ -302,7 +302,7 @@ const vector = { const normalizedAddress = getNormalizedAddress( addr, - coilMemoryRange, + coilMemoryRange ); console.log(`Setting coil @${addr} to ${value}`); diff --git a/things/elevator/modbus/js/test/td.test.js b/things/elevator/modbus/js/test/td.test.js index c0f2e84..e6d15c1 100644 --- a/things/elevator/modbus/js/test/td.test.js +++ b/things/elevator/modbus/js/test/td.test.js @@ -48,7 +48,7 @@ describe("Elevator Modbus JS", () => { 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 a4aa841..6142856 100644 --- a/things/elevator/tm.test.js +++ b/things/elevator/tm.test.js @@ -23,7 +23,7 @@ describe("Elevator", () => { validate = ajv.compile(tmSchema); done(); }); - }, + } ); }); diff --git a/util/util.ts b/util/util.ts index 2e9f050..0018c3a 100644 --- a/util/util.ts +++ b/util/util.ts @@ -31,7 +31,7 @@ const spawn = require("node:child_process").spawn; export const getInitiateMain = ( mainCmd: string, - cmdArgs: string[], + cmdArgs: string[] ): Promise => { return new Promise((resolve, reject) => { const thingProcess = spawn(mainCmd, cmdArgs); @@ -98,6 +98,6 @@ const getTDJSONSchema = new Promise((resolve, reject) => { const tdSchema = JSON.parse(Buffer.concat(body).toString()); resolve(tdSchema); }); - }, + } ); });