diff --git a/package-lock.json b/package-lock.json index e53df72ea..bc4ac17e0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -51,18 +51,6 @@ "wot-typescript-definitions": "0.8.0-SNAPSHOT.29" } }, - "node_modules/@babel/runtime": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.7.tgz", - "integrity": "sha512-UwgBRMjJP+xv857DCngvqXI3Iq6J4v0wXmwc6sapg+zyhbwmQX67LUEFrkK5tbyJ30jGuG3ZvWpBiB9LCy1kWw==", - "license": "MIT", - "dependencies": { - "regenerator-runtime": "^0.14.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@bcoe/v8-coverage": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", @@ -74,7 +62,7 @@ "version": "0.8.1", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@jridgewell/trace-mapping": "0.3.9" @@ -243,7 +231,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">=6.0.0" @@ -253,117 +241,32 @@ "version": "1.5.0", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.9", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "devOptional": true, + "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", - "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz", - "integrity": "sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==", - "license": "MIT" - }, - "node_modules/@node-oauth/express-oauth-server": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@node-oauth/express-oauth-server/-/express-oauth-server-3.0.1.tgz", - "integrity": "sha512-SD6vykQY8uA6BPquR14nVNK4ofIoEbamW1ixTIRMcRFtr6oNKyv0AOo75DbUbKcGs4Aa2HuDU33J/K1lbHZ5KA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@node-oauth/oauth2-server": "^4.3.0", - "bluebird": "^3.7.2", - "express": "^4.18.2" - }, - "engines": { - "node": ">=0.11" - } - }, - "node_modules/@node-oauth/formats": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@node-oauth/formats/-/formats-1.0.0.tgz", - "integrity": "sha512-DwSbLtdC8zC5B5gTJkFzJj5s9vr9SGzOgQvV9nH7tUVuMSScg0EswAczhjIapOmH3Y8AyP7C4Jv7b8+QJObWZA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@node-oauth/oauth2-server": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@node-oauth/oauth2-server/-/oauth2-server-4.3.3.tgz", - "integrity": "sha512-0LiQ6sGwgd+WPQCkayORgv3ju6JNcZsU9IpiXSGCQ/LNlvhQCgwriVaVageXl1J9GwY/rq4wMg6DPwakGZLC2g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@node-oauth/formats": "1.0.0", - "basic-auth": "2.0.1", - "bluebird": "3.7.2", - "promisify-any": "2.0.1", - "type-is": "1.6.18" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@node-wot/binding-coap": { - "resolved": "packages/binding-coap", - "link": true - }, - "node_modules/@node-wot/binding-file": { - "resolved": "packages/binding-file", - "link": true - }, - "node_modules/@node-wot/binding-http": { - "resolved": "packages/binding-http", - "link": true - }, "node_modules/@node-wot/binding-mbus": { "resolved": "packages/binding-mbus", "link": true }, - "node_modules/@node-wot/binding-modbus": { - "resolved": "packages/binding-modbus", - "link": true - }, - "node_modules/@node-wot/binding-mqtt": { - "resolved": "packages/binding-mqtt", - "link": true - }, "node_modules/@node-wot/binding-netconf": { "resolved": "packages/binding-netconf", "link": true }, - "node_modules/@node-wot/binding-opcua": { - "resolved": "packages/binding-opcua", - "link": true - }, - "node_modules/@node-wot/binding-websockets": { - "resolved": "packages/binding-websockets", - "link": true - }, - "node_modules/@node-wot/browser-bundle": { - "resolved": "packages/browser-bundle", - "link": true - }, - "node_modules/@node-wot/cli": { - "resolved": "packages/cli", - "link": true - }, "node_modules/@node-wot/core": { "resolved": "packages/core", "link": true }, - "node_modules/@node-wot/examples": { - "resolved": "packages/examples", - "link": true - }, "node_modules/@node-wot/td-tools": { "resolved": "packages/td-tools", "link": true @@ -406,5719 +309,618 @@ "node": ">= 8" } }, - "node_modules/@peculiar/asn1-cms": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/@peculiar/asn1-cms/-/asn1-cms-2.3.8.tgz", - "integrity": "sha512-Wtk9R7yQxGaIaawHorWKP2OOOm/RZzamOmSWwaqGphIuU6TcKYih0slL6asZlSSZtVoYTrBfrddSOD/jTu9vuQ==", - "license": "MIT", - "dependencies": { - "@peculiar/asn1-schema": "^2.3.8", - "@peculiar/asn1-x509": "^2.3.8", - "@peculiar/asn1-x509-attr": "^2.3.8", - "asn1js": "^3.0.5", - "tslib": "^2.6.2" - } + "node_modules/@petamoriken/float16": { + "version": "3.8.7", + "resolved": "https://registry.npmjs.org/@petamoriken/float16/-/float16-3.8.7.tgz", + "integrity": "sha512-/Ri4xDDpe12NT6Ex/DRgHzLlobiQXEW/hmG08w1wj/YU7hLemk97c+zHQFp0iZQ9r7YqgLEXZR2sls4HxBf9NA==", + "license": "MIT" }, - "node_modules/@peculiar/asn1-csr": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/@peculiar/asn1-csr/-/asn1-csr-2.3.8.tgz", - "integrity": "sha512-ZmAaP2hfzgIGdMLcot8gHTykzoI+X/S53x1xoGbTmratETIaAbSWMiPGvZmXRA0SNEIydpMkzYtq4fQBxN1u1w==", - "license": "MIT", + "node_modules/@sinonjs/commons": { + "version": "1.8.6", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.6.tgz", + "integrity": "sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ==", + "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "@peculiar/asn1-schema": "^2.3.8", - "@peculiar/asn1-x509": "^2.3.8", - "asn1js": "^3.0.5", - "tslib": "^2.6.2" + "type-detect": "4.0.8" } }, - "node_modules/@peculiar/asn1-ecc": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/@peculiar/asn1-ecc/-/asn1-ecc-2.3.8.tgz", - "integrity": "sha512-Ah/Q15y3A/CtxbPibiLM/LKcMbnLTdUdLHUgdpB5f60sSvGkXzxJCu5ezGTFHogZXWNX3KSmYqilCrfdmBc6pQ==", - "license": "MIT", + "node_modules/@sinonjs/fake-timers": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-7.1.2.tgz", + "integrity": "sha512-iQADsW4LBMISqZ6Ci1dupJL9pprqwcVFTcOsEmQOEhW+KLCVn/Y4Jrvg2k19fIHCp+iFprriYPTdRcQR8NbUPg==", + "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "@peculiar/asn1-schema": "^2.3.8", - "@peculiar/asn1-x509": "^2.3.8", - "asn1js": "^3.0.5", - "tslib": "^2.6.2" + "@sinonjs/commons": "^1.7.0" } }, - "node_modules/@peculiar/asn1-pfx": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/@peculiar/asn1-pfx/-/asn1-pfx-2.3.8.tgz", - "integrity": "sha512-XhdnCVznMmSmgy68B9pVxiZ1XkKoE1BjO4Hv+eUGiY1pM14msLsFZ3N7K46SoITIVZLq92kKkXpGiTfRjlNLyg==", - "license": "MIT", + "node_modules/@sinonjs/samsam": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-6.1.3.tgz", + "integrity": "sha512-nhOb2dWPeb1sd3IQXL/dVPnKHDOAFfvichtBf4xV00/rU1QbPCQqKMbvIheIjqwVjh7qIgf2AHTHi391yMOMpQ==", + "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "@peculiar/asn1-cms": "^2.3.8", - "@peculiar/asn1-pkcs8": "^2.3.8", - "@peculiar/asn1-rsa": "^2.3.8", - "@peculiar/asn1-schema": "^2.3.8", - "asn1js": "^3.0.5", - "tslib": "^2.6.2" + "@sinonjs/commons": "^1.6.0", + "lodash.get": "^4.4.2", + "type-detect": "^4.0.8" } }, - "node_modules/@peculiar/asn1-pkcs8": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/@peculiar/asn1-pkcs8/-/asn1-pkcs8-2.3.8.tgz", - "integrity": "sha512-rL8k2x59v8lZiwLRqdMMmOJ30GHt6yuHISFIuuWivWjAJjnxzZBVzMTQ72sknX5MeTSSvGwPmEFk2/N8+UztFQ==", - "license": "MIT", - "dependencies": { - "@peculiar/asn1-schema": "^2.3.8", - "@peculiar/asn1-x509": "^2.3.8", - "asn1js": "^3.0.5", - "tslib": "^2.6.2" - } + "node_modules/@sinonjs/text-encoding": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.2.tgz", + "integrity": "sha512-sXXKG+uL9IrKqViTtao2Ws6dy0znu9sOaP1di/jKGW1M6VssO8vlpXCQcpZ+jisQ1tTFAC5Jo/EOzFbggBagFQ==", + "dev": true, + "license": "(Unlicense OR Apache-2.0)" }, - "node_modules/@peculiar/asn1-pkcs9": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/@peculiar/asn1-pkcs9/-/asn1-pkcs9-2.3.8.tgz", - "integrity": "sha512-+nONq5tcK7vm3qdY7ZKoSQGQjhJYMJbwJGbXLFOhmqsFIxEWyQPHyV99+wshOjpOjg0wUSSkEEzX2hx5P6EKeQ==", - "license": "MIT", - "dependencies": { - "@peculiar/asn1-cms": "^2.3.8", - "@peculiar/asn1-pfx": "^2.3.8", - "@peculiar/asn1-pkcs8": "^2.3.8", - "@peculiar/asn1-schema": "^2.3.8", - "@peculiar/asn1-x509": "^2.3.8", - "@peculiar/asn1-x509-attr": "^2.3.8", - "asn1js": "^3.0.5", - "tslib": "^2.6.2" - } + "node_modules/@testdeck/core": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@testdeck/core/-/core-0.1.2.tgz", + "integrity": "sha512-rggcFQqSejqtkqK1JcwZE/utD4oNsM1JMHwrYxH1PKYx0uL2cMs0Q4zMVLKw0oacIASCfkIUSdXx7rDNdCDlvA==", + "dev": true, + "license": "Apache-2.0" }, - "node_modules/@peculiar/asn1-rsa": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/@peculiar/asn1-rsa/-/asn1-rsa-2.3.8.tgz", - "integrity": "sha512-ES/RVEHu8VMYXgrg3gjb1m/XG0KJWnV4qyZZ7mAg7rrF3VTmRbLxO8mk+uy0Hme7geSMebp+Wvi2U6RLLEs12Q==", - "license": "MIT", + "node_modules/@testdeck/mocha": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@testdeck/mocha/-/mocha-0.1.2.tgz", + "integrity": "sha512-yn3OkFUlO9EkdBkDV08bPV0r26U2FC/8KvU9FdiS0ligOsjB5Ry4sp3eUQJAFxjrGJBpih0t7rZ/GzdsgCv/EQ==", + "dev": true, + "license": "Apache-2.0", "dependencies": { - "@peculiar/asn1-schema": "^2.3.8", - "@peculiar/asn1-x509": "^2.3.8", - "asn1js": "^3.0.5", - "tslib": "^2.6.2" + "@testdeck/core": "^0.1.2" + }, + "bin": { + "testdeck-watch": "bin/watch" } }, - "node_modules/@peculiar/asn1-schema": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/@peculiar/asn1-schema/-/asn1-schema-2.3.8.tgz", - "integrity": "sha512-ULB1XqHKx1WBU/tTFIA+uARuRoBVZ4pNdOA878RDrRbBfBGcSzi5HBkdScC6ZbHn8z7L8gmKCgPC1LHRrP46tA==", - "license": "MIT", - "dependencies": { - "asn1js": "^3.0.5", - "pvtsutils": "^1.3.5", - "tslib": "^2.6.2" - } + "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, + "license": "MIT" }, - "node_modules/@peculiar/asn1-x509": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/@peculiar/asn1-x509/-/asn1-x509-2.3.8.tgz", - "integrity": "sha512-voKxGfDU1c6r9mKiN5ZUsZWh3Dy1BABvTM3cimf0tztNwyMJPhiXY94eRTgsMQe6ViLfT6EoXxkWVzcm3mFAFw==", - "license": "MIT", - "dependencies": { - "@peculiar/asn1-schema": "^2.3.8", - "asn1js": "^3.0.5", - "ipaddr.js": "^2.1.0", - "pvtsutils": "^1.3.5", - "tslib": "^2.6.2" - } + "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, + "license": "MIT" }, - "node_modules/@peculiar/asn1-x509-attr": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/@peculiar/asn1-x509-attr/-/asn1-x509-attr-2.3.8.tgz", - "integrity": "sha512-4Z8mSN95MOuX04Aku9BUyMdsMKtVQUqWnr627IheiWnwFoheUhX3R4Y2zh23M7m80r4/WG8MOAckRKc77IRv6g==", - "license": "MIT", - "dependencies": { - "@peculiar/asn1-schema": "^2.3.8", - "@peculiar/asn1-x509": "^2.3.8", - "asn1js": "^3.0.5", - "tslib": "^2.6.2" - } + "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, + "license": "MIT" }, - "node_modules/@peculiar/json-schema": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/@peculiar/json-schema/-/json-schema-1.1.12.tgz", - "integrity": "sha512-coUfuoMeIB7B8/NMekxaDzLhaYmp0HZNPEjYRm9goRou8UZIC3z21s0sL9AWoCw4EG876QyO3kYrc61WNF9B/w==", - "license": "MIT", - "dependencies": { - "tslib": "^2.0.0" - }, - "engines": { - "node": ">=8.0.0" - } + "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, + "license": "MIT" }, - "node_modules/@peculiar/webcrypto": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@peculiar/webcrypto/-/webcrypto-1.5.0.tgz", - "integrity": "sha512-BRs5XUAwiyCDQMsVA9IDvDa7UBR9gAvPHgugOeGng3YN6vJ9JYonyDc0lNczErgtCWtucjR5N7VtaonboD/ezg==", + "node_modules/@types/chai": { + "version": "4.3.16", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.16.tgz", + "integrity": "sha512-PatH4iOdyh3MyWtmHVFXLWCCIhUbopaltqddG9BzB+gMIzee2MJrvd+jouii9Z3wzQJruGWAm7WOMjgfG8hQlQ==", + "dev": true, + "license": "MIT" + }, + "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, "license": "MIT", "dependencies": { - "@peculiar/asn1-schema": "^2.3.8", - "@peculiar/json-schema": "^1.1.12", - "pvtsutils": "^1.3.5", - "tslib": "^2.6.2", - "webcrypto-core": "^1.8.0" - }, - "engines": { - "node": ">=10.12.0" + "@types/chai": "*" } }, - "node_modules/@peculiar/x509": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@peculiar/x509/-/x509-1.11.0.tgz", - "integrity": "sha512-8rdxE//tsWLb2Yo2TYO2P8gieStbrHK/huFMV5PPfwX8I5HmtOus+Ox6nTKrPA9o+WOPaa5xKenee+QdmHBd5g==", + "node_modules/@types/chai-spies": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/chai-spies/-/chai-spies-1.0.6.tgz", + "integrity": "sha512-xkk4HmhBB9OQeTAifa9MJ+6R5/Rq9+ungDe4JidZD+vqZVeiWZwc2i7/pd1ZKjyGlSBIQePoWdyUyFUGT0rv5w==", + "dev": true, "license": "MIT", "dependencies": { - "@peculiar/asn1-cms": "^2.3.8", - "@peculiar/asn1-csr": "^2.3.8", - "@peculiar/asn1-ecc": "^2.3.8", - "@peculiar/asn1-pkcs9": "^2.3.8", - "@peculiar/asn1-rsa": "^2.3.8", - "@peculiar/asn1-schema": "^2.3.8", - "@peculiar/asn1-x509": "^2.3.8", - "pvtsutils": "^1.3.5", - "reflect-metadata": "^0.2.2", - "tslib": "^2.6.2", - "tsyringe": "^4.8.0" + "@types/chai": "*" } }, - "node_modules/@petamoriken/float16": { - "version": "3.8.7", - "resolved": "https://registry.npmjs.org/@petamoriken/float16/-/float16-3.8.7.tgz", - "integrity": "sha512-/Ri4xDDpe12NT6Ex/DRgHzLlobiQXEW/hmG08w1wj/YU7hLemk97c+zHQFp0iZQ9r7YqgLEXZR2sls4HxBf9NA==", + "node_modules/@types/content-type": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/@types/content-type/-/content-type-1.1.8.tgz", + "integrity": "sha512-1tBhmVUeso3+ahfyaKluXe38p+94lovUZdoVfQ3OnJo9uJC42JT7CBoN3k9HYhAae+GwiBYmHu+N9FZhOG+2Pg==", + "dev": true, "license": "MIT" }, - "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==", + "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, "license": "MIT", "dependencies": { - "@serialport/bindings-interface": "^1.2.1", - "debug": "^4.3.3" - }, - "engines": { - "node": ">=12.0.0" + "@types/ms": "*" } }, - "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, - "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/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "dev": true, + "license": "MIT" }, - "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==", - "license": "MIT", - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://opencollective.com/serialport/donate" - } + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" }, - "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==", - "license": "MIT", - "dependencies": { - "@serialport/parser-delimiter": "11.0.0" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://opencollective.com/serialport/donate" - } + "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, + "license": "MIT" }, - "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==", - "license": "MIT", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } + "node_modules/@types/mocha": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.1.1.tgz", + "integrity": "sha512-Z61JK7DKDtdKTWwLeElSEBcWGRLY8g95ic5FoQqI9CMx0ns/Ghep3B4DfcEimiKMvtamNVULVNKEsiwV3aQmXw==", + "dev": true, + "license": "MIT" }, - "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==", - "license": "MIT", - "engines": { - "node": "^12.22 || ^14.13 || >=16" - } + "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, + "license": "MIT" }, - "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==", - "license": "MIT", - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://opencollective.com/serialport/donate" - } + "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==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node-netconf": { + "name": "@types/netconf", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/netconf/-/netconf-2.0.4.tgz", + "integrity": "sha512-QQIRdqsXWFwPNDYP85c7FpYPdz5tYb8Ev6U9yiHK3aOadIcl5wlZ9WwqQAAUv6yM5fbDdzqCwk5E/0bSfCbaDg==", + "license": "MIT" }, - "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==", + "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==", + "dev": true, "license": "MIT", - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://opencollective.com/serialport/donate" + "dependencies": { + "@types/node": "*", + "safe-buffer": "~5.1.1" } }, - "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==", + "node_modules/@types/semver": { + "version": "7.5.8", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", + "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/sinon": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.2.tgz", + "integrity": "sha512-BHn8Bpkapj8Wdfxvh2jWIUoaYB/9/XhsL0oOvBfRagJtKlSl9NWPcFOz2lRukI9szwGxFtYZCTejJSqsGDbdmw==", + "dev": true, "license": "MIT", - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://opencollective.com/serialport/donate" + "dependencies": { + "@sinonjs/fake-timers": "^7.1.0" } }, - "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==", + "node_modules/@types/uritemplate": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@types/uritemplate/-/uritemplate-0.3.6.tgz", + "integrity": "sha512-31BMGZ8GgLxgXxLnqg4KbbyYJjU1flhTTD2+PVQStVUPXSk0IIpK0zt+tH3eLT7ZRwLnzQw6JhYx69qza3U0wg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/url-parse": { + "version": "1.4.11", + "resolved": "https://registry.npmjs.org/@types/url-parse/-/url-parse-1.4.11.tgz", + "integrity": "sha512-FKvKIqRaykZtd4n47LbK/W/5fhQQ1X7cxxzG9A48h0BGN+S04NH7ervcCjM8tyR0lyGru83FAHSmw2ObgKoESg==", + "license": "MIT" + }, + "node_modules/@types/uuid": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.4.tgz", + "integrity": "sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz", + "integrity": "sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA==", + "dev": true, "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.5.1", + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/type-utils": "6.21.0", + "@typescript-eslint/utils": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", + "debug": "^4.3.4", + "graphemer": "^1.4.0", + "ignore": "^5.2.4", + "natural-compare": "^1.4.0", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, "engines": { - "node": ">=12.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { - "url": "https://opencollective.com/serialport/donate" - } - }, - "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==", - "license": "MIT", - "engines": { - "node": ">=8.6.0" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "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==", - "license": "MIT", + "node_modules/@typescript-eslint/parser": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.21.0.tgz", + "integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==", + "dev": true, + "license": "BSD-2-Clause", "dependencies": { - "@serialport/parser-delimiter": "12.0.0" + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/typescript-estree": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", + "debug": "^4.3.4" }, "engines": { - "node": ">=12.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { - "url": "https://opencollective.com/serialport/donate" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "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==", + "node_modules/@typescript-eslint/scope-manager": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz", + "integrity": "sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==", + "dev": true, "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0" + }, "engines": { - "node": ">=12.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { - "url": "https://opencollective.com/serialport/donate" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "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==", + "node_modules/@typescript-eslint/type-utils": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.21.0.tgz", + "integrity": "sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag==", + "dev": true, "license": "MIT", + "dependencies": { + "@typescript-eslint/typescript-estree": "6.21.0", + "@typescript-eslint/utils": "6.21.0", + "debug": "^4.3.4", + "ts-api-utils": "^1.0.1" + }, "engines": { - "node": ">=12.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { - "url": "https://opencollective.com/serialport/donate" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "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==", + "node_modules/@typescript-eslint/types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.21.0.tgz", + "integrity": "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==", + "dev": true, "license": "MIT", "engines": { - "node": ">=12.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { - "url": "https://opencollective.com/serialport/donate" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "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==", - "license": "MIT", + "node_modules/@typescript-eslint/typescript-estree": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz", + "integrity": "sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "9.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, "engines": { - "node": ">=12.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { - "url": "https://opencollective.com/serialport/donate" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "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==", + "node_modules/@typescript-eslint/utils": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.21.0.tgz", + "integrity": "sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==", + "dev": true, "license": "MIT", "dependencies": { - "@serialport/bindings-interface": "1.2.2", - "debug": "4.3.4" + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/typescript-estree": "6.21.0", + "semver": "^7.5.4" }, "engines": { - "node": ">=12.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { - "url": "https://opencollective.com/serialport/donate" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" } }, - "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==", + "node_modules/@typescript-eslint/visitor-keys": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz", + "integrity": "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==", + "dev": true, "license": "MIT", "dependencies": { - "ms": "2.1.2" + "@typescript-eslint/types": "6.21.0", + "eslint-visitor-keys": "^3.4.1" }, "engines": { - "node": ">=6.0" + "node": "^16.0.0 || >=18.0.0" }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@servie/events": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@servie/events/-/events-1.0.0.tgz", - "integrity": "sha512-sBSO19KzdrJCM3gdx6eIxV8M9Gxfgg6iDQmH5TIAGaUu+X9VDdsINXJOnoiZ1Kx3TrHdH4bt5UVglkjsEGBcvw==", - "license": "MIT" - }, - "node_modules/@sinonjs/commons": { - "version": "1.8.6", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.6.tgz", - "integrity": "sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "type-detect": "4.0.8" - } - }, - "node_modules/@sinonjs/fake-timers": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-7.1.2.tgz", - "integrity": "sha512-iQADsW4LBMISqZ6Ci1dupJL9pprqwcVFTcOsEmQOEhW+KLCVn/Y4Jrvg2k19fIHCp+iFprriYPTdRcQR8NbUPg==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@sinonjs/commons": "^1.7.0" - } - }, - "node_modules/@sinonjs/samsam": { - "version": "6.1.3", - "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-6.1.3.tgz", - "integrity": "sha512-nhOb2dWPeb1sd3IQXL/dVPnKHDOAFfvichtBf4xV00/rU1QbPCQqKMbvIheIjqwVjh7qIgf2AHTHi391yMOMpQ==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@sinonjs/commons": "^1.6.0", - "lodash.get": "^4.4.2", - "type-detect": "^4.0.8" - } - }, - "node_modules/@sinonjs/text-encoding": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.2.tgz", - "integrity": "sha512-sXXKG+uL9IrKqViTtao2Ws6dy0znu9sOaP1di/jKGW1M6VssO8vlpXCQcpZ+jisQ1tTFAC5Jo/EOzFbggBagFQ==", - "dev": true, - "license": "(Unlicense OR Apache-2.0)" - }, - "node_modules/@ster5/global-mutex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@ster5/global-mutex/-/global-mutex-2.0.0.tgz", - "integrity": "sha512-nlp5BM4E7ybkGt6ouZsohSnliWtXgRoUWHMl8uzi64gKwZSONsssEstfBGnQ0OpdQlE0HBP0qq9RDxP0JTW57w==", - "license": "MIT", - "dependencies": { - "@types/proper-lockfile": "^4.1.2", - "proper-lockfile": "^4.1.2" - } - }, - "node_modules/@testdeck/core": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/@testdeck/core/-/core-0.1.2.tgz", - "integrity": "sha512-rggcFQqSejqtkqK1JcwZE/utD4oNsM1JMHwrYxH1PKYx0uL2cMs0Q4zMVLKw0oacIASCfkIUSdXx7rDNdCDlvA==", - "dev": true, - "license": "Apache-2.0" - }, - "node_modules/@testdeck/mocha": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/@testdeck/mocha/-/mocha-0.1.2.tgz", - "integrity": "sha512-yn3OkFUlO9EkdBkDV08bPV0r26U2FC/8KvU9FdiS0ligOsjB5Ry4sp3eUQJAFxjrGJBpih0t7rZ/GzdsgCv/EQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@testdeck/core": "^0.1.2" - }, - "bin": { - "testdeck-watch": "bin/watch" - } - }, - "node_modules/@tsconfig/node10": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", - "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", - "devOptional": true, - "license": "MIT" - }, - "node_modules/@tsconfig/node12": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", - "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "devOptional": true, - "license": "MIT" - }, - "node_modules/@tsconfig/node14": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", - "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "devOptional": true, - "license": "MIT" - }, - "node_modules/@tsconfig/node16": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", - "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", - "devOptional": true, - "license": "MIT" - }, - "node_modules/@types/accept-language-parser": { - "version": "1.5.6", - "resolved": "https://registry.npmjs.org/@types/accept-language-parser/-/accept-language-parser-1.5.6.tgz", - "integrity": "sha512-lhSQUsAhAtbKjYgaw3f0c4EQKNQHFXhX87+OXUIqDHMkycvHGaqGskSRtnzysIUiqHPqNJ4BqI5SE++drsxx6A==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/asn1": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/@types/asn1/-/asn1-0.2.4.tgz", - "integrity": "sha512-V91DSJ2l0h0gRhVP4oBfBzRBN9lAbPUkGDMCnwedqPKX2d84aAMc9CulOvxdw1f7DfEYx99afab+Rsm3e52jhA==", - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/async": { - "version": "3.2.24", - "resolved": "https://registry.npmjs.org/@types/async/-/async-3.2.24.tgz", - "integrity": "sha512-8iHVLHsCCOBKjCF2KwFe0p9Z3rfM9mL+sSP8btyR5vTjJRAqpBYD28/ZLgXPf0pjG1VxOvtCV/BgXkQbpSe8Hw==", - "license": "MIT" - }, - "node_modules/@types/aws-iot-device-sdk": { - "version": "2.2.8", - "resolved": "https://registry.npmjs.org/@types/aws-iot-device-sdk/-/aws-iot-device-sdk-2.2.8.tgz", - "integrity": "sha512-l7JWoN1vHDpvm13kppPSmfnIQbQrJK7kvG95LIKf3qYF+WJjaUDOTdMn8BA8djHWb7ASRu2D4jtuVzSTj2We6Q==", - "license": "MIT", - "dependencies": { - "@types/node": "*", - "@types/ws": "*", - "mqtt": "^4.2.8" - } - }, - "node_modules/@types/aws-iot-device-sdk/node_modules/bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "license": "MIT", - "dependencies": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, - "node_modules/@types/aws-iot-device-sdk/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" - } - ], - "license": "MIT", - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, - "node_modules/@types/aws-iot-device-sdk/node_modules/commist": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/commist/-/commist-1.1.0.tgz", - "integrity": "sha512-rraC8NXWOEjhADbZe9QBNzLAN5Q3fsTPQtBV+fEVj6xKIgDgNiEVE6ZNfHpZOqfQ21YUzfVNUXLOEZquYvQPPg==", - "license": "MIT", - "dependencies": { - "leven": "^2.1.0", - "minimist": "^1.1.0" - } - }, - "node_modules/@types/aws-iot-device-sdk/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" - ], - "license": "MIT", - "dependencies": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.0.2", - "typedarray": "^0.0.6" - } - }, - "node_modules/@types/aws-iot-device-sdk/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==", - "license": "MIT", - "dependencies": { - "glob": "^7.1.6", - "readable-stream": "^3.6.0" - } - }, - "node_modules/@types/aws-iot-device-sdk/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==", - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@types/aws-iot-device-sdk/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==", - "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" - } - }, - "node_modules/@types/aws-iot-device-sdk/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==", - "license": "MIT", - "dependencies": { - "bl": "^4.0.2", - "debug": "^4.1.1", - "process-nextick-args": "^2.0.1" - } - }, - "node_modules/@types/aws-iot-device-sdk/node_modules/split2": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz", - "integrity": "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==", - "license": "ISC", - "dependencies": { - "readable-stream": "^3.0.0" - } - }, - "node_modules/@types/basic-auth": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@types/basic-auth/-/basic-auth-1.1.3.tgz", - "integrity": "sha512-W3rv6J0IGlxqgE2eQ2pTb0gBjaGtejQpJ6uaCjz3UQ65+TFTPC5/lAE+POfx1YLdjtxvejJzsIAfd3MxWiVmfg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "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, - "license": "MIT", - "dependencies": { - "@types/connect": "*", - "@types/node": "*" - } - }, - "node_modules/@types/chai": { - "version": "4.3.16", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.16.tgz", - "integrity": "sha512-PatH4iOdyh3MyWtmHVFXLWCCIhUbopaltqddG9BzB+gMIzee2MJrvd+jouii9Z3wzQJruGWAm7WOMjgfG8hQlQ==", - "dev": true, - "license": "MIT" - }, - "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, - "license": "MIT", - "dependencies": { - "@types/chai": "*" - } - }, - "node_modules/@types/chai-spies": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@types/chai-spies/-/chai-spies-1.0.6.tgz", - "integrity": "sha512-xkk4HmhBB9OQeTAifa9MJ+6R5/Rq9+ungDe4JidZD+vqZVeiWZwc2i7/pd1ZKjyGlSBIQePoWdyUyFUGT0rv5w==", - "dev": true, - "license": "MIT", - "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, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/content-type": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/@types/content-type/-/content-type-1.1.8.tgz", - "integrity": "sha512-1tBhmVUeso3+ahfyaKluXe38p+94lovUZdoVfQ3OnJo9uJC42JT7CBoN3k9HYhAae+GwiBYmHu+N9FZhOG+2Pg==", - "dev": true, - "license": "MIT" - }, - "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, - "license": "MIT", - "dependencies": { - "@types/ms": "*" - } - }, - "node_modules/@types/dns-packet": { - "version": "5.6.5", - "resolved": "https://registry.npmjs.org/@types/dns-packet/-/dns-packet-5.6.5.tgz", - "integrity": "sha512-qXOC7XLOEe43ehtWJCMnQXvgcIpv6rPmQ1jXT98Ad8A3TB1Ue50jsCbSSSyuazScEuZ/Q026vHbrOTVkmwA+7Q==", - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "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==", - "license": "MIT" - }, - "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, - "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", - "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, - "license": "MIT", - "dependencies": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*", - "@types/send": "*" - } - }, - "node_modules/@types/http-errors": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", - "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/istanbul-lib-coverage": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", - "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "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, - "license": "MIT" - }, - "node_modules/@types/jsrsasign": { - "version": "10.5.14", - "resolved": "https://registry.npmjs.org/@types/jsrsasign/-/jsrsasign-10.5.14.tgz", - "integrity": "sha512-lppSlfK6etu+cuKs40K4rg8As79PH6hzIB+v55zSqImbSH3SE6Fm8MBHCiI91cWlAP3Z4igtJK1VL3fSN09blQ==", - "license": "MIT" - }, - "node_modules/@types/lodash": { - "version": "4.17.6", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.6.tgz", - "integrity": "sha512-OpXEVoCKSS3lQqjx9GGGOapBeuW5eUboYHRlHP9urXPX25IKZ6AnP5ZRxtVf63iieUbsHxLn8NQ5Nlftc6yzAA==", - "license": "MIT" - }, - "node_modules/@types/mime": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", - "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/mkdirp": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@types/mkdirp/-/mkdirp-1.0.2.tgz", - "integrity": "sha512-o0K1tSO0Dx5X6xlU5F1D6625FawhC3dU3iqr25lluNv/+/QIVH8RLNEiVokgIZo+mz+87w/3Mkg/VvQS+J51fQ==", - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/mocha": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.1.1.tgz", - "integrity": "sha512-Z61JK7DKDtdKTWwLeElSEBcWGRLY8g95ic5FoQqI9CMx0ns/Ghep3B4DfcEimiKMvtamNVULVNKEsiwV3aQmXw==", - "dev": true, - "license": "MIT" - }, - "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, - "license": "MIT" - }, - "node_modules/@types/multicast-dns": { - "version": "7.2.4", - "resolved": "https://registry.npmjs.org/@types/multicast-dns/-/multicast-dns-7.2.4.tgz", - "integrity": "sha512-ib5K4cIDR4Ro5SR3Sx/LROkMDa0BHz0OPaCBL/OSPDsAXEGZ3/KQeS6poBKYVN7BfjXDL9lWNwzyHVgt/wkyCw==", - "license": "MIT", - "dependencies": { - "@types/dns-packet": "*", - "@types/node": "*" - } - }, - "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==", - "license": "MIT" - }, - "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, - "license": "MIT", - "dependencies": { - "@types/node": "*", - "form-data": "^4.0.0" - } - }, - "node_modules/@types/node-netconf": { - "name": "@types/netconf", - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@types/netconf/-/netconf-2.0.4.tgz", - "integrity": "sha512-QQIRdqsXWFwPNDYP85c7FpYPdz5tYb8Ev6U9yiHK3aOadIcl5wlZ9WwqQAAUv6yM5fbDdzqCwk5E/0bSfCbaDg==", - "license": "MIT" - }, - "node_modules/@types/proper-lockfile": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@types/proper-lockfile/-/proper-lockfile-4.1.4.tgz", - "integrity": "sha512-uo2ABllncSqg9F1D4nugVl9v93RmjxF6LJzQLMLDdPaXCUIDPeOJ21Gbqi43xNKzBi/WQ0Q0dICqufzQbMjipQ==", - "license": "MIT", - "dependencies": { - "@types/retry": "*" - } - }, - "node_modules/@types/qs": { - "version": "6.9.15", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.15.tgz", - "integrity": "sha512-uXHQKES6DQKKCLh441Xv/dwxOq1TVS3JPUMlEqoEglvlhR6Mxnlew/Xq/LRVHpLyk7iK3zODe1qYHIMltO7XGg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/range-parser": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", - "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", - "dev": true, - "license": "MIT" - }, - "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==", - "license": "MIT", - "dependencies": { - "@types/node": "*", - "safe-buffer": "~5.1.1" - } - }, - "node_modules/@types/retry": { - "version": "0.12.5", - "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.5.tgz", - "integrity": "sha512-3xSjTp3v03X/lSQLkczaN9UIEwJMoMCA1+Nb5HfbJEQWogdeQIyVtTvxPXDQjZ5zws8rFQfVfRdz03ARihPJgw==", - "license": "MIT" - }, - "node_modules/@types/semver": { - "version": "7.5.8", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", - "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", - "license": "MIT" - }, - "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, - "license": "MIT", - "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, - "license": "MIT", - "dependencies": { - "@types/http-errors": "*", - "@types/node": "*", - "@types/send": "*" - } - }, - "node_modules/@types/sinon": { - "version": "10.0.2", - "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.2.tgz", - "integrity": "sha512-BHn8Bpkapj8Wdfxvh2jWIUoaYB/9/XhsL0oOvBfRagJtKlSl9NWPcFOz2lRukI9szwGxFtYZCTejJSqsGDbdmw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@sinonjs/fake-timers": "^7.1.0" - } - }, - "node_modules/@types/sshpk": { - "version": "1.17.4", - "resolved": "https://registry.npmjs.org/@types/sshpk/-/sshpk-1.17.4.tgz", - "integrity": "sha512-5gI/7eJn6wmkuIuFY8JZJ1g5b30H9K5U5vKrvOuYu+hoZLb2xcVEgxhYZ2Vhbs0w/ACyzyfkJq0hQtBfSCugjw==", - "license": "MIT", - "dependencies": { - "@types/asn1": "*", - "@types/node": "*" - } - }, - "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==", - "license": "MIT" - }, - "node_modules/@types/uritemplate": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/@types/uritemplate/-/uritemplate-0.3.6.tgz", - "integrity": "sha512-31BMGZ8GgLxgXxLnqg4KbbyYJjU1flhTTD2+PVQStVUPXSk0IIpK0zt+tH3eLT7ZRwLnzQw6JhYx69qza3U0wg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/url-parse": { - "version": "1.4.11", - "resolved": "https://registry.npmjs.org/@types/url-parse/-/url-parse-1.4.11.tgz", - "integrity": "sha512-FKvKIqRaykZtd4n47LbK/W/5fhQQ1X7cxxzG9A48h0BGN+S04NH7ervcCjM8tyR0lyGru83FAHSmw2ObgKoESg==", - "license": "MIT" - }, - "node_modules/@types/uuid": { - "version": "8.3.4", - "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.4.tgz", - "integrity": "sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/ws": { - "version": "8.5.10", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.10.tgz", - "integrity": "sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==", - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz", - "integrity": "sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "6.21.0", - "@typescript-eslint/type-utils": "6.21.0", - "@typescript-eslint/utils": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0", - "debug": "^4.3.4", - "graphemer": "^1.4.0", - "ignore": "^5.2.4", - "natural-compare": "^1.4.0", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", - "eslint": "^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/parser": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.21.0.tgz", - "integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "@typescript-eslint/scope-manager": "6.21.0", - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/typescript-estree": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0", - "debug": "^4.3.4" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz", - "integrity": "sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/type-utils": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.21.0.tgz", - "integrity": "sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/typescript-estree": "6.21.0", - "@typescript-eslint/utils": "6.21.0", - "debug": "^4.3.4", - "ts-api-utils": "^1.0.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.21.0.tgz", - "integrity": "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz", - "integrity": "sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "minimatch": "9.0.3", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/utils": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.21.0.tgz", - "integrity": "sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "@types/json-schema": "^7.0.12", - "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "6.21.0", - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/typescript-estree": "6.21.0", - "semver": "^7.5.4" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" - } - }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz", - "integrity": "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "6.21.0", - "eslint-visitor-keys": "^3.4.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@ungap/promise-all-settled": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", - "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", - "dev": true, - "license": "ISC" - }, - "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, - "license": "ISC" - }, - "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==", - "license": "MIT", - "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==", - "license": "MIT" - }, - "node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "dev": true, - "license": "MIT", - "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==", - "license": "MIT", - "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, - "license": "MIT", - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/acorn-node": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.8.2.tgz", - "integrity": "sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "acorn": "^7.0.0", - "acorn-walk": "^7.0.0", - "xtend": "^4.0.2" - } - }, - "node_modules/acorn-node/node_modules/acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", - "dev": true, - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-node/node_modules/acorn-walk": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", - "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-walk": { - "version": "8.3.3", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.3.tgz", - "integrity": "sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw==", - "license": "MIT", - "dependencies": { - "acorn": "^8.11.0" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "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==", - "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", - "resolved": "https://registry.npmjs.org/aedes-packet/-/aedes-packet-2.3.1.tgz", - "integrity": "sha512-LqBd57uc2rui2RbjycW17dylglejG26mM4ewVXGNDnVp/SUHFVEgm7d1HTmYrnSkSCNoHti042qgcTwv/F+BtQ==", - "license": "MIT", - "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==", - "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", - "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" - } - ], - "license": "MIT", - "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==", - "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", - "resolved": "https://registry.npmjs.org/aedes-persistence/-/aedes-persistence-8.1.3.tgz", - "integrity": "sha512-VMCjEV+2g1TNJb/IlDEUy6SP9crT+QUhe2xc6UjyqrFNBNgTvHmOefXY7FxWrwmR2QA02vwg3+5p/JXkyg/Dkw==", - "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", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "license": "MIT", - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "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, - "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/ajv-formats": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", - "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", - "license": "MIT", - "dependencies": { - "ajv": "^8.0.0" - }, - "peerDependencies": { - "ajv": "^8.0.0" - }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } - } - }, - "node_modules/ajv-formats/node_modules/ajv": { - "version": "8.16.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.16.0.tgz", - "integrity": "sha512-F0twR8U1ZU67JIEtekUcLkXkoO5mMMmgGD8sK/xUFzJ805jxHQl92hImFAqqXMyMYjSPOyUPAwHYhB72g5sTXw==", - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.4.1" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ajv-formats/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "license": "MIT" - }, - "node_modules/ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", - "dev": true, - "license": "MIT", - "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==", - "license": "MIT", - "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==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/any-promise": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", - "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", - "license": "MIT" - }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "license": "ISC", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/aproba": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", - "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", - "license": "ISC", - "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.", - "license": "ISC", - "optional": true, - "dependencies": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" - } - }, - "node_modules/are-we-there-yet/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "license": "MIT", - "optional": true - }, - "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==", - "license": "MIT", - "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==", - "license": "MIT", - "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==", - "devOptional": true, - "license": "MIT" - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true, - "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, - "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", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", - "dev": true, - "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, - "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-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "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", - "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, - "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", - "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, - "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", - "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz", - "integrity": "sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==", - "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/asn1": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", - "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", - "license": "MIT", - "dependencies": { - "safer-buffer": "~2.1.0" - } - }, - "node_modules/asn1.js": { - "version": "4.10.1", - "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", - "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", - "dev": true, - "license": "MIT", - "dependencies": { - "bn.js": "^4.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0" - } - }, - "node_modules/asn1.js/node_modules/bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", - "dev": true, - "license": "MIT" - }, - "node_modules/asn1js": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/asn1js/-/asn1js-3.0.5.tgz", - "integrity": "sha512-FVnvrKJwpt9LP2lAMl8qZswRNm3T4q9CON+bxldk2iwk3FFpuwhx2FfinyitizWHsVYyaY+y5JzDR0rCMV5yTQ==", - "license": "BSD-3-Clause", - "dependencies": { - "pvtsutils": "^1.3.2", - "pvutils": "^1.1.3", - "tslib": "^2.4.0" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/assert": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/assert/-/assert-1.5.1.tgz", - "integrity": "sha512-zzw1uCAgLbsKwBfFc8CX78DDg+xZeBksSO3vwVIDDN5i94eOrPsSSyiVhmsSABFDM/OcpE2aagCat9dnWQLG1A==", - "dev": true, - "license": "MIT", - "dependencies": { - "object.assign": "^4.1.4", - "util": "^0.10.4" - } - }, - "node_modules/assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", - "license": "MIT", - "engines": { - "node": ">=0.8" - } - }, - "node_modules/assert/node_modules/inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", - "dev": true, - "license": "ISC" - }, - "node_modules/assert/node_modules/util": { - "version": "0.10.4", - "resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz", - "integrity": "sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==", - "dev": true, - "license": "MIT", - "dependencies": { - "inherits": "2.0.3" - } - }, - "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, - "license": "MIT", - "engines": { - "node": "*" - } - }, - "node_modules/async": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", - "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==", - "license": "MIT" - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "dev": true, - "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==", - "license": "MIT", - "dependencies": { - "possible-typed-array-names": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/babel-code-frame": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", - "integrity": "sha512-XqYMR2dfdGMW+hd0IUZ2PwK+fGeFkOxZJ0wY+JaQAHzt1Zx8LcvpiZD2NiGkEG8qx0CfkAOr5xt76d1e8vG90g==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^1.1.3", - "esutils": "^2.0.2", - "js-tokens": "^3.0.2" - } - }, - "node_modules/babel-code-frame/node_modules/ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/babel-code-frame/node_modules/ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/babel-code-frame/node_modules/chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/babel-code-frame/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/babel-code-frame/node_modules/strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/babel-code-frame/node_modules/supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/backoff": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/backoff/-/backoff-2.5.0.tgz", - "integrity": "sha512-wC5ihrnUXmR2douXmXLCe5O3zg3GKIyvRi/hi58a/XyRxVI+3/yM0PYueQOZXPXQ9pxBislYkw+sF9b7C/RuMA==", - "license": "MIT", - "dependencies": { - "precond": "0.2" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "license": "MIT" - }, - "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" - } - ], - "license": "MIT" - }, - "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==", - "license": "MIT", - "dependencies": { - "safe-buffer": "5.1.2" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", - "license": "BSD-3-Clause", - "dependencies": { - "tweetnacl": "^0.14.3" - } - }, - "node_modules/better-assert": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz", - "integrity": "sha512-bYeph2DFlpK1XmGs6fvlLRUN29QISM3GBuUwSFsMY2XRx4AvC0WNCS57j4c/xGrK2RS24C1w3YoBOsw9fT46tQ==", - "dependencies": { - "callsite": "1.0.0" - }, - "engines": { - "node": "*" - } - }, - "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==", - "license": "MIT", - "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==", - "license": "MIT", - "dependencies": { - "file-uri-to-path": "1.0.0" - } - }, - "node_modules/bl": { - "version": "6.0.14", - "resolved": "https://registry.npmjs.org/bl/-/bl-6.0.14.tgz", - "integrity": "sha512-TJfbvGdL7KFGxTsEbsED7avqpFdY56q9IW0/aiytyheJzxST/+Io6cx/4Qx0K2/u0BPRDs65mjaQzYvMZeNocQ==", - "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/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" - } - ], - "license": "MIT", - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } - }, - "node_modules/bl/node_modules/events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "license": "MIT", - "engines": { - "node": ">=0.8.x" - } - }, - "node_modules/bl/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==", - "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/bluebird": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", - "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", - "dev": true, - "license": "MIT" - }, - "node_modules/bn.js": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", - "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/body-parser": { - "version": "1.20.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", - "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", - "dev": true, - "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.11.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==", - "dev": true, - "license": "MIT", - "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==", - "dev": true, - "license": "MIT" - }, - "node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "license": "MIT", - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/brorand": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", - "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==", - "dev": true, - "license": "MIT" - }, - "node_modules/browser-pack": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/browser-pack/-/browser-pack-6.1.0.tgz", - "integrity": "sha512-erYug8XoqzU3IfcU8fUgyHqyOXqIE4tUTTQ+7mqUjQlvnXkOO6OlT9c/ZoJVHYoAaqGxr09CN53G7XIsO4KtWA==", - "dev": true, - "license": "MIT", - "dependencies": { - "combine-source-map": "~0.8.0", - "defined": "^1.0.0", - "JSONStream": "^1.0.3", - "safe-buffer": "^5.1.1", - "through2": "^2.0.0", - "umd": "^3.0.0" - }, - "bin": { - "browser-pack": "bin/cmd.js" - } - }, - "node_modules/browser-resolve": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-2.0.0.tgz", - "integrity": "sha512-7sWsQlYL2rGLy2IWm8WL8DCTJvYLc/qlOnsakDac87SOoCd16WLsaAMdCiAqsTNHIe+SXfaqyxyo6THoWqs8WQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "resolve": "^1.17.0" - } - }, - "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, - "license": "ISC" - }, - "node_modules/browserify": { - "version": "17.0.0", - "resolved": "https://registry.npmjs.org/browserify/-/browserify-17.0.0.tgz", - "integrity": "sha512-SaHqzhku9v/j6XsQMRxPyBrSP3gnwmE27gLJYZgMT2GeK3J0+0toN+MnuNYDfHwVGQfLiMZ7KSNSIXHemy905w==", - "dev": true, - "license": "MIT", - "dependencies": { - "assert": "^1.4.0", - "browser-pack": "^6.0.1", - "browser-resolve": "^2.0.0", - "browserify-zlib": "~0.2.0", - "buffer": "~5.2.1", - "cached-path-relative": "^1.0.0", - "concat-stream": "^1.6.0", - "console-browserify": "^1.1.0", - "constants-browserify": "~1.0.0", - "crypto-browserify": "^3.0.0", - "defined": "^1.0.0", - "deps-sort": "^2.0.1", - "domain-browser": "^1.2.0", - "duplexer2": "~0.1.2", - "events": "^3.0.0", - "glob": "^7.1.0", - "has": "^1.0.0", - "htmlescape": "^1.1.0", - "https-browserify": "^1.0.0", - "inherits": "~2.0.1", - "insert-module-globals": "^7.2.1", - "JSONStream": "^1.0.3", - "labeled-stream-splicer": "^2.0.0", - "mkdirp-classic": "^0.5.2", - "module-deps": "^6.2.3", - "os-browserify": "~0.3.0", - "parents": "^1.0.1", - "path-browserify": "^1.0.0", - "process": "~0.11.0", - "punycode": "^1.3.2", - "querystring-es3": "~0.2.0", - "read-only-stream": "^2.0.0", - "readable-stream": "^2.0.2", - "resolve": "^1.1.4", - "shasum-object": "^1.0.0", - "shell-quote": "^1.6.1", - "stream-browserify": "^3.0.0", - "stream-http": "^3.0.0", - "string_decoder": "^1.1.1", - "subarg": "^1.0.0", - "syntax-error": "^1.1.1", - "through2": "^2.0.0", - "timers-browserify": "^1.0.1", - "tty-browserify": "0.0.1", - "url": "~0.11.0", - "util": "~0.12.0", - "vm-browserify": "^1.0.0", - "xtend": "^4.0.0" - }, - "bin": { - "browserify": "bin/cmd.js" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/browserify-aes": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", - "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", - "dev": true, - "license": "MIT", - "dependencies": { - "buffer-xor": "^1.0.3", - "cipher-base": "^1.0.0", - "create-hash": "^1.1.0", - "evp_bytestokey": "^1.0.3", - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/browserify-cipher": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", - "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", - "dev": true, - "license": "MIT", - "dependencies": { - "browserify-aes": "^1.0.4", - "browserify-des": "^1.0.0", - "evp_bytestokey": "^1.0.0" - } - }, - "node_modules/browserify-des": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", - "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "cipher-base": "^1.0.1", - "des.js": "^1.0.0", - "inherits": "^2.0.1", - "safe-buffer": "^5.1.2" - } - }, - "node_modules/browserify-rsa": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.1.0.tgz", - "integrity": "sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog==", - "dev": true, - "license": "MIT", - "dependencies": { - "bn.js": "^5.0.0", - "randombytes": "^2.0.1" - } - }, - "node_modules/browserify-sign": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.3.tgz", - "integrity": "sha512-JWCZW6SKhfhjJxO8Tyiiy+XYB7cqd2S5/+WeYHsKdNKFlCBhKbblba1A/HN/90YwtxKc8tCErjffZl++UNmGiw==", - "dev": true, - "license": "ISC", - "dependencies": { - "bn.js": "^5.2.1", - "browserify-rsa": "^4.1.0", - "create-hash": "^1.2.0", - "create-hmac": "^1.1.7", - "elliptic": "^6.5.5", - "hash-base": "~3.0", - "inherits": "^2.0.4", - "parse-asn1": "^5.1.7", - "readable-stream": "^2.3.8", - "safe-buffer": "^5.2.1" - }, - "engines": { - "node": ">= 0.12" - } - }, - "node_modules/browserify-sign/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/browserify-sign/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dev": true, - "license": "MIT", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/browserify-sign/node_modules/readable-stream/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true, - "license": "MIT" - }, - "node_modules/browserify-sign/node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/browserify-sign/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/browserify-sign/node_modules/string_decoder/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true, - "license": "MIT" - }, - "node_modules/browserify-zlib": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", - "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", - "dev": true, - "license": "MIT", - "dependencies": { - "pako": "~1.0.5" - } - }, - "node_modules/browserify/node_modules/events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.x" - } - }, - "node_modules/browserify/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/browserify/node_modules/punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/browserify/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dev": true, - "license": "MIT", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/browserify/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.2.1.tgz", - "integrity": "sha512-c+Ko0loDaFfuPWiL02ls9Xd3GO3cPVmUobQ6t3rXNUk304u6hGq+8N/kFi+QEIKhzK3uwolVhLzszmfLmMLnqg==", - "license": "MIT", - "dependencies": { - "base64-js": "^1.0.2", - "ieee754": "^1.1.4" - } - }, - "node_modules/buffer-crc32": { - "version": "0.2.13", - "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", - "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", - "license": "MIT", - "engines": { - "node": "*" - } - }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "license": "MIT" - }, - "node_modules/buffer-xor": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", - "integrity": "sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==", - "dev": true, - "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, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/builtin-status-codes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", - "integrity": "sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/builtins": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/builtins/-/builtins-5.1.0.tgz", - "integrity": "sha512-SW9lzGTLvWTP1AY8xeAMZimqDrIaSdLQUcVr9DMef51niJ022Ri87SwRRKYm4A6iHfkPaiVUu/Duw2Wc4J7kKg==", - "dev": true, - "license": "MIT", - "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==", - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "readable-stream": "^3.1.1" - } - }, - "node_modules/byline": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/byline/-/byline-5.0.0.tgz", - "integrity": "sha512-s6webAy+R4SR8XVuJWt2V2rGvhnrhxN+9S15GNuTK3wKPOXFF6RNc+8ug2XhH+2s4f+uudG4kUVYmYOQWL2g0Q==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "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==", - "license": "MIT" - }, - "node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/c8": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/c8/-/c8-7.14.0.tgz", - "integrity": "sha512-i04rtkkcNcCf7zsQcSv/T9EbUn4RXQ6mropeMcjFOsQXQ0iGLAr/xT6TImQg4+U9hmNpN9XdvPkjUL1IzbgxJw==", - "dev": true, - "license": "ISC", - "dependencies": { - "@bcoe/v8-coverage": "^0.2.3", - "@istanbuljs/schema": "^0.1.3", - "find-up": "^5.0.0", - "foreground-child": "^2.0.0", - "istanbul-lib-coverage": "^3.2.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-reports": "^3.1.4", - "rimraf": "^3.0.2", - "test-exclude": "^6.0.0", - "v8-to-istanbul": "^9.0.0", - "yargs": "^16.2.0", - "yargs-parser": "^20.2.9" - }, - "bin": { - "c8": "bin/c8.js" - }, - "engines": { - "node": ">=10.12.0" - } - }, - "node_modules/cached-path-relative": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/cached-path-relative/-/cached-path-relative-1.1.0.tgz", - "integrity": "sha512-WF0LihfemtesFcJgO7xfOoOcnWzY/QHR4qeDqV44jPU3HTI54+LnfXK3SA27AVVGCdZFgjjFFaqUA9Jx7dMJZA==", - "dev": true, - "license": "MIT" - }, - "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==", - "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/callsite": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz", - "integrity": "sha512-0vdNRFXn5q+dtOqjfFtmtlI9N2eVZ7LMyEV2iKC5mEEFvSg/69Ml6b/WU2qF8W1nLRa0wiSrDT3Y5jOHZCwKPQ==", - "engines": { - "node": "*" - } - }, - "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, - "license": "MIT", - "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, - "license": "MIT", - "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==", - "license": "MIT" - }, - "node_modules/case-1.5.3": { - "name": "case", - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/case/-/case-1.6.3.tgz", - "integrity": "sha512-mzDSXIPaFwVDvZAHqZ9VlbyF4yyXRuX6IvB06WvPYkqJVO24kX1PPhv9bfpKNFZyxYFmmgo03HUiD8iklmJYRQ==", - "license": "(MIT OR GPL-3.0-or-later)", - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/cbor": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/cbor/-/cbor-8.1.0.tgz", - "integrity": "sha512-DwGjNW9omn6EwP70aXsn7FQJx5kO12tX0bZkaTjzdVFM6/7nhA4t0EENocKGx6D2Bch9PE2KzCUf5SceBdeijg==", - "license": "MIT", - "dependencies": { - "nofilter": "^3.1.0" - }, - "engines": { - "node": ">=12.19" - } - }, - "node_modules/chai": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.4.1.tgz", - "integrity": "sha512-13sOfMv2+DWduEU+/xbun3LScLoqN17nBeTLUsmDfKdoiC1fr0n9PU4guu4AhRcOVFk/sW8LyZWHuhWtQZiF+g==", - "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.0.8" - }, - "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, - "license": "WTFPL", - "dependencies": { - "check-error": "^1.0.2" - }, - "peerDependencies": { - "chai": ">= 2.1.2 < 6" - } - }, - "node_modules/chai-spies": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/chai-spies/-/chai-spies-1.1.0.tgz", - "integrity": "sha512-ikaUhQvQWchRYj2K54itFp3nrcxaFRpSDQxDlRzSn9aWgu9Pi7lD8yFxTso4WnQ39+WZ69oB/qOvqp+isJIIWA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4.0.0" - }, - "peerDependencies": { - "chai": "*" - } - }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "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, - "license": "MIT", - "dependencies": { - "get-func-name": "^2.0.2" - }, - "engines": { - "node": "*" - } - }, - "node_modules/chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], - "license": "MIT", - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/chokidar/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==", - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", - "license": "ISC", - "optional": true - }, - "node_modules/cipher-base": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", - "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/cli-table": { - "version": "0.3.11", - "resolved": "https://registry.npmjs.org/cli-table/-/cli-table-0.3.11.tgz", - "integrity": "sha512-IqLQi4lO0nIB4tcdTpN4LCB9FI3uqrJZK7RC515EnhZ6qBaglkIgICb1wjeAqpdoOabm1+SuQtkXIPdYC93jhQ==", - "dependencies": { - "colors": "1.0.3" - }, - "engines": { - "node": ">= 0.2.0" - } - }, - "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==", - "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", - "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/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "node_modules/co-bluebird": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/co-bluebird/-/co-bluebird-1.1.0.tgz", - "integrity": "sha512-JuoemMXxQjYAxbfRrNpOsLyiwDiY8mXvGqJyYLM7jMySDJtnMklW3V2o8uyubpc1eN2YoRsAdfZ1lfKCd3lsrA==", - "dev": true, - "dependencies": { - "bluebird": "^2.10.0", - "co-use": "^1.1.0" - }, - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/co-bluebird/node_modules/bluebird": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-2.11.0.tgz", - "integrity": "sha512-UfFSr22dmHPQqPP9XWHRhq+gWnHCYguQGkXQlbyPtW5qTnhFWA8/iXg765tH0cAjy7l/zPJ1aBTO0g5XgA7kvQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/co-use": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/co-use/-/co-use-1.1.0.tgz", - "integrity": "sha512-1lVRtdywv41zQO/xvI2wU8w6oFcUYT6T84YKSxN25KN4N4Kld3scLovt8FjDmD63Cm7HtyRWHjezt+IanXmkyA==", - "dev": true, - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/coap": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/coap/-/coap-1.4.0.tgz", - "integrity": "sha512-guVtoWEr48XcauWlebQThMWKrZU8iUURwghh2+CtOPicvL5TXo6h9qbvTwiBQQTgnBUS/jtfzMBoqX8iNWtGyQ==", - "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-packet": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/coap-packet/-/coap-packet-1.1.1.tgz", - "integrity": "sha512-Bkz2ZKI/7hU2gm6nUuo5l+MBSkdFJx7My1ZgNEhKUC7K2yYfQYVbBPRa64BBYLcEcYgaSlau4A1Uw5xfM2I0zw==", - "license": "MIT", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/coap/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" - } - ], - "license": "MIT", - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } - }, - "node_modules/coap/node_modules/events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "license": "MIT", - "engines": { - "node": ">=0.8.x" - } - }, - "node_modules/coap/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==", - "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/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==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">=0.10.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==", - "license": "MIT", - "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==", - "license": "MIT" - }, - "node_modules/colors": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", - "integrity": "sha512-pFGrxThWcWQ2MsAz6RtgeWe4NK2kUE1WfsrvvlctdII745EW9I0yflqhe7++M5LEc7bV2c/9/5zc8sFcpL0Drw==", - "license": "MIT", - "engines": { - "node": ">=0.1.90" - } - }, - "node_modules/combine-source-map": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/combine-source-map/-/combine-source-map-0.8.0.tgz", - "integrity": "sha512-UlxQ9Vw0b/Bt/KYwCFqdEwsQ1eL8d1gibiFb7lxQJFdvTgc2hIZi6ugsg+kyhzhPV+QEpUiEIwInIAIrgoEkrg==", - "dev": true, - "license": "MIT", - "dependencies": { - "convert-source-map": "~1.1.0", - "inline-source-map": "~0.6.0", - "lodash.memoize": "~3.0.3", - "source-map": "~0.5.3" - } - }, - "node_modules/combine-source-map/node_modules/convert-source-map": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.1.3.tgz", - "integrity": "sha512-Y8L5rp6jo+g9VEPgvqNfEopjTR4OTYct8lXlS8iVQdmnjDvbdbzYe9rjtFCB9egC86JoNCU61WRY+ScjkZpnIg==", - "dev": true, - "license": "MIT" - }, - "node_modules/combine-source-map/node_modules/source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "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, - "license": "MIT", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/commander": { - "version": "9.5.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", - "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", - "license": "MIT", - "engines": { - "node": "^12.20.0 || >=14" - } - }, - "node_modules/commist": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/commist/-/commist-3.2.0.tgz", - "integrity": "sha512-4PIMoPniho+LqXmpS5d3NuGYncG6XWlkBSVGiWycL22dd42OYdUGil2CWuzklaJoNxyxUSpO4MKIBU94viWNAw==", - "license": "MIT" - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "license": "MIT" - }, - "node_modules/concat-stream": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", - "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", - "dev": true, - "engines": [ - "node >= 0.8" - ], - "license": "MIT", - "dependencies": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^2.2.2", - "typedarray": "^0.0.6" - } - }, - "node_modules/concat-stream/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/concat-stream/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dev": true, - "license": "MIT", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/concat-stream/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/confbox": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.7.tgz", - "integrity": "sha512-uJcB/FKZtBMCJpK8MQji6bJHgu1tixKPxRLeGkNzBoOZzpnZUJm0jm2/sBDWcuBx1dYgxV4JU+g5hmNxCyAmdA==", - "dev": true, - "license": "MIT" - }, - "node_modules/console-browserify": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz", - "integrity": "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==", - "dev": true - }, - "node_modules/console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", - "license": "ISC", - "optional": true - }, - "node_modules/constants-browserify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", - "integrity": "sha512-xFxOwqIzR/e1k1gLiWEophSCMqXcwVHIH7akf7b/vxcUeGunlj3hvZaaqxwHsTgn+IndtkQJgSztIDWeumWJDQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "dev": true, - "license": "MIT", - "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==", - "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/content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true, - "license": "MIT" - }, - "node_modules/cookie": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", - "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", - "dev": true, - "license": "MIT", - "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==", - "dev": true, - "license": "MIT" - }, - "node_modules/core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", - "license": "MIT" - }, - "node_modules/create-ecdh": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz", - "integrity": "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==", - "dev": true, - "license": "MIT", - "dependencies": { - "bn.js": "^4.1.0", - "elliptic": "^6.5.3" - } - }, - "node_modules/create-ecdh/node_modules/bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", - "dev": true, - "license": "MIT" - }, - "node_modules/create-hash": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", - "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", - "dev": true, - "license": "MIT", - "dependencies": { - "cipher-base": "^1.0.1", - "inherits": "^2.0.1", - "md5.js": "^1.3.4", - "ripemd160": "^2.0.1", - "sha.js": "^2.4.0" - } - }, - "node_modules/create-hmac": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", - "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", - "dev": true, - "license": "MIT", - "dependencies": { - "cipher-base": "^1.0.3", - "create-hash": "^1.1.0", - "inherits": "^2.0.1", - "ripemd160": "^2.0.0", - "safe-buffer": "^5.0.1", - "sha.js": "^2.4.8" - } - }, - "node_modules/create-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "devOptional": true, - "license": "MIT" - }, - "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/crypto-browserify": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", - "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", - "dev": true, - "license": "MIT", - "dependencies": { - "browserify-cipher": "^1.0.0", - "browserify-sign": "^4.0.0", - "create-ecdh": "^4.0.0", - "create-hash": "^1.1.0", - "create-hmac": "^1.1.0", - "diffie-hellman": "^5.0.0", - "inherits": "^2.0.1", - "pbkdf2": "^3.0.3", - "public-encrypt": "^4.0.0", - "randombytes": "^2.0.0", - "randomfill": "^1.0.3" - }, - "engines": { - "node": "*" - } - }, - "node_modules/dash-ast": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/dash-ast/-/dash-ast-1.0.0.tgz", - "integrity": "sha512-Vy4dx7gquTeMcQR/hDkYLGUnwVil6vk4FOOct+djUnHOUWt+zJPJAaRIXaAFkPXtJjvlY7o3rfRu0/3hpnwoUA==", - "dev": true, - "license": "Apache-2.0" - }, - "node_modules/dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", - "license": "MIT", - "dependencies": { - "assert-plus": "^1.0.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "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, - "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", - "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, - "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", - "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, - "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.5", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", - "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", - "license": "MIT", - "dependencies": { - "ms": "2.1.2" - }, - "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, - "license": "MIT", - "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==", - "license": "MIT", - "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==", - "license": "MIT", - "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, - "license": "MIT", - "dependencies": { - "type-detect": "^4.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">=4.0.0" - } - }, - "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, - "license": "MIT" - }, - "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==", - "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", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", - "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", - "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/defined": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.1.tgz", - "integrity": "sha512-hsBd2qSVCRE+5PmNdHt1uzyrFu5d3RwmFDKzyNZMFq/EwDNJF7Ee5+D5oEKF0hU6LhtoUF1macFvOe4AskQC1Q==", - "dev": true, - "license": "MIT", - "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, - "license": "MIT", - "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==", - "license": "MIT", - "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==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/deps-sort": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/deps-sort/-/deps-sort-2.0.1.tgz", - "integrity": "sha512-1orqXQr5po+3KI6kQb9A4jnXT1PBwggGl2d7Sq2xsnOeI9GPcE/tGcF9UiSZtZBM7MukY4cAh7MemS6tZYipfw==", - "dev": true, - "license": "MIT", - "dependencies": { - "JSONStream": "^1.0.3", - "shasum-object": "^1.0.0", - "subarg": "^1.0.0", - "through2": "^2.0.0" - }, - "bin": { - "deps-sort": "bin/cmd.js" - } - }, - "node_modules/dequeue": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/dequeue/-/dequeue-1.0.5.tgz", - "integrity": "sha512-2FIVJZTaWhUj0Y2uKmDAasTP6ZwFWRjkRc01MYN5jFm96iIzkYyNzGADfJ13C5W7CTN7XO9mBYDcVB68eNybBA==", - "engines": { - "node": "*" - } - }, - "node_modules/des.js": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.1.0.tgz", - "integrity": "sha512-r17GxjhUCjSRy8aiJpr8/UadFIzMzJGexI3Nmz4ADi9LYSFx4gTBp80+NaX/YsXWWLhpZ7v/v/ubEc/bCNfKwg==", - "dev": true, - "license": "MIT", - "dependencies": { - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0" - } - }, - "node_modules/destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", - "dev": true, - "license": "MIT", - "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==", - "license": "Apache-2.0", - "optional": true, - "bin": { - "detect-libc": "bin/detect-libc.js" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/detective": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/detective/-/detective-5.2.1.tgz", - "integrity": "sha512-v9XE1zRnz1wRtgurGu0Bs8uHKFSTdteYZNbIPFVhUZ39L/S79ppMpdmVOZAnoz1jfEFodc48n6MX483Xo3t1yw==", - "dev": true, - "license": "MIT", - "dependencies": { - "acorn-node": "^1.8.2", - "defined": "^1.0.0", - "minimist": "^1.2.6" - }, - "bin": { - "detective": "bin/detective.js" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/diff": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", - "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/diffie-hellman": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", - "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", - "dev": true, - "license": "MIT", - "dependencies": { - "bn.js": "^4.1.0", - "miller-rabin": "^4.0.0", - "randombytes": "^2.0.0" - } - }, - "node_modules/diffie-hellman/node_modules/bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", - "dev": true, - "license": "MIT" - }, - "node_modules/dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/dns-equal": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", - "integrity": "sha512-z+paD6YUQsk+AbGCEM4PrOXSss5gd66QfcVBFTKR/HpFL9jCqikS94HYwKww6fQyO7IxrIIyUu+g0Ka9tUS2Cg==", - "license": "MIT" - }, - "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==", - "license": "MIT", - "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, - "license": "Apache-2.0", - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/domain-browser": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", - "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.4", - "npm": ">=1.2" - } - }, - "node_modules/dotenv": { - "version": "8.6.0", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.6.0.tgz", - "integrity": "sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=10" - } - }, - "node_modules/duplexer2": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", - "integrity": "sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "readable-stream": "^2.0.2" - } - }, - "node_modules/duplexer2/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/duplexer2/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dev": true, - "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/duplexer2/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/duplexify": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.3.tgz", - "integrity": "sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA==", - "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/ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", - "license": "MIT", - "dependencies": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" - } - }, - "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==", - "dev": true, - "license": "MIT" - }, - "node_modules/elliptic": { - "version": "6.5.5", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.5.tgz", - "integrity": "sha512-7EjbcmUm17NQFu4Pmgmq2olYMj8nwMnpcddByChSUjArp8F5DQWcIcpriwO4ZToLNAJig0yiyjswfyGNje/ixw==", - "dev": true, - "license": "MIT", - "dependencies": { - "bn.js": "^4.11.9", - "brorand": "^1.1.0", - "hash.js": "^1.0.0", - "hmac-drbg": "^1.0.1", - "inherits": "^2.0.4", - "minimalistic-assert": "^1.0.1", - "minimalistic-crypto-utils": "^1.0.1" - } - }, - "node_modules/elliptic/node_modules/bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", - "dev": true, - "license": "MIT" - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "license": "MIT" - }, - "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==", - "dev": true, - "license": "MIT", - "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==", - "license": "MIT", - "dependencies": { - "once": "^1.4.0" - } - }, - "node_modules/env-paths": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", - "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "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, - "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", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", - "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", - "license": "MIT", - "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==", - "license": "MIT", - "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, - "license": "MIT", - "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, - "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", - "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz", - "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", - "dev": true, - "license": "MIT", - "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, - "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.1.2", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", - "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", - "license": "MIT", - "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==", - "dev": true, - "license": "MIT" - }, - "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, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", - "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", - "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.0", - "@humanwhocodes/config-array": "^0.11.14", - "@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, - "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", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz", - "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==", - "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", - "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" - } - ], - "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", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", - "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", - "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", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/eslint-module-utils": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.1.tgz", - "integrity": "sha512-rXDXR3h7cs7dy9RNpUlQf80nX31XWJEyGq1tRMo+6GsO5VmTe4UTwtmonAD4ZkAsrfMVDA2wlGJ3790Ys+D49Q==", - "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", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "license": "MIT", - "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, - "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", - "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" - ], - "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.29.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.29.1.tgz", - "integrity": "sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==", - "dev": true, - "license": "MIT", - "dependencies": { - "array-includes": "^3.1.7", - "array.prototype.findlastindex": "^1.2.3", - "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.8.0", - "hasown": "^2.0.0", - "is-core-module": "^2.13.1", - "is-glob": "^4.0.3", - "minimatch": "^3.1.2", - "object.fromentries": "^2.0.7", - "object.groupby": "^1.0.1", - "object.values": "^1.1.7", - "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, - "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", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "license": "MIT", - "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, - "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", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/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, - "license": "ISC", - "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, - "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", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "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", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "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, - "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", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/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, - "license": "ISC", - "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, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/eslint-plugin-notice": { - "version": "0.9.10", - "resolved": "https://registry.npmjs.org/eslint-plugin-notice/-/eslint-plugin-notice-0.9.10.tgz", - "integrity": "sha512-rF79EuqdJKu9hhTmwUkNeSvLmmq03m/NXq/NHwUENHbdJ0wtoyOjxZBhW4QCug8v5xYE6cGe3AWkGqSIe9KUbQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "find-root": "^1.1.0", - "lodash": "^4.17.15", - "metric-lcs": "^0.1.2" - }, - "peerDependencies": { - "eslint": ">=3.0.0" - } - }, - "node_modules/eslint-plugin-promise": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-6.4.0.tgz", - "integrity": "sha512-/KWWRaD3fGkVCZsdR0RU53PSthFmoHVhZl+y9+6DqeDLSikLdlUVpVEAmI6iCRR5QyOjBYBqHZV/bdv4DJ4Gtw==", - "dev": true, - "license": "ISC", - "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": "3.2.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-unused-imports/-/eslint-plugin-unused-imports-3.2.0.tgz", - "integrity": "sha512-6uXyn6xdINEpxE1MtDjxQsyXB37lfyO2yKGVVgtD7WEWQGORSOZjgrD6hBhvGv4/SO+TOlS+UnC6JppRqbuwGQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "eslint-rule-composer": "^0.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "peerDependencies": { - "@typescript-eslint/eslint-plugin": "6 - 7", - "eslint": "8" - }, - "peerDependenciesMeta": { - "@typescript-eslint/eslint-plugin": { - "optional": true - } - } - }, - "node_modules/eslint-plugin-workspaces": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-workspaces/-/eslint-plugin-workspaces-0.9.0.tgz", - "integrity": "sha512-krMuZ+yZgzwv1oTBfz50oamNVPDIm7CDyot3i1GRKBqMD2oXAwnXHLQWH7ctpV8k6YVrkhcaZhuV9IJxD8OPAQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "find-workspaces": "^0.2.0" - } - }, - "node_modules/eslint-rule-composer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/eslint-rule-composer/-/eslint-rule-composer-0.3.0.tgz", - "integrity": "sha512-bt+Sh8CtDmn2OajxvNO+BX7Wn4CIWMpTRm3MaiKPCQcnnlm0CS2mhui6QaoeQugs+3Kj2ESKEEGJUdVafwhiCg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4.0.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, - "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", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", - "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", - "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", - "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, - "license": "Apache-2.0", - "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, - "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/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/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, - "license": "ISC", - "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, - "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/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, - "license": "BSD-2-Clause", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/esquery": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", - "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", - "dev": true, - "license": "BSD-3-Clause", - "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, - "license": "BSD-2-Clause", - "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, - "license": "BSD-2-Clause", - "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, - "license": "BSD-2-Clause", - "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==", - "dev": true, - "license": "MIT", - "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==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/events": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/events/-/events-2.1.0.tgz", - "integrity": "sha512-3Zmiobend8P9DjmKAty0Era4jV8oJ0yGYe2nJJAxgymF9+N8F2m0hhZiMoWtcfepExzNKZumFU3ksdQbInGWCg==", - "license": "MIT", - "engines": { - "node": ">=0.4.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==", - "license": "MIT", - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/evp_bytestokey": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", - "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", - "dev": true, - "license": "MIT", - "dependencies": { - "md5.js": "^1.3.4", - "safe-buffer": "^5.1.1" - } - }, - "node_modules/execa": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", - "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", - "dev": true, - "license": "MIT", - "dependencies": { - "cross-spawn": "^7.0.0", - "get-stream": "^5.0.0", - "human-signals": "^1.1.1", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.0", - "onetime": "^5.1.0", - "signal-exit": "^3.0.2", - "strip-final-newline": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "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==", - "license": "(MIT OR WTFPL)", - "optional": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/express": { - "version": "4.19.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz", - "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "1.20.2", - "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": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "1.2.0", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.7", - "qs": "6.11.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "0.18.0", - "serve-static": "1.15.0", - "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==", - "dev": true, - "license": "MIT", - "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==", - "dev": true, - "license": "MIT" - }, - "node_modules/express/node_modules/path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==", - "dev": true, - "license": "MIT" - }, - "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==", - "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/extsprintf": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.4.1.tgz", - "integrity": "sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA==", - "engines": [ - "node >=0.6.0" - ], - "license": "MIT" - }, - "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==", - "license": "MIT" - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "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, - "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-glob/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/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, - "license": "MIT" - }, - "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, - "license": "MIT" - }, - "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==", - "license": "MIT", - "dependencies": { - "fast-decode-uri-component": "^1.0.1" - } - }, - "node_modules/fast-safe-stringify": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", - "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==", - "dev": true, - "license": "MIT" - }, - "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==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.23.8", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.1.0" - } - }, - "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==", - "license": "MIT", - "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==", - "license": "ISC", - "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, - "license": "ISC", - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/fastseries": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fastseries/-/fastseries-2.0.0.tgz", - "integrity": "sha512-XBU9RXeoYc2/VnvMhplAxEmZLfIk7cvTBu+xwoBuTI8pL19E03cmca17QQycKIdxgwCeFA/a4u27gv1h3ya5LQ==", - "license": "ISC" - }, - "node_modules/fd-slicer": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", - "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", - "license": "MIT", - "dependencies": { - "pend": "~1.2.0" - } - }, - "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, - "license": "MIT", - "dependencies": { - "flat-cache": "^3.0.4" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/file-uri-to-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", - "license": "MIT" - }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "license": "MIT", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/filter-obj": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz", - "integrity": "sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/finalhandler": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", - "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "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==", - "dev": true, - "license": "MIT", - "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==", - "dev": true, - "license": "MIT" - }, - "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==", - "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", - "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", - "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", - "dev": true, - "license": "MIT" - }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "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.2.0", - "resolved": "https://registry.npmjs.org/find-workspaces/-/find-workspaces-0.2.0.tgz", - "integrity": "sha512-OTHryv88yjzwvbXHGi0+XRFu7Jqe5pFuIR2mhqdatDJQOBJd7MFJOPFJv4EbNo8n1BNM/13Y2KcyDpFQYf0ETw==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-glob": "^3.2.12", - "pkg-types": "^1.0.3", - "yaml": "^2.3.1" - } - }, - "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, - "license": "BSD-3-Clause", - "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, - "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", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", - "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", - "dev": true, - "license": "ISC" - }, - "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==", - "license": "MIT", - "dependencies": { - "is-callable": "^1.1.3" - } - }, - "node_modules/foreground-child": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", - "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==", - "dev": true, - "license": "ISC", - "dependencies": { - "cross-spawn": "^7.0.0", - "signal-exit": "^3.0.2" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "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, - "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", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/from2": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", - "integrity": "sha512-OMcX/4IC/uqEPVgGeyfN22LJk6AZrMkRZHxcHBMBvHScDGgwTm2GT2Wkgtocyd3JfZffjj2kYUDXXII0Fk9W0g==", - "license": "MIT", - "dependencies": { - "inherits": "^2.0.1", - "readable-stream": "^2.0.0" - } - }, - "node_modules/from2/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "license": "MIT" - }, - "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==", - "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", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "license": "MIT", - "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==", - "license": "MIT", - "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==", - "license": "ISC" - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { + "node_modules/@ungap/promise-all-settled": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/function.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, - "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", - "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", - "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", + "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/gauge": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", - "integrity": "sha512-14x4kjc6lkD3ltw589k0NrPD6cCNTD6CWoVUNpB85+DrtONoZn+Rug6xZU5RvSC4+TZPxA5AnBibQYAvZn41Hg==", - "deprecated": "This package is no longer supported.", - "license": "ISC", - "optional": true, - "dependencies": { - "aproba": "^1.0.3", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.0", - "object-assign": "^4.1.0", - "signal-exit": "^3.0.0", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wide-align": "^1.1.0" - } - }, - "node_modules/gauge/node_modules/ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/gauge/node_modules/is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw==", - "license": "MIT", - "optional": true, - "dependencies": { - "number-is-nan": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/gauge/node_modules/string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha512-0XsVpQLnVCXHJfyEs8tC0zpTVIr5PKKsQtkT29IwupnPTjtPmQ3xT/4yCREF9hYkV/3M3kzcUTSAZT6a6h81tw==", - "license": "MIT", - "optional": true, - "dependencies": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/gauge/node_modules/strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", - "license": "MIT", - "optional": true, - "dependencies": { - "ansi-regex": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } + "license": "ISC" }, - "node_modules/get-assigned-identifiers": { + "node_modules/@ungap/structured-clone": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/get-assigned-identifiers/-/get-assigned-identifiers-1.2.0.tgz", - "integrity": "sha512-mBBwmeGTrxEMO4pMaaf/uUEFHnYtwr8FTe8Y/mer4rcV/bye0qGm6pw1bGZFGStxC5O76c5ZAVBGnqHmOaJpdQ==", - "dev": true, - "license": "Apache-2.0" - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "license": "ISC", - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-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, - "license": "MIT", - "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==", - "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-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "dev": true, - "license": "MIT", - "dependencies": { - "pump": "^3.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "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==", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", "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" - } + "license": "ISC" }, - "node_modules/get-tsconfig": { - "version": "4.7.5", - "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.7.5.tgz", - "integrity": "sha512-ZCuZCnlqNzjb4QprAzXKdpp/gh6KTxSJuw3IBsPnV/7fV4NxC9ckB+vPTt8w7fJA0TaSD7c55BR47JD6MEDyDw==", + "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, "license": "MIT", - "peer": true, - "dependencies": { - "resolve-pkg-maps": "^1.0.0" - }, - "funding": { - "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" - } - }, - "node_modules/getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", - "license": "MIT", - "dependencies": { - "assert-plus": "^1.0.0" - } - }, - "node_modules/github-from-package": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", - "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", - "license": "MIT", - "optional": true - }, - "node_modules/glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "bin": { + "acorn": "bin/acorn" }, "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=0.4.0" } }, - "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==", + "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, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/glob/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/glob/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, - "node_modules/globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "node_modules/acorn-walk": { + "version": "8.3.3", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.3.tgz", + "integrity": "sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw==", "dev": true, "license": "MIT", "dependencies": { - "type-fest": "^0.20.2" + "acorn": "^8.11.0" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=0.4.0" } }, - "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==", + "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, "license": "MIT", "dependencies": { - "define-properties": "^1.2.1", - "gopd": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" + "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": { - "url": "https://github.com/sponsors/ljharb" + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, + "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==", "license": "MIT", "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" + "ajv": "^8.0.0" }, - "engines": { - "node": ">=10" + "peerDependencies": { + "ajv": "^8.0.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependenciesMeta": { + "ajv": { + "optional": true + } } }, - "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "node_modules/ajv-formats/node_modules/ajv": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.16.0.tgz", + "integrity": "sha512-F0twR8U1ZU67JIEtekUcLkXkoO5mMMmgGD8sK/xUFzJ805jxHQl92hImFAqqXMyMYjSPOyUPAwHYhB72g5sTXw==", "license": "MIT", "dependencies": { - "get-intrinsic": "^1.1.3" + "fast-deep-equal": "^3.1.3", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.4.1" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "license": "ISC" - }, - "node_modules/graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true, + "node_modules/ajv-formats/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "license": "MIT" }, - "node_modules/growl": { - "version": "1.10.5", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", - "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", + "node_modules/ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", "dev": true, "license": "MIT", "engines": { - "node": ">=4.x" + "node": ">=6" } }, - "node_modules/has": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.4.tgz", - "integrity": "sha512-qdSAmqLF6209RFj4VVItywPMbm3vWylknmB3nvNiUIs72xAimcM8nVYxYr7ncvZq5qzk9MKIZR8ijqD/1QuYjQ==", + "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, "license": "MIT", "engines": { - "node": ">= 0.4.0" + "node": ">=8" } }, - "node_modules/has-ansi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "integrity": "sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==", + "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, "license": "MIT", "dependencies": { - "ansi-regex": "^2.0.0" + "color-convert": "^2.0.1" }, "engines": { - "node": ">=0.10.0" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/has-ansi/node_modules/ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "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, - "license": "MIT", + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, "engines": { - "node": ">=0.10.0" + "node": ">= 8" } }, - "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==", + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } + "license": "MIT" }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" }, - "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==", + "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, "license": "MIT", "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==", - "license": "MIT", - "engines": { - "node": ">= 0.4" + "call-bind": "^1.0.5", + "is-array-buffer": "^3.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==", - "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -6126,13 +928,19 @@ "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==", + "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": { - "has-symbols": "^1.0.3" + "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" @@ -6141,421 +949,363 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", - "license": "ISC", - "optional": true - }, - "node_modules/hash-base": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", - "integrity": "sha512-EeeoJKjTyt868liAlVmcv2ZsUfGHlE3Q+BICOXcZiwN3osr5Q/zFGYmTJpoIzuaSTAwndFy+GqhEwlU4L3j4Ow==", + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", "dev": true, "license": "MIT", - "dependencies": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - }, "engines": { - "node": ">=4" + "node": ">=8" } }, - "node_modules/hash.js": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", - "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "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", "dependencies": { - "inherits": "^2.0.3", - "minimalistic-assert": "^1.0.1" - } - }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/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, - "license": "MIT", - "bin": { - "he": "bin/he" - } - }, - "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==", - "license": "MIT" - }, - "node_modules/hexy": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/hexy/-/hexy-0.3.5.tgz", - "integrity": "sha512-UCP7TIZPXz5kxYJnNOym+9xaenxCLor/JyhKieo8y8/bJWunGh9xbhy3YrgYJUQ87WwfXGm05X330DszOfINZw==", - "license": "MIT", - "bin": { - "hexy": "bin/hexy_cmd.js" + "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": ">=10.4" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/hmac-drbg": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", - "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==", + "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, "license": "MIT", "dependencies": { - "hash.js": "^1.0.3", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.1" + "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/html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true, - "license": "MIT" - }, - "node_modules/htmlescape": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/htmlescape/-/htmlescape-1.1.1.tgz", - "integrity": "sha512-eVcrzgbR4tim7c7soKQKtxa/kQM4TzjnlU83rcZ9bHU6t31ehfV7SktN6McWgwPWg+JYMA/O3qpGxBvFq1z2Jg==", + "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, "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.10" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "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==", + "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, "license": "MIT", "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" + "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.8" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/https-browserify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", - "integrity": "sha512-J+FkSdyD+0mA0N+81tMotaRMfSL9SGi+xpD3T6YApKsc3bGSXJlfXri3VyFOeYkfLRQisDk1W+jIFFKBeUBbBg==", - "dev": true, - "license": "MIT" + "node_modules/asn1": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", + "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", + "license": "MIT", + "dependencies": { + "safer-buffer": "~2.1.0" + } }, - "node_modules/human-signals": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", - "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", - "dev": true, - "license": "Apache-2.0", + "node_modules/assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", + "license": "MIT", "engines": { - "node": ">=8.12.0" + "node": ">=0.8" } }, - "node_modules/humanize": { - "version": "0.0.9", - "resolved": "https://registry.npmjs.org/humanize/-/humanize-0.0.9.tgz", - "integrity": "sha512-bvZZ7vXpr1RKoImjuQ45hJb5OvE2oJafHysiD/AL3nkqTZH2hFCjQ3YZfCd63FefDitbJze/ispUPP0gfDsT2Q==", + "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, + "license": "MIT", "engines": { "node": "*" } }, - "node_modules/husky": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/husky/-/husky-7.0.4.tgz", - "integrity": "sha512-vbaCKN2QLtP/vD4yvs6iz6hBEo6wkSzs8HpRah1Z6aGmF2KW5PdYuAd7uX5a+OyBZHBhd+TFLqgjUgytQr4RvQ==", + "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, "license": "MIT", - "bin": { - "husky": "lib/bin.js" + "dependencies": { + "possible-typed-array-names": "^1.0.0" }, "engines": { - "node": ">=12" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/typicode" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/hyperid": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/hyperid/-/hyperid-3.2.0.tgz", - "integrity": "sha512-PdTtDo+Rmza9nEhTunaDSUKwbC69TIzLEpZUwiB6f+0oqmY0UPfhyHCPt6K1NQ4WFv5yJBTG5vELztVWP+nEVQ==", + "node_modules/babel-code-frame": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", + "integrity": "sha512-XqYMR2dfdGMW+hd0IUZ2PwK+fGeFkOxZJ0wY+JaQAHzt1Zx8LcvpiZD2NiGkEG8qx0CfkAOr5xt76d1e8vG90g==", + "dev": true, "license": "MIT", "dependencies": { - "buffer": "^5.2.1", - "uuid": "^8.3.2", - "uuid-parse": "^1.1.0" - } - }, - "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==", - "license": "MIT", - "bin": { - "uuid": "dist/bin/uuid" + "chalk": "^1.1.3", + "esutils": "^2.0.2", + "js-tokens": "^3.0.2" } }, - "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==", + "node_modules/babel-code-frame/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", "dev": true, "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, "engines": { "node": ">=0.10.0" } }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "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.1", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", - "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", + "node_modules/babel-code-frame/node_modules/ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==", "dev": true, "license": "MIT", "engines": { - "node": ">= 4" + "node": ">=0.10.0" } }, - "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==", + "node_modules/babel-code-frame/node_modules/chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", "dev": true, "license": "MIT", "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" }, "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=0.10.0" } }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "node_modules/babel-code-frame/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "dev": true, "license": "MIT", "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.", - "license": "ISC", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" + "node": ">=0.8.0" } }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "license": "ISC" - }, - "node_modules/ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "license": "ISC", - "optional": true - }, - "node_modules/inline-source-map": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/inline-source-map/-/inline-source-map-0.6.3.tgz", - "integrity": "sha512-1aVsPEsJWMJq/pdMU61CDlm1URcW702MTB4w9/zUjMus6H/Py8o7g68Pr9D4I6QluWGt/KdmswuRhaA05xVR1w==", + "node_modules/babel-code-frame/node_modules/strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", "dev": true, "license": "MIT", "dependencies": { - "source-map": "~0.5.3" + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "node_modules/inline-source-map/node_modules/source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "node_modules/babel-code-frame/node_modules/supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==", "dev": true, - "license": "BSD-3-Clause", + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=0.8.0" } }, - "node_modules/insert-module-globals": { - "version": "7.2.1", - "resolved": "https://registry.npmjs.org/insert-module-globals/-/insert-module-globals-7.2.1.tgz", - "integrity": "sha512-ufS5Qq9RZN+Bu899eA9QCAYThY+gGW7oRkmb0vC93Vlyu/CFGcH0OYPEjVkDXA5FEbTt1+VWzdoOD3Ny9N+8tg==", + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true, - "license": "MIT", + "license": "MIT" + }, + "node_modules/bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", + "license": "BSD-3-Clause", "dependencies": { - "acorn-node": "^1.5.2", - "combine-source-map": "^0.8.0", - "concat-stream": "^1.6.1", - "is-buffer": "^1.1.0", - "JSONStream": "^1.0.3", - "path-is-absolute": "^1.0.1", - "process": "~0.11.0", - "through2": "^2.0.0", - "undeclared-identifiers": "^1.1.2", - "xtend": "^4.0.0" - }, - "bin": { - "insert-module-globals": "bin/cmd.js" + "tweetnacl": "^0.14.3" } }, - "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==", + "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, "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "hasown": "^2.0.0", - "side-channel": "^1.0.4" - }, "engines": { - "node": ">= 0.4" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/ipaddr.js": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.2.0.tgz", - "integrity": "sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==", + "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==", "license": "MIT", - "engines": { - "node": ">= 10" + "dependencies": { + "file-uri-to-path": "1.0.0" } }, - "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==", + "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, "license": "MIT", - "engines": { - "node": ">=8" + "dependencies": { + "balanced-match": "^1.0.0" } }, - "node_modules/is-arguments": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", - "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "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, "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" + "fill-range": "^7.1.1" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "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==", + "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, + "license": "ISC" + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true, + "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, "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.1" - }, + "peer": true, "engines": { - "node": ">= 0.4" + "node": ">=6" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-bigint": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "node_modules/builtins": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/builtins/-/builtins-5.1.0.tgz", + "integrity": "sha512-SW9lzGTLvWTP1AY8xeAMZimqDrIaSdLQUcVr9DMef51niJ022Ri87SwRRKYm4A6iHfkPaiVUu/Duw2Wc4J7kKg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { - "has-bigints": "^1.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "semver": "^7.0.0" } }, - "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==", - "license": "MIT", + "node_modules/c8": { + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/c8/-/c8-7.14.0.tgz", + "integrity": "sha512-i04rtkkcNcCf7zsQcSv/T9EbUn4RXQ6mropeMcjFOsQXQ0iGLAr/xT6TImQg4+U9hmNpN9XdvPkjUL1IzbgxJw==", + "dev": true, + "license": "ISC", "dependencies": { - "binary-extensions": "^2.0.0" + "@bcoe/v8-coverage": "^0.2.3", + "@istanbuljs/schema": "^0.1.3", + "find-up": "^5.0.0", + "foreground-child": "^2.0.0", + "istanbul-lib-coverage": "^3.2.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-reports": "^3.1.4", + "rimraf": "^3.0.2", + "test-exclude": "^6.0.0", + "v8-to-istanbul": "^9.0.0", + "yargs": "^16.2.0", + "yargs-parser": "^20.2.9" + }, + "bin": { + "c8": "bin/c8.js" }, "engines": { - "node": ">=8" + "node": ">=10.12.0" } }, - "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==", + "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==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" + "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" @@ -6564,224 +1314,274 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "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, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">=6" + } }, - "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==", + "node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", "dev": true, "license": "MIT", - "peer": true, - "dependencies": { - "builtin-modules": "^3.3.0" - }, "engines": { - "node": ">=6" + "node": ">=10" }, "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==", - "license": "MIT", + "node_modules/case-1.5.3": { + "name": "case", + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/case/-/case-1.6.3.tgz", + "integrity": "sha512-mzDSXIPaFwVDvZAHqZ9VlbyF4yyXRuX6IvB06WvPYkqJVO24kX1PPhv9bfpKNFZyxYFmmgo03HUiD8iklmJYRQ==", + "license": "(MIT OR GPL-3.0-or-later)", "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">= 0.8.0" } }, - "node_modules/is-core-module": { - "version": "2.14.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.14.0.tgz", - "integrity": "sha512-a5dFJih5ZLYlRtDc0dZWP7RiKr6xIKzmn/oAYCDvdLThadVgyJwlaoQPmRtMSpz+rk0OGAgIu+TcM9HUF0fk1A==", + "node_modules/cbor": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/cbor/-/cbor-8.1.0.tgz", + "integrity": "sha512-DwGjNW9omn6EwP70aXsn7FQJx5kO12tX0bZkaTjzdVFM6/7nhA4t0EENocKGx6D2Bch9PE2KzCUf5SceBdeijg==", "license": "MIT", "dependencies": { - "hasown": "^2.0.2" + "nofilter": "^3.1.0" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=12.19" } }, - "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==", + "node_modules/chai": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.4.1.tgz", + "integrity": "sha512-13sOfMv2+DWduEU+/xbun3LScLoqN17nBeTLUsmDfKdoiC1fr0n9PU4guu4AhRcOVFk/sW8LyZWHuhWtQZiF+g==", "dev": true, "license": "MIT", "dependencies": { - "is-typed-array": "^1.1.13" + "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.0.8" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=4" } }, - "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==", + "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, - "license": "MIT", + "license": "WTFPL", "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" + "check-error": "^1.0.2" }, - "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==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" + "peerDependencies": { + "chai": ">= 2.1.2 < 6" } }, - "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==", + "node_modules/chai-spies": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/chai-spies/-/chai-spies-1.1.0.tgz", + "integrity": "sha512-ikaUhQvQWchRYj2K54itFp3nrcxaFRpSDQxDlRzSn9aWgu9Pi7lD8yFxTso4WnQ39+WZ69oB/qOvqp+isJIIWA==", + "dev": true, "license": "MIT", "engines": { - "node": ">=8" + "node": ">= 4.0.0" + }, + "peerDependencies": { + "chai": "*" } }, - "node_modules/is-generator": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-generator/-/is-generator-1.0.3.tgz", - "integrity": "sha512-G56jBpbJeg7ds83HW1LuShNs8J73Fv3CPz/bmROHOHlnKkN8sWb9ujiagjmxxMUywftgq48HlBZELKKqFLk0oA==", + "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, - "license": "MIT" - }, - "node_modules/is-generator-function": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", - "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", "license": "MIT", "dependencies": { - "has-tostringtag": "^1.0.0" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, "engines": { - "node": ">= 0.4" + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "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==", + "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, "license": "MIT", "dependencies": { - "is-extglob": "^2.1.1" + "get-func-name": "^2.0.2" }, "engines": { - "node": ">=0.10.0" + "node": "*" } }, - "node_modules/is-nan": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/is-nan/-/is-nan-1.3.2.tgz", - "integrity": "sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==", + "node_modules/chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], "license": "MIT", "dependencies": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3" + "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": ">= 0.4" + "node": ">= 8.10.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "optionalDependencies": { + "fsevents": "~2.3.2" } }, - "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==", + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">= 6" } }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "license": "MIT", - "engines": { - "node": ">=0.12.0" + "node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" } }, - "node_modules/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==", + "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, "license": "MIT", "dependencies": { - "has-tostringtag": "^1.0.0" + "color-name": "~1.1.4" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=7.0.0" } }, - "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==", + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "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, + "license": "MIT" + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", "license": "MIT", "engines": { - "node": ">=8" + "node": ">= 0.6" } }, - "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==", + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", + "license": "MIT" + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true, + "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, "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, "engines": { - "node": ">=8" + "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==", + "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, "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" }, "engines": { "node": ">= 0.4" @@ -6790,14 +1590,16 @@ "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==", + "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, "license": "MIT", "dependencies": { - "call-bind": "^1.0.7" + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" }, "engines": { "node": ">= 0.4" @@ -6806,58 +1608,84 @@ "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==", + "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, "license": "MIT", + "dependencies": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, "engines": { - "node": ">=8" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "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, + "node_modules/debug": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", + "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", "license": "MIT", "dependencies": { - "has-tostringtag": "^1.0.0" + "ms": "2.1.2" }, "engines": { - "node": ">= 0.4" + "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, + "license": "MIT", + "engines": { + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "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==", + "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, "license": "MIT", "dependencies": { - "has-symbols": "^1.0.2" + "type-detect": "^4.0.0" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=6" } }, - "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==", + "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, + "license": "MIT" + }, + "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==", + "dev": true, "license": "MIT", "dependencies": { - "which-typed-array": "^1.1.14" + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" }, "engines": { "node": ">= 0.4" @@ -6866,352 +1694,233 @@ "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, - "license": "MIT", - "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==", + "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, "license": "MIT", "dependencies": { - "call-bind": "^1.0.2" + "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/isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true, - "license": "MIT" - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, - "license": "ISC" - }, - "node_modules/istanbul-lib-coverage": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", - "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "node_modules/diff": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", + "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", "dev": true, "license": "BSD-3-Clause", "engines": { - "node": ">=8" + "node": ">=0.3.1" } }, - "node_modules/istanbul-lib-report": { + "node_modules/dir-glob": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", - "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", "dev": true, - "license": "BSD-3-Clause", + "license": "MIT", "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^4.0.0", - "supports-color": "^7.1.0" + "path-type": "^4.0.0" }, "engines": { - "node": ">=10" + "node": ">=8" } }, - "node_modules/istanbul-reports": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", - "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", "dev": true, - "license": "BSD-3-Clause", + "license": "Apache-2.0", "dependencies": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" + "esutils": "^2.0.2" }, "engines": { - "node": ">=8" - } - }, - "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==", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/js-sdsl" + "node": ">=6.0.0" } }, - "node_modules/js-tokens": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", - "integrity": "sha512-RjTcuD4xjtthQkaWH7dFlH85L+QaVtSoOyGdZ3g6HFhS9dFNDfLyqgm2NFe2X6cQpeFmt0452FJjFG5UameExg==", + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true, "license": "MIT" }, - "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "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==", "dev": true, "license": "MIT", "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", - "license": "MIT" - }, - "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, - "license": "MIT" - }, - "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==", - "license": "MIT", - "bin": { - "jpr": "dist/index.js", - "json-placeholder-replacer": "dist/index.js" + "once": "^1.4.0" } }, - "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, - "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, - "license": "MIT" - }, - "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==", + "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, "license": "MIT", "dependencies": { - "minimist": "^1.2.0" - }, - "bin": { - "json5": "lib/cli.js" - } - }, - "node_modules/jsonparse": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", - "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==", - "dev": true, - "engines": [ - "node >= 0.2.0" - ], - "license": "MIT" - }, - "node_modules/JSONStream": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", - "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", - "dev": true, - "license": "(MIT OR Apache-2.0)", - "dependencies": { - "jsonparse": "^1.2.0", - "through": ">=2.2.7 <3" - }, - "bin": { - "JSONStream": "bin.js" + "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": "*" - } - }, - "node_modules/jsrsasign": { - "version": "10.9.0", - "resolved": "https://registry.npmjs.org/jsrsasign/-/jsrsasign-10.9.0.tgz", - "integrity": "sha512-QWLUikj1SBJGuyGK8tjKSx3K7Y69KYJnrs/pQ1KZ6wvZIkHkWjZ1PJDpuvc1/28c1uP0KW9qn1eI1LzHQqDOwQ==", - "license": "MIT", + "node": ">= 0.4" + }, "funding": { - "url": "https://github.com/kjur/jsrsasign#donations" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/just-extend": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-6.2.0.tgz", - "integrity": "sha512-cYofQu2Xpom82S6qD778jBDpwvvy39s1l/hrYij2u9AMdQcGRpaBu6kY4mVhuno5kJVi1DAz4aiphA2WI1/OAw==", - "dev": true, - "license": "MIT" - }, - "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==", + "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==", "dev": true, "license": "MIT", "dependencies": { - "json-buffer": "3.0.1" + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" } }, - "node_modules/labeled-stream-splicer": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/labeled-stream-splicer/-/labeled-stream-splicer-2.0.2.tgz", - "integrity": "sha512-Ca4LSXFFZUjPScRaqOcFxneA0VpKZr4MMYCljyQr4LIewTLb3Y0IUTIsnBBsVubIeEfxeSZpSjSsRM8APEQaAw==", + "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==", "dev": true, "license": "MIT", - "dependencies": { - "inherits": "^2.0.1", - "stream-splicer": "^2.0.0" - } - }, - "node_modules/leven": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-2.1.0.tgz", - "integrity": "sha512-nvVPLpIHUxCUoRLrFqTgSxXJ614d8AgQoWl7zPe/2VadE8+1dpU3LBhowRuBAcuwruWtOdD8oYC9jDNJjXDPyA==", - "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">= 0.4" } }, - "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==", + "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, "license": "MIT", "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" + "es-errors": "^1.3.0" }, "engines": { - "node": ">= 0.8.0" + "node": ">= 0.4" } }, - "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==", + "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, "license": "MIT", "dependencies": { - "p-locate": "^5.0.0" + "get-intrinsic": "^1.2.4", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.1" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 0.4" } }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "license": "MIT" - }, - "node_modules/lodash.get": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", - "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.memoize": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-3.0.4.tgz", - "integrity": "sha512-eDn9kqrAmVUC1wmZvlQ6Uhde44n+tXpqPrN8olQJbttgh0oKclk+SF54P47VEGE9CEiMeRwAP8BaM7UHvBkz2A==", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "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, - "license": "MIT" + "license": "MIT", + "dependencies": { + "hasown": "^2.0.0" + } }, - "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==", + "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, "license": "MIT", "dependencies": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" }, "engines": { - "node": ">=10" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/long": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", - "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==", - "license": "Apache-2.0" - }, - "node_modules/loupe": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", - "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==", + "node_modules/escalade": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", + "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", "dev": true, "license": "MIT", - "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==", - "license": "ISC" - }, - "node_modules/ltx": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ltx/-/ltx-3.0.0.tgz", - "integrity": "sha512-bu3/4/ApUmMqVNuIkHaRhqVtEi6didYcBDIF56xhPRCzVpdztCipZ62CUuaxMlMBUzaVL93+4LZRqe02fuAG6A==", - "license": "MIT", "engines": { - "node": ">= 12.4.0" + "node": ">=6" } }, - "node_modules/make-dir": { + "node_modules/escape-string-regexp": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", - "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, "license": "MIT", - "dependencies": { - "semver": "^7.5.3" - }, "engines": { "node": ">=10" }, @@ -7219,294 +1928,382 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "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==", - "license": "ISC" - }, - "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==", - "license": "Apache-2.0", - "dependencies": { - "make-error": "^1.3.5" - } - }, - "node_modules/md5.js": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", - "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "node_modules/eslint": { + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", + "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", "dev": true, "license": "MIT", "dependencies": { - "hash-base": "^3.0.0", - "inherits": "^2.0.1", - "safe-buffer": "^5.1.2" + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.0", + "@humanwhocodes/config-array": "^0.11.14", + "@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/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==", + "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, "license": "MIT", + "peer": true, + "dependencies": { + "semver": "^7.5.4" + }, "engines": { - "node": ">= 0.6" + "node": ">=12" + }, + "peerDependencies": { + "eslint": ">=6.0.0" } }, - "node_modules/merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==", - "dev": true, - "license": "MIT" - }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true, - "license": "MIT" - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "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, "license": "MIT", - "engines": { - "node": ">= 8" + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "peerDependencies": { + "eslint": ">=7.0.0" } }, - "node_modules/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "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" + } + ], "license": "MIT", "engines": { - "node": ">= 0.6" + "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/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==", + "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, - "license": "MIT" + "license": "MIT", + "dependencies": { + "debug": "^3.2.7", + "is-core-module": "^2.13.0", + "resolve": "^1.22.4" + } }, - "node_modules/micromatch": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz", - "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==", + "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, "license": "MIT", "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" + "ms": "^2.1.1" } }, - "node_modules/miller-rabin": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", - "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "node_modules/eslint-module-utils": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.1.tgz", + "integrity": "sha512-rXDXR3h7cs7dy9RNpUlQf80nX31XWJEyGq1tRMo+6GsO5VmTe4UTwtmonAD4ZkAsrfMVDA2wlGJ3790Ys+D49Q==", "dev": true, "license": "MIT", "dependencies": { - "bn.js": "^4.0.0", - "brorand": "^1.0.1" + "debug": "^3.2.7" }, - "bin": { - "miller-rabin": "bin/miller-rabin" + "engines": { + "node": ">=4" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } } }, - "node_modules/miller-rabin/node_modules/bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "node_modules/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, - "license": "MIT" + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } }, - "node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "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, "license": "MIT", - "bin": { - "mime": "cli.js" + "dependencies": { + "eslint-utils": "^2.0.0", + "regexpp": "^3.0.0" }, "engines": { - "node": ">=4" + "node": ">=8.10.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=4.19.1" } }, - "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==", + "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" + ], "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": ">= 0.6" + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": ">=8" } }, - "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==", + "node_modules/eslint-plugin-import": { + "version": "2.29.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.29.1.tgz", + "integrity": "sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==", "dev": true, "license": "MIT", "dependencies": { - "mime-db": "1.52.0" + "array-includes": "^3.1.7", + "array.prototype.findlastindex": "^1.2.3", + "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.8.0", + "hasown": "^2.0.0", + "is-core-module": "^2.13.1", + "is-glob": "^4.0.3", + "minimatch": "^3.1.2", + "object.fromentries": "^2.0.7", + "object.groupby": "^1.0.1", + "object.values": "^1.1.7", + "semver": "^6.3.1", + "tsconfig-paths": "^3.15.0" }, "engines": { - "node": ">= 0.6" + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" } }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "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, "license": "MIT", - "engines": { - "node": ">=6" + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "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==", + "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, "license": "MIT", - "optional": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "dependencies": { + "ms": "^2.1.1" } }, - "node_modules/minimalistic-assert": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", - "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", - "dev": true, - "license": "ISC" - }, - "node_modules/minimalistic-crypto-utils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", - "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==", + "node_modules/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, - "license": "MIT" + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } }, - "node_modules/minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "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, "license": "ISC", "dependencies": { - "brace-expansion": "^2.0.1" + "brace-expansion": "^1.1.7" }, "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": "*" } }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" + "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, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" } }, - "node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "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, "license": "MIT", - "bin": { - "mkdirp": "bin/cmd.js" + "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": ">=10" + "node": ">=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=7.0.0" } }, - "node_modules/mkdirp-classic": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", - "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", - "devOptional": true, - "license": "MIT" - }, - "node_modules/mlly": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.7.1.tgz", - "integrity": "sha512-rrVRZRELyQzrIUAVMHxP97kv+G786pHmOKzuFII8zDYahFBS7qnHh2AlYSl1GAHhaMPCz6/oHjVMcfFYgFYHgA==", + "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, "license": "MIT", + "peer": true, "dependencies": { - "acorn": "^8.11.3", - "pathe": "^1.1.2", - "pkg-types": "^1.1.1", - "ufo": "^1.5.3" + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "node_modules/mocha": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.2.2.tgz", - "integrity": "sha512-L6XC3EdwT6YrIk0yXpavvLkn8h+EU+Y5UcCHKECyMbdUIxyMuZj4bX4U9e1nvnvUUvQVsV2VHQr5zLdcUkhW/g==", + "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, - "license": "MIT", + "license": "ISC", + "peer": true, "dependencies": { - "@ungap/promise-all-settled": "1.1.2", - "ansi-colors": "4.1.1", - "browser-stdout": "1.3.1", - "chokidar": "3.5.3", - "debug": "4.3.3", - "diff": "5.0.0", - "escape-string-regexp": "4.0.0", - "find-up": "5.0.0", - "glob": "7.2.0", - "growl": "1.10.5", - "he": "1.2.0", - "js-yaml": "4.1.0", - "log-symbols": "4.1.0", - "minimatch": "4.2.1", - "ms": "2.1.3", - "nanoid": "3.3.1", - "serialize-javascript": "6.0.0", - "strip-json-comments": "3.1.1", - "supports-color": "8.1.1", - "which": "2.0.2", - "workerpool": "6.2.0", - "yargs": "16.2.0", - "yargs-parser": "20.2.4", - "yargs-unparser": "2.0.0" + "brace-expansion": "^1.1.7" }, - "bin": { - "_mocha": "bin/_mocha", - "mocha": "bin/mocha" + "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, + "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": ">= 12.0.0" + "node": ">=8.10.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mochajs" + "peerDependencies": { + "eslint": ">=5.16.0" } }, - "node_modules/mocha/node_modules/brace-expansion": { + "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==", @@ -7517,1877 +2314,1288 @@ "concat-map": "0.0.1" } }, - "node_modules/mocha/node_modules/debug": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", - "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/mocha/node_modules/debug/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==", - "dev": true, - "license": "MIT" - }, - "node_modules/mocha/node_modules/minimatch": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-4.2.1.tgz", - "integrity": "sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g==", + "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, "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, "engines": { - "node": ">=10" + "node": "*" } }, - "node_modules/mocha/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/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, - "license": "MIT" + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } }, - "node_modules/mocha/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==", + "node_modules/eslint-plugin-notice": { + "version": "0.9.10", + "resolved": "https://registry.npmjs.org/eslint-plugin-notice/-/eslint-plugin-notice-0.9.10.tgz", + "integrity": "sha512-rF79EuqdJKu9hhTmwUkNeSvLmmq03m/NXq/NHwUENHbdJ0wtoyOjxZBhW4QCug8v5xYE6cGe3AWkGqSIe9KUbQ==", "dev": true, "license": "MIT", "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" + "find-root": "^1.1.0", + "lodash": "^4.17.15", + "metric-lcs": "^0.1.2" }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" + "peerDependencies": { + "eslint": ">=3.0.0" } }, - "node_modules/mocha/node_modules/yargs-parser": { - "version": "20.2.4", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", - "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "node_modules/eslint-plugin-promise": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-6.4.0.tgz", + "integrity": "sha512-/KWWRaD3fGkVCZsdR0RU53PSthFmoHVhZl+y9+6DqeDLSikLdlUVpVEAmI6iCRR5QyOjBYBqHZV/bdv4DJ4Gtw==", "dev": true, "license": "ISC", "engines": { - "node": ">=10" - } - }, - "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==", - "license": "ISC", - "dependencies": { - "debug": "^4.3.1", - "serialport": "^12.0.0" + "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/module-deps": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/module-deps/-/module-deps-6.2.3.tgz", - "integrity": "sha512-fg7OZaQBcL4/L+AK5f4iVqf9OMbCclXfy/znXRxTVhJSeW5AIlS9AwheYwDaXM3lVW7OBeaeUEY3gbaC6cLlSA==", + "node_modules/eslint-plugin-unused-imports": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-unused-imports/-/eslint-plugin-unused-imports-3.2.0.tgz", + "integrity": "sha512-6uXyn6xdINEpxE1MtDjxQsyXB37lfyO2yKGVVgtD7WEWQGORSOZjgrD6hBhvGv4/SO+TOlS+UnC6JppRqbuwGQ==", "dev": true, "license": "MIT", "dependencies": { - "browser-resolve": "^2.0.0", - "cached-path-relative": "^1.0.2", - "concat-stream": "~1.6.0", - "defined": "^1.0.0", - "detective": "^5.2.0", - "duplexer2": "^0.1.2", - "inherits": "^2.0.1", - "JSONStream": "^1.0.3", - "parents": "^1.0.0", - "readable-stream": "^2.0.2", - "resolve": "^1.4.0", - "stream-combiner2": "^1.1.1", - "subarg": "^1.0.0", - "through2": "^2.0.0", - "xtend": "^4.0.0" - }, - "bin": { - "module-deps": "bin/cmd.js" + "eslint-rule-composer": "^0.3.0" }, "engines": { - "node": ">= 0.8.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "@typescript-eslint/eslint-plugin": "6 - 7", + "eslint": "8" + }, + "peerDependenciesMeta": { + "@typescript-eslint/eslint-plugin": { + "optional": true + } } }, - "node_modules/module-deps/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/module-deps/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==", + "node_modules/eslint-plugin-workspaces": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-workspaces/-/eslint-plugin-workspaces-0.9.0.tgz", + "integrity": "sha512-krMuZ+yZgzwv1oTBfz50oamNVPDIm7CDyot3i1GRKBqMD2oXAwnXHLQWH7ctpV8k6YVrkhcaZhuV9IJxD8OPAQ==", "dev": true, "license": "MIT", "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "find-workspaces": "^0.2.0" } }, - "node_modules/module-deps/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==", + "node_modules/eslint-rule-composer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/eslint-rule-composer/-/eslint-rule-composer-0.3.0.tgz", + "integrity": "sha512-bt+Sh8CtDmn2OajxvNO+BX7Wn4CIWMpTRm3MaiKPCQcnnlm0CS2mhui6QaoeQugs+3Kj2ESKEEGJUdVafwhiCg==", "dev": true, "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.0" + "engines": { + "node": ">=4.0.0" } }, - "node_modules/mqemitter": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/mqemitter/-/mqemitter-4.5.0.tgz", - "integrity": "sha512-Mp/zytFeIv6piJQkEKnncHcP4R/ErJc5C7dfonkhkNUT2LA/nTayrfNxbipp3M5iCJUTQSUtzfQAQA3XVcKz6w==", - "license": "ISC", + "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, + "license": "BSD-2-Clause", "dependencies": { - "fastparallel": "^2.3.0", - "qlobber": "^5.0.0" + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" }, "engines": { - "node": ">=10" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/mqtt": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/mqtt/-/mqtt-5.8.0.tgz", - "integrity": "sha512-/+H04mv6goy6K5gHMNH3uS0icBzXapS+4uUf4yZyQWXi72APPZNb81bQhvkm99poEQettXVT8XETB0mPxl5Wjg==", + "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, "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": "^5.2.0", - "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" + "eslint-visitor-keys": "^1.1.0" }, - "bin": { - "mqtt": "build/bin/mqtt.js", - "mqtt_pub": "build/bin/pub.js", - "mqtt_sub": "build/bin/sub.js" + "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, + "license": "Apache-2.0", "engines": { - "node": ">=16.0.0" + "node": ">=4" } }, - "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==", - "license": "MIT", - "dependencies": { - "bl": "^4.0.2", - "debug": "^4.1.1", - "process-nextick-args": "^2.0.1" + "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, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "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==", + "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, "license": "MIT", "dependencies": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "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" - } - ], - "license": "MIT", + "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, + "license": "ISC", "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" } }, - "node_modules/mqtt/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" - } - ], - "license": "MIT", + "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, + "license": "BSD-2-Clause", "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" + "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/mqtt/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" - ], - "license": "MIT", + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.0.2", - "typedarray": "^0.0.6" + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" } }, - "node_modules/mqtt/node_modules/concat-stream/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "license": "MIT", + "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, + "license": "BSD-2-Clause", "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" + "estraverse": "^5.2.0" }, "engines": { - "node": ">= 6" + "node": ">=4.0" } }, - "node_modules/mqtt/node_modules/events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "license": "MIT", + "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, + "license": "BSD-2-Clause", "engines": { - "node": ">=0.8.x" + "node": ">=4.0" } }, - "node_modules/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==", - "license": "MIT", - "dependencies": { - "bl": "^6.0.8", - "debug": "^4.3.4", - "process-nextick-args": "^2.0.1" + "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, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" } }, - "node_modules/mqtt/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==", + "node_modules/execa": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", + "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", + "dev": true, "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" + "cross-spawn": "^7.0.0", + "get-stream": "^5.0.0", + "human-signals": "^1.1.1", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.0", + "onetime": "^5.1.0", + "signal-exit": "^3.0.2", + "strip-final-newline": "^2.0.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, - "node_modules/mqtt/node_modules/ws": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", - "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", + "node_modules/extsprintf": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.4.1.tgz", + "integrity": "sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA==", + "engines": [ + "node >=0.6.0" + ], + "license": "MIT" + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "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, "license": "MIT", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" + "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" }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } + "engines": { + "node": ">=8.6.0" } }, - "node_modules/mri": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", - "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, - "license": "MIT", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, "engines": { - "node": ">=4" + "node": ">= 6" } }, - "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/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, + "license": "MIT" + }, + "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, "license": "MIT" }, - "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==", + "node_modules/fastq": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "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, "license": "MIT", "dependencies": { - "dns-packet": "^5.2.2", - "thunky": "^1.0.2" + "flat-cache": "^3.0.4" }, - "bin": { - "multicast-dns": "cli.js" + "engines": { + "node": "^10.12.0 || >=12.0.0" } }, - "node_modules/nan": { - "version": "2.14.2", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz", - "integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==", - "license": "MIT", - "optional": true + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "license": "MIT" }, - "node_modules/nanoid": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz", - "integrity": "sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==", + "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, "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" + "dependencies": { + "to-regex-range": "^5.0.1" }, "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + "node": ">=8" } }, - "node_modules/napi-build-utils": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", - "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==", - "license": "MIT", - "optional": true - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "node_modules/find-root": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", + "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", "dev": true, "license": "MIT" }, - "node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "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, "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, "engines": { - "node": ">= 0.6" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/nise": { - "version": "5.1.9", - "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.9.tgz", - "integrity": "sha512-qOnoujW4SV6e40dYxJOb3uvuoPHtmLzIk4TFo+j0jPJoC+5Z9xja5qH5JZobEPsa8+YYphMrOSwnrshEhG2qww==", + "node_modules/find-workspaces": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/find-workspaces/-/find-workspaces-0.2.0.tgz", + "integrity": "sha512-OTHryv88yjzwvbXHGi0+XRFu7Jqe5pFuIR2mhqdatDJQOBJd7MFJOPFJv4EbNo8n1BNM/13Y2KcyDpFQYf0ETw==", "dev": true, - "license": "BSD-3-Clause", + "license": "MIT", "dependencies": { - "@sinonjs/commons": "^3.0.0", - "@sinonjs/fake-timers": "^11.2.2", - "@sinonjs/text-encoding": "^0.7.2", - "just-extend": "^6.2.0", - "path-to-regexp": "^6.2.1" + "fast-glob": "^3.2.12", + "pkg-types": "^1.0.3", + "yaml": "^2.3.1" } }, - "node_modules/nise/node_modules/@sinonjs/commons": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", - "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "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, "license": "BSD-3-Clause", - "dependencies": { - "type-detect": "4.0.8" + "bin": { + "flat": "cli.js" } }, - "node_modules/nise/node_modules/@sinonjs/fake-timers": { - "version": "11.2.2", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-11.2.2.tgz", - "integrity": "sha512-G2piCSxQ7oWOxwGSAyFHfPIsyeJGXYtc6mFbnFA+kRXkiEnTl8c/8jul2S329iFBnDI9HGoeWWAZvuvOkZccgw==", + "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, - "license": "BSD-3-Clause", + "license": "MIT", "dependencies": { - "@sinonjs/commons": "^3.0.0" + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" } }, - "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==", + "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, + "license": "ISC" + }, + "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, "license": "MIT", - "optional": true, "dependencies": { - "semver": "^5.4.1" + "is-callable": "^1.1.3" } }, - "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==", + "node_modules/foreground-child": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", + "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==", + "dev": true, "license": "ISC", - "optional": true, - "bin": { - "semver": "bin/semver" + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8.0.0" } }, - "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==", - "license": "MIT" + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" }, - "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==", + "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, "license": "MIT", "optional": true, - "dependencies": { - "bindings": "^1.5.0", - "nan": "2.14.x", - "prebuild-install": "^6.1.3" - }, + "os": [ + "darwin" + ], "engines": { - "node": ">4" + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, - "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==", + "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==", + "dev": true, "license": "MIT", - "dependencies": { - "debug": "^4.1.1", - "node-dtls-client": "^0.6.0" - }, - "engines": { - "node": ">= 6.13.0" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "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==", + "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, "license": "MIT", "dependencies": { - "debug": "^4.1.1", - "semver": "^7.0.0" + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "functions-have-names": "^1.2.3" }, "engines": { - "node": ">=6" + "node": ">= 0.4" }, - "optionalDependencies": { - "node-aead-crypto": "^2.0.0" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "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==", + "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, "license": "MIT", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "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==", - "license": "MIT", - "bin": { - "node-gyp-build": "bin.js", - "node-gyp-build-optional": "optional.js", - "node-gyp-build-test": "build-test.js" + "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, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" } }, - "node_modules/node-mbus": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/node-mbus/-/node-mbus-2.2.4.tgz", - "integrity": "sha512-xFNx+tgLb7mxL3K1CX+LufoBYUq4Zmu/3NyTBV75tQXkk706+dgBloopm7Pnd0x4Cut/6e6lD4HVw/01qgpYog==", - "hasInstallScript": true, + "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, "license": "MIT", - "dependencies": { - "bindings": "^1.5.0", - "nan": "^2.19.0", - "xml2js": "^0.6.2" - }, "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/node-mbus/node_modules/nan": { - "version": "2.20.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.20.0.tgz", - "integrity": "sha512-bk3gXBZDGILuuo/6sKtr0DQmSThYHLtNCdSdXk9YkxD/jK6X2vmCyyXBBxyqZ4XcnzTyYEAThfX3DCEnLf6igw==", - "license": "MIT" - }, - "node_modules/node-netconf": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/node-netconf/-/node-netconf-1.1.2.tgz", - "integrity": "sha512-4v8zyFk1/Bxtt8pIN5wl0tYvpagdrRBphaDjLPNku4Mcql3GGGzeM5KmChQXrS5/LCOZl1vUK3D4GT+1PqxWhQ==", - "license": "ISC", - "dependencies": { - "ssh2": "^0.8.9", - "vasync": "^2.2.0", - "xml2js": "^0.4.23" + "node": "*" } }, - "node_modules/node-netconf/node_modules/xml2js": { - "version": "0.4.23", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz", - "integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==", + "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==", + "dev": true, "license": "MIT", "dependencies": { - "sax": ">=0.6.0", - "xmlbuilder": "~11.0.0" + "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": ">=4.0.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/node-opcua": { - "version": "2.113.0", - "resolved": "https://registry.npmjs.org/node-opcua/-/node-opcua-2.113.0.tgz", - "integrity": "sha512-05XJie63Qr0klN+NmQAGSC/ZIVZQQKbY7ZhkeypDbT96NfZfPglhsPW7yng5gTe0U5WHOrtRogvZl1+rjfhdXQ==", - "license": "MIT", - "dependencies": { - "@types/semver": "^7.5.1", - "chalk": "4.1.2", - "node-opcua-address-space": "2.113.0", - "node-opcua-address-space-for-conformance-testing": "2.113.0", - "node-opcua-aggregates": "2.113.0", - "node-opcua-assert": "2.105.0", - "node-opcua-basic-types": "2.113.0", - "node-opcua-binary-stream": "2.110.0", - "node-opcua-certificate-manager": "2.113.0", - "node-opcua-client": "2.113.0", - "node-opcua-client-crawler": "2.113.0", - "node-opcua-client-proxy": "2.113.0", - "node-opcua-common": "2.113.0", - "node-opcua-constants": "2.98.1", - "node-opcua-crypto": "4.5.0", - "node-opcua-data-access": "2.113.0", - "node-opcua-data-model": "2.113.0", - "node-opcua-data-value": "2.113.0", - "node-opcua-debug": "2.113.0", - "node-opcua-enum": "2.110.0", - "node-opcua-factory": "2.113.0", - "node-opcua-hostname": "2.105.0", - "node-opcua-nodeid": "2.113.0", - "node-opcua-nodesets": "2.110.0", - "node-opcua-numeric-range": "2.113.0", - "node-opcua-packet-analyzer": "2.113.0", - "node-opcua-secure-channel": "2.113.0", - "node-opcua-server": "2.113.0", - "node-opcua-server-discovery": "2.113.0", - "node-opcua-service-browse": "2.113.0", - "node-opcua-service-call": "2.113.0", - "node-opcua-service-discovery": "2.113.0", - "node-opcua-service-endpoints": "2.113.0", - "node-opcua-service-filter": "2.113.0", - "node-opcua-service-history": "2.113.0", - "node-opcua-service-node-management": "2.113.0", - "node-opcua-service-query": "2.113.0", - "node-opcua-service-read": "2.113.0", - "node-opcua-service-register-node": "2.113.0", - "node-opcua-service-secure-channel": "2.113.0", - "node-opcua-service-session": "2.113.0", - "node-opcua-service-subscription": "2.113.0", - "node-opcua-service-translate-browse-path": "2.113.0", - "node-opcua-service-write": "2.113.0", - "node-opcua-status-code": "2.110.0", - "node-opcua-transport": "2.113.0", - "node-opcua-types": "2.113.0", - "node-opcua-utils": "2.110.0", - "node-opcua-variant": "2.113.0", - "node-opcua-vendor-diagnostic": "2.113.0", - "semver": "^7.5.4" + "node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pump": "^3.0.0" }, "engines": { - "node": ">=8.10" + "node": ">=8" }, "funding": { - "url": "https://github.com/sponsors/erossignon" - } - }, - "node_modules/node-opcua-address-space": { - "version": "2.113.0", - "resolved": "https://registry.npmjs.org/node-opcua-address-space/-/node-opcua-address-space-2.113.0.tgz", - "integrity": "sha512-rUrWb60z19LcXVnwl3n0ro2nGdP4V5Wxql8VJFSiNx94OqLaJt5hyhXjOKtk5jPLKUxWRf+N9R1POrWe7fOsfA==", - "license": "MIT", - "dependencies": { - "@types/lodash": "4.14.198", - "@types/semver": "^7.5.1", - "async": "^3.2.4", - "chalk": "4.1.2", - "dequeue": "^1.0.5", - "lodash": "4.17.21", - "node-opcua-address-space-base": "2.113.0", - "node-opcua-assert": "2.105.0", - "node-opcua-basic-types": "2.113.0", - "node-opcua-binary-stream": "2.110.0", - "node-opcua-client-dynamic-extension-object": "2.113.0", - "node-opcua-constants": "2.98.1", - "node-opcua-crypto": "4.5.0", - "node-opcua-data-access": "2.113.0", - "node-opcua-data-model": "2.113.0", - "node-opcua-data-value": "2.113.0", - "node-opcua-date-time": "2.113.0", - "node-opcua-debug": "2.113.0", - "node-opcua-enum": "2.110.0", - "node-opcua-extension-object": "2.113.0", - "node-opcua-factory": "2.113.0", - "node-opcua-nodeid": "2.113.0", - "node-opcua-nodeset-ua": "2.113.0", - "node-opcua-numeric-range": "2.113.0", - "node-opcua-object-registry": "2.113.0", - "node-opcua-pseudo-session": "2.113.0", - "node-opcua-service-browse": "2.113.0", - "node-opcua-service-call": "2.113.0", - "node-opcua-service-history": "2.113.0", - "node-opcua-service-translate-browse-path": "2.113.0", - "node-opcua-service-write": "2.113.0", - "node-opcua-status-code": "2.110.0", - "node-opcua-types": "2.113.0", - "node-opcua-utils": "2.110.0", - "node-opcua-variant": "2.113.0", - "node-opcua-xml2json": "2.113.0", - "semver": "^7.5.4", - "set-prototype-of": "^1.0.0", - "thenify": "^3.3.1", - "xml-writer": "^1.7.0" - }, - "engines": { - "node": ">=6.10" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/node-opcua-address-space-base": { - "version": "2.113.0", - "resolved": "https://registry.npmjs.org/node-opcua-address-space-base/-/node-opcua-address-space-base-2.113.0.tgz", - "integrity": "sha512-0R7IukPQiWY2y3vzJe6AVxDsFE7R0lvUJhDpEfmMYcvmRyPOp3+m5ib2jWn/ubiiovzmOulL81SXkZzwtWbBeQ==", + "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, "license": "MIT", "dependencies": { - "node-opcua-assert": "2.105.0", - "node-opcua-basic-types": "2.113.0", - "node-opcua-constants": "2.98.1", - "node-opcua-crypto": "4.5.0", - "node-opcua-data-model": "2.113.0", - "node-opcua-data-value": "2.113.0", - "node-opcua-date-time": "2.113.0", - "node-opcua-debug": "2.113.0", - "node-opcua-extension-object": "2.113.0", - "node-opcua-nodeid": "2.113.0", - "node-opcua-numeric-range": "2.113.0", - "node-opcua-schemas": "2.113.0", - "node-opcua-status-code": "2.110.0", - "node-opcua-types": "2.113.0", - "node-opcua-variant": "2.113.0" + "call-bind": "^1.0.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4" }, "engines": { - "node": ">=6.10" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/node-opcua-address-space-for-conformance-testing": { - "version": "2.113.0", - "resolved": "https://registry.npmjs.org/node-opcua-address-space-for-conformance-testing/-/node-opcua-address-space-for-conformance-testing-2.113.0.tgz", - "integrity": "sha512-ft1oy39/+KLUWQEhUifSHd064Me9N5xdPwnxS80xQvQohCCfzMK0HYKMFDwtmsJxiQFLRLyWiLL6Vt9uNXtUUQ==", + "node_modules/get-tsconfig": { + "version": "4.7.5", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.7.5.tgz", + "integrity": "sha512-ZCuZCnlqNzjb4QprAzXKdpp/gh6KTxSJuw3IBsPnV/7fV4NxC9ckB+vPTt8w7fJA0TaSD7c55BR47JD6MEDyDw==", + "dev": true, "license": "MIT", + "peer": true, "dependencies": { - "node-opcua-address-space": "2.113.0", - "node-opcua-assert": "2.105.0", - "node-opcua-basic-types": "2.113.0", - "node-opcua-data-access": "2.113.0", - "node-opcua-data-model": "2.113.0", - "node-opcua-data-value": "2.113.0", - "node-opcua-debug": "2.113.0", - "node-opcua-factory": "2.113.0", - "node-opcua-nodeid": "2.113.0", - "node-opcua-status-code": "2.110.0", - "node-opcua-variant": "2.113.0" + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" } }, - "node_modules/node-opcua-address-space/node_modules/@types/lodash": { - "version": "4.14.198", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.198.tgz", - "integrity": "sha512-trNJ/vtMZYMLhfN45uLq4ShQSw0/S7xCTLLVM+WM1rmFpba/VS42jVUgaO3w/NOLiWR/09lnYk0yMaA/atdIsg==", - "license": "MIT" - }, - "node_modules/node-opcua-aggregates": { - "version": "2.113.0", - "resolved": "https://registry.npmjs.org/node-opcua-aggregates/-/node-opcua-aggregates-2.113.0.tgz", - "integrity": "sha512-3CC9uBzyOrnoLPo/RI3olj1XUS81q29c1u1ReY2Jl3yTG2A28T5Xfq4/730W0CfZYIv1hO0KtxHxmJgp6fFO1w==", - "license": "MIT", + "node_modules/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", "dependencies": { - "node-opcua-address-space": "2.113.0", - "node-opcua-assert": "2.105.0", - "node-opcua-constants": "2.98.1", - "node-opcua-data-model": "2.113.0", - "node-opcua-data-value": "2.113.0", - "node-opcua-nodeid": "2.113.0", - "node-opcua-numeric-range": "2.113.0", - "node-opcua-server": "2.113.0", - "node-opcua-service-history": "2.113.0", - "node-opcua-status-code": "2.110.0", - "node-opcua-types": "2.113.0", - "node-opcua-utils": "2.110.0", - "node-opcua-variant": "2.113.0" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/node-opcua-assert": { - "version": "2.105.0", - "resolved": "https://registry.npmjs.org/node-opcua-assert/-/node-opcua-assert-2.105.0.tgz", - "integrity": "sha512-q4VVsbfeXdXarTRga8d100NxkALvhEeAeN/YMBUsOkDIHh/VjrozknSSUT1c0h406QRZdmcoz7MnHCLG0+Rwxw==", - "license": "MIT", + "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, + "license": "ISC", "dependencies": { - "chalk": "4.1.2" + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" } }, - "node_modules/node-opcua-basic-types": { - "version": "2.113.0", - "resolved": "https://registry.npmjs.org/node-opcua-basic-types/-/node-opcua-basic-types-2.113.0.tgz", - "integrity": "sha512-F+7vSGXddiCq+ZsL6bEmZaeHpeUCKknqbmp1vWHC+tdtHjhliTrVfIc8FkdjJepmEKdObzbQLPJnw3q0Ow1fOg==", + "node_modules/glob/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, "license": "MIT", "dependencies": { - "node-opcua-assert": "2.105.0", - "node-opcua-binary-stream": "2.110.0", - "node-opcua-buffer-utils": "2.110.0", - "node-opcua-date-time": "2.113.0", - "node-opcua-guid": "2.98.1", - "node-opcua-nodeid": "2.113.0", - "node-opcua-status-code": "2.110.0" + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "node_modules/node-opcua-binary-stream": { - "version": "2.110.0", - "resolved": "https://registry.npmjs.org/node-opcua-binary-stream/-/node-opcua-binary-stream-2.110.0.tgz", - "integrity": "sha512-Cj7Klnh2kBzoyAEZmS5XTMImptVqWa/6WdV+j5hXgeFplljR5vsWA3j0W328Jjal+HczrSSqGM8zNLpGkRc4og==", - "license": "MIT", + "node_modules/glob/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", "dependencies": { - "node-opcua-assert": "2.105.0", - "node-opcua-buffer-utils": "2.110.0" + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" } }, - "node_modules/node-opcua-buffer-utils": { - "version": "2.110.0", - "resolved": "https://registry.npmjs.org/node-opcua-buffer-utils/-/node-opcua-buffer-utils-2.110.0.tgz", - "integrity": "sha512-BkmPyX8G+0FVJqRIHdWtC3m4GE6wMl1mP5csSJg83iSEz1eY99cN0TbAZ0jGMM5AAlEABBnDotCKkdbyO3lJEQ==", - "license": "MIT" - }, - "node_modules/node-opcua-certificate-manager": { - "version": "2.113.0", - "resolved": "https://registry.npmjs.org/node-opcua-certificate-manager/-/node-opcua-certificate-manager-2.113.0.tgz", - "integrity": "sha512-s17D+rsnvaA1GxDVf64UdW036hs58YOzOaSBJ6lwb4lrL/BTTVXLD/5zoV0WkagB25ddPyrMsAm/kGzDJPd5mA==", - "license": "MIT", - "dependencies": { - "@types/mkdirp": "1.0.2", - "env-paths": "2.2.1", - "mkdirp": "1.0.4", - "node-opcua-assert": "2.105.0", - "node-opcua-crypto": "4.5.0", - "node-opcua-debug": "2.113.0", - "node-opcua-object-registry": "2.113.0", - "node-opcua-pki": "4.7.0", - "node-opcua-status-code": "2.110.0", - "thenify": "^3.3.1" - } - }, - "node_modules/node-opcua-chunkmanager": { - "version": "2.113.0", - "resolved": "https://registry.npmjs.org/node-opcua-chunkmanager/-/node-opcua-chunkmanager-2.113.0.tgz", - "integrity": "sha512-R21RiTAPCrmHC1sVTBLDmGq4dh8r1546tGF907TNUCSMNglb7xm0TGvasGjJjv6bBPqQczVjuygzaRsXBPWHVQ==", - "license": "MIT", - "dependencies": { - "node-opcua-assert": "2.105.0", - "node-opcua-basic-types": "2.113.0", - "node-opcua-binary-stream": "2.110.0", - "node-opcua-buffer-utils": "2.110.0", - "node-opcua-factory": "2.113.0", - "node-opcua-packet-assembler": "2.113.0" - } - }, - "node_modules/node-opcua-client": { - "version": "2.113.0", - "resolved": "https://registry.npmjs.org/node-opcua-client/-/node-opcua-client-2.113.0.tgz", - "integrity": "sha512-PyopaOvo37LJg4OQHR79cRwz1fCamkv5sHQdWiBvt9epeYEn57+7GvhZWdgQLyUv6afZ/qqBfFIru1SN/qa3Ew==", - "license": "MIT", - "dependencies": { - "@ster5/global-mutex": "^2.0.0", - "@types/async": "^3.2.20", - "async": "^3.2.4", - "chalk": "4.1.2", - "node-opcua-assert": "2.105.0", - "node-opcua-basic-types": "2.113.0", - "node-opcua-buffer-utils": "2.110.0", - "node-opcua-certificate-manager": "2.113.0", - "node-opcua-client-dynamic-extension-object": "2.113.0", - "node-opcua-common": "2.113.0", - "node-opcua-constants": "2.98.1", - "node-opcua-crypto": "4.5.0", - "node-opcua-data-model": "2.113.0", - "node-opcua-data-value": "2.113.0", - "node-opcua-date-time": "2.113.0", - "node-opcua-debug": "2.113.0", - "node-opcua-extension-object": "2.113.0", - "node-opcua-hostname": "2.105.0", - "node-opcua-nodeid": "2.113.0", - "node-opcua-object-registry": "2.113.0", - "node-opcua-pki": "4.7.0", - "node-opcua-pseudo-session": "2.113.0", - "node-opcua-schemas": "2.113.0", - "node-opcua-secure-channel": "2.113.0", - "node-opcua-service-browse": "2.113.0", - "node-opcua-service-call": "2.113.0", - "node-opcua-service-discovery": "2.113.0", - "node-opcua-service-endpoints": "2.113.0", - "node-opcua-service-filter": "2.113.0", - "node-opcua-service-history": "2.113.0", - "node-opcua-service-query": "2.113.0", - "node-opcua-service-read": "2.113.0", - "node-opcua-service-register-node": "2.113.0", - "node-opcua-service-secure-channel": "2.113.0", - "node-opcua-service-session": "2.113.0", - "node-opcua-service-subscription": "2.113.0", - "node-opcua-service-translate-browse-path": "2.113.0", - "node-opcua-service-write": "2.113.0", - "node-opcua-status-code": "2.110.0", - "node-opcua-types": "2.113.0", - "node-opcua-utils": "2.110.0", - "node-opcua-variant": "2.113.0", - "thenify": "^3.3.1" - } - }, - "node_modules/node-opcua-client-crawler": { - "version": "2.113.0", - "resolved": "https://registry.npmjs.org/node-opcua-client-crawler/-/node-opcua-client-crawler-2.113.0.tgz", - "integrity": "sha512-8wFkAc9ALtnH7ugaq/1BBPIv4OURI3CO8D1JomMWdZUN7lW/3LRIHRij0ajHDzx0SUPFwWT05tC3KD4S3vY1WQ==", - "deprecated": "This package is deprecated, please contact sterfive to acquire access to @sterfive/crawler revamped and improved module", - "license": "MIT", - "dependencies": { - "async": "^3.2.4", - "chalk": "4.1.2", - "node-opcua-address-space": "2.113.0", - "node-opcua-assert": "2.105.0", - "node-opcua-client": "2.113.0", - "node-opcua-constants": "2.98.1", - "node-opcua-data-model": "2.113.0", - "node-opcua-data-value": "2.113.0", - "node-opcua-debug": "2.113.0", - "node-opcua-nodeid": "2.113.0", - "node-opcua-service-browse": "2.113.0", - "node-opcua-status-code": "2.110.0", - "node-opcua-types": "2.113.0", - "node-opcua-utils": "2.110.0", - "thenify": "^3.3.1" - } - }, - "node_modules/node-opcua-client-dynamic-extension-object": { - "version": "2.113.0", - "resolved": "https://registry.npmjs.org/node-opcua-client-dynamic-extension-object/-/node-opcua-client-dynamic-extension-object-2.113.0.tgz", - "integrity": "sha512-sNgWtyxQgmf1Ix1jQ4kp96FHxH27AU9lflvCMabB+emd9Xh7XLSJnLH11UfPuEHkXPLMNAQE718dRxTD48tuHA==", - "license": "MIT", - "dependencies": { - "chalk": "4.1.2", - "node-opcua-assert": "2.105.0", - "node-opcua-binary-stream": "2.110.0", - "node-opcua-constants": "2.98.1", - "node-opcua-data-model": "2.113.0", - "node-opcua-data-value": "2.113.0", - "node-opcua-debug": "2.113.0", - "node-opcua-extension-object": "2.113.0", - "node-opcua-factory": "2.113.0", - "node-opcua-nodeid": "2.113.0", - "node-opcua-pseudo-session": "2.113.0", - "node-opcua-schemas": "2.113.0", - "node-opcua-service-browse": "2.113.0", - "node-opcua-service-translate-browse-path": "2.113.0", - "node-opcua-status-code": "2.110.0", - "node-opcua-types": "2.113.0", - "node-opcua-variant": "2.113.0" - } - }, - "node_modules/node-opcua-client-proxy": { - "version": "2.113.0", - "resolved": "https://registry.npmjs.org/node-opcua-client-proxy/-/node-opcua-client-proxy-2.113.0.tgz", - "integrity": "sha512-/W+tfHYK1+Uxqk7OvmvL0WolaPfsEH7PtsAETSOFhhxe0gSUyTEGOx6NVo/+1YCMF3G7z8veMoXuVwTTicCSbA==", - "license": "MIT", - "dependencies": { - "async": "^3.2.4", - "node-opcua-assert": "2.105.0", - "node-opcua-constants": "2.98.1", - "node-opcua-data-model": "2.113.0", - "node-opcua-data-value": "2.113.0", - "node-opcua-debug": "2.113.0", - "node-opcua-nodeid": "2.113.0", - "node-opcua-pseudo-session": "2.113.0", - "node-opcua-service-browse": "2.113.0", - "node-opcua-service-call": "2.113.0", - "node-opcua-service-read": "2.113.0", - "node-opcua-service-subscription": "2.113.0", - "node-opcua-service-write": "2.113.0", - "node-opcua-status-code": "2.110.0", - "node-opcua-utils": "2.110.0", - "node-opcua-variant": "2.113.0", - "thenify": "^3.3.1" - } - }, - "node_modules/node-opcua-common": { - "version": "2.113.0", - "resolved": "https://registry.npmjs.org/node-opcua-common/-/node-opcua-common-2.113.0.tgz", - "integrity": "sha512-U8O3Ke9+IQstFVtv22xsZCap1hBvXBo9YLfPYt9TQFCKlDQragJd/yYWZZpcx3LfY0ppjRQsOzSgsxckTLb7Dg==", - "license": "MIT", - "dependencies": { - "node-opcua-assert": "2.105.0", - "node-opcua-crypto": "4.5.0", - "node-opcua-types": "2.113.0" - } - }, - "node_modules/node-opcua-constants": { - "version": "2.98.1", - "resolved": "https://registry.npmjs.org/node-opcua-constants/-/node-opcua-constants-2.98.1.tgz", - "integrity": "sha512-7RDmofF6vajYmmsbm/t0obqZlL0K7KKgYe4V+QT8qSGdNFrmDANHiAUhgPljur8e8taaDUXFcaOhS4fYjMN1WQ==", - "license": "MIT" - }, - "node_modules/node-opcua-crypto": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/node-opcua-crypto/-/node-opcua-crypto-4.5.0.tgz", - "integrity": "sha512-ydeXsU3B1IV2hvVb74Kuttwcy5pcxyALS/r7bgNFedb9Ca5btiUUP9VCCKq3O/sNfW9tFllfeCzN7Zbxifk4Cw==", + "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, "license": "MIT", "dependencies": { - "@peculiar/webcrypto": "^1.4.3", - "@peculiar/x509": "^1.9.5", - "@types/jsrsasign": "^10.5.8", - "@types/sshpk": "^1.17.1", - "assert": "^2.0.0", - "better-assert": "^1.0.2", - "chalk": "^4.1.2", - "hexy": "0.3.5", - "jsrsasign": "^10.8.6", - "sshpk": "^1.17.0" + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/node-opcua-crypto/node_modules/assert": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/assert/-/assert-2.1.0.tgz", - "integrity": "sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw==", + "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, "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "is-nan": "^1.3.2", - "object-is": "^1.1.5", - "object.assign": "^4.1.4", - "util": "^0.12.5" + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/node-opcua-data-access": { - "version": "2.113.0", - "resolved": "https://registry.npmjs.org/node-opcua-data-access/-/node-opcua-data-access-2.113.0.tgz", - "integrity": "sha512-bodMVwiqCOwZvwrRk1+xf1BGIjhnks0vi0itIY4tVRK2WpbVwUnZ7oO5zzjimGw/DC4Wwz/CeWj38wNsOm63Rg==", + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, "license": "MIT", "dependencies": { - "node-opcua-data-model": "2.113.0", - "node-opcua-types": "2.113.0" + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/node-opcua-data-model": { - "version": "2.113.0", - "resolved": "https://registry.npmjs.org/node-opcua-data-model/-/node-opcua-data-model-2.113.0.tgz", - "integrity": "sha512-BoHU6JiSvRQ4nXQ74PJag5nyeHQdczXZdzsDcK8NAajH1zCr5T3L0t/2QMbt0rgSL8JNL+5ZhXCOofVbsJ1XVA==", + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dev": true, "license": "MIT", "dependencies": { - "node-opcua-assert": "2.105.0", - "node-opcua-basic-types": "2.113.0", - "node-opcua-binary-stream": "2.110.0", - "node-opcua-enum": "2.110.0", - "node-opcua-factory": "2.113.0", - "node-opcua-nodeid": "2.113.0", - "node-opcua-status-code": "2.110.0" + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/node-opcua-data-value": { - "version": "2.113.0", - "resolved": "https://registry.npmjs.org/node-opcua-data-value/-/node-opcua-data-value-2.113.0.tgz", - "integrity": "sha512-nOTejK412JvTerhDLSaYgAmsbvBt4heH+mavqg4PO5hRY8fVXIWro1jOP2WYNplCUR4G9a8K9sbYHpkpnmQ3wg==", + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true, + "license": "MIT" + }, + "node_modules/growl": { + "version": "1.10.5", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", + "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", + "dev": true, "license": "MIT", - "dependencies": { - "node-opcua-assert": "2.105.0", - "node-opcua-basic-types": "2.113.0", - "node-opcua-binary-stream": "2.110.0", - "node-opcua-data-model": "2.113.0", - "node-opcua-date-time": "2.113.0", - "node-opcua-enum": "2.110.0", - "node-opcua-factory": "2.113.0", - "node-opcua-status-code": "2.110.0", - "node-opcua-variant": "2.113.0" + "engines": { + "node": ">=4.x" } }, - "node_modules/node-opcua-date-time": { - "version": "2.113.0", - "resolved": "https://registry.npmjs.org/node-opcua-date-time/-/node-opcua-date-time-2.113.0.tgz", - "integrity": "sha512-Gk/8t18Tjf+g+RIGFpOd6lCG5eScxE8PJWvOxQ7NjZRU0uGB/+Xxmy/2iRx5I9VKmyWxAj/ehzeBfvAec7PrVA==", + "node_modules/has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==", + "dev": true, "license": "MIT", "dependencies": { - "long": "^4.0.0", - "node-opcua-assert": "2.105.0", - "node-opcua-binary-stream": "2.110.0", - "node-opcua-utils": "2.110.0" + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "node_modules/node-opcua-debug": { - "version": "2.113.0", - "resolved": "https://registry.npmjs.org/node-opcua-debug/-/node-opcua-debug-2.113.0.tgz", - "integrity": "sha512-+0NDhyIvupWwCSZgPfvMLHxIPlWFmUKUMkOeRurIEezKfH2m+ZFeIVyCsl8sBiaNozhjFpFv0iRtNe9LoZ78CA==", + "node_modules/has-ansi/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "dev": true, "license": "MIT", - "dependencies": { - "chalk": "4.1.2", - "hexy": "0.3.5", - "node-opcua-assert": "2.105.0", - "node-opcua-buffer-utils": "2.110.0" + "engines": { + "node": ">=0.10.0" } }, - "node_modules/node-opcua-enum": { - "version": "2.110.0", - "resolved": "https://registry.npmjs.org/node-opcua-enum/-/node-opcua-enum-2.110.0.tgz", - "integrity": "sha512-SX3oS7Q8cGaSHCX0Y9bluD2s0NzQ+aLEbj4i7i8rgE9Tl1Msj1dIIvRoUmn5MIdHlx58UyGAYfik/4mSiLKi1w==", - "license": "MIT" - }, - "node_modules/node-opcua-extension-object": { - "version": "2.113.0", - "resolved": "https://registry.npmjs.org/node-opcua-extension-object/-/node-opcua-extension-object-2.113.0.tgz", - "integrity": "sha512-iWhoJEmXnyiR/pltDVnKmQ9S/X1o6h/BiEGhJVPADjmjQrk+lV49L5IcpkG+RhMU8kUfPZWr4NzBHmnJU0jGmw==", + "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, "license": "MIT", - "dependencies": { - "chalk": "4.1.2", - "node-opcua-basic-types": "2.113.0", - "node-opcua-binary-stream": "2.110.0", - "node-opcua-debug": "2.113.0", - "node-opcua-factory": "2.113.0", - "node-opcua-nodeid": "2.113.0" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/node-opcua-factory": { - "version": "2.113.0", - "resolved": "https://registry.npmjs.org/node-opcua-factory/-/node-opcua-factory-2.113.0.tgz", - "integrity": "sha512-IgkhQ9pxsBUbrFpWsl0gpDt57xlU/fXK9VzSORMlIOdvnT3H6h+IN/uOazw16KXZ0smsTFDz2VebxpJOOSrXEQ==", + "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, "license": "MIT", - "dependencies": { - "chalk": "4.1.2", - "node-opcua-assert": "2.105.0", - "node-opcua-basic-types": "2.113.0", - "node-opcua-binary-stream": "2.110.0", - "node-opcua-constants": "2.98.1", - "node-opcua-debug": "2.113.0", - "node-opcua-enum": "2.110.0", - "node-opcua-guid": "2.98.1", - "node-opcua-nodeid": "2.113.0", - "node-opcua-status-code": "2.110.0", - "node-opcua-utils": "2.110.0" + "engines": { + "node": ">=8" } }, - "node_modules/node-opcua-generator": { - "version": "2.113.0", - "resolved": "https://registry.npmjs.org/node-opcua-generator/-/node-opcua-generator-2.113.0.tgz", - "integrity": "sha512-/2ElUHZH07mk+rBZo24qEjrFplMadVnRLpWHajvNLTCEthDbR4gRK+55RMVtppG/L/xn1uDtXUXyISqxExg5LA==", + "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==", + "dev": true, "license": "MIT", "dependencies": { - "chalk": "4.1.2", - "node-opcua-assert": "2.105.0", - "node-opcua-constants": "2.98.1", - "node-opcua-debug": "2.113.0", - "node-opcua-factory": "2.113.0", - "node-opcua-nodeid": "2.113.0", - "node-opcua-schemas": "2.113.0", - "node-opcua-utils": "2.110.0" + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/node-opcua-guid": { - "version": "2.98.1", - "resolved": "https://registry.npmjs.org/node-opcua-guid/-/node-opcua-guid-2.98.1.tgz", - "integrity": "sha512-09hWgnEUhq6t0QPrCIklAa4/x2aNhp1te0l2IkFQdNkJ8iYEBEKk0lJG7+nA+fNyys0ccUohvHdvuFae2fSGTw==", - "license": "MIT" - }, - "node_modules/node-opcua-hostname": { - "version": "2.105.0", - "resolved": "https://registry.npmjs.org/node-opcua-hostname/-/node-opcua-hostname-2.105.0.tgz", - "integrity": "sha512-nb55yjaaRaxxyypcy3QQ1brml1eK1lBTECy6+36v9v/gs0Kuv9rtdQbu4sZ089qOeuvsWNCFHPDULlLyfDMgeQ==", - "license": "MIT" - }, - "node_modules/node-opcua-json": { - "version": "0.50.0", - "resolved": "https://registry.npmjs.org/node-opcua-json/-/node-opcua-json-0.50.0.tgz", - "integrity": "sha512-h0bhbxHjzCcyDg1CrpLKIdRPh94QjupiytVk7WdZCnTZ4uuxvJJnnW+sfZ1id9pnAFchFEpI4sFgn7TCljegpg==", - "license": "MIT - Copyright (c) 2021-2023 - sterfive.com", - "peerDependencies": { - "node-opcua-address-space": ">=2.110.0", - "node-opcua-basic-types": ">=2.110.0", - "node-opcua-binary-stream": ">=2.110.0", - "node-opcua-buffer-utils": ">=2.110.0", - "node-opcua-data-model": ">=2.110.0", - "node-opcua-data-value": ">=2.110.0", - "node-opcua-date-time": ">=2.110.0", - "node-opcua-debug": ">=2.110.0", - "node-opcua-extension-object": ">=2.110.0", - "node-opcua-factory": ">=2.110.0", - "node-opcua-nodeid": ">=2.110.0", - "node-opcua-numeric-range": ">=2.110.0", - "node-opcua-status-code": ">=2.110.0", - "node-opcua-types": ">=2.110.0", - "node-opcua-variant": ">=2.110.0" - } - }, - "node_modules/node-opcua-nodeid": { - "version": "2.113.0", - "resolved": "https://registry.npmjs.org/node-opcua-nodeid/-/node-opcua-nodeid-2.113.0.tgz", - "integrity": "sha512-XkgKMcQoQ6omm8sw3156kUj9nFK/BbdyaGHEk/Hu10AvP2OE9B5OcG0YpYLXFqhLfr2R3BaifWDqf4RU3R7c/A==", - "license": "MIT", - "dependencies": { - "node-opcua-assert": "2.105.0", - "node-opcua-constants": "2.98.1", - "node-opcua-guid": "2.98.1" - } - }, - "node_modules/node-opcua-nodeset-ua": { - "version": "2.113.0", - "resolved": "https://registry.npmjs.org/node-opcua-nodeset-ua/-/node-opcua-nodeset-ua-2.113.0.tgz", - "integrity": "sha512-OqIY7rWXfo9EhCJqZCfqm7ie1hq7xz7d65pwB5tKvGUGMqUsG8xLCU+cfxCCW1pHkR0DJaPhBhuZxlS0UmRB3Q==", - "license": "MIT", - "dependencies": { - "node-opcua-address-space-base": "2.113.0", - "node-opcua-basic-types": "2.113.0", - "node-opcua-data-access": "2.113.0", - "node-opcua-data-model": "2.113.0", - "node-opcua-data-value": "2.113.0", - "node-opcua-extension-object": "2.113.0", - "node-opcua-nodeid": "2.113.0", - "node-opcua-status-code": "2.110.0", - "node-opcua-variant": "2.113.0" - } - }, - "node_modules/node-opcua-nodesets": { - "version": "2.110.0", - "resolved": "https://registry.npmjs.org/node-opcua-nodesets/-/node-opcua-nodesets-2.110.0.tgz", - "integrity": "sha512-ELd0d0VFbf9QoWRzv+WsqbqWHikoF9l2SikEP3VyPc6H4j8/TH4QbKHf5Rt1LjJbM6mPyVX0fQIidqRKg1fDEg==", - "license": "MIT" - }, - "node_modules/node-opcua-numeric-range": { - "version": "2.113.0", - "resolved": "https://registry.npmjs.org/node-opcua-numeric-range/-/node-opcua-numeric-range-2.113.0.tgz", - "integrity": "sha512-HoV/zCqD/I0fmUQw/mw8w4TlChKljK4/19ydGYpO+9UzGgtug1BriXR4IBY4B5d22yc+YZ25fbekV/zjNznhvA==", + "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==", + "dev": true, "license": "MIT", - "dependencies": { - "node-opcua-assert": "2.105.0", - "node-opcua-basic-types": "2.113.0", - "node-opcua-binary-stream": "2.110.0", - "node-opcua-factory": "2.113.0", - "node-opcua-status-code": "2.110.0" + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/node-opcua-object-registry": { - "version": "2.113.0", - "resolved": "https://registry.npmjs.org/node-opcua-object-registry/-/node-opcua-object-registry-2.113.0.tgz", - "integrity": "sha512-qNSC+3+a0xKDm6v/YgMaizxBHna19o9SJuxTZVUOD9jCUEZxpHmvmrGcsfar4wFyXCsAw00PwFjYLs9+CJ8QMw==", + "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==", + "dev": true, "license": "MIT", - "dependencies": { - "node-opcua-assert": "2.105.0", - "node-opcua-debug": "2.113.0" + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/node-opcua-packet-analyzer": { - "version": "2.113.0", - "resolved": "https://registry.npmjs.org/node-opcua-packet-analyzer/-/node-opcua-packet-analyzer-2.113.0.tgz", - "integrity": "sha512-HwScO02t9Bk88gg4D79OKbmL3NPxJzbj93nmClD4709aLKzi6XshJbhH62nDh7zHvRUu3DqsJFbOcJMvj5CggQ==", + "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, "license": "MIT", "dependencies": { - "chalk": "4.1.2", - "node-opcua-assert": "2.105.0", - "node-opcua-basic-types": "2.113.0", - "node-opcua-binary-stream": "2.110.0", - "node-opcua-debug": "2.113.0", - "node-opcua-factory": "2.113.0", - "node-opcua-utils": "2.110.0" + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/node-opcua-packet-assembler": { - "version": "2.113.0", - "resolved": "https://registry.npmjs.org/node-opcua-packet-assembler/-/node-opcua-packet-assembler-2.113.0.tgz", - "integrity": "sha512-uLYiKz1Kj6CCx3GnOIiNACt3c3vQc4UUTTN+96kOF8pXLQm7XxWcDdQJB2p7SHI8Yo3n55rkRUYYlarxzqib9Q==", + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, "license": "MIT", "dependencies": { - "node-opcua-assert": "2.105.0", - "node-opcua-debug": "2.113.0" + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" } }, - "node_modules/node-opcua-pki": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/node-opcua-pki/-/node-opcua-pki-4.7.0.tgz", - "integrity": "sha512-Ur5hNlpnsV7KMW4E30VnajZ+SdKCAfIWf/+GUu2hZEoKptE3zYGNT+lTGEnmWHi07trsxmURfNCJM5QUKXrWwg==", + "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, "license": "MIT", - "dependencies": { - "@ster5/global-mutex": "^2.0.0", - "async": "^3.2.4", - "byline": "^5.0.0", - "chalk": "4.1.2", - "chokidar": "^3.5.3", - "cli-table": "^0.3.11", - "minimist": "^1.2.8", - "node-opcua-crypto": "4.5.0", - "progress": "^2.0.3", - "rimraf": "3.0.2", - "thenify": "^3.3.1", - "wget-improved-2": "^3.3.0", - "yargs": "17.7.2", - "yauzl": "^2.10.0" - }, "bin": { - "pki": "bin/crypto_create_CA.js" + "he": "bin/he" } }, - "node_modules/node-opcua-pki/node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true, + "license": "MIT" + }, + "node_modules/human-signals": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", + "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", + "dev": true, + "license": "Apache-2.0", "engines": { - "node": ">=12" + "node": ">=8.12.0" } }, - "node_modules/node-opcua-pki/node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "node_modules/husky": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/husky/-/husky-7.0.4.tgz", + "integrity": "sha512-vbaCKN2QLtP/vD4yvs6iz6hBEo6wkSzs8HpRah1Z6aGmF2KW5PdYuAd7uX5a+OyBZHBhd+TFLqgjUgytQr4RvQ==", + "dev": true, "license": "MIT", - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" + "bin": { + "husky": "lib/bin.js" }, "engines": { "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/typicode" } }, - "node_modules/node-opcua-pki/node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "license": "ISC", + "node_modules/ignore": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", + "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=12" + "node": ">= 4" } }, - "node_modules/node-opcua-pseudo-session": { - "version": "2.113.0", - "resolved": "https://registry.npmjs.org/node-opcua-pseudo-session/-/node-opcua-pseudo-session-2.113.0.tgz", - "integrity": "sha512-DU1McAtb9w1JB0b6wqm2ZfOu5sQODqRqfKkP/mlrqLBe780k1ysWr77TEPkdklQaXGqr9HUkXsRMIC2Zg2KBIg==", + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, "license": "MIT", "dependencies": { - "node-opcua-assert": "2.105.0", - "node-opcua-basic-types": "2.113.0", - "node-opcua-constants": "2.98.1", - "node-opcua-data-model": "2.113.0", - "node-opcua-data-value": "2.113.0", - "node-opcua-debug": "2.113.0", - "node-opcua-nodeid": "2.113.0", - "node-opcua-service-browse": "2.113.0", - "node-opcua-service-call": "2.113.0", - "node-opcua-service-read": "2.113.0", - "node-opcua-service-subscription": "2.113.0", - "node-opcua-service-translate-browse-path": "2.113.0", - "node-opcua-service-write": "2.113.0", - "node-opcua-status-code": "2.110.0", - "node-opcua-types": "2.113.0", - "node-opcua-variant": "2.113.0", - "thenify": "^3.3.1" - } - }, - "node_modules/node-opcua-pubsub-client": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/node-opcua-pubsub-client/-/node-opcua-pubsub-client-0.19.1.tgz", - "integrity": "sha512-TVqgMum+GF9Wlq1L4N5F5ZrSpLoPnreWs7zwxebSBDyCtKmF5giNGqvtJLs6yPHXLiCpKviC+cLgRWh0+gdxyQ==", - "license": "MIT - Copyright (c) 2021 - sterfive.com", - "dependencies": { - "@types/aws-iot-device-sdk": "^2.2.4" + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" }, - "peerDependencies": { - "node-opcua-basic-types": ">=2.64.0", - "node-opcua-client": ">=2.64.0", - "node-opcua-constants": ">=2.64.0", - "node-opcua-data-model": ">=2.64.0", - "node-opcua-debug": ">=2.64.0", - "node-opcua-nodeid": ">=2.64.0", - "node-opcua-pseudo-session": ">=2.64.0", - "node-opcua-service-browse": ">=2.64.0", - "node-opcua-service-translate-browse-path": ">=2.64.0", - "node-opcua-status-code": ">=2.64.0", - "node-opcua-types": ">=2.64.0", - "node-opcua-variant": ">=2.64.0" - } - }, - "node_modules/node-opcua-schemas": { - "version": "2.113.0", - "resolved": "https://registry.npmjs.org/node-opcua-schemas/-/node-opcua-schemas-2.113.0.tgz", - "integrity": "sha512-louJxttAhpVuvfENVzx60q/zoWthdmxo55c7qToy/cq/P9mp3fLHIJzc3tWGMR1iqzAoW/NwGFqab3oGI/U18A==", - "license": "MIT", - "dependencies": { - "chalk": "4.1.2", - "node-opcua-assert": "2.105.0", - "node-opcua-binary-stream": "2.110.0", - "node-opcua-debug": "2.113.0", - "node-opcua-extension-object": "2.113.0", - "node-opcua-factory": "2.113.0", - "node-opcua-nodeid": "2.113.0", - "node-opcua-variant": "2.113.0", - "node-opcua-xml2json": "2.113.0" - } - }, - "node_modules/node-opcua-secure-channel": { - "version": "2.113.0", - "resolved": "https://registry.npmjs.org/node-opcua-secure-channel/-/node-opcua-secure-channel-2.113.0.tgz", - "integrity": "sha512-UqmWk2hvD2Kv0Cw4qIyut4z7794RIfiFPRHCv4QINewYTtjLeTgDhpbixMzcLqtE9TSJdyyWZTWR2gHriYh2yA==", - "license": "MIT", - "dependencies": { - "async": "^3.2.4", - "backoff": "^2.5.0", - "chalk": "4.1.2", - "node-opcua-assert": "2.105.0", - "node-opcua-basic-types": "2.113.0", - "node-opcua-binary-stream": "2.110.0", - "node-opcua-certificate-manager": "2.113.0", - "node-opcua-chunkmanager": "2.113.0", - "node-opcua-common": "2.113.0", - "node-opcua-crypto": "4.5.0", - "node-opcua-debug": "2.113.0", - "node-opcua-factory": "2.113.0", - "node-opcua-nodeid": "2.113.0", - "node-opcua-object-registry": "2.113.0", - "node-opcua-packet-analyzer": "2.113.0", - "node-opcua-service-endpoints": "2.113.0", - "node-opcua-service-secure-channel": "2.113.0", - "node-opcua-status-code": "2.110.0", - "node-opcua-transport": "2.113.0", - "node-opcua-types": "2.113.0", - "node-opcua-utils": "2.110.0" - } - }, - "node_modules/node-opcua-server": { - "version": "2.113.0", - "resolved": "https://registry.npmjs.org/node-opcua-server/-/node-opcua-server-2.113.0.tgz", - "integrity": "sha512-hdFmTST51npCneVYR6d0qzYN+qIf4LDDallLoffg0qbcLIE2JKxrdZADkfX37RNM/iTfMbHyDHB4XLE/A64/wg==", - "license": "MIT", - "dependencies": { - "@ster5/global-mutex": "^2.0.0", - "async": "^3.2.4", - "chalk": "4.1.2", - "dequeue": "^1.0.5", - "lodash": "4.17.21", - "node-opcua-address-space": "2.113.0", - "node-opcua-address-space-base": "2.113.0", - "node-opcua-assert": "2.105.0", - "node-opcua-basic-types": "2.113.0", - "node-opcua-binary-stream": "2.110.0", - "node-opcua-certificate-manager": "2.113.0", - "node-opcua-client": "2.113.0", - "node-opcua-client-dynamic-extension-object": "2.113.0", - "node-opcua-common": "2.113.0", - "node-opcua-constants": "2.98.1", - "node-opcua-crypto": "4.5.0", - "node-opcua-data-model": "2.113.0", - "node-opcua-data-value": "2.113.0", - "node-opcua-date-time": "2.113.0", - "node-opcua-debug": "2.113.0", - "node-opcua-extension-object": "2.113.0", - "node-opcua-factory": "2.113.0", - "node-opcua-hostname": "2.105.0", - "node-opcua-nodeid": "2.113.0", - "node-opcua-nodesets": "2.110.0", - "node-opcua-numeric-range": "2.113.0", - "node-opcua-object-registry": "2.113.0", - "node-opcua-secure-channel": "2.113.0", - "node-opcua-service-browse": "2.113.0", - "node-opcua-service-call": "2.113.0", - "node-opcua-service-discovery": "2.113.0", - "node-opcua-service-endpoints": "2.113.0", - "node-opcua-service-filter": "2.113.0", - "node-opcua-service-history": "2.113.0", - "node-opcua-service-node-management": "2.113.0", - "node-opcua-service-query": "2.113.0", - "node-opcua-service-read": "2.113.0", - "node-opcua-service-register-node": "2.113.0", - "node-opcua-service-secure-channel": "2.113.0", - "node-opcua-service-session": "2.113.0", - "node-opcua-service-subscription": "2.113.0", - "node-opcua-service-translate-browse-path": "2.113.0", - "node-opcua-service-write": "2.113.0", - "node-opcua-status-code": "2.110.0", - "node-opcua-types": "2.113.0", - "node-opcua-utils": "2.110.0", - "node-opcua-variant": "2.113.0", - "thenify": "^3.3.1" - } - }, - "node_modules/node-opcua-server-discovery": { - "version": "2.113.0", - "resolved": "https://registry.npmjs.org/node-opcua-server-discovery/-/node-opcua-server-discovery-2.113.0.tgz", - "integrity": "sha512-Tf2dzwVIm0IZu4U6mks+nBJ8V9Bi0hX/JIwkC0FNFbwbk2auBjcUOGP8lp9WzmmuIbYZMeM9E1j101Mg8HhDnQ==", - "license": "MIT", - "dependencies": { - "chalk": "4.1.2", - "env-paths": "2.2.1", - "node-opcua-assert": "2.105.0", - "node-opcua-basic-types": "2.113.0", - "node-opcua-certificate-manager": "2.113.0", - "node-opcua-common": "2.113.0", - "node-opcua-debug": "2.113.0", - "node-opcua-hostname": "2.105.0", - "node-opcua-object-registry": "2.113.0", - "node-opcua-secure-channel": "2.113.0", - "node-opcua-server": "2.113.0", - "node-opcua-service-discovery": "2.113.0", - "node-opcua-service-endpoints": "2.113.0", - "node-opcua-status-code": "2.110.0", - "sterfive-bonjour-service": "1.1.4", - "thenify": "^3.3.1" - } - }, - "node_modules/node-opcua-service-browse": { - "version": "2.113.0", - "resolved": "https://registry.npmjs.org/node-opcua-service-browse/-/node-opcua-service-browse-2.113.0.tgz", - "integrity": "sha512-89A2ujS30BX/yE3PQ53MmZeFHkSPHnZwqt/RkGuT28DvOYWkhq5knhNHEf6k1UzEhDfNM/4ifXx/Ep314uK8Pg==", - "license": "MIT", - "dependencies": { - "node-opcua-data-model": "2.113.0", - "node-opcua-types": "2.113.0" - } - }, - "node_modules/node-opcua-service-call": { - "version": "2.113.0", - "resolved": "https://registry.npmjs.org/node-opcua-service-call/-/node-opcua-service-call-2.113.0.tgz", - "integrity": "sha512-xpjOTs7JI0ifZvrocGLRWxQJg4Lkz0Z7qsnacNMHKA5Ny5GQXrU2y0Avm4gnAg5XS2oB3HfDVcpXRq8cegeriQ==", - "license": "MIT", - "dependencies": { - "node-opcua-assert": "2.105.0", - "node-opcua-nodeid": "2.113.0", - "node-opcua-types": "2.113.0", - "node-opcua-variant": "2.113.0" - } - }, - "node_modules/node-opcua-service-discovery": { - "version": "2.113.0", - "resolved": "https://registry.npmjs.org/node-opcua-service-discovery/-/node-opcua-service-discovery-2.113.0.tgz", - "integrity": "sha512-OHoIXHIco68p2TtDZSB11Z+J2KdGj1BwLb53DS8uyhwchy+Fg4UIprdLS2J9iAeO3VJygbI5SOEQOejH8bu2yA==", - "license": "MIT", - "dependencies": { - "chalk": "4.1.2", - "node-opcua-assert": "2.105.0", - "node-opcua-debug": "2.113.0", - "node-opcua-object-registry": "2.113.0", - "node-opcua-types": "2.113.0", - "sterfive-bonjour-service": "1.1.4" + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/node-opcua-service-endpoints": { - "version": "2.113.0", - "resolved": "https://registry.npmjs.org/node-opcua-service-endpoints/-/node-opcua-service-endpoints-2.113.0.tgz", - "integrity": "sha512-jPIphZ8xNuAECkW+F3l5FCkrqECswipz6M4iwQHTe8doResv6z2mM4nhf8MOLnmdh1nGtSD29AMd9gzaTDSSpg==", + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, "license": "MIT", - "dependencies": { - "node-opcua-types": "2.113.0" + "engines": { + "node": ">=0.8.19" } }, - "node_modules/node-opcua-service-filter": { - "version": "2.113.0", - "resolved": "https://registry.npmjs.org/node-opcua-service-filter/-/node-opcua-service-filter-2.113.0.tgz", - "integrity": "sha512-lk4574gHEMzg+gC359lSIgphfbUVV3N8mRzX/tMyd/sZQPuUoNbustID8k/H+uh2gLnQD0e2aSsrHL82y6+K7g==", - "license": "MIT", + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "license": "ISC", "dependencies": { - "node-opcua-address-space-base": "2.113.0", - "node-opcua-assert": "2.105.0", - "node-opcua-basic-types": "2.113.0", - "node-opcua-constants": "2.98.1", - "node-opcua-data-model": "2.113.0", - "node-opcua-data-value": "2.113.0", - "node-opcua-debug": "2.113.0", - "node-opcua-extension-object": "2.113.0", - "node-opcua-nodeid": "2.113.0", - "node-opcua-service-translate-browse-path": "2.113.0", - "node-opcua-status-code": "2.110.0", - "node-opcua-types": "2.113.0", - "node-opcua-variant": "2.113.0" + "once": "^1.3.0", + "wrappy": "1" } }, - "node_modules/node-opcua-service-history": { - "version": "2.113.0", - "resolved": "https://registry.npmjs.org/node-opcua-service-history/-/node-opcua-service-history-2.113.0.tgz", - "integrity": "sha512-LkGjpAtSL/STXx8KpzRGKdhuQ7f5j1dhPTKKAS5i+oDjiNfEjIrhLJexoGYKxIFLI//awpemD3SOH1l7d1i72g==", + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "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, "license": "MIT", "dependencies": { - "node-opcua-assert": "2.105.0", - "node-opcua-data-value": "2.113.0", - "node-opcua-types": "2.113.0" + "es-errors": "^1.3.0", + "hasown": "^2.0.0", + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" } }, - "node_modules/node-opcua-service-node-management": { - "version": "2.113.0", - "resolved": "https://registry.npmjs.org/node-opcua-service-node-management/-/node-opcua-service-node-management-2.113.0.tgz", - "integrity": "sha512-tp3WhUOrjM/NJ6yEfgbvaIXSj/Nd4Kh1rw/q/cQsYrs9uIOTq2B/q0XoGlNj0CLIiXUTtPx62CZWEZxR11oL2Q==", + "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==", "license": "MIT", - "dependencies": { - "node-opcua-types": "2.113.0" + "engines": { + "node": ">=8" } }, - "node_modules/node-opcua-service-query": { - "version": "2.113.0", - "resolved": "https://registry.npmjs.org/node-opcua-service-query/-/node-opcua-service-query-2.113.0.tgz", - "integrity": "sha512-5MEuwn3hYZWkjXVdz1m9GxIB2iAh2hXMK1AirjiTKWaN7J5U6tUBU+I4C5zCnpjJqYo+7jATSpXGEN7suOSxdQ==", + "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, "license": "MIT", "dependencies": { - "node-opcua-types": "2.113.0" + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/node-opcua-service-read": { - "version": "2.113.0", - "resolved": "https://registry.npmjs.org/node-opcua-service-read/-/node-opcua-service-read-2.113.0.tgz", - "integrity": "sha512-+fEfKZ+gbgv2RVMDyGheQ9gJ8NhW01VEotaZQFczP3RNnwCTz5km7hr7iOY/9KXALBTKYsDmFO2rjPntz54daw==", + "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, "license": "MIT", "dependencies": { - "node-opcua-assert": "2.105.0", - "node-opcua-data-model": "2.113.0", - "node-opcua-data-value": "2.113.0", - "node-opcua-service-secure-channel": "2.113.0", - "node-opcua-types": "2.113.0" + "has-bigints": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/node-opcua-service-register-node": { - "version": "2.113.0", - "resolved": "https://registry.npmjs.org/node-opcua-service-register-node/-/node-opcua-service-register-node-2.113.0.tgz", - "integrity": "sha512-2nDvSi2DEMDXPvy5YFFD/rjhlWb4etZO/Ak09OkMnzZg+9ZNdpzpozpLtB0r4Sfhq62yY4eoVx+o4EUmvYF4NQ==", + "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, "license": "MIT", "dependencies": { - "node-opcua-types": "2.113.0" + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" } }, - "node_modules/node-opcua-service-secure-channel": { - "version": "2.113.0", - "resolved": "https://registry.npmjs.org/node-opcua-service-secure-channel/-/node-opcua-service-secure-channel-2.113.0.tgz", - "integrity": "sha512-rD/7An36GrQBgBD1RAcvrWmN1JZPDxlWoRR4P/x+DzdvCPvZKrWukbXfrZRqxu1+zYBB9bnFmS+wXz0oDp2y/A==", + "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, "license": "MIT", "dependencies": { - "node-opcua-assert": "2.105.0", - "node-opcua-basic-types": "2.113.0", - "node-opcua-binary-stream": "2.110.0", - "node-opcua-factory": "2.113.0", - "node-opcua-types": "2.113.0" + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/node-opcua-service-session": { - "version": "2.113.0", - "resolved": "https://registry.npmjs.org/node-opcua-service-session/-/node-opcua-service-session-2.113.0.tgz", - "integrity": "sha512-Jc5oh7y5NKsNt6CiJGaoufE34irkpFk7WNpnyZ0xxtuT2/GFasry4yPCiKo5dtNm+rS4bWtaNTJBMKmbeCMUjQ==", + "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, "license": "MIT", + "peer": true, "dependencies": { - "node-opcua-factory": "2.113.0", - "node-opcua-types": "2.113.0" + "builtin-modules": "^3.3.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/node-opcua-service-subscription": { - "version": "2.113.0", - "resolved": "https://registry.npmjs.org/node-opcua-service-subscription/-/node-opcua-service-subscription-2.113.0.tgz", - "integrity": "sha512-6CK5wC6427UUUNtYJRUC0CGyK+d1KFF+ZHLVuIWJVnHczxmXYtB57QBbElZidc9XPBl5dJZvFYxxz5xFgK9sSg==", + "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, "license": "MIT", - "dependencies": { - "node-opcua-assert": "2.105.0", - "node-opcua-types": "2.113.0", - "node-opcua-variant": "2.113.0" + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/node-opcua-service-translate-browse-path": { - "version": "2.113.0", - "resolved": "https://registry.npmjs.org/node-opcua-service-translate-browse-path/-/node-opcua-service-translate-browse-path-2.113.0.tgz", - "integrity": "sha512-2Q+jhwUFpOmsl88ohNIodoNfU5hJunKkwslbqhIoa5p573HzfNtnBXqNmRWe3QFhRt2L4qNV4h1SfybJGTjDlQ==", + "node_modules/is-core-module": { + "version": "2.14.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.14.0.tgz", + "integrity": "sha512-a5dFJih5ZLYlRtDc0dZWP7RiKr6xIKzmn/oAYCDvdLThadVgyJwlaoQPmRtMSpz+rk0OGAgIu+TcM9HUF0fk1A==", + "dev": true, "license": "MIT", "dependencies": { - "node-opcua-assert": "2.105.0", - "node-opcua-constants": "2.98.1", - "node-opcua-data-model": "2.113.0", - "node-opcua-nodeid": "2.113.0", - "node-opcua-types": "2.113.0" + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/node-opcua-service-write": { - "version": "2.113.0", - "resolved": "https://registry.npmjs.org/node-opcua-service-write/-/node-opcua-service-write-2.113.0.tgz", - "integrity": "sha512-vmkcvZgXT3rq6lm058syh+mFGIjiwbp6Cq+5o//fbsibH5MYLQn5VL/c9LklhH1lPyyy0v9UDp9+4s7dmmZNaA==", + "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, "license": "MIT", "dependencies": { - "node-opcua-types": "2.113.0" + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/node-opcua-status-code": { - "version": "2.110.0", - "resolved": "https://registry.npmjs.org/node-opcua-status-code/-/node-opcua-status-code-2.110.0.tgz", - "integrity": "sha512-35iSc6SW8S4FyMdU6BfhtYD/2TJWemQb1quMXBJnR97/OzkS+uBOOSbP8NNBxbcT0rXMC4B3EqC2psYALIy9Fw==", + "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, "license": "MIT", "dependencies": { - "node-opcua-assert": "2.105.0", - "node-opcua-binary-stream": "2.110.0" + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/node-opcua-transport": { - "version": "2.113.0", - "resolved": "https://registry.npmjs.org/node-opcua-transport/-/node-opcua-transport-2.113.0.tgz", - "integrity": "sha512-xg5pBiYPT5zbQlT79xeKmeNyQjdRBP1VRcK+bdRZVARM34SRfLgUCY7//Z5CNyijeqbFwyuhthHKD8ae5pUbvw==", + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, "license": "MIT", - "dependencies": { - "chalk": "4.1.2", - "node-opcua-assert": "2.105.0", - "node-opcua-basic-types": "2.113.0", - "node-opcua-binary-stream": "2.110.0", - "node-opcua-buffer-utils": "2.110.0", - "node-opcua-chunkmanager": "2.113.0", - "node-opcua-debug": "2.113.0", - "node-opcua-factory": "2.113.0", - "node-opcua-object-registry": "2.113.0", - "node-opcua-packet-assembler": "2.113.0", - "node-opcua-status-code": "2.110.0", - "node-opcua-utils": "2.110.0" + "engines": { + "node": ">=0.10.0" } }, - "node_modules/node-opcua-types": { - "version": "2.113.0", - "resolved": "https://registry.npmjs.org/node-opcua-types/-/node-opcua-types-2.113.0.tgz", - "integrity": "sha512-htUgU8/Zrp2zR3cFocnEgwgyJpu0xoplSFDwx685l3Xu9Hy8migbohsg8kDiOXjm86mgCCjkcVS55YaMhuEbuQ==", + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, "license": "MIT", - "dependencies": { - "node-opcua-assert": "2.105.0", - "node-opcua-basic-types": "2.113.0", - "node-opcua-binary-stream": "2.110.0", - "node-opcua-data-model": "2.113.0", - "node-opcua-data-value": "2.113.0", - "node-opcua-enum": "2.110.0", - "node-opcua-extension-object": "2.113.0", - "node-opcua-factory": "2.113.0", - "node-opcua-generator": "2.113.0", - "node-opcua-nodeid": "2.113.0", - "node-opcua-numeric-range": "2.113.0", - "node-opcua-status-code": "2.110.0", - "node-opcua-variant": "2.113.0" + "engines": { + "node": ">=8" } }, - "node_modules/node-opcua-utils": { - "version": "2.110.0", - "resolved": "https://registry.npmjs.org/node-opcua-utils/-/node-opcua-utils-2.110.0.tgz", - "integrity": "sha512-Va0An8sZUoYsoJxkjRxaPIFK2LWQuzd3p342tb5TQBKIKcnXcJeMZK8gzupJQ96u9nwVfvJcOWATvWRj1OyRhw==", + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, "license": "MIT", "dependencies": { - "chalk": "4.1.2", - "node-opcua-assert": "2.105.0" + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" } }, - "node_modules/node-opcua-variant": { - "version": "2.113.0", - "resolved": "https://registry.npmjs.org/node-opcua-variant/-/node-opcua-variant-2.113.0.tgz", - "integrity": "sha512-fLBi4x2W16259poqBlybJUHrB/z/0DpWtDs5aZ7xZT9tlkSCxtva30gnlMjnGruNEdvSKGN68GJduEAYYBROIQ==", + "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, "license": "MIT", - "dependencies": { - "node-opcua-assert": "2.105.0", - "node-opcua-basic-types": "2.113.0", - "node-opcua-binary-stream": "2.110.0", - "node-opcua-data-model": "2.113.0", - "node-opcua-enum": "2.110.0", - "node-opcua-factory": "2.113.0", - "node-opcua-nodeid": "2.113.0", - "node-opcua-utils": "2.110.0" + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/node-opcua-vendor-diagnostic": { - "version": "2.113.0", - "resolved": "https://registry.npmjs.org/node-opcua-vendor-diagnostic/-/node-opcua-vendor-diagnostic-2.113.0.tgz", - "integrity": "sha512-sN6SlCq13HUkTdhSEIiJWEJ82dEtKc3hjElDrfR1jn4l24tl5E4FohQMItgF4m4bZxXZu4DKtD2ZmMeb043PYw==", + "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, "license": "MIT", - "dependencies": { - "humanize": "0.0.9", - "node-opcua-address-space": "2.113.0", - "node-opcua-assert": "2.105.0", - "node-opcua-constants": "2.98.1", - "node-opcua-debug": "2.113.0", - "node-opcua-server": "2.113.0", - "node-opcua-status-code": "2.110.0", - "node-opcua-variant": "2.113.0" + "engines": { + "node": ">=0.12.0" } }, - "node_modules/node-opcua-xml2json": { - "version": "2.113.0", - "resolved": "https://registry.npmjs.org/node-opcua-xml2json/-/node-opcua-xml2json-2.113.0.tgz", - "integrity": "sha512-BMJI7yuK3BakzbCW1SuzuATlbCHtxKb596g4FLwC/cIxr25F4GRc1bl9ZrfmQBVvCt/Denk8pT66NfcKeGB8ZQ==", + "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, "license": "MIT", "dependencies": { - "ltx": "^3.0.0", - "node-opcua-assert": "2.105.0", - "node-opcua-utils": "2.110.0", - "thenify": "^3.3.1", - "xml-writer": "^1.7.0" + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/nofilter": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/nofilter/-/nofilter-3.1.0.tgz", - "integrity": "sha512-l2NNj07e9afPnhAhvgVrCD/oy2Ai1yfLpuo3EpiO1jFTsB4sFz6oIfAfSZyQzVpkZQ9xS8ZS5g1jCBgq4Hwo0g==", + "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, "license": "MIT", "engines": { - "node": ">=12.19" + "node": ">=8" } }, - "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==", + "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, "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "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, "license": "MIT", "dependencies": { - "path-key": "^3.0.0" + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" }, "engines": { - "node": ">=8" - } - }, - "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.", - "license": "ISC", - "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": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/number-allocator": { - "version": "1.0.14", - "resolved": "https://registry.npmjs.org/number-allocator/-/number-allocator-1.0.14.tgz", - "integrity": "sha512-OrL44UTVAvkKdOdRQZIJpLkAdjXGTRda052sN4sO77bKEzYYqWKMBjQvrJFzqygI99gL6Z4u2xctPW1tB8ErvA==", + "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, "license": "MIT", "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==", - "license": "MIT", - "optional": true, + "call-bind": "^1.0.7" + }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "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==", + "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==", + "dev": true, "license": "MIT", - "optional": true, "engines": { - "node": ">=0.10.0" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/object-inspect": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", - "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==", + "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, "license": "MIT", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, "engines": { "node": ">= 0.4" }, @@ -9395,14 +3603,14 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/object-is": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.6.tgz", - "integrity": "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==", + "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, "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1" + "has-symbols": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -9411,156 +3619,242 @@ "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==", + "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, "license": "MIT", + "dependencies": { + "which-typed-array": "^1.1.14" + }, "engines": { "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "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==", + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, "license": "MIT", - "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" + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "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==", + "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, "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" + "call-bind": "^1.0.2" }, "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==", + "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, - "license": "MIT", + "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, + "license": "ISC" + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2" + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" }, "engines": { - "node": ">= 0.4" + "node": ">=10" } }, - "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==", + "node_modules/istanbul-reports": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", + "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", "dev": true, - "license": "MIT", + "license": "BSD-3-Clause", "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" }, "engines": { - "node": ">= 0.4" + "node": ">=8" + } + }, + "node_modules/js-tokens": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", + "integrity": "sha512-RjTcuD4xjtthQkaWH7dFlH85L+QaVtSoOyGdZ3g6HFhS9dFNDfLyqgm2NFe2X6cQpeFmt0452FJjFG5UameExg==", + "dev": true, + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "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, + "license": "MIT" + }, + "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==", + "license": "MIT", + "bin": { + "jpr": "dist/index.js", + "json-placeholder-replacer": "dist/index.js" } }, - "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==", + "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, + "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, + "license": "MIT" + }, + "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, "license": "MIT", "dependencies": { - "ee-first": "1.1.1" + "minimist": "^1.2.0" }, - "engines": { - "node": ">= 0.8" + "bin": { + "json5": "lib/cli.js" } }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "license": "ISC", + "node_modules/just-extend": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-6.2.0.tgz", + "integrity": "sha512-cYofQu2Xpom82S6qD778jBDpwvvy39s1l/hrYij2u9AMdQcGRpaBu6kY4mVhuno5kJVi1DAz4aiphA2WI1/OAw==", + "dev": true, + "license": "MIT" + }, + "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, + "license": "MIT", "dependencies": { - "wrappy": "1" + "json-buffer": "3.0.1" } }, - "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "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, "license": "MIT", "dependencies": { - "mimic-fn": "^2.1.0" + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" }, "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 0.8.0" } }, - "node_modules/optionator": { - "version": "0.9.4", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", - "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "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, "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" + "p-locate": "^5.0.0" }, "engines": { - "node": ">= 0.8.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/os-browserify": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", - "integrity": "sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A==", + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "dev": true, "license": "MIT" }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "node_modules/lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", "dev": true, "license": "MIT", "dependencies": { - "yocto-queue": "^0.1.0" + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" }, "engines": { "node": ">=10" @@ -9569,14 +3863,24 @@ "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==", + "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, "license": "MIT", "dependencies": { - "p-limit": "^3.0.2" + "get-func-name": "^2.0.1" + } + }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" }, "engines": { "node": ">=10" @@ -9585,449 +3889,485 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "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==", + "dev": true, + "license": "ISC" + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true, + "license": "MIT" + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", "dev": true, "license": "MIT", "engines": { - "node": ">=6" + "node": ">= 8" } }, - "node_modules/pako": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", - "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "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, - "license": "(MIT AND Zlib)" + "license": "MIT" }, - "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==", + "node_modules/micromatch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz", + "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==", "dev": true, "license": "MIT", "dependencies": { - "callsites": "^3.0.0" + "braces": "^3.0.3", + "picomatch": "^2.3.1" }, "engines": { - "node": ">=6" + "node": ">=8.6" } }, - "node_modules/parents": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parents/-/parents-1.0.1.tgz", - "integrity": "sha512-mXKF3xkoUt5td2DoxpLmtOmZvko9VfFpwRwkKDHSNvgmpLAeBo18YDhcPbBzJq+QLCHMbGOfzia2cX4U+0v9Mg==", + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", "dev": true, "license": "MIT", - "dependencies": { - "path-platform": "~0.11.15" + "engines": { + "node": ">=6" } }, - "node_modules/parse-asn1": { - "version": "5.1.7", - "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.7.tgz", - "integrity": "sha512-CTM5kuWR3sx9IFamcl5ErfPl6ea/N8IYwiJ+vpeB2g+1iknv7zBl5uPwbMbRVznRVbrNY6lGuDoE5b30grmbqg==", + "node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", "dev": true, "license": "ISC", "dependencies": { - "asn1.js": "^4.10.1", - "browserify-aes": "^1.2.0", - "evp_bytestokey": "^1.0.3", - "hash-base": "~3.0", - "pbkdf2": "^3.1.2", - "safe-buffer": "^5.2.1" + "brace-expansion": "^2.0.1" }, "engines": { - "node": ">= 0.10" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/parse-asn1/node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", "dev": true, "license": "MIT", - "engines": { - "node": ">= 0.8" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/path-browserify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", - "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", + "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, - "license": "MIT" + "license": "MIT", + "dependencies": { + "acorn": "^8.11.3", + "pathe": "^1.1.2", + "pkg-types": "^1.1.1", + "ufo": "^1.5.3" + } }, - "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==", + "node_modules/mocha": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.2.2.tgz", + "integrity": "sha512-L6XC3EdwT6YrIk0yXpavvLkn8h+EU+Y5UcCHKECyMbdUIxyMuZj4bX4U9e1nvnvUUvQVsV2VHQr5zLdcUkhW/g==", "dev": true, "license": "MIT", + "dependencies": { + "@ungap/promise-all-settled": "1.1.2", + "ansi-colors": "4.1.1", + "browser-stdout": "1.3.1", + "chokidar": "3.5.3", + "debug": "4.3.3", + "diff": "5.0.0", + "escape-string-regexp": "4.0.0", + "find-up": "5.0.0", + "glob": "7.2.0", + "growl": "1.10.5", + "he": "1.2.0", + "js-yaml": "4.1.0", + "log-symbols": "4.1.0", + "minimatch": "4.2.1", + "ms": "2.1.3", + "nanoid": "3.3.1", + "serialize-javascript": "6.0.0", + "strip-json-comments": "3.1.1", + "supports-color": "8.1.1", + "which": "2.0.2", + "workerpool": "6.2.0", + "yargs": "16.2.0", + "yargs-parser": "20.2.4", + "yargs-unparser": "2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha" + }, "engines": { - "node": ">=8" + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mochajs" } }, - "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==", + "node_modules/mocha/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, "license": "MIT", - "engines": { - "node": ">=0.10.0" + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "node_modules/mocha/node_modules/debug": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", "dev": true, "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, "engines": { - "node": ">=8" + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "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==", + "node_modules/mocha/node_modules/debug/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==", + "dev": true, "license": "MIT" }, - "node_modules/path-platform": { - "version": "0.11.15", - "resolved": "https://registry.npmjs.org/path-platform/-/path-platform-0.11.15.tgz", - "integrity": "sha512-Y30dB6rab1A/nfEKsZxmr01nUotHX0c/ZiIAsCTatEe1CmS5Pm5He7fZ195bPT7RdquoaL8lLxFCMQi/bS7IJg==", + "node_modules/mocha/node_modules/minimatch": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-4.2.1.tgz", + "integrity": "sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g==", "dev": true, - "license": "MIT", + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, "engines": { - "node": ">= 0.8.0" + "node": ">=10" } }, - "node_modules/path-to-regexp": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.2.2.tgz", - "integrity": "sha512-GQX3SSMokngb36+whdpRXE+3f9V8UzyAorlYvOGx87ufGHehNTn5lCxrKtLyZ4Yl/wEKnNnr98ZzOwwDZV5ogw==", + "node_modules/mocha/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true, "license": "MIT" }, - "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "node_modules/mocha/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, "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" } }, - "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, - "license": "MIT" - }, - "node_modules/pathval": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", - "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "node_modules/mocha/node_modules/yargs-parser": { + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", "dev": true, - "license": "MIT", + "license": "ISC", "engines": { - "node": "*" + "node": ">=10" } }, - "node_modules/pbkdf2": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", - "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==", + "node_modules/mri": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", + "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", "dev": true, "license": "MIT", - "dependencies": { - "create-hash": "^1.1.2", - "create-hmac": "^1.1.4", - "ripemd160": "^2.0.1", - "safe-buffer": "^5.0.1", - "sha.js": "^2.4.8" - }, "engines": { - "node": ">=0.12" + "node": ">=4" } }, - "node_modules/pend": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", - "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", + "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==", "license": "MIT" }, - "node_modules/picocolors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", - "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", - "dev": true, - "license": "ISC" + "node_modules/nan": { + "version": "2.20.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.20.0.tgz", + "integrity": "sha512-bk3gXBZDGILuuo/6sKtr0DQmSThYHLtNCdSdXk9YkxD/jK6X2vmCyyXBBxyqZ4XcnzTyYEAThfX3DCEnLf6igw==", + "license": "MIT" }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "node_modules/nanoid": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz", + "integrity": "sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==", + "dev": true, "license": "MIT", - "engines": { - "node": ">=8.6" + "bin": { + "nanoid": "bin/nanoid.cjs" }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, - "node_modules/pkg-types": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.1.3.tgz", - "integrity": "sha512-+JrgthZG6m3ckicaOB74TwQ+tBWsFl3qVQg7mN8ulwSOElJ7gBhKzj2VkCPnZ4NlF6kEquYU+RIYNVAvzd54UA==", + "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, - "license": "MIT", + "license": "MIT" + }, + "node_modules/nise": { + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.9.tgz", + "integrity": "sha512-qOnoujW4SV6e40dYxJOb3uvuoPHtmLzIk4TFo+j0jPJoC+5Z9xja5qH5JZobEPsa8+YYphMrOSwnrshEhG2qww==", + "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "confbox": "^0.1.7", - "mlly": "^1.7.1", - "pathe": "^1.1.2" + "@sinonjs/commons": "^3.0.0", + "@sinonjs/fake-timers": "^11.2.2", + "@sinonjs/text-encoding": "^0.7.2", + "just-extend": "^6.2.0", + "path-to-regexp": "^6.2.1" } }, - "node_modules/popsicle": { - "version": "12.1.2", - "resolved": "https://registry.npmjs.org/popsicle/-/popsicle-12.1.2.tgz", - "integrity": "sha512-xE2vEUa15TiHvFhGmKTtdKk9aSLL5CHX8Vw5kHfVM3R0YHiaTon6Ybsamw0XYqMR+Ng2RijX88iYUKPBMpLBww==", - "license": "MIT", + "node_modules/nise/node_modules/@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "dev": true, + "license": "BSD-3-Clause", "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" + "type-detect": "4.0.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==", - "license": "MIT", - "peerDependencies": { - "servie": "^4.0.0" + "node_modules/nise/node_modules/@sinonjs/fake-timers": { + "version": "11.2.2", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-11.2.2.tgz", + "integrity": "sha512-G2piCSxQ7oWOxwGSAyFHfPIsyeJGXYtc6mFbnFA+kRXkiEnTl8c/8jul2S329iFBnDI9HGoeWWAZvuvOkZccgw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.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==", + "node_modules/node-mbus": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/node-mbus/-/node-mbus-2.2.4.tgz", + "integrity": "sha512-xFNx+tgLb7mxL3K1CX+LufoBYUq4Zmu/3NyTBV75tQXkk706+dgBloopm7Pnd0x4Cut/6e6lD4HVw/01qgpYog==", + "hasInstallScript": true, "license": "MIT", "dependencies": { - "@types/tough-cookie": "^4.0.2", - "tough-cookie": "^4.1.3" + "bindings": "^1.5.0", + "nan": "^2.19.0", + "xml2js": "^0.6.2" }, "engines": { - "node": ">=8" - }, - "peerDependencies": { - "servie": "^4.0.0" + "node": ">=12.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==", - "license": "MIT", - "peerDependencies": { - "servie": "^4.1.0" + "node_modules/node-netconf": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/node-netconf/-/node-netconf-1.1.2.tgz", + "integrity": "sha512-4v8zyFk1/Bxtt8pIN5wl0tYvpagdrRBphaDjLPNku4Mcql3GGGzeM5KmChQXrS5/LCOZl1vUK3D4GT+1PqxWhQ==", + "license": "ISC", + "dependencies": { + "ssh2": "^0.8.9", + "vasync": "^2.2.0", + "xml2js": "^0.4.23" } }, - "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==", + "node_modules/node-netconf/node_modules/xml2js": { + "version": "0.4.23", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz", + "integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==", "license": "MIT", "dependencies": { - "make-error-cause": "^2.2.0" + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.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==", - "license": "MIT", - "peerDependencies": { - "servie": "^4.2.0" + "engines": { + "node": ">=4.0.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==", + "node_modules/nofilter": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/nofilter/-/nofilter-3.1.0.tgz", + "integrity": "sha512-l2NNj07e9afPnhAhvgVrCD/oy2Ai1yfLpuo3EpiO1jFTsB4sFz6oIfAfSZyQzVpkZQ9xS8ZS5g1jCBgq4Hwo0g==", "license": "MIT", - "peerDependencies": { - "servie": "^4.0.0" + "engines": { + "node": ">=12.19" } }, - "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==", + "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, "license": "MIT", "engines": { - "node": ">= 0.4" + "node": ">=0.10.0" } }, - "node_modules/prebuild-install": { - "version": "6.1.4", - "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-6.1.4.tgz", - "integrity": "sha512-Z4vpywnK1lBg+zdPCVCsKq0xO66eEV9rWo2zrROGGiRS4JtueBOdlB1FnY8lcy7JsUud/Q3ijUxyWN26Ika0vQ==", + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, "license": "MIT", - "optional": true, "dependencies": { - "detect-libc": "^1.0.3", - "expand-template": "^2.0.3", - "github-from-package": "0.0.0", - "minimist": "^1.2.3", - "mkdirp-classic": "^0.5.3", - "napi-build-utils": "^1.0.1", - "node-abi": "^2.21.0", - "npmlog": "^4.0.1", - "pump": "^3.0.0", - "rc": "^1.2.7", - "simple-get": "^3.0.3", - "tar-fs": "^2.0.0", - "tunnel-agent": "^0.6.0" - }, - "bin": { - "prebuild-install": "bin.js" + "path-key": "^3.0.0" }, "engines": { - "node": ">=6" + "node": ">=8" } }, - "node_modules/precond": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/precond/-/precond-0.2.3.tgz", - "integrity": "sha512-QCYG84SgGyGzqJ/vlMsxeXd/pgL/I94ixdNFyh1PusWmTCyVfPJjZ1K1jvHtsbfnXQs2TSkEP2fR7QiMZAnKFQ==", + "node_modules/object-inspect": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", + "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==", + "dev": true, + "license": "MIT", "engines": { - "node": ">= 0.6" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "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==", + "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, "license": "MIT", "engines": { - "node": ">= 0.8.0" + "node": ">= 0.4" } }, - "node_modules/prettier": { - "version": "2.8.8", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", - "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", + "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, "license": "MIT", - "bin": { - "prettier": "bin-prettier.js" + "dependencies": { + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", + "has-symbols": "^1.0.3", + "object-keys": "^1.1.1" }, "engines": { - "node": ">=10.13.0" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/pretty-quick": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/pretty-quick/-/pretty-quick-3.3.1.tgz", - "integrity": "sha512-3b36UXfYQ+IXXqex6mCca89jC8u0mYLqFAN5eTQKoXO6oCQYcIVYZEB/5AlBHI7JPYygReM2Vv6Vom/Gln7fBg==", + "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, "license": "MIT", "dependencies": { - "execa": "^4.1.0", - "find-up": "^4.1.0", - "ignore": "^5.3.0", - "mri": "^1.2.0", - "picocolors": "^1.0.0", - "picomatch": "^3.0.1", - "tslib": "^2.6.2" - }, - "bin": { - "pretty-quick": "dist/cli.js" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" }, "engines": { - "node": ">=10.13" + "node": ">= 0.4" }, - "peerDependencies": { - "prettier": "^2.0.0" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/pretty-quick/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "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, + "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", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.0.tgz", + "integrity": "sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==", "dev": true, "license": "MIT", "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" }, "engines": { - "node": ">=8" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/pretty-quick/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" + "wrappy": "1" } }, - "node_modules/pretty-quick/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", "dev": true, "license": "MIT", "dependencies": { - "p-try": "^2.0.0" + "mimic-fn": "^2.1.0" }, "engines": { "node": ">=6" @@ -10036,408 +4376,342 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/pretty-quick/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "dev": true, "license": "MIT", "dependencies": { - "p-limit": "^2.2.0" + "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": ">=8" + "node": ">= 0.8.0" } }, - "node_modules/pretty-quick/node_modules/picomatch": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-3.0.1.tgz", - "integrity": "sha512-I3EurrIQMlRc9IaAZnqRR044Phh2DXY+55o7uJ0V+hYZAcQYSuFWsc9q5PvyDHUSCe1Qxn/iBz+78s86zWnGag==", + "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, "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, "engines": { "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "url": "https://github.com/sponsors/sindresorhus" } }, - "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==", + "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, "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, "engines": { - "node": ">= 0.6.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "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==", - "license": "MIT" - }, - "node_modules/progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, "license": "MIT", "engines": { - "node": ">=0.4.0" + "node": ">=6" } }, - "node_modules/promisify-any": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/promisify-any/-/promisify-any-2.0.1.tgz", - "integrity": "sha512-pVaGouFbTVxqpVJ+T5A15olNJDASAZHYq5cXz6mWdr6/X34mVWiG9MSdzHTcVBCv4aqBP7wGspi7BUSRbEmhsw==", + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dev": true, + "license": "MIT", "dependencies": { - "bluebird": "^2.10.0", - "co-bluebird": "^1.1.0", - "is-generator": "^1.0.2" + "callsites": "^3.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=6" } }, - "node_modules/promisify-any/node_modules/bluebird": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-2.11.0.tgz", - "integrity": "sha512-UfFSr22dmHPQqPP9XWHRhq+gWnHCYguQGkXQlbyPtW5qTnhFWA8/iXg765tH0cAjy7l/zPJ1aBTO0g5XgA7kvQ==", + "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, - "license": "MIT" - }, - "node_modules/proper-lockfile": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.2.tgz", - "integrity": "sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==", "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.4", - "retry": "^0.12.0", - "signal-exit": "^3.0.2" + "engines": { + "node": ">=8" } }, - "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==", + "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==", "dev": true, "license": "MIT", - "dependencies": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - }, "engines": { - "node": ">= 0.10" + "node": ">=0.10.0" } }, - "node_modules/proxy-addr/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==", + "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, "license": "MIT", "engines": { - "node": ">= 0.10" + "node": ">=8" } }, - "node_modules/psl": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", - "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", - "license": "MIT" - }, - "node_modules/public-encrypt": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", - "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", + "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, - "license": "MIT", - "dependencies": { - "bn.js": "^4.1.0", - "browserify-rsa": "^4.0.0", - "create-hash": "^1.1.0", - "parse-asn1": "^5.0.0", - "randombytes": "^2.0.1", - "safe-buffer": "^5.1.2" - } + "license": "MIT" }, - "node_modules/public-encrypt/node_modules/bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "node_modules/path-to-regexp": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.2.2.tgz", + "integrity": "sha512-GQX3SSMokngb36+whdpRXE+3f9V8UzyAorlYvOGx87ufGHehNTn5lCxrKtLyZ4Yl/wEKnNnr98ZzOwwDZV5ogw==", "dev": true, "license": "MIT" }, - "node_modules/pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "license": "MIT", - "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==", + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, "license": "MIT", "engines": { - "node": ">=6" - } - }, - "node_modules/pvtsutils": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/pvtsutils/-/pvtsutils-1.3.5.tgz", - "integrity": "sha512-ARvb14YB9Nm2Xi6nBq1ZX6dAM0FsJnuk+31aUp4TrcZEdKUlSqOqsxJHUPJDNE3qiIp+iUPEIeR6Je/tgV7zsA==", - "license": "MIT", - "dependencies": { - "tslib": "^2.6.1" + "node": ">=8" } }, - "node_modules/pvutils": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/pvutils/-/pvutils-1.1.3.tgz", - "integrity": "sha512-pMpnA0qRdFp32b1sJl1wOJNxZLQ2cbQx+k6tjNtZ8CpvVhNqEPRgivZ2WOUev2YMajecdH7ctUPDvEe87nariQ==", - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } + "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, + "license": "MIT" }, - "node_modules/qlobber": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/qlobber/-/qlobber-5.0.3.tgz", - "integrity": "sha512-wW4GTZPePyh0RgOsM18oDyOUlXfurVRgoNyJfS+y7VWPyd0GYhQp5T2tycZFZjonH+hngxIfklGJhTP/ghidgQ==", + "node_modules/pathval": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "dev": true, "license": "MIT", "engines": { - "node": ">= 8" + "node": "*" } }, - "node_modules/qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "node_modules/picocolors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", + "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } + "license": "ISC" }, - "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==", + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, "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" + "node": ">=8.6" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/querystring-es3": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", - "integrity": "sha512-773xhDQnZBMFobEiztv8LIl70ch5MSF/jUQVlhwFyBILqq96anmoctVIYz+ZRp0qbCKATTn6ev02M3r7Ga5vqA==", - "dev": true, - "engines": { - "node": ">=0.4.x" + "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/querystringify": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", - "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", - "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" - } - ], - "license": "MIT" - }, - "node_modules/randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "node_modules/pkg-types": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.1.3.tgz", + "integrity": "sha512-+JrgthZG6m3ckicaOB74TwQ+tBWsFl3qVQg7mN8ulwSOElJ7gBhKzj2VkCPnZ4NlF6kEquYU+RIYNVAvzd54UA==", "dev": true, "license": "MIT", "dependencies": { - "safe-buffer": "^5.1.0" + "confbox": "^0.1.7", + "mlly": "^1.7.1", + "pathe": "^1.1.2" } }, - "node_modules/randomfill": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", - "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", + "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, "license": "MIT", - "dependencies": { - "randombytes": "^2.0.5", - "safe-buffer": "^5.1.0" + "engines": { + "node": ">= 0.4" } }, - "node_modules/range-parser": { + "node_modules/prelude-ls": { "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true, "license": "MIT", "engines": { - "node": ">= 0.6" + "node": ">= 0.8.0" } }, - "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==", + "node_modules/prettier": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", "dev": true, "license": "MIT", - "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" + "bin": { + "prettier": "bin-prettier.js" }, "engines": { - "node": ">= 0.8" + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" } }, - "node_modules/rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", - "optional": true, + "node_modules/pretty-quick": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/pretty-quick/-/pretty-quick-3.3.1.tgz", + "integrity": "sha512-3b36UXfYQ+IXXqex6mCca89jC8u0mYLqFAN5eTQKoXO6oCQYcIVYZEB/5AlBHI7JPYygReM2Vv6Vom/Gln7fBg==", + "dev": true, + "license": "MIT", "dependencies": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" + "execa": "^4.1.0", + "find-up": "^4.1.0", + "ignore": "^5.3.0", + "mri": "^1.2.0", + "picocolors": "^1.0.0", + "picomatch": "^3.0.1", + "tslib": "^2.6.2" }, "bin": { - "rc": "cli.js" + "pretty-quick": "dist/cli.js" + }, + "engines": { + "node": ">=10.13" + }, + "peerDependencies": { + "prettier": "^2.0.0" } }, - "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==", + "node_modules/pretty-quick/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, "license": "MIT", - "optional": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/read-only-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-only-stream/-/read-only-stream-2.0.0.tgz", - "integrity": "sha512-3ALe0bjBVZtkdWKIcThYpQCLbBMd/+Tbh2CDSrAIDO3UsZ4Xs+tnyjv2MjCOMMgBG+AsUOeuP1cgtY1INISc8w==", + "node_modules/pretty-quick/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, "license": "MIT", "dependencies": { - "readable-stream": "^2.0.2" + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" } }, - "node_modules/read-only-stream/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/read-only-stream/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==", + "node_modules/pretty-quick/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, "license": "MIT", "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/read-only-stream/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==", + "node_modules/pretty-quick/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, "license": "MIT", "dependencies": { - "safe-buffer": "~5.1.0" + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" } }, - "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==", + "node_modules/pretty-quick/node_modules/picomatch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-3.0.1.tgz", + "integrity": "sha512-I3EurrIQMlRc9IaAZnqRR044Phh2DXY+55o7uJ0V+hYZAcQYSuFWsc9q5PvyDHUSCe1Qxn/iBz+78s86zWnGag==", + "dev": true, "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, "engines": { - "node": ">= 6" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/readable-stream4": { - "name": "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==", + "node_modules/pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", "dev": true, "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" - }, + "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==", + "license": "MIT", "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">=6" } }, - "node_modules/readable-stream4/node_modules/buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "node_modules/querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", + "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": [ { @@ -10453,26 +4727,23 @@ "url": "https://feross.org/support" } ], - "license": "MIT", - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } + "license": "MIT" }, - "node_modules/readable-stream4/node_modules/events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", "dev": true, "license": "MIT", - "engines": { - "node": ">=0.8.x" + "dependencies": { + "safe-buffer": "^5.1.0" } }, "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, "license": "MIT", "dependencies": { "picomatch": "^2.2.1" @@ -10481,18 +4752,6 @@ "node": ">=8.10.0" } }, - "node_modules/reflect-metadata": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz", - "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==", - "license": "Apache-2.0" - }, - "node_modules/regenerator-runtime": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", - "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", - "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", @@ -10525,16 +4784,11 @@ "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==", - "license": "MIT" - }, "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, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -10559,6 +4813,7 @@ "version": "1.22.8", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "dev": true, "license": "MIT", "dependencies": { "is-core-module": "^2.13.0", @@ -10593,51 +4848,23 @@ "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==", - "license": "MIT", - "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==", - "license": "MIT" - }, - "node_modules/retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", - "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, "node_modules/reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, "license": "MIT", "engines": { "iojs": ">=1.0.0", "node": ">=0.10.0" } }, - "node_modules/rfdc": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", - "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", - "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, "license": "ISC", "dependencies": { "glob": "^7.1.3" @@ -10649,17 +4876,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/ripemd160": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", - "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", - "dev": true, - "license": "MIT", - "dependencies": { - "hash-base": "^3.0.0", - "inherits": "^2.0.1" - } - }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -10685,15 +4901,12 @@ } }, "node_modules/rxjs": { - "version": "5.5.11", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-5.5.11.tgz", - "integrity": "sha512-3bjO7UwWfA2CV7lmwYMBzj4fQ6Cq+ftHc2MvUe+WMS7wcdJ1LosDWmdjPQanYp2dBRj572p7PeU81JUxHKOcBA==", + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", "license": "Apache-2.0", "dependencies": { - "symbol-observable": "1.0.1" - }, - "engines": { - "npm": ">=2.0.0" + "tslib": "^2.1.0" } }, "node_modules/safe-array-concat": { @@ -10719,6 +4932,7 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true, "license": "MIT" }, "node_modules/safe-regex-test": { @@ -10729,191 +4943,56 @@ "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", - "resolved": "https://registry.npmjs.org/safe-regex2/-/safe-regex2-2.0.0.tgz", - "integrity": "sha512-PaUSFsUaNNuKwkBijoAPHAK6/eM6VirvyPWlZ7BAQy4D+hCvh4B6lIG+nPdhbFfIbP+gTGBcrdsOaUs0F+ZBOQ==", - "license": "MIT", - "dependencies": { - "ret": "~0.2.0" - } - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "license": "MIT" - }, - "node_modules/sax": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", - "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==", - "license": "ISC" - }, - "node_modules/semver": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", - "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/send": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", - "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", - "dev": true, - "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", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "license": "MIT", - "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==", - "dev": true, - "license": "MIT" - }, - "node_modules/send/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" - }, - "node_modules/serialize-javascript": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", - "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "randombytes": "^2.1.0" - } - }, - "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==", - "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/serialport/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "license": "MIT", - "dependencies": { - "ms": "2.1.2" + "es-errors": "^1.3.0", + "is-regex": "^1.1.4" }, "engines": { - "node": ">=6.0" + "node": ">= 0.4" }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/serve-static": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", - "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/sax": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", + "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==", + "license": "ISC" + }, + "node_modules/semver": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", "dev": true, - "license": "MIT", - "dependencies": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.18.0" + "license": "ISC", + "bin": { + "semver": "bin/semver.js" }, "engines": { - "node": ">= 0.8.0" + "node": ">=10" } }, - "node_modules/servie": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/servie/-/servie-4.3.3.tgz", - "integrity": "sha512-b0IrY3b1gVMsWvJppCf19g1p3JSnS0hQi6xu4Hi40CIhf0Lx8pQHcvBL+xunShpmOiQzg1NOia812NAWdSaShw==", - "license": "Apache-2.0", + "node_modules/serialize-javascript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "@servie/events": "^1.0.0", - "byte-length": "^1.0.2", - "ts-expect": "^1.1.0" + "randombytes": "^2.1.0" } }, - "node_modules/set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", - "license": "ISC", - "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==", + "dev": true, "license": "MIT", "dependencies": { "define-data-property": "^1.1.4", @@ -10943,43 +5022,6 @@ "node": ">= 0.4" } }, - "node_modules/set-prototype-of": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/set-prototype-of/-/set-prototype-of-1.0.0.tgz", - "integrity": "sha512-OeTRSF+prexqa0ZOjfYR2pdGG/9nyzoXhsDj9M/0R8cgK1r9SkiQiqGdQQcObmnalKVPaTLrF8P71OacYqcYGw==", - "license": "MIT" - }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", - "dev": true, - "license": "ISC" - }, - "node_modules/sha.js": { - "version": "2.4.11", - "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", - "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", - "dev": true, - "license": "(MIT AND BSD-3-Clause)", - "dependencies": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - }, - "bin": { - "sha.js": "bin.js" - } - }, - "node_modules/shasum-object": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shasum-object/-/shasum-object-1.0.0.tgz", - "integrity": "sha512-Iqo5rp/3xVi6M4YheapzZhhGPVs0yZwHj7wvwQ1B9z8H6zk+FEnI7y3Teq7qwnekfEhu8WmG2z0z4iWZaxLWVg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "fast-safe-stringify": "^2.0.7" - } - }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -11003,76 +5045,6 @@ "node": ">=8" } }, - "node_modules/shell-quote": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz", - "integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/should": { - "version": "13.2.3", - "resolved": "https://registry.npmjs.org/should/-/should-13.2.3.tgz", - "integrity": "sha512-ggLesLtu2xp+ZxI+ysJTmNjh2U0TsC+rQ/pfED9bUZZ4DKefP27D+7YJVVTvKsmjLpIi9jAa7itwDGkDDmt1GQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "should-equal": "^2.0.0", - "should-format": "^3.0.3", - "should-type": "^1.4.0", - "should-type-adaptors": "^1.0.1", - "should-util": "^1.0.0" - } - }, - "node_modules/should-equal": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/should-equal/-/should-equal-2.0.0.tgz", - "integrity": "sha512-ZP36TMrK9euEuWQYBig9W55WPC7uo37qzAEmbjHz4gfyuXrEUgF8cUvQVO+w+d3OMfPvSRQJ22lSm8MQJ43LTA==", - "dev": true, - "license": "MIT", - "dependencies": { - "should-type": "^1.4.0" - } - }, - "node_modules/should-format": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/should-format/-/should-format-3.0.3.tgz", - "integrity": "sha512-hZ58adtulAk0gKtua7QxevgUaXTTXxIi8t41L3zo9AHvjXO1/7sdLECuHeIN2SRtYXpNkmhoUP2pdeWgricQ+Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "should-type": "^1.3.0", - "should-type-adaptors": "^1.0.1" - } - }, - "node_modules/should-type": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/should-type/-/should-type-1.4.0.tgz", - "integrity": "sha512-MdAsTu3n25yDbIe1NeN69G4n6mUnJGtSJHygX3+oN0ZbO3DTiATnf7XnYJdGT42JCXurTb1JI0qOBR65shvhPQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/should-type-adaptors": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/should-type-adaptors/-/should-type-adaptors-1.1.0.tgz", - "integrity": "sha512-JA4hdoLnN+kebEp2Vs8eBe9g7uy0zbRo+RMcU0EsNy+R+k049Ki+N5tT5Jagst2g7EAja+euFuoXFCa8vIklfA==", - "dev": true, - "license": "MIT", - "dependencies": { - "should-type": "^1.3.0", - "should-util": "^1.0.0" - } - }, - "node_modules/should-util": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/should-util/-/should-util-1.0.1.tgz", - "integrity": "sha512-oXF8tfxx5cDk8r2kYqlkUJzZpDBqVY/II2WhvU0n9Y3XYvAYRmeaf1PvvIvTgPnv4KJ+ES5M0PyDq5Jp+Ygy2g==", - "dev": true, - "license": "MIT" - }, "node_modules/side-channel": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", @@ -11096,41 +5068,9 @@ "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, "license": "ISC" }, - "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==", - "devOptional": 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/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==", - "license": "MIT", - "optional": true, - "dependencies": { - "decompress-response": "^4.2.0", - "once": "^1.3.1", - "simple-concat": "^1.0.0" - } - }, "node_modules/sinon": { "version": "11.1.1", "resolved": "https://registry.npmjs.org/sinon/-/sinon-11.1.1.tgz", @@ -11161,15 +5101,6 @@ "node": ">=8" } }, - "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==", - "license": "MIT", - "engines": { - "node": ">=8.0.0" - } - }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -11191,24 +5122,6 @@ "source-map": "^0.6.0" } }, - "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==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/split2": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", - "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", - "license": "ISC", - "engines": { - "node": ">= 10.x" - } - }, "node_modules/sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", @@ -11216,13 +5129,6 @@ "dev": true, "license": "BSD-3-Clause" }, - "node_modules/ssestream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/ssestream/-/ssestream-1.1.0.tgz", - "integrity": "sha512-UOS3JTuGqGEOH89mfHFwVOJNH2+JX9ebIWuw6WBQXpkVOxbdoY3RMliSHzshL4XVYJJrcul5NkuvDFCzgYu1Lw==", - "dev": true, - "license": "MIT" - }, "node_modules/ssh2": { "version": "0.8.9", "resolved": "https://registry.npmjs.org/ssh2/-/ssh2-0.8.9.tgz", @@ -11247,228 +5153,19 @@ "node": ">=5.2.0" } }, - "node_modules/sshpk": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.18.0.tgz", - "integrity": "sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==", - "license": "MIT", - "dependencies": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - }, - "bin": { - "sshpk-conv": "bin/sshpk-conv", - "sshpk-sign": "bin/sshpk-sign", - "sshpk-verify": "bin/sshpk-verify" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/sterfive-bonjour-service": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/sterfive-bonjour-service/-/sterfive-bonjour-service-1.1.4.tgz", - "integrity": "sha512-QqDpnBb3KLD6ytdY2KSxsynw1jJAvzfOloQt83GQNXO6CGf84ZY+37tpOEZo1FzgUkFiVsL7pYyg71olDppI/w==", - "license": "MIT", - "dependencies": { - "@types/multicast-dns": "^7.2.1", - "array-flatten": "^2.1.2", - "dns-equal": "^1.0.0", - "fast-deep-equal": "^3.1.3", - "multicast-dns": "^7.2.4" - } - }, - "node_modules/sterfive-bonjour-service/node_modules/array-flatten": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", - "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==", - "license": "MIT" - }, - "node_modules/stream-browserify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-3.0.0.tgz", - "integrity": "sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==", - "dev": true, - "license": "MIT", - "dependencies": { - "inherits": "~2.0.4", - "readable-stream": "^3.5.0" - } - }, - "node_modules/stream-combiner2": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/stream-combiner2/-/stream-combiner2-1.1.1.tgz", - "integrity": "sha512-3PnJbYgS56AeWgtKF5jtJRT6uFJe56Z0Hc5Ngg/6sI6rIt8iiMBTa9cvdyFfpMQjaVHr8dusbNeFGIIonxOvKw==", - "dev": true, - "license": "MIT", - "dependencies": { - "duplexer2": "~0.1.0", - "readable-stream": "^2.0.2" - } - }, - "node_modules/stream-combiner2/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/stream-combiner2/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dev": true, - "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/stream-combiner2/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/stream-http": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-3.2.0.tgz", - "integrity": "sha512-Oq1bLqisTyK3TSCXpPbT4sdeYNdmyZJv1LxpEm2vu1ZhK89kSE5YXwZc3cWk0MagGaKriBh9mCFbVGtO+vY29A==", - "dev": true, - "license": "MIT", - "dependencies": { - "builtin-status-codes": "^3.0.0", - "inherits": "^2.0.4", - "readable-stream": "^3.6.0", - "xtend": "^4.0.2" - } - }, - "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==", - "license": "MIT" - }, - "node_modules/stream-splicer": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/stream-splicer/-/stream-splicer-2.0.1.tgz", - "integrity": "sha512-Xizh4/NPuYSyAXyT7g8IvdJ9HJpxIGL9PjyhtywCZvvP0OPIdqyrr4dMikeuvY8xahpdKEBlBTySe583totajg==", - "dev": true, - "license": "MIT", - "dependencies": { - "inherits": "^2.0.1", - "readable-stream": "^2.0.2" - } - }, - "node_modules/stream-splicer/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/stream-splicer/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dev": true, - "license": "MIT", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/stream-splicer/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, "node_modules/streamsearch": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz", "integrity": "sha512-jos8u++JKm0ARcSUTAZXOVC0mSox7Bhn6sBgty73P1f3JGf7yG2clTbBNHUdde/kdvP2FESam+vM6l8jBrNxHA==", "engines": { - "node": ">=0.8.0" - } - }, - "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==", - "license": "MIT", - "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==", - "license": "MIT", - "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": ">=0.8.0" + } }, "node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", @@ -11535,6 +5232,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" @@ -11576,20 +5274,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/subarg": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/subarg/-/subarg-1.0.0.tgz", - "integrity": "sha512-RIrIdRY0X1xojthNcVtgT9sjpOGagEUKpZdgBUi054OEPFo282yg+zE+t1Rj3+RqKq2xStL7uUHhY+AjbC4BXg==", - "dev": true, - "license": "MIT", - "dependencies": { - "minimist": "^1.1.0" - } - }, "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, "license": "MIT", "dependencies": { "has-flag": "^4.0.0" @@ -11602,6 +5291,7 @@ "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, "license": "MIT", "engines": { "node": ">= 0.4" @@ -11610,92 +5300,6 @@ "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==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/syntax-error": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/syntax-error/-/syntax-error-1.4.0.tgz", - "integrity": "sha512-YPPlu67mdnHGTup2A8ff7BC2Pjq0e0Yp/IyTFN03zWO0RcK07uLcbi7C2KpGR2FvWbaB0+bfE27a+sBKebSo7w==", - "dev": true, - "license": "MIT", - "dependencies": { - "acorn-node": "^1.2.0" - } - }, - "node_modules/tar-fs": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", - "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", - "license": "MIT", - "optional": true, - "dependencies": { - "chownr": "^1.1.1", - "mkdirp-classic": "^0.5.2", - "pump": "^3.0.0", - "tar-stream": "^2.1.4" - } - }, - "node_modules/tar-stream": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", - "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", - "license": "MIT", - "optional": true, - "dependencies": { - "bl": "^4.0.3", - "end-of-stream": "^1.4.1", - "fs-constants": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/tar-stream/node_modules/bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "license": "MIT", - "optional": true, - "dependencies": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, - "node_modules/tar-stream/node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "optional": true, - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, "node_modules/test-exclude": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", @@ -11742,101 +5346,11 @@ "dev": true, "license": "MIT" }, - "node_modules/thenify": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", - "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", - "license": "MIT", - "dependencies": { - "any-promise": "^1.0.0" - } - }, - "node_modules/through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", - "dev": true, - "license": "MIT" - }, - "node_modules/through2": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", - "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" - } - }, - "node_modules/through2/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/through2/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dev": true, - "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/through2/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "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==", - "license": "MIT" - }, - "node_modules/thunky": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", - "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", - "license": "MIT" - }, - "node_modules/timekeeper": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/timekeeper/-/timekeeper-2.3.1.tgz", - "integrity": "sha512-LeQRS7/4JcC0PgdSFnfUiStQEdiuySlCj/5SJ18D+T1n9BoY7PxKFfCwLulpHXoLUFr67HxBddQdEX47lDGx1g==", - "dev": true, - "license": "MIT" - }, - "node_modules/timers-browserify": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-1.4.2.tgz", - "integrity": "sha512-PIxwAupJZiYU4JmVZYwXp9FKsHMXb5h0ZEFyuXTAn8WLHOlcij+FEcbrvDsom1o5dr1YggEtFbECvGCW2sT53Q==", - "dev": true, - "dependencies": { - "process": "~0.11.0" - }, - "engines": { - "node": ">=0.6.0" - } - }, "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, "license": "MIT", "dependencies": { "is-number": "^7.0.0" @@ -11845,37 +5359,6 @@ "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==", - "dev": true, - "license": "MIT", - "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==", - "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", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "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", @@ -11889,12 +5372,6 @@ "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==", - "license": "MIT" - }, "node_modules/ts-node": { "version": "10.9.2", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", @@ -12074,13 +5551,6 @@ "dev": true, "license": "MIT" }, - "node_modules/tslint/node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true, - "license": "MIT" - }, "node_modules/tslint/node_modules/diff": { "version": "3.5.0", "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", @@ -12188,53 +5658,6 @@ "dev": true, "license": "0BSD" }, - "node_modules/tsyringe": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/tsyringe/-/tsyringe-4.8.0.tgz", - "integrity": "sha512-YB1FG+axdxADa3ncEtRnQCFq/M0lALGLxSZeVNbTU8NqhOVc51nnv2CISTcvc1kyv6EGPtXVr0v6lWeDxiijOA==", - "license": "MIT", - "dependencies": { - "tslib": "^1.9.3" - }, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/tsyringe/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "license": "0BSD" - }, - "node_modules/tty-browserify": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.1.tgz", - "integrity": "sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw==", - "dev": true, - "license": "MIT" - }, - "node_modules/tunnel": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", - "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==", - "license": "MIT", - "engines": { - "node": ">=0.6.11 <=0.7.0 || >=0.7.3" - } - }, - "node_modules/tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", - "license": "Apache-2.0", - "optional": true, - "dependencies": { - "safe-buffer": "^5.0.1" - }, - "engines": { - "node": "*" - } - }, "node_modules/tweetnacl": { "version": "0.14.5", "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", @@ -12277,20 +5700,6 @@ "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==", - "dev": true, - "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", "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz", @@ -12368,17 +5777,11 @@ "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==", - "license": "MIT" - }, "node_modules/typescript": { "version": "4.7.4", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.4.tgz", "integrity": "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==", - "devOptional": true, + "dev": true, "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", @@ -12427,16 +5830,6 @@ "dev": true, "license": "MIT" }, - "node_modules/umd": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/umd/-/umd-3.0.3.tgz", - "integrity": "sha512-4IcGSufhFshvLNcMCV80UnQVlZ5pMOC8mvNPForqwA4+lzYQuetTESLDQkeLmihq8bRcnpbQa48Wb8Lh16/xow==", - "dev": true, - "license": "MIT", - "bin": { - "umd": "bin/cli.js" - } - }, "node_modules/unbox-primitive": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", @@ -12453,42 +5846,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/undeclared-identifiers": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/undeclared-identifiers/-/undeclared-identifiers-1.1.3.tgz", - "integrity": "sha512-pJOW4nxjlmfwKApE4zvxLScM/njmwj/DiUBv7EabwE4O8kRUy+HIwxQtZLBPll/jx1LJyBcqNfB3/cpv9EZwOw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "acorn-node": "^1.3.0", - "dash-ast": "^1.0.0", - "get-assigned-identifiers": "^1.2.0", - "simple-concat": "^1.0.0", - "xtend": "^4.0.1" - }, - "bin": { - "undeclared-identifiers": "bin.js" - } - }, - "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==", - "license": "MIT", - "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==", - "dev": true, - "license": "MIT", - "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", @@ -12503,17 +5860,6 @@ "resolved": "https://registry.npmjs.org/uritemplate/-/uritemplate-0.3.4.tgz", "integrity": "sha512-enADBvHfhjrwxFMTVWeIIYz51SZ91uC6o2MR/NQTVljJB6HTZ8eQL3Q7JBj3RxNISA14MOwJaU3vpf5R6dyxHA==" }, - "node_modules/url": { - "version": "0.11.3", - "resolved": "https://registry.npmjs.org/url/-/url-0.11.3.tgz", - "integrity": "sha512-6hxOLGfZASQK/cijlZnZJTq8OXAkt/3YGfQX45vvMYXpZoo8NdWZcY73K108Jf759lS1Bv/8wXnHDTSz17dSRw==", - "dev": true, - "license": "MIT", - "dependencies": { - "punycode": "^1.4.1", - "qs": "^6.11.2" - } - }, "node_modules/url-parse": { "version": "1.5.10", "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", @@ -12530,58 +5876,6 @@ "integrity": "sha512-UaZ2+50am4HwrV2crR/JAf63Q4VvPYphe63WGeoJxeu8gmOm0qxPt+KsukfakPNrX9aymGNEkkaoICwn+OuvBw==", "license": "Apache-2.0" }, - "node_modules/url/node_modules/punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/url/node_modules/qs": { - "version": "6.12.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.12.3.tgz", - "integrity": "sha512-AWJm14H1vVaO/iNZ4/hO+HyaTehuy9nRqVdkTqlJt0HWvBiBIEXFmb4C0DGeYo3Xes9rrEW+TxHsaigCbN5ICQ==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "side-channel": "^1.0.6" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/util": { - "version": "0.12.5", - "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", - "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "is-arguments": "^1.0.4", - "is-generator-function": "^1.0.7", - "is-typed-array": "^1.1.3", - "which-typed-array": "^1.1.2" - } - }, - "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==", - "license": "MIT" - }, - "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==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4.0" - } - }, "node_modules/uuid": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/uuid/-/uuid-7.0.3.tgz", @@ -12591,17 +5885,11 @@ "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==", - "license": "MIT" - }, "node_modules/v8-compile-cache-lib": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/v8-to-istanbul": { @@ -12630,16 +5918,6 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "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==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, "node_modules/vasync": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/vasync/-/vasync-2.2.1.tgz", @@ -12655,39 +5933,15 @@ "node_modules/verror": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", - "engines": [ - "node >=0.6.0" - ], - "license": "MIT", - "dependencies": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - } - }, - "node_modules/vm-browserify": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", - "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/vm2": { - "version": "3.9.18", - "resolved": "https://registry.npmjs.org/vm2/-/vm2-3.9.18.tgz", - "integrity": "sha512-iM7PchOElv6Uv6Q+0Hq7dcgDtWWT6SizYqVcvol+1WQc+E9HlgTCnPozbQNSP3yDV9oXHQOEQu530w2q/BCVZg==", - "deprecated": "The library contains critical security issues and should not be used for production! The maintenance of the project has been discontinued. Consider migrating your code to isolated-vm.", + "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", + "engines": [ + "node >=0.6.0" + ], "license": "MIT", "dependencies": { - "acorn": "^8.7.0", - "acorn-walk": "^8.2.0" - }, - "bin": { - "vm2": "bin/vm2" - }, - "engines": { - "node": ">=6.0" + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" } }, "node_modules/web-streams-polyfill": { @@ -12699,57 +5953,6 @@ "node": ">= 8" } }, - "node_modules/webcrypto-core": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/webcrypto-core/-/webcrypto-core-1.8.0.tgz", - "integrity": "sha512-kR1UQNH8MD42CYuLzvibfakG5Ew5seG85dMMoAM/1LqvckxaF6pUiidLuraIu4V+YCIFabYecUZAW0TuxAoaqw==", - "license": "MIT", - "dependencies": { - "@peculiar/asn1-schema": "^2.3.8", - "@peculiar/json-schema": "^1.1.12", - "asn1js": "^3.0.1", - "pvtsutils": "^1.3.5", - "tslib": "^2.6.2" - } - }, - "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==", - "license": "BSD-2-Clause" - }, - "node_modules/wget-improved-2": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/wget-improved-2/-/wget-improved-2-3.3.0.tgz", - "integrity": "sha512-NSPde/8mUqgmznPhO7oB5gS8IVUlR7GOlY857IaAf3PkkHbx/6FwZxUhW+GRP1GQbZDnCMF5fPieWXFng8Z43A==", - "license": "MIT", - "dependencies": { - "minimist": "1.2.6", - "tunnel": "0.0.6" - }, - "bin": { - "nwget": "bin/nwget" - }, - "engines": { - "node": ">= 0.6.18" - } - }, - "node_modules/wget-improved-2/node_modules/minimist": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", - "license": "MIT" - }, - "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==", - "license": "MIT", - "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", @@ -12787,6 +5990,7 @@ "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, "license": "MIT", "dependencies": { "available-typed-arrays": "^1.0.7", @@ -12802,16 +6006,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/wide-align": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", - "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", - "license": "ISC", - "optional": true, - "dependencies": { - "string-width": "^1.0.2 || 2 || 3 || 4" - } - }, "node_modules/word-wrap": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", @@ -12822,40 +6016,6 @@ "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==", - "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", - "resolved": "https://registry.npmjs.org/worker-timers-broker/-/worker-timers-broker-6.1.8.tgz", - "integrity": "sha512-FUCJu9jlK3A8WqLTKXM9E6kAmI/dR1vAJ8dHYLMisLNB/n3GuaFIjJ7pn16ZcD1zCOf7P6H62lWIEBi+yz/zQQ==", - "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", - "resolved": "https://registry.npmjs.org/worker-timers-worker/-/worker-timers-worker-7.0.71.tgz", - "integrity": "sha512-ks/5YKwZsto1c2vmljroppOKCivB/ma97g9y77MAAz2TBBjPPgpoOiS1qYQKIgvGTr2QYPT3XhJWIB6Rj2MVPQ==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.24.5", - "tslib": "^2.6.2" - } - }, "node_modules/workerpool": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.0.tgz", @@ -12888,6 +6048,7 @@ "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, "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", @@ -12905,38 +6066,9 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, "license": "ISC" }, - "node_modules/ws": { - "version": "7.5.10", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", - "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", - "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 - } - } - }, - "node_modules/xml-writer": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/xml-writer/-/xml-writer-1.7.0.tgz", - "integrity": "sha512-elFVMRiV5jb59fbc87zzVa0C01QLBEWP909mRuWqFqrYC5wNTH5QW4AaKMNv7d6zAsuOulkD7wnztZNLQW0Nfg==", - "license": "MIT", - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/xml2js": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.6.2.tgz", @@ -12959,30 +6091,16 @@ "node": ">=4.0" } }, - "node_modules/xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "license": "MIT", - "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, "license": "ISC", "engines": { "node": ">=10" } }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "license": "ISC" - }, "node_modules/yaml": { "version": "2.4.5", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.4.5.tgz", @@ -13041,21 +6159,11 @@ "node": ">=10" } }, - "node_modules/yauzl": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", - "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", - "license": "MIT", - "dependencies": { - "buffer-crc32": "~0.2.3", - "fd-slicer": "~1.1.0" - } - }, "node_modules/yn": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -13074,62 +6182,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "packages/binding-coap": { - "name": "@node-wot/binding-coap", - "version": "0.8.15", - "license": "EPL-2.0 OR W3C-20150513", - "dependencies": { - "@node-wot/core": "0.8.15", - "@node-wot/td-tools": "0.8.15", - "@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" - } - }, - "packages/binding-file": { - "name": "@node-wot/binding-file", - "version": "0.8.15", - "license": "EPL-2.0 OR W3C-20150513", - "dependencies": { - "@node-wot/core": "0.8.15" - }, - "devDependencies": { - "@node-wot/td-tools": "0.8.15" - } - }, - "packages/binding-http": { - "name": "@node-wot/binding-http", - "version": "0.8.15", - "license": "EPL-2.0 OR W3C-20150513", - "dependencies": { - "@node-wot/core": "0.8.15", - "@node-wot/td-tools": "0.8.15", - "@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" - }, - "devDependencies": { - "@node-oauth/express-oauth-server": "^3.0.1", - "@node-oauth/oauth2-server": "^4.3.0", - "@types/accept-language-parser": "^1.5.2", - "@types/basic-auth": "1.1.3", - "@types/express": "^4.17.3", - "@types/node-fetch": "^2.5.10", - "ssestream": "^1.1.0", - "timekeeper": "^2.2.0" - } - }, "packages/binding-mbus": { "name": "@node-wot/binding-mbus", "version": "0.8.15", @@ -13138,33 +6190,10 @@ "@node-wot/core": "0.8.15", "@node-wot/td-tools": "0.8.15", "node-mbus": "^2.2.4", + "rxjs": "^7.8.1", "wot-typescript-definitions": "0.8.0-SNAPSHOT.29" } }, - "packages/binding-modbus": { - "name": "@node-wot/binding-modbus", - "version": "0.8.15", - "license": "EPL-2.0 OR W3C-20150513", - "dependencies": { - "@node-wot/core": "0.8.15", - "@node-wot/td-tools": "0.8.15", - "modbus-serial": "^8.0.17", - "rxjs": "5.5.11", - "wot-typescript-definitions": "0.8.0-SNAPSHOT.29" - } - }, - "packages/binding-mqtt": { - "name": "@node-wot/binding-mqtt", - "version": "0.8.15", - "license": "EPL-2.0 OR W3C-20150513", - "dependencies": { - "@node-wot/core": "0.8.15", - "@node-wot/td-tools": "0.8.15", - "aedes": "^0.46.2", - "mqtt": "^5.3.2", - "rxjs": "5.5.11" - } - }, "packages/binding-netconf": { "name": "@node-wot/binding-netconf", "version": "0.8.15", @@ -13179,189 +6208,6 @@ "url-parse": "^1.5.8" } }, - "packages/binding-opcua": { - "name": "@node-wot/binding-opcua", - "version": "0.8.15", - "license": "EPL-2.0 OR W3C-20150513", - "dependencies": { - "@node-wot/core": "0.8.15", - "@node-wot/td-tools": "0.8.15", - "ajv": "^8.16.0", - "node-opcua": "2.113.0", - "node-opcua-address-space": "2.113.0", - "node-opcua-basic-types": "2.113.0", - "node-opcua-binary-stream": "2.110.0", - "node-opcua-buffer-utils": "2.110.0", - "node-opcua-client": "2.113.0", - "node-opcua-constants": "2.98.1", - "node-opcua-data-model": "2.113.0", - "node-opcua-data-value": "2.113.0", - "node-opcua-date-time": "2.113.0", - "node-opcua-debug": "2.113.0", - "node-opcua-extension-object": "2.113.0", - "node-opcua-factory": "2.113.0", - "node-opcua-json": "0.50.0", - "node-opcua-nodeid": "2.113.0", - "node-opcua-numeric-range": "2.113.0", - "node-opcua-pseudo-session": "2.113.0", - "node-opcua-pubsub-client": "0.19.1", - "node-opcua-service-browse": "2.113.0", - "node-opcua-service-translate-browse-path": "2.113.0", - "node-opcua-status-code": "2.110.0", - "node-opcua-types": "2.113.0", - "node-opcua-variant": "2.113.0", - "rxjs": "5.5.11" - }, - "devDependencies": { - "should": "^13.2.3" - } - }, - "packages/binding-opcua/node_modules/ajv": { - "version": "8.16.0", - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.4.1" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "packages/binding-opcua/node_modules/json-schema-traverse": { - "version": "1.0.0", - "license": "MIT" - }, - "packages/binding-websockets": { - "name": "@node-wot/binding-websockets", - "version": "0.8.15", - "license": "EPL-2.0 OR W3C-20150513", - "dependencies": { - "@node-wot/binding-http": "0.8.15", - "@node-wot/core": "0.8.15", - "@node-wot/td-tools": "0.8.15", - "slugify": "^1.4.5", - "ws": "^7.5.10" - } - }, - "packages/browser-bundle": { - "name": "@node-wot/browser-bundle", - "version": "0.8.15", - "license": "EPL-2.0 OR W3C-20150513", - "dependencies": { - "events": "^2.1.0", - "inherits": "^2.0.3", - "resolve": "^1.1.7" - }, - "devDependencies": { - "@node-wot/binding-http": "0.8.15", - "@node-wot/binding-websockets": "0.8.15", - "@node-wot/core": "0.8.15", - "@node-wot/td-tools": "0.8.15", - "browserify": "^17.0.0", - "readable-stream4": "npm:readable-stream@^4.0.0" - } - }, - "packages/cli": { - "name": "@node-wot/cli", - "version": "0.8.15", - "license": "EPL-2.0 OR W3C-20150513", - "dependencies": { - "@node-wot/binding-coap": "0.8.15", - "@node-wot/binding-file": "0.8.15", - "@node-wot/binding-http": "0.8.15", - "@node-wot/binding-mqtt": "0.8.15", - "@node-wot/binding-websockets": "0.8.15", - "@node-wot/core": "0.8.15", - "@node-wot/td-tools": "0.8.15", - "@types/lodash": "^4.14.199", - "ajv": "^8.11.0", - "commander": "^9.1.0", - "dotenv": "^8.6.0", - "lodash": "^4.17.21", - "vm2": "3.9.18" - }, - "bin": { - "wot-servient": "bin/index.js" - }, - "optionalDependencies": { - "ts-node": "10.9.1" - } - }, - "packages/cli/node_modules/ajv": { - "version": "8.16.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.16.0.tgz", - "integrity": "sha512-F0twR8U1ZU67JIEtekUcLkXkoO5mMMmgGD8sK/xUFzJ805jxHQl92hImFAqqXMyMYjSPOyUPAwHYhB72g5sTXw==", - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.4.1" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "packages/cli/node_modules/diff": { - "version": "4.0.2", - "license": "BSD-3-Clause", - "optional": true, - "engines": { - "node": ">=0.3.1" - } - }, - "packages/cli/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "license": "MIT" - }, - "packages/cli/node_modules/ts-node": { - "version": "10.9.1", - "license": "MIT", - "optional": 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 - } - } - }, "packages/core": { "name": "@node-wot/core", "version": "0.8.15", @@ -13374,6 +6220,7 @@ "cbor": "^8.1.0", "content-type": "^1.0.5", "debug": "^4.3.4", + "rxjs": "^7.8.1", "uritemplate": "0.3.4", "uuid": "^7.0.3", "web-streams-polyfill": "^3.0.1" @@ -13407,21 +6254,6 @@ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "license": "MIT" }, - "packages/examples": { - "name": "@node-wot/examples", - "version": "0.8.15", - "license": "EPL-2.0 OR W3C-20150513", - "dependencies": { - "@node-wot/binding-coap": "0.8.15", - "@node-wot/binding-file": "0.8.15", - "@node-wot/binding-http": "0.8.15", - "@node-wot/binding-mqtt": "0.8.15", - "@node-wot/binding-opcua": "0.8.15", - "@node-wot/core": "0.8.15", - "@node-wot/td-tools": "0.8.15", - "rxjs": "5.5.11" - } - }, "packages/td-tools": { "name": "@node-wot/td-tools", "version": "0.8.15", diff --git a/package.json b/package.json index c795cd2ed..36d969ec9 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ } }, "scripts": { - "build": "tsc -b && npm run build -w packages/browser-bundle", + "build": "tsc -b", "pretest": "npm run build", "start": "cd packages/cli && npm run start", "debug": "cd packages/cli && npm run debug", diff --git a/packages/binding-coap/.eslintrc.json b/packages/binding-coap/.eslintrc.json deleted file mode 100644 index c4237119e..000000000 --- a/packages/binding-coap/.eslintrc.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "extends": "../../.eslintrc.js" -} diff --git a/packages/binding-coap/README.md b/packages/binding-coap/README.md deleted file mode 100644 index 1562f44ec..000000000 --- a/packages/binding-coap/README.md +++ /dev/null @@ -1,99 +0,0 @@ -# CoAP Protocol Binding of node-wot - -W3C WoT Binding Template specification for CoAP can be found [here](https://w3c.github.io/wot-binding-templates/bindings/protocols/coap/index.html). - -Current Maintainer(s): [@JKRhb](https://github.com/JKRhb) - -## Protocol specifier - -The protocol prefix handled by this binding is `coap://` or `coaps://`. - -## Getting Started - -In the following examples, how to use the CoAP binding of node-wot is shown. - -### Prerequisites - -- `npm install @node-wot/core` -- `npm install @node-wot/binding-coap` - -### Client Example - -The client example tries to connect to a TestThing via CoAP and read the `string` property. -The Thing Description is located under the following CoAP URI `coap://plugfest.thingweb.io:5683/testthing`. - -`node example-client.js` - -```js -// example-client.js -const { Servient } = require("@node-wot/core"); -const { CoapClientFactory } = require("@node-wot/binding-coap"); - -// create Servient and add CoAP binding -const servient = new Servient(); -servient.addClientFactory(new CoapClientFactory()); - -servient - .start() - .then(async (WoT) => { - try { - const td = await WoT.requestThingDescription("coap://plugfest.thingweb.io:5683/testthing"); - const thing = await WoT.consume(td); - - // read property - const read1 = await thing.readProperty("string"); - console.log("string value is: ", await read1.value()); - } catch (err) { - console.error("Script error:", err); - } - }) - .catch((err) => { - console.error("Start error:", err); - }); -``` - -### Server Example - -The server example produces a thing that allows for setting a property `count`. The thing is reachable through CoAP. - -`node example-server.js` - -```js -// example-server.js -const { Servient } = require("@node-wot/core"); -const { CoapServer } = require("@node-wot/binding-coap"); - -// create Servient add HTTP binding -const servient = new Servient(); -servient.addServer(new CoapServer()); - -servient.start().then((WoT) => { - WoT.produce({ - title: "MyCounter", - properties: { - count: { - type: "integer", - }, - }, - }).then((thing) => { - console.log("Produced " + thing.getThingDescription().title); - - let count = 0; - - // set property handlers (using async-await) - thing.setPropertyReadHandler("count", async () => count); - thing.setPropertyWriteHandler("count", async (intOutput) => { - count = await intOutput.value(); - }); - - thing.expose().then(() => { - console.info(thing.getThingDescription().title + " ready"); - console.info("TD : " + JSON.stringify(thing.getThingDescription())); - }); - }); -}); -``` - -### More Details - -See diff --git a/packages/binding-coap/package.json b/packages/binding-coap/package.json deleted file mode 100644 index 48ba4ee9b..000000000 --- a/packages/binding-coap/package.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "name": "@node-wot/binding-coap", - "version": "0.8.15", - "description": "CoAP client & server protocol binding for node-wot", - "author": "Eclipse Thingweb (https://thingweb.io/)", - "license": "EPL-2.0 OR W3C-20150513", - "repository": "https://github.com/eclipse-thingweb/node-wot/tree/master/packages/binding-coap", - "publishConfig": { - "access": "public" - }, - "files": [ - "dist/" - ], - "main": "dist/coap.js", - "types": "dist/coap.d.ts", - "dependencies": { - "@node-wot/core": "0.8.15", - "@node-wot/td-tools": "0.8.15", - "@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" - }, - "scripts": { - "build": "tsc -b", - "test": "mocha --require ts-node/register --extension ts", - "lint": "eslint .", - "lint:fix": "eslint . --fix", - "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\" \"**/*.json\"" - }, - "bugs": { - "url": "https://github.com/eclipse-thingweb/node-wot/issues" - }, - "homepage": "https://github.com/eclipse-thingweb/node-wot/tree/master/packages/binding-coap#readme", - "directories": { - "test": "test" - } -} diff --git a/packages/binding-coap/src/coap-client-factory.ts b/packages/binding-coap/src/coap-client-factory.ts deleted file mode 100644 index ee1c36817..000000000 --- a/packages/binding-coap/src/coap-client-factory.ts +++ /dev/null @@ -1,51 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2018 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 - ********************************************************************************/ - -/** - * CoAP client Factory - */ - -import { ProtocolClientFactory, ProtocolClient, createLoggers } from "@node-wot/core"; -import CoapClient from "./coap-client"; -import CoapServer from "./coap-server"; - -const { debug } = createLoggers("binding-coap", "coap-client-factory"); - -export default class CoapClientFactory implements ProtocolClientFactory { - public readonly scheme: string = "coap"; - - private readonly server?: CoapServer; - - constructor(server?: CoapServer) { - this.server = server; - } - - public getClient(): ProtocolClient { - debug(`CoapClientFactory creating client for '${this.scheme}'`); - return new CoapClient(this.server); - } - - public init(): boolean { - // info(`CoapClientFactory for '${this.scheme}' initializing`); - // TODO uncomment info if something is executed here - return true; - } - - public destroy(): boolean { - // info(`CoapClientFactory for '${this.scheme}' destroyed`); - // TODO uncomment info if something is executed here - return true; - } -} diff --git a/packages/binding-coap/src/coap-client.ts b/packages/binding-coap/src/coap-client.ts deleted file mode 100644 index 9cddc9033..000000000 --- a/packages/binding-coap/src/coap-client.ts +++ /dev/null @@ -1,289 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2018 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 - ********************************************************************************/ - -/** - * CoAP client based on coap by mcollina - */ - -import * as url from "url"; -import * as net from "net"; - -import { Subscription } from "rxjs/Subscription"; - -// for Security definition -import * as TD from "@node-wot/td-tools"; - -import { ProtocolClient, Content, ContentSerdes, createLoggers } from "@node-wot/core"; -import { BlockSize, blockSizeToOptionValue, CoapForm, CoapMethodName } from "./coap"; -import CoapServer from "./coap-server"; -import { Readable } from "stream"; -import { - Agent, - registerFormat, - AgentOptions, - CoapRequestParams, - IncomingMessage, - OutgoingMessage, - ObserveReadStream, -} from "coap"; - -const { debug, warn } = createLoggers("binding-coap", "coap-client"); - -export default class CoapClient implements ProtocolClient { - // FIXME coap Agent closes socket when no messages in flight -> new socket with every request - private agent: Agent; - private readonly agentOptions: AgentOptions; - - constructor(server?: CoapServer) { - // if server is passed, feed its socket into the CoAP agent for socket re-use - this.agent = new Agent(server ? { socket: server.getSocket() } : undefined); - this.agentOptions = server ? { socket: server.getSocket() } : {}; - - // WoT-specific content formats - registerFormat(ContentSerdes.JSON_LD, 2100); - } - - public toString(): string { - return "[CoapClient]"; - } - - public async readResource(form: CoapForm): Promise { - const req = await this.generateRequest(form, "GET"); - debug(`CoapClient sending ${req.statusCode} to ${form.href}`); - return new Promise((resolve, reject) => { - req.on("response", (res: ObserveReadStream) => { - debug(`CoapClient received ${res.code} from ${form.href}`); - debug(`CoapClient received Content-Format: ${res.headers["Content-Format"]}`); - - // FIXME does not work with blockwise because of node-coap - const contentType = (res.headers["Content-Format"] as string) ?? form.contentType; - - resolve(new Content(contentType, Readable.from(res.payload))); - }); - req.on("error", (err: Error) => reject(err)); - req.end(); - }); - } - - public writeResource(form: CoapForm, content: Content): Promise { - return new Promise((resolve, reject) => { - content - .toBuffer() - .then((buffer) => { - const req = this.generateRequest(form, "PUT"); - - // TODO set Content-FOrmat - - debug(`CoapClient sending ${req.statusCode} to ${form.href}`); - - req.on("response", (res: IncomingMessage) => { - debug(`CoapClient received ${res.code} from ${form.href}`); - debug(`CoapClient received headers: ${JSON.stringify(res.headers)}`); - resolve(); - }); - req.on("error", (err: Error) => reject(err)); - req.setOption("Content-Format", content.type); - req.write(buffer); - req.end(); - }) - .catch(reject); - }); - } - - public invokeResource(form: CoapForm, content?: Content): Promise { - return new Promise((resolve, reject) => { - const req = this.generateRequest(form, "POST"); - - debug(`CoapClient sending ${req.statusCode} to ${form.href}`); - - req.on("response", (res: IncomingMessage) => { - debug(`CoapClient received ${res.code} from ${form.href}`); - debug(`CoapClient received Content-Format: ${res.headers["Content-Format"]}`); - debug(`CoapClient received headers: ${JSON.stringify(res.headers)}`); - const contentType = res.headers["Content-Format"] as string; - resolve(new Content(contentType ?? "", Readable.from(res.payload))); - }); - req.on("error", (err: Error) => reject(err)); - (async () => { - if (content != null) { - const buffer = await content.toBuffer(); - req.setOption("Content-Format", content.type); - req.write(buffer); - } - req.end(); - })(); - }); - } - - public unlinkResource(form: CoapForm): Promise { - return new Promise((resolve, reject) => { - const req = this.generateRequest(form, "GET", false); - - debug(`CoapClient sending ${req.statusCode} to ${form.href}`); - - req.on("response", (res: IncomingMessage) => { - debug(`CoapClient received ${res.code} from ${form.href}`); - debug(`CoapClient received headers: ${JSON.stringify(res.headers)}`); - resolve(); - }); - req.on("error", (err: Error) => reject(err)); - req.end(); - }); - } - - public subscribeResource( - form: CoapForm, - next: (value: Content) => void, - error?: (error: Error) => void, - complete?: () => void - ): Promise { - return new Promise((resolve, reject) => { - const req = this.generateRequest(form, "GET", true); - - debug(`CoapClient sending ${req.statusCode} to ${form.href}`); - - req.on("response", (res: ObserveReadStream) => { - debug(`CoapClient received ${res.code} from ${form.href}`); - debug(`CoapClient received Content-Format: ${res.headers["Content-Format"]}`); - - // FIXME does not work with blockwise because of node-coap - const contentType = res.headers["Content-Format"] ?? form.contentType ?? ContentSerdes.DEFAULT; - - res.on("data", (data: Buffer) => { - next(new Content(`${contentType}`, Readable.from(res.payload))); - }); - - resolve( - new Subscription(() => { - res.close(); - if (complete) complete(); - }) - ); - }); - - req.on("error", (err: Error) => { - if (error) { - error(err); - } - }); - - req.end(); - }); - } - - /** - * @inheritdoc - */ - requestThingDescription(uri: string): Promise { - const options: CoapRequestParams = this.uriToOptions(uri); - const req = this.agent.request(options); - - req.setOption("Accept", "application/td+json"); - return new Promise((resolve, reject) => { - req.on("response", (res: IncomingMessage) => { - const contentType = (res.headers["Content-Format"] as string) ?? "application/td+json"; - resolve(new Content(contentType, Readable.from(res.payload))); - }); - req.on("error", (err: Error) => reject(err)); - req.end(); - }); - } - - public async start(): Promise { - // do nothing - } - - public async stop(): Promise { - this.agent.close(); - } - - public setSecurity = (metadata: Array): boolean => true; - - private uriToOptions(uri: string): CoapRequestParams { - // eslint-disable-next-line n/no-deprecated-api - const requestUri = url.parse(uri); - const agentOptions = this.agentOptions; - agentOptions.type = net.isIPv6(requestUri.hostname ?? "") ? "udp6" : "udp4"; - this.agent = new Agent(agentOptions); - - const options: CoapRequestParams = { - agent: this.agent, - hostname: requestUri.hostname ?? "", - port: requestUri.port != null ? parseInt(requestUri.port, 10) : 5683, - pathname: requestUri.pathname ?? "", - query: requestUri.query ?? "", - observe: false, - multicast: false, - confirmable: true, - }; - - // TODO auth - - return options; - } - - private setBlockOption(req: OutgoingMessage, optionName: "Block1" | "Block2", blockSize?: BlockSize): void { - if (blockSize == null) { - return; - } - - try { - const block2OptionValue = blockSizeToOptionValue(blockSize); - req.setOption(optionName, block2OptionValue); - } catch (error) { - warn(`${error}`); - } - } - - private getRequestParamsFromForm(form: CoapForm, defaultMethod: CoapMethodName, observe = false) { - // TODO: Add qblock parameters and hop limit, once implemented in node-coap - - const method = form["cov:method"] ?? defaultMethod; - debug(`CoapClient got Form "method" ${method}`); - - const contentFormat = form["cov:contentFormat"] ?? form.contentType ?? "application/json"; - debug(`"CoapClient got Form 'contentType' ${contentFormat} `); - - const accept = form["cov:accept"]; - if (accept != null) { - debug(`"CoapClient determined Form 'accept' ${accept} `); - } - - return { - ...this.uriToOptions(form.href), - method, - observe, - contentFormat, - accept, - }; - } - - private applyFormDataToRequest(form: CoapForm, req: OutgoingMessage) { - const blockwise = form["cov:blockwise"]; - if (blockwise != null) { - this.setBlockOption(req, "Block2", blockwise["cov:block2Size"]); - this.setBlockOption(req, "Block1", blockwise["cov:block1Size"]); - } - } - - private generateRequest(form: CoapForm, defaultMethod: CoapMethodName, observable = false): OutgoingMessage { - const requestParams = this.getRequestParamsFromForm(form, defaultMethod, observable); - - const req = this.agent.request(requestParams); - this.applyFormDataToRequest(form, req); - - return req; - } -} diff --git a/packages/binding-coap/src/coap-server.ts b/packages/binding-coap/src/coap-server.ts deleted file mode 100644 index 6340f9265..000000000 --- a/packages/binding-coap/src/coap-server.ts +++ /dev/null @@ -1,1011 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2018 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 - ********************************************************************************/ - -/** - * CoAP Server based on coap by mcollina - */ - -import * as TD from "@node-wot/td-tools"; -import Servient, { - ProtocolServer, - ContentSerdes, - ExposedThing, - Helpers, - ProtocolHelpers, - Content, - createLoggers, -} from "@node-wot/core"; -import { Socket } from "dgram"; -import { Server, createServer, registerFormat, IncomingMessage, OutgoingMessage } from "coap"; -import slugify from "slugify"; -import { Readable } from "stream"; -import { MdnsIntroducer } from "./mdns-introducer"; -import { PropertyElement, DataSchema, ActionElement, EventElement } from "wot-thing-description-types"; -import { CoapServerConfig } from "./coap"; -import { DataSchemaValue } from "wot-typescript-definitions"; -import { filterPropertyObserveOperations, getPropertyOpValues } from "./util"; - -const { debug, warn, info, error } = createLoggers("binding-coap", "coap-server"); - -type CoreLinkFormatParameters = Map; - -type AffordanceElement = PropertyElement | ActionElement | EventElement; - -// TODO: Move to core? -type AugmentedInteractionOptions = WoT.InteractionOptions & { formIndex: number }; - -const thingDescriptionParameters: CoreLinkFormatParameters = new Map( - Object.entries({ - rt: ["wot.thing"], - ct: [50, 432], - }) -); - -interface CoreLinkFormatResource { - urlPath: string; - parameters?: CoreLinkFormatParameters; -} - -export default class CoapServer implements ProtocolServer { - public readonly scheme: string = "coap"; - - private readonly PROPERTY_DIR = "properties"; - private readonly ACTION_DIR = "actions"; - private readonly EVENT_DIR = "events"; - - private readonly port: number; - private readonly address?: string; - - private mdnsIntroducer?: MdnsIntroducer; - - private readonly server: Server = createServer( - { reuseAddr: false }, - (req: IncomingMessage, res: OutgoingMessage) => { - this.handleRequest(req, res); - } - ); - - private readonly things: Map = new Map(); - - private readonly coreResources = new Map(); - - constructor(config?: CoapServerConfig) { - this.port = config?.port ?? 5683; - this.address = config?.address; - - // WoT-specific content formats - registerFormat(ContentSerdes.JSON_LD, 2100); - } - - public start(servient: Servient): Promise { - info(`CoapServer starting on ${this.address !== undefined ? this.address + " " : ""}port ${this.port}`); - return new Promise((resolve, reject) => { - // start promise handles all errors until successful start - this.server.once("error", (err: Error) => { - reject(err); - }); - this.server.listen(this.port, this.address, () => { - // once started, console "handles" errors - this.server.on("error", (err: Error) => { - error(`CoapServer for port ${this.port} failed: ${err.message}`); - }); - this.mdnsIntroducer = new MdnsIntroducer(this.address); - resolve(); - }); - }); - } - - private closeServer(): Promise { - return new Promise((resolve, reject) => { - // stop promise handles all errors from now on - this.server.once("error", (err: Error) => { - reject(err); - }); - this.server.close(() => { - resolve(); - }); - }); - } - - public async stop(): Promise { - info(`CoapServer stopping on port ${this.getPort()}`); - await this.closeServer(); - await this.mdnsIntroducer?.close(); - } - - /** returns socket to be re-used by CoapClients */ - public getSocket(): Socket { - // FIXME: node-coap needs an explicit getter for this - return this.server._sock as Socket; - } - - /** returns server port number and indicates that server is running when larger than -1 */ - public getPort(): number { - if (this.server._sock) { - const socket = this.server._sock as Socket; - return socket.address().port; - } else { - return -1; - } - } - - public async expose(thing: ExposedThing, tdTemplate?: WoT.ExposedThingInit): Promise { - const port = this.getPort(); - const urlPath = this.createThingUrlPath(thing); - - if (port === -1) { - warn("CoapServer is assigned an invalid port, aborting expose process."); - return; - } - - this.fillInBindingData(thing, port, urlPath); - - debug(`CoapServer on port ${port} exposes '${thing.title}' as unique '/${urlPath}'`); - - this.setUpIntroductionMethods(thing, urlPath, port); - } - - private createThingUrlPath(thing: ExposedThing) { - const urlPath = slugify(thing.title, { lower: true }); - - if (this.things.has(urlPath)) { - return Helpers.generateUniqueName(urlPath); - } - - return urlPath; - } - - private fillInBindingData(thing: ExposedThing, port: number, urlPath: string) { - const addresses = Helpers.getAddresses(); - const offeredMediaTypes = ContentSerdes.get().getOfferedMediaTypes(); - - for (const address of addresses) { - for (const offeredMediaType of offeredMediaTypes) { - const base = this.createThingBase(address, port, urlPath); - - this.fillInMetaPropertiesBindingData(thing, base, offeredMediaType); - - this.fillInPropertyBindingData(thing, base, offeredMediaType); - this.fillInActionBindingData(thing, base, offeredMediaType); - this.fillInEventBindingData(thing, base, offeredMediaType); - } - } - } - - private createThingBase(address: string, port: number, urlPath: string): string { - return `${this.scheme}://${address}:${port}/${encodeURIComponent(urlPath)}`; - } - - private fillInMetaPropertiesBindingData(thing: ExposedThing, base: string, offeredMediaType: string) { - const opValues = this.createPropertyMetaOpValues(thing); - - if (opValues.length === 0) { - return; - } - - if (thing.forms == null) { - thing.forms = []; - } - - const form = this.createAffordanceForm(base, this.PROPERTY_DIR, offeredMediaType, opValues, thing.uriVariables); - - thing.forms.push(form); - } - - private getReadableProperties(thing: ExposedThing) { - return Object.entries(thing.properties).filter(([_, value]) => value.writeOnly !== true); - } - - private createPropertyMetaOpValues(thing: ExposedThing): string[] { - const properties = Object.values(thing.properties); - const numberOfProperties = properties.length; - - if (numberOfProperties === 0) { - return []; - } - - const readableProperties = this.getReadableProperties(thing).length; - - const opValues: string[] = []; - - if (readableProperties > 0) { - opValues.push("readmultipleproperties"); - } - - if (readableProperties === numberOfProperties) { - opValues.push("readallproperties"); - } - - return opValues; - } - - private addFormToAffordance(form: TD.Form, affordance: AffordanceElement): void { - const affordanceForms = affordance.forms; - if (affordanceForms == null) { - affordance.forms = [form]; - } else { - affordanceForms.push(form); - } - } - - private fillInPropertyBindingData(thing: ExposedThing, base: string, offeredMediaType: string) { - for (const [propertyName, property] of Object.entries(thing.properties)) { - const [readWriteOpValues, observeOpValues] = getPropertyOpValues(property); - for (const formOpValues of [observeOpValues, readWriteOpValues]) { - if (formOpValues.length === 0) { - continue; - } - - let subprotocol: string | undefined; - - const observeOpValues = filterPropertyObserveOperations(formOpValues); - - if (observeOpValues.length > 0) { - subprotocol = "cov:observe"; - } - - const form = this.createAffordanceForm( - base, - this.PROPERTY_DIR, - offeredMediaType, - formOpValues, - thing.uriVariables, - propertyName, - property.uriVariables, - subprotocol - ); - - this.addFormToAffordance(form, property); - this.logHrefAssignment(form, "Property", propertyName); - } - } - } - - private fillInActionBindingData(thing: ExposedThing, base: string, offeredMediaType: string) { - for (const [actionName, action] of Object.entries(thing.actions)) { - const form = this.createAffordanceForm( - base, - this.ACTION_DIR, - offeredMediaType, - "invokeaction", - thing.uriVariables, - actionName, - action.uriVariables - ); - - this.addFormToAffordance(form, action); - this.logHrefAssignment(form, "Action", actionName); - } - } - - private fillInEventBindingData(thing: ExposedThing, base: string, offeredMediaType: string) { - for (const [eventName, event] of Object.entries(thing.events)) { - const form = this.createAffordanceForm( - base, - this.EVENT_DIR, - offeredMediaType, - ["subscribeevent", "unsubscribeevent"], - thing.uriVariables, - eventName, - event.uriVariables, - "cov:observe" - ); - - this.addFormToAffordance(form, event); - this.logHrefAssignment(form, "Event", eventName); - } - } - - private createAffordanceForm( - base: string, - affordancePathSegment: string, - offeredMediaType: string, - opValues: string | string[], - thingUriVariables: PropertyElement["uriVariables"], - affordanceName?: string, - affordanceUriVariables?: PropertyElement["uriVariables"], - subprotocol?: string - ): TD.Form { - const affordanceNamePattern = Helpers.updateInteractionNameWithUriVariablePattern( - affordanceName ?? "", - affordanceUriVariables, - thingUriVariables - ); - - let href = `${base}/${affordancePathSegment}`; - - if (affordanceNamePattern.length > 0) { - href += `/${encodeURIComponent(affordanceNamePattern)}`; - } - - const form = new TD.Form(href, offeredMediaType); - form.op = opValues; - form.subprotocol = subprotocol; - - return form; - } - - private logHrefAssignment(form: TD.Form, affordanceType: string, affordanceName: string) { - debug(`CoapServer on port ${this.port} assigns '${form.href}' to ${affordanceType} '${affordanceName}'`); - } - - private setUpIntroductionMethods(thing: ExposedThing, urlPath: string, port: number) { - this.createCoreResource(urlPath); - this.things.set(urlPath, thing); - - const parameters = { - urlPath, - port, - serviceName: "_wot._udp.local", - }; - - this.mdnsIntroducer?.registerExposedThing(thing, parameters); - } - - private createCoreResource(urlPath: string): void { - this.coreResources.set(urlPath, { urlPath, parameters: thingDescriptionParameters }); - } - - public async destroy(thingId: string): Promise { - debug(`CoapServer on port ${this.getPort()} destroying thingId '${thingId}'`); - for (const name of this.things.keys()) { - const exposedThing = this.things.get(name); - if (exposedThing?.id === thingId) { - this.things.delete(name); - this.coreResources.delete(name); - this.mdnsIntroducer?.delete(name); - - info(`CoapServer successfully destroyed '${exposedThing.title}'`); - return true; - } - } - - info(`CoapServer failed to destroy thing with thingId '${thingId}'`); - return false; - } - - private formatCoreLinkFormatResources() { - return Array.from(this.coreResources.values()) - .map((resource) => { - const formattedPath = ``; - const parameters = Array.from(resource.parameters?.entries() ?? []); - - const parameterValues = parameters.map((parameter) => { - const key = parameter[0]; - const values = parameter[1].join(" "); - return `${key}="${values}"`; - }); - - return [formattedPath, ...parameterValues].join(";"); - }) - .join(","); - } - - private handleWellKnownCore(req: IncomingMessage, res: OutgoingMessage) { - if (req.method !== "GET") { - this.sendMethodNotAllowedResponse(res); - return; - } - - const payload = this.formatCoreLinkFormatResources(); - this.sendContentResponse(res, payload, "application/link-format"); - } - - /** - * Handles a CoAP request for an ExposedThing, negotiates the TD Content-Format and sends - * a response. - * - * If a specific Content-Format for the TD is requested by a client, as indicated by - * an Accept option, it will be set for the outgoing response if it is supported. - * If no Accept option is set, the default Content-Format will be used as a fallback. - * - * If an Accept option is present but the Content-Format is not supported, the response - * will be sent with a status code `4.06` (Not Acceptable) and an error - * message as a diagnostic payload in accordance with RFC 7252, sections 5.10.4 and - * 5.5.2. - * - * @param req The incoming request. - * @param res The outgoing response. - * @param thing The ExposedThing whose TD is requested. - */ - private async handleTdRequest(req: IncomingMessage, res: OutgoingMessage, thing: ExposedThing) { - if (req.method !== "GET") { - this.sendMethodNotAllowedResponse(res); - return; - } - - const { contentType, isSupported } = this.processAcceptValue(req); - - if (!isSupported) { - this.sendResponse(res, "4.06", `Content-Format ${contentType} is not supported by this resource.`); - return; - } - - const content = ContentSerdes.get().valueToContent(thing.getThingDescription(), undefined, contentType); - const payload = await content.toBuffer(); - - debug(`Sending CoAP response for TD with Content-Format ${contentType}.`); - this.sendContentResponse(res, payload, contentType); - } - - private processAcceptValue(req: IncomingMessage): { contentType: string; isSupported: boolean } { - const accept = req.headers.Accept; - - if (typeof accept !== "string") { - debug(`Request contained no Accept option.`); - return { - contentType: ContentSerdes.TD, - isSupported: true, - }; - } - - const isSupported: boolean = ContentSerdes.get().isSupported(accept); - - if (!isSupported) { - debug(`Request contained an accept option with value ${accept} which is not supported.`); - } - - debug(`Received an available Content-Format ${accept} in Accept option.`); - return { - contentType: accept, - isSupported, - }; - } - - private async handleRequest(req: IncomingMessage, res: OutgoingMessage) { - const sourcePort = req.rsinfo.port; - const hasInvalidPortRange = sourcePort < 1 || sourcePort > 65535; - if (hasInvalidPortRange) { - // Ignore requests with an invalid source port - // See https://github.com/eclipse-thingweb/node-wot/issues/1182 - return; - } - - const origin = this.formatRequestOrigin(req); - - debug( - `CoapServer on port ${this.getPort()} received '${req.method}(${req._packet.messageId}) ${ - req.url - }' from ${origin}` - ); - res.on("finish", () => debug(`CoapServer replied with '${res.code}' to ${origin}`)); - - const contentType = this.getContentTypeFromRequest(req); - const method = req.method; - - if (!this.checkContentTypeSupportForInput(method, contentType)) { - this.sendResponse(res, "4.15", "Unsupported Media Type"); - return; - } - - const requestUri = this.processRequestUri(req); - - if (requestUri === "/") { - this.handleThingsRequest(method, res); - return; - } - - if (requestUri === "/.well-known/core") { - this.handleWellKnownCore(req, res); - return; - } - - const { thingKey, affordanceType, affordanceKey } = this.parseUriSegments(requestUri); - const thing = this.things.get(thingKey); - - if (thing == null) { - this.sendNotFoundResponse(res); - return; - } - - // TODO: Remove support for trailing slashes (or rather: trailing empty URI path segments) - if (affordanceType == null || affordanceType === "") { - await this.handleTdRequest(req, res, thing); - return; - } - - switch (affordanceType) { - case this.PROPERTY_DIR: - this.handlePropertyRequest(thing, affordanceKey, req, res, contentType); - break; - case this.ACTION_DIR: - this.handleActionRequest(thing, affordanceKey, req, res, contentType); - break; - case this.EVENT_DIR: - this.handleEventRequest(thing, affordanceKey, req, res, contentType); - break; - default: - this.sendNotFoundResponse(res); - } - } - - private processRequestUri(req: IncomingMessage) { - const uri = req.url; - - if (uri.includes("?")) { - return uri.substring(0, uri.indexOf("?")); - } - - return uri; - } - - private handleThingsRequest(method: string, res: OutgoingMessage) { - if (method !== "GET") { - this.sendMethodNotAllowedResponse(res); - return; - } - - const payload = JSON.stringify(this.getThingDescriptionPayload()); - this.sendContentResponse(res, payload, ContentSerdes.DEFAULT); - } - - private async handlePropertyRequest( - thing: ExposedThing, - affordanceKey: string, - req: IncomingMessage, - res: OutgoingMessage, - contentType: string - ) { - const property = thing.properties[affordanceKey]; - - if (property == null) { - this.handlePropertiesRequest(req, contentType, thing, res); - return; - } - - switch (req.method) { - case "GET": - if (req.headers.Observe == null) { - this.handleReadProperty(property, req, contentType, thing, res, affordanceKey); - } else { - this.handleObserveProperty(property, req, contentType, thing, res, affordanceKey); - } - break; - case "PUT": - if (property.readOnly === true) { - this.sendResponse(res, "4.00", "Property readOnly"); - return; - } - - this.handleWriteProperty(property, req, contentType, thing, res, affordanceKey); - break; - default: - this.sendMethodNotAllowedResponse(res); - } - } - - private async handlePropertiesRequest( - req: IncomingMessage, - contentType: string, - thing: ExposedThing, - res: OutgoingMessage - ) { - const forms = thing.forms; - - if (forms == null) { - this.sendNotFoundResponse(res); - return; - } - - switch (req.method) { - case "GET": - this.handleReadMultipleProperties(forms, req, contentType, thing, res); - break; - default: - this.sendMethodNotAllowedResponse(res); - break; - } - } - - private async handleReadMultipleProperties( - forms: TD.Form[], - req: IncomingMessage, - contentType: string, - thing: ExposedThing, - res: OutgoingMessage - ) { - try { - const interactionOptions = this.createInteractionOptions( - forms, - thing, - req, - contentType, - thing.uriVariables - ); - const readablePropertyKeys = this.getReadableProperties(thing).map(([key, _]) => key); - const contentMap = await thing.handleReadMultipleProperties(readablePropertyKeys, interactionOptions); - - const recordResponse: Record = {}; - for (const [key, content] of contentMap.entries()) { - const value = ContentSerdes.get().contentToValue( - { type: ContentSerdes.DEFAULT, body: await content.toBuffer() }, - {} - ); - - if (value == null) { - // TODO: How should this case be handled? - continue; - } - - recordResponse[key] = value; - } - - const content = ContentSerdes.get().valueToContent(recordResponse, undefined, contentType); - this.streamContentResponse(res, content); - } catch (err) { - const errorMessage = `${err}`; - error(`CoapServer on port ${this.getPort()} got internal error on read '${req.url}': ${errorMessage}`); - this.sendResponse(res, "5.00", errorMessage); - } - } - - private async handleReadProperty( - property: PropertyElement, - req: IncomingMessage, - contentType: string, - thing: ExposedThing, - res: OutgoingMessage, - affordanceKey: string - ) { - try { - const interactionOptions = this.createInteractionOptions( - property.forms, - thing, - req, - contentType, - property.uriVariables - ); - const content = await thing.handleReadProperty(affordanceKey, interactionOptions); - this.streamContentResponse(res, content); - } catch (err) { - const errorMessage = `${err}`; - error(`CoapServer on port ${this.getPort()} got internal error on read '${req.url}': ${errorMessage}`); - this.sendResponse(res, "5.00", errorMessage); - } - } - - private async handleObserveProperty( - property: PropertyElement, - req: IncomingMessage, - contentType: string, - thing: ExposedThing, - res: OutgoingMessage, - affordanceKey: string - ) { - const interactionOptions = this.createInteractionOptions( - property.forms, - thing, - req, - contentType, - property.uriVariables - ); - - const listener = this.createContentListener(req, res, this.PROPERTY_DIR, affordanceKey); - - try { - await thing.handleObserveProperty(affordanceKey, listener, interactionOptions); - } catch (error) { - warn(`${error}`); - } - - res.end(); - - res.on("finish", (err: Error) => { - error(`CoapServer on port ${this.port} failed on observe with: ${err.message}`); - thing.handleUnobserveProperty(affordanceKey, listener, interactionOptions); - }); - - setTimeout(() => thing.handleUnobserveProperty(affordanceKey, listener, interactionOptions), 60 * 60 * 1000); - } - - private createContentListener( - req: IncomingMessage, - res: OutgoingMessage, - affordanceType: string, - affordanceKey: string - ) { - return async (content: Content) => { - try { - debug( - `CoapServer on port ${this.getPort()} sends notification to ${Helpers.toUriLiteral( - req.rsinfo.address - )}:${req.rsinfo.port}` - ); - this.streamContentResponse(res, content, { end: true }); - } catch (err) { - const code = "5.00"; - - if (affordanceType === this.EVENT_DIR) { - debug(`CoapServer on port ${this.getPort()} failed '${affordanceKey}' subscription`); - this.sendResponse(res, code, "Subscription to event failed"); - } else { - const errorMessage = `${err}`; - debug( - `CoapServer on port ${this.getPort()} got internal error on observe '${ - req.url - }': ${errorMessage}` - ); - this.sendResponse(res, code, errorMessage); - } - } - }; - } - - private async handleWriteProperty( - property: PropertyElement, - req: IncomingMessage, - contentType: string, - thing: ExposedThing, - res: OutgoingMessage, - affordanceKey: string - ) { - try { - const interactionOptions = this.createInteractionOptions( - property.forms, - thing, - req, - contentType, - property.uriVariables - ); - await thing.handleWriteProperty( - affordanceKey, - new Content(contentType, Readable.from(req.payload)), - interactionOptions - ); - this.sendChangedResponse(res); - } catch (err) { - const errorMessage = `${err}`; - error(`CoapServer on port ${this.getPort()} got internal error on write '${req.url}': ${errorMessage}`); - this.sendResponse(res, "5.00", errorMessage); - } - } - - private async handleActionRequest( - thing: ExposedThing, - affordanceKey: string, - req: IncomingMessage, - res: OutgoingMessage, - contentType: string - ) { - const action = thing.actions[affordanceKey]; - - if (action == null) { - this.sendNotFoundResponse(res); - return; - } - - if (req.method !== "POST") { - this.sendMethodNotAllowedResponse(res); - return; - } - - const interactionOptions = this.createInteractionOptions( - action.forms, - thing, - req, - contentType, - action.uriVariables - ); - try { - const output = await thing.handleInvokeAction( - affordanceKey, - new Content(contentType, Readable.from(req.payload)), - interactionOptions - ); - if (output != null) { - this.streamContentResponse(res, output, { end: true }); - } else { - this.sendChangedResponse(res); - } - } catch (err) { - const errorMessage = `${err}`; - error(`CoapServer on port ${this.getPort()} got internal error on invoke '${req.url}': ${errorMessage}`); - this.sendResponse(res, "5.00", errorMessage); - } - } - - private createInteractionOptions( - forms: TD.Form[], - thing: ExposedThing, - req: IncomingMessage, - contentType: string, - affordanceUriVariables?: { [k: string]: DataSchema } - ) { - const options: AugmentedInteractionOptions = { - formIndex: ProtocolHelpers.findRequestMatchingFormIndex(forms, this.scheme, req.url, contentType), - }; - const uriVariables = Helpers.parseUrlParameters(req.url, thing.uriVariables, affordanceUriVariables); - if (!this.isEmpty(uriVariables)) { - options.uriVariables = uriVariables; - } - - return options; - } - - private async handleEventRequest( - thing: ExposedThing, - affordanceKey: string, - req: IncomingMessage, - res: OutgoingMessage, - contentType: string - ) { - const event = thing.events[affordanceKey]; - - if (event == null) { - this.sendNotFoundResponse(res); - return; - } - - if (req.method !== "GET") { - this.sendMethodNotAllowedResponse(res); - return; - } - - const observe = req.headers.Observe as number; - - if (observe == null) { - debug( - `CoapServer on port ${this.getPort()} rejects '${affordanceKey}' event subscription from ${Helpers.toUriLiteral( - req.rsinfo.address - )}:${req.rsinfo.port}` - ); - this.sendResponse(res, "4.00", "No Observe Option"); - return; - } - - if (observe === 0) { - this.avoidDuplicatedObserveRegistration(res); // TODO: Get rid of this workaround - - const interactionOptions = this.createInteractionOptions( - event.forms, - thing, - req, - contentType, - event.uriVariables - ); - - const listener = this.createContentListener(req, res, this.EVENT_DIR, affordanceKey); - - try { - await thing.handleSubscribeEvent(affordanceKey, listener, interactionOptions); - } catch (error) { - warn(`${error}`); - } - - res.end(); - - res.on("finish", () => { - debug( - `CoapServer on port ${this.getPort()} ends '${affordanceKey}' observation from ${Helpers.toUriLiteral( - req.rsinfo.address - )}:${req.rsinfo.port}` - ); - thing.handleUnsubscribeEvent(affordanceKey, listener, interactionOptions); - }); - } else if (observe > 0) { - debug( - `CoapServer on port ${this.getPort()} sends '${affordanceKey}' response to ${Helpers.toUriLiteral( - req.rsinfo.address - )}:${req.rsinfo.port}` - ); - // TODO: Check if this has been fixed in the meantime - // node-coap does not support GET cancellation - this.sendResponse(res, "5.01", "node-coap issue: no GET cancellation, send RST"); - } - } - - /** - * Work-around to avoid duplicate requests in the case of observe - * registration (resend due to no response) - * - * (node-coap does not deduplicate when Observe is set) - * - * @param res The response that is being prepared for the observe registration request - */ - private avoidDuplicatedObserveRegistration(res: OutgoingMessage) { - const packet = res._packet; - packet.code = "0.00"; - packet.payload = Buffer.from(""); - packet.reset = false; - packet.ack = true; - packet.token = Buffer.alloc(0); - - res._send(res, packet); - - res._packet.confirmable = res._request.confirmable; - res._packet.token = res._request.token; - } - - private getContentTypeFromRequest(req: IncomingMessage): string { - const contentType = req.headers["Content-Format"] as string; - - if (contentType == null) { - warn( - `CoapServer on port ${this.getPort()} received no Content-Format from ${Helpers.toUriLiteral( - req.rsinfo.address - )}:${req.rsinfo.port}` - ); - } - - return contentType ?? ContentSerdes.DEFAULT; - } - - private checkContentTypeSupportForInput(method: string, contentType: string): boolean { - const methodsWithPayload: string[] = ["PUT", "POST", "FETCH", "iPATCH", "PATCH"]; - const notAMethodWithPayload = !methodsWithPayload.includes(method); - - return notAMethodWithPayload || ContentSerdes.get().isSupported(contentType); - } - - private getThingDescriptionPayload() { - return Helpers.getAddresses().flatMap((address) => - Array.from(this.things.keys()).map( - (thingKey) => - `${this.scheme}://${Helpers.toUriLiteral(address)}:${this.getPort()}/${encodeURIComponent( - thingKey - )}` - ) - ); - } - - private parseUriSegments(requestUri: string) { - const segments = decodeURI(requestUri).split("/"); - - return { - thingKey: segments[1], - affordanceType: segments[2], - affordanceKey: segments[3], - }; - } - - private sendContentResponse(res: OutgoingMessage, payload: Buffer | string, contentType: string) { - res.setOption("Content-Format", contentType); - this.sendResponse(res, "2.05", payload); - } - - private sendChangedResponse(res: OutgoingMessage, payload?: Buffer | string) { - this.sendResponse(res, "2.04", payload); - } - - // TODO: The name of this method might not be ideal yet. - private streamContentResponse(res: OutgoingMessage, content: Content, options?: { end?: boolean | undefined }) { - res.setOption("Content-Format", content.type); - res.code = "2.05"; - content.body.pipe(res, options); - } - - private sendNotFoundResponse(res: OutgoingMessage) { - this.sendResponse(res, "4.04", "Not Found"); - } - - private sendMethodNotAllowedResponse(res: OutgoingMessage) { - this.sendResponse(res, "4.05", "Method Not Allowed"); - } - - private sendResponse(res: OutgoingMessage, responseCode: string, payload?: string | Buffer) { - res.code = responseCode; - res.end(payload); - } - - private formatRequestOrigin(req: IncomingMessage) { - const originAddress = req.rsinfo.address; - const originPort = req.rsinfo.port; - return `${Helpers.toUriLiteral(originAddress)}:${originPort}`; - } - - private isEmpty(object: Record) { - return Object.keys(object).length === 0; - } -} diff --git a/packages/binding-coap/src/coap.ts b/packages/binding-coap/src/coap.ts deleted file mode 100644 index 7c96505db..000000000 --- a/packages/binding-coap/src/coap.ts +++ /dev/null @@ -1,114 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2018 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 { Form } from "@node-wot/td-tools"; - -export { default as CoapServer } from "./coap-server"; -export { default as CoapClientFactory } from "./coap-client-factory"; -export { default as CoapClient } from "./coap-client"; -export { default as CoapsClientFactory } from "./coaps-client-factory"; -export { default as CoapsClient } from "./coaps-client"; - -export * from "./coap-server"; -export * from "./coap-client-factory"; -export * from "./coap-client"; -export * from "./coaps-client-factory"; -export * from "./coaps-client"; - -export interface CoapServerConfig { - port?: number; - address?: string; -} - -export type CoapMethodName = "GET" | "POST" | "PUT" | "DELETE" | "FETCH" | "PATCH" | "iPATCH"; - -export type BlockSize = 16 | 32 | 64 | 128 | 256 | 512 | 1024; - -export type BlockSizeOptionValue = 0 | 1 | 2 | 3 | 4 | 5 | 6; - -export interface BlockWiseTransferParameters { - "cov:block2Size"?: BlockSize; - "cov:block1Size"?: BlockSize; -} - -export class CoapForm extends Form { - public "cov:method"?: CoapMethodName; - - public "cov:hopLimit"?: number; - - public "cov:blockwise"?: BlockWiseTransferParameters; - - public "cov:qblockwise"?: BlockWiseTransferParameters; - - public "cov:contentFormat"?: number; - - public "cov:accept"?: number; -} - -/** - * Checks if a given method name is valid against the CoAP binding templates. - * - * Valid method names are `GET`, `POST`, `PUT`, `DELETE`, `FETCH`, `PATCH`, and `iPATCH`. - * - * @param methodName The method name to check. - * @returns `true` if the given `methodName` is valid. - */ -export function isValidCoapMethod(methodName: CoapMethodName): methodName is CoapMethodName { - return ["GET", "POST", "PUT", "DELETE", "FETCH", "PATCH", "iPATCH"].includes(methodName); -} - -/** - * Checks if a given method name is supported by the current CoAP implementation. - * - * Valid method names are `GET`, `POST`, `PUT`, and `DELETE`. - * - * @param methodName The method name to check. - * @returns `true` if the given `methodName` is supported. - */ -export function isSupportedCoapMethod(methodName: CoapMethodName): methodName is CoapMethodName { - return ["GET", "POST", "PUT", "DELETE"].includes(methodName); -} - -/** - * Encodes block size values for blockwise transfer to CoAP-specific values. - * - * Maps the values 16, 32, 64, 128, 256, 512, and 1024 to 0, 1, 2, 3, 4, 5, and 6, - * respectively. - * - * Throws an `Error` if an invalid value `blockSize` is passed. - * - * @param blockSize The original block size value. - * @returns A mapped block size value usable by CoAP. - */ -export function blockSizeToOptionValue(blockSize: BlockSize): BlockSizeOptionValue { - switch (blockSize) { - case 16: - return 0; - case 32: - return 1; - case 64: - return 2; - case 128: - return 3; - case 256: - return 4; - case 512: - return 5; - case 1024: - return 6; - default: - throw Error(`Expected one of 16, 32, 64, 128, 256, 512, or 1024 as blockSize value, got ${blockSize}.`); - } -} diff --git a/packages/binding-coap/src/coaps-client-factory.ts b/packages/binding-coap/src/coaps-client-factory.ts deleted file mode 100644 index 70a700af0..000000000 --- a/packages/binding-coap/src/coaps-client-factory.ts +++ /dev/null @@ -1,44 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2018 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 - ********************************************************************************/ - -/** - * CoAPS client Factory - */ - -import { ProtocolClientFactory, ProtocolClient, createLoggers } from "@node-wot/core"; -import CoapsClient from "./coaps-client"; - -const { debug } = createLoggers("binding-coap", "coaps-client-factory"); - -export default class CoapsClientFactory implements ProtocolClientFactory { - public readonly scheme: string = "coaps"; - - public getClient(): ProtocolClient { - debug(`CoapsClientFactory creating client for '${this.scheme}'`); - return new CoapsClient(); - } - - public init(): boolean { - // info(`CoapsClientFactory for '${this.scheme}' initializing`); - // TODO uncomment info if something is executed here - return true; - } - - public destroy(): boolean { - // info(`CoapsClientFactory for '${this.scheme}' destroyed`); - // TODO uncomment info if something is executed here - return true; - } -} diff --git a/packages/binding-coap/src/coaps-client.ts b/packages/binding-coap/src/coaps-client.ts deleted file mode 100644 index 6379f6a9c..000000000 --- a/packages/binding-coap/src/coaps-client.ts +++ /dev/null @@ -1,255 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2018 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 - ********************************************************************************/ - -/** - * CoAPS client based on node-coap-client by AlCalzone - */ - -import * as TD from "@node-wot/td-tools"; - -import { Subscription } from "rxjs/Subscription"; - -import { ProtocolClient, Content, createLoggers, ContentSerdes } from "@node-wot/core"; -import { CoapForm, CoapMethodName, isValidCoapMethod, isSupportedCoapMethod } from "./coap"; -import { CoapClient as coaps, CoapResponse, RequestMethod, SecurityParameters } from "node-coap-client"; -import { Readable } from "stream"; - -const { debug, warn, error } = createLoggers("binding-coap", "coaps-client"); - -declare interface pskSecurityParameters { - [identity: string]: string; -} - -export default class CoapsClient implements ProtocolClient { - // FIXME coap Agent closes socket when no messages in flight -> new socket with every request - private authorization?: SecurityParameters; - - public toString(): string { - return "[CoapsClient]"; - } - - public readResource(form: CoapForm): Promise { - return new Promise((resolve, reject) => { - this.generateRequest(form, "GET") - .then((res: CoapResponse) => { - debug(`CoapsClient received ${res.code} from ${form.href}`); - - // FIXME: Add toString conversion for response Content-Format - const contentType = form.contentType ?? ContentSerdes.DEFAULT; - const body = Readable.from(res.payload ?? Buffer.alloc(0)); - - resolve(new Content(contentType, body)); - }) - .catch((err: Error) => { - reject(err); - }); - }); - } - - public writeResource(form: CoapForm, content: Content): Promise { - return new Promise((resolve, reject) => { - this.generateRequest(form, "PUT", content) - .then((res: CoapResponse) => { - debug(`CoapsClient received ${res.code} from ${form.href}`); - - resolve(); - }) - .catch((err: Error) => { - reject(err); - }); - }); - } - - public invokeResource(form: CoapForm, content?: Content): Promise { - return new Promise((resolve, reject) => { - this.generateRequest(form, "POST", content) - .then((res: CoapResponse) => { - debug(`CoapsClient received ${res.code} from ${form.href}`); - - // FIXME: Add toString conversion for response Content-Format - const contentType = form.contentType ?? ContentSerdes.DEFAULT; - const body = Readable.from(res.payload ?? Buffer.alloc(0)); - - resolve(new Content(contentType, body)); - }) - .catch((err: Error) => { - reject(err); - }); - }); - } - - public unlinkResource(form: CoapForm): Promise { - return new Promise((resolve, reject) => { - this.generateRequest(form, "DELETE") - .then((res: CoapResponse) => { - debug(`CoapsClient received ${res.code} from ${form.href}`); - debug(`CoapsClient received headers: ${JSON.stringify(res.format)}`); - resolve(); - }) - .catch((err: Error) => { - reject(err); - }); - }); - } - - public subscribeResource( - form: CoapForm, - next: (value: Content) => void, - error?: (error: Error) => void, - complete?: () => void - ): Promise { - return new Promise((resolve, reject) => { - const requestUri = new URL(form.href.replace(/$coaps/, "https")); - if (this.authorization != null) { - coaps.setSecurityParams(requestUri.hostname, this.authorization); - } - - const callback = (resp: CoapResponse) => { - if (resp.payload != null) { - next(new Content(form?.contentType ?? ContentSerdes.DEFAULT, Readable.from(resp.payload))); - } - }; - - coaps - .observe(form.href, "get", callback) - .then(() => { - resolve( - new Subscription(() => { - coaps.stopObserving(form.href); - complete?.(); - }) - ); - }) - .catch((err) => { - error?.(err); - reject(err); - }); - }); - } - - /** - * @inheritdoc - */ - public async requestThingDescription(uri: string): Promise { - const response = await coaps.request(uri, "get", undefined, { - // FIXME: Add accept option - // Currently not supported by node-coap-client - }); - - // TODO: Respect Content-Format in response. - // Currently not really well supported by node-coap-client - const contentType = "application/td+json"; - const payload = response.payload ?? Buffer.alloc(0); - - return new Content(contentType, Readable.from(payload)); - } - - public async start(): Promise { - // do nothing - } - - public async stop(): Promise { - // FIXME coap does not provide proper API to close Agent - } - - public setSecurity(metadata: Array, credentials?: pskSecurityParameters): boolean { - if (metadata === undefined || !Array.isArray(metadata) || metadata.length === 0) { - warn(`CoapsClient received empty security metadata`); - return false; - } - - const security: TD.SecurityScheme = metadata[0]; - - if (security.scheme === "psk" && credentials != null) { - this.authorization = { psk: {} }; - this.authorization.psk[credentials.identity] = credentials.psk; - } else if (security.scheme === "apikey") { - error(`CoapsClient cannot use Apikey: Not implemented`); - return false; - } else { - error(`CoapsClient cannot set security scheme '${security.scheme}'`); - error(`${metadata}`); - return false; - } - - // TODO: node-coap-client does not support proxy / options in general :o - /* - if (security.proxyURI) { - if (this.proxyOptions !== null) { - info(`HttpClient overriding client-side proxy with security proxyURI '${security.proxyURI}`); - } - - this.proxyOptions = this.uriToOptions(security.proxyURI); - - if (metadata.proxyauthorization == "Basic") { - this.proxyOptions.headers = {}; - this.proxyOptions.headers['Proxy-Authorization'] = "Basic " + Buffer.from(credentials.username + ":" + credentials.password).toString('base64'); - } else if (metadata.proxyauthorization == "Bearer") { - this.proxyOptions.headers = {}; - this.proxyOptions.headers['Proxy-Authorization'] = "Bearer " + credentials.token; - } - } - */ - - debug(`CoapsClient using security scheme '${security.scheme}'`); - return true; - } - - private determineRequestMethod(formMethod: CoapMethodName, defaultMethod: string) { - if (isSupportedCoapMethod(formMethod)) { - return formMethod; - } else if (isValidCoapMethod(formMethod)) { - debug(`Method ${formMethod} is not supported yet.`, `Using default method ${defaultMethod} instead.`); - } else { - debug(`Unknown method ${formMethod} found.`, `Using default method ${defaultMethod} instead.`); - } - - return defaultMethod; - } - - private async generateRequest( - form: CoapForm, - defaultMethod: CoapMethodName, - content?: Content - ): Promise { - // url only works with http* - const requestUri = new URL(form.href.replace(/$coaps/, "https")); - if (this.authorization != null) { - coaps.setSecurityParams(requestUri.hostname, this.authorization); - } - - let method; - - if (form["cov:method"] != null) { - const formMethodName = form["cov:method"]; - debug(`CoapClient got Form "methodName" ${formMethodName}`); - method = this.determineRequestMethod(formMethodName, defaultMethod); - } else { - method = defaultMethod; - } - - debug(`CoapsClient sending ${method} to ${form.href}`); - - const body = await content?.toBuffer(); - - const req = coaps.request( - form.href /* string */, - method.toLowerCase() as RequestMethod /* "get" | "post" | "put" | "delete" */, - body - ); - - return req; - } -} diff --git a/packages/binding-coap/src/mdns-introducer.ts b/packages/binding-coap/src/mdns-introducer.ts deleted file mode 100644 index 05876ede8..000000000 --- a/packages/binding-coap/src/mdns-introducer.ts +++ /dev/null @@ -1,149 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 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 makeMdns = require("multicast-dns"); -import { networkInterfaces } from "os"; -import { MulticastDNS } from "multicast-dns"; -import { Answer } from "dns-packet"; -import { ExposedThing } from "@node-wot/core"; - -interface MdnsDiscoveryParameters { - urlPath: string; - port: number; - serviceName: string; - scheme?: string; - type?: "Thing" | "Directory"; -} - -type IpAddressFamily = "IPv4" | "IPv6"; - -/** - * Implements DNS-based Service Discovery as a TD discovery mechanism - * using MDNS. - * - * @see https://www.w3.org/TR/wot-discovery/#introduction-dns-sd-sec - */ -export class MdnsIntroducer { - private readonly mdns: MulticastDNS; - - private readonly mdnsEntries: Map; - - private readonly ipAddressFamily: IpAddressFamily; - - constructor(address?: string, ipAddressFamily?: IpAddressFamily) { - this.ipAddressFamily = ipAddressFamily ?? "IPv4"; - - const type = ipAddressFamily === "IPv6" ? "udp6" : "udp4"; - - this.mdns = makeMdns({ - ip: address, - type, - }); - this.mdnsEntries = new Map(); - this.mdns.on("query", (query) => { - this.sendMdnsResponses(query); - }); - } - - private sendMdnsResponses(query: makeMdns.QueryPacket): void { - this.mdnsEntries.forEach((value) => { - const entryName = value[0].name; - const matchingQuestions = query.questions.filter((question) => question.name === entryName); - - if (matchingQuestions.length <= 0) { - return; - } - - this.mdns.respond(value); - }); - } - - private determineTarget(): string { - const interfaces = networkInterfaces(); - - for (const iface in interfaces) { - for (const entry of interfaces[iface] ?? []) { - if (entry.internal === false) { - if (entry.family === this.ipAddressFamily) { - return entry.address; - } - } - } - } - - // TODO: Is it correct to throw an error here? - throw Error("Found no suitable IP address for performing MDNS introduction."); - } - - private createTxtData(parameters: MdnsDiscoveryParameters): Array { - const txtData = [`td=${parameters.urlPath}`]; - - const type = parameters.type; - if (type != null) { - txtData.push(`type=${type}`); - } - - const scheme = parameters.scheme; - if (scheme != null) { - txtData.push(`scheme=${scheme}`); - } - - return txtData; - } - - public registerExposedThing(thing: ExposedThing, parameters: MdnsDiscoveryParameters): void { - const serviceName = parameters.serviceName; - const instanceName = `${thing.title}.${serviceName}`; - - const target = this.determineTarget(); - const txtData = this.createTxtData(parameters); - - this.mdnsEntries.set(parameters.urlPath, [ - { - name: serviceName, - type: "PTR", - data: instanceName, - }, - { - name: instanceName, - type: "SRV", - data: { - port: parameters.port, - target, - }, - }, - { - name: instanceName, - type: "TXT", - data: txtData, - }, - ]); - } - - public delete(urlPath: string): void { - this.mdnsEntries.delete(urlPath); - } - - public async close(): Promise { - return new Promise((resolve, reject) => { - this.mdns.destroy((error?: Error) => { - if (error != null) { - reject(error); - } - resolve(); - }); - }); - } -} diff --git a/packages/binding-coap/src/util.ts b/packages/binding-coap/src/util.ts deleted file mode 100644 index cf4f4529e..000000000 --- a/packages/binding-coap/src/util.ts +++ /dev/null @@ -1,81 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2018 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 { ProtocolHelpers } from "@node-wot/core"; -import { PropertyElement } from "wot-thing-description-types"; - -const observeOpFilter = ["observeproperty", "unobserveproperty"]; -const readWriteOpFilter = ["readproperty", "writeproperty"]; -const eventOpFilter = ["subscribeevent", "unsubscribeevent"]; - -function filterOpValues(opValues: string[], filterValues: string[]) { - return opValues.filter((opValue) => filterValues.includes(opValue)); -} - -/** - * Convenience function to filter out the `op` values "observeproperty" and - * "unobserveproperty" from a string array. - * - * @param opValues The `op` values to be filtered. - * @returns A filtered array that might be empty. - */ -export function filterPropertyObserveOperations(opValues: string[]) { - return filterOpValues(opValues, observeOpFilter); -} - -/** - * Convenience function to filter out the `op` values "readproperty" and - * "writeproperty" from a string array. - * - * @param opValues The `op` values to be filtered. - * @returns A filtered array that might be empty. - */ -export function filterPropertyReadWriteOperations(opValues: string[]) { - return filterOpValues(opValues, readWriteOpFilter); -} - -/** - * Convenience function to filter out the `op` values "subscribeevent" and - * "unsubscribeevent" from a string array. - * - * @param opValues The `op` values to be filtered. - * @returns A filtered array that might be empty. - */ -export function filterEventOperations(opValues: string[]) { - return filterOpValues(opValues, eventOpFilter); -} - -/** - * Function to (potentially) generate two arrays of `op` values: One with the - * values "readproperty" and "writeproperty", and one with - * "observerproperty" and "unobserveproperty". - * - * This CoAP-specific distinction is made to be able to generate - * separate forms for the observe-related operations, where the addition - * of a `subprotocol` field with a value of `cov:observe` has to be added. - * - * @param property The property for which the forms are going to be - * generated. - * @returns A tuple consisting of two op value arrays (one for read and - * write operations, one for observe-related operations). - */ -export function getPropertyOpValues(property: PropertyElement) { - const opValues = ProtocolHelpers.getPropertyOpValues(property); - - const readWriteOpValues = filterPropertyReadWriteOperations(opValues); - const observeOpValues = filterPropertyObserveOperations(opValues); - - return [readWriteOpValues, observeOpValues]; -} diff --git a/packages/binding-coap/test/coap-client-test.ts b/packages/binding-coap/test/coap-client-test.ts deleted file mode 100644 index 92a58bf3e..000000000 --- a/packages/binding-coap/test/coap-client-test.ts +++ /dev/null @@ -1,129 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2018 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 - ********************************************************************************/ - -/** - * Protocol test suite to test protocol implementations - */ - -import { suite, test, timeout } from "@testdeck/mocha"; -import { expect } from "chai"; - -import CoapServer from "../src/coap-server"; -import CoapClient from "../src/coap-client"; -import { CoapForm } from "../src/coap"; -import Servient from "@node-wot/core"; - -const port1 = 31833; -const port2 = 31834; - -/** - * Helper function for waiting during tests. - * - * @param ms The time to wait in milliseconds. - * @returns A Promise that is resolved after the given time period. - */ -function sleep(ms: number) { - return new Promise((resolve) => { - setTimeout(resolve, ms); - }); -} - -@suite("CoAP client implementation") -class CoapClientTest { - @test async "should apply form information"() { - // const testThing = Helpers.extend( - // { - // name: "Test", - // properties: { - // test: {}, - // }, - // }, - // new ExposedThing(null) - // ); - // testThing.extendInteractions(); - // await testThing.writeProperty("test", "UNSET"); - - const coapServer = new CoapServer({ port: port1 }); - - await coapServer.start(new Servient()); - expect(coapServer.getPort()).to.equal(port1); - - /* - coapServer.expose(testThing); - - let client = new CoapClient(); - - // read with POST instead of GET - await client.readResource({ - href: "coap://localhost:56833/", - "cov:method": "POST" - }); - expect(testThing.expect).to.equal("POST"); - testVector.expect = "UNSET"; - - // write with POST instead of PUT - representation = await client.writeResource({ - href: "coap://localhost:56833/", - "cov:method": "POST" - }, { contentType: ContentSerdes.DEFAULT, body: Buffer.from("test") }); - expect(testVector.expect).to.equal("POST"); - testVector.expect = "UNSET"; - - // invoke with PUT instead of POST - representation = await client.invokeResource({ - href: "coap://localhost:56833/", - "cov:method": "PUT" - }, { contentType: ContentSerdes.DEFAULT, body: Buffer.from("test") }); - expect(testVector.expect).to.equal("PUT"); - testVector.expect = "UNSET"; - - // invoke with DELETE instead of POST - representation = await client.invokeResource({ - href: "coap://localhost:56833/", - "cov:method": "DELETE" - }, { contentType: ContentSerdes.DEFAULT, body: Buffer.from("test") }); - expect(testVector.expect).to.equal("DELETE"); - testVector.expect = "UNSET"; - */ - - await coapServer.stop(); - } - - @test async "should re-use port"() { - const coapServer = new CoapServer({ port: port2, address: "localhost" }); - await coapServer.start(new Servient()); - const coapClient = new CoapClient(coapServer); - await coapClient.readResource({ - href: `coap://localhost:${port2}/`, - }); - await sleep(50); // Wait for client to send its ACK - await coapServer.stop(); - } - - @test(timeout(5000)) async "subscribe test"() { - const coapServer = new CoapServer({ port: port2, address: "localhost" }); - await coapServer.start(new Servient()); - const coapClient = new CoapClient(coapServer); - const form: CoapForm = { - href: `coap://127.0.0.1:${port2}`, - "cov:method": "GET", - }; - const subscription = await coapClient.subscribeResource(form, (value) => { - /** */ - }); - subscription.unsubscribe(); - await coapServer.stop(); - } -} diff --git a/packages/binding-coap/test/coap-server-test.ts b/packages/binding-coap/test/coap-server-test.ts deleted file mode 100644 index ec9b76743..000000000 --- a/packages/binding-coap/test/coap-server-test.ts +++ /dev/null @@ -1,725 +0,0 @@ -import Servient, { ExposedThing, Content } from "@node-wot/core"; -/******************************************************************************** - * Copyright (c) 2018 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 - ********************************************************************************/ - -/** - * Protocol test suite to test protocol implementations - */ - -import { suite, test } from "@testdeck/mocha"; -import { expect, should } from "chai"; -import { DataSchemaValue, InteractionInput, InteractionOptions, ThingDescription } from "wot-typescript-definitions"; -import * as TD from "@node-wot/td-tools"; -import CoapServer from "../src/coap-server"; -import { CoapClient } from "../src/coap"; -import { Readable } from "stream"; -import { IncomingMessage, registerFormat, request } from "coap"; -import { filterEventOperations, filterPropertyObserveOperations, filterPropertyReadWriteOperations } from "../src/util"; - -// should must be called to augment all variables -should(); - -const PORT = 31831; -@suite("CoAP server implementation") -class CoapServerTest { - @test async "should start and stop a server"() { - const coapServer = new CoapServer({ port: PORT }); - - await coapServer.start(new Servient()); - expect(coapServer.getPort()).to.eq(PORT); // from test - - await coapServer.stop(); - expect(coapServer.getPort()).to.eq(-1); // from getPort() when not listening - } - - @test async "should read property"() { - const coapServer = new CoapServer({ port: PORT }); - - await coapServer.start(new Servient()); - - const testThing = new ExposedThing(new Servient(), { - title: "Test", - properties: { - test: { - type: "string", - }, - }, - }); - - const test: DataSchemaValue = "testValue"; - testThing.setPropertyReadHandler("test", (_) => Promise.resolve(test)); - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - testThing.properties.test.forms = []; - - await coapServer.expose(testThing); - - const uri = `coap://localhost:${coapServer.getPort()}/test/`; - - const coapClient = new CoapClient(coapServer); - const resp = await coapClient.readResource(new TD.Form(uri + "properties/test")); - expect((await resp.toBuffer()).toString()).to.equal('"testValue"'); - - await coapServer.stop(); - } - - @test async "should write property"() { - const coapServer = new CoapServer({ port: PORT }); - - await coapServer.start(new Servient()); - - const testThing = new ExposedThing(new Servient(), { - title: "Test", - properties: { - test: { - type: "string", - }, - }, - }); - - let test: DataSchemaValue = "testValue"; - testThing.setPropertyReadHandler("test", (_) => Promise.resolve(test)); - testThing.setPropertyWriteHandler("test", async (value) => { - test = await value.value(); - }); - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - testThing.properties.test.forms = []; - - await coapServer.expose(testThing); - - const uri = `coap://localhost:${coapServer.getPort()}/test/`; - - const coapClient = new CoapClient(coapServer); - await coapClient.writeResource( - new TD.Form(uri + "properties/test"), - new Content("text/plain", Readable.from(Buffer.from("testValue1", "utf-8"))) - ); - const resp = await coapClient.readResource(new TD.Form(uri + "properties/test")); - const data = (await resp.toBuffer()).toString(); - expect(data).to.equal('"testValue1"'); - - await coapServer.stop(); - } - - @test async "should perform an action"() { - const coapServer = new CoapServer({ port: PORT }); - - await coapServer.start(new Servient()); - - const testThing = new ExposedThing(new Servient(), { - title: "Test", - actions: { - try: { - output: { type: "string" }, - }, - }, - }); - - testThing.setActionHandler("try", async (input: WoT.InteractionOutput) => { - return "TEST"; - }); - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - testThing.actions.try.forms = []; - - await coapServer.expose(testThing); - - const uri = `coap://localhost:${coapServer.getPort()}/test/`; - - const coapClient = new CoapClient(coapServer); - const resp = await coapClient.invokeResource( - new TD.Form(uri + "actions/try"), - new Content("text/plain", Readable.from(Buffer.from("testValue1", "utf-8"))) - ); - expect((await resp.toBuffer()).toString()).to.equal('"TEST"'); - - await coapServer.stop(); - } - - @test async "should subscribe to event"() { - const coapServer = new CoapServer({ port: PORT }); - - await coapServer.start(new Servient()); - - const testThing = new ExposedThing(new Servient(), { - title: "Test", - events: { - eventTest: { - forms: [ - { - href: "http://test", - op: "subscribeevent", - }, - ], - }, - }, - }); - - await coapServer.expose(testThing); - - const uri = `coap://localhost:${coapServer.getPort()}/test/`; - - const coapClient = new CoapClient(coapServer); - const form = new TD.Form(uri + "events/eventTest"); - const subscription = await coapClient.subscribeResource(form, (value) => { - /** */ - }); - - subscription.unsubscribe(); - - await coapServer.stop(); - } - - @test async "should cause EADDRINUSE error when already running"() { - const port = 9000; - const coapServer1 = new CoapServer({ port }); - await coapServer1.start(new Servient()); - - expect(coapServer1.getPort()).to.eq(port); - - const coapServer2 = new CoapServer({ port: coapServer1.getPort() }); - - try { - await coapServer2.start(new Servient()); - } catch (err) { - expect((err as Error).message).to.eql(`bind EADDRINUSE 0.0.0.0:${port}`); - } - - await coapServer1.stop(); - } - - @test async "should support IPv6"() { - const coapServer = new CoapServer({ port: PORT, address: "::" }); - await coapServer.start(new Servient()); - - const testThing = new ExposedThing(new Servient(), { - title: "Test", - properties: { - test: { - type: "string", - forms: [], - }, - }, - }); - - const test: DataSchemaValue = "testValue"; - testThing.setPropertyReadHandler("test", (_) => Promise.resolve(test)); - - await coapServer.expose(testThing); - - const uri = `coap://[::1]:${coapServer.getPort()}/test/`; - - const coapClient = new CoapClient(coapServer); - const resp = await coapClient.readResource(new TD.Form(uri + "properties/test")); - expect((await resp.toBuffer()).toString()).to.equal('"testValue"'); - - await coapClient.stop(); - await coapServer.stop(); - } - - @test async "should take in account global uriVariables"() { - const port = 9001; - const coapServer = new CoapServer({ port }); - - await coapServer.start(new Servient()); - - const testThing = new ExposedThing(new Servient(), { - title: "Test", - uriVariables: { - globalVarTest: { - type: "string", - enum: ["test1", "test2", "test3"], - description: "test", - }, - id: { - type: "string", - enum: ["test1", "test2", "test3"], - }, - }, - properties: { - test: { - type: "string", - uriVariables: { - id: { - type: "string", - }, - }, - }, - }, - }); - const test: DataSchemaValue = "testValue"; - testThing.setPropertyReadHandler("test", (options) => { - expect(options?.uriVariables).to.deep.equal({ id: "testId", globalVarTest: "test1" }); - return new Promise((resolve, reject) => { - resolve(test); - }); - }); - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - testThing.properties.test.forms = []; - - await coapServer.expose(testThing); - - const uri = `coap://localhost:${coapServer.getPort()}/test/`; - - const coapClient = new CoapClient(coapServer); - const resp = await coapClient.readResource(new TD.Form(uri + "properties/test?id=testId&globalVarTest=test1")); - expect((await resp.toBuffer()).toString()).to.equal('"testValue"'); - - await coapServer.stop(); - } - - @test async "should support /.well-known/core"() { - const port = 9001; - const coapServer = new CoapServer({ port }); - - await coapServer.start(new Servient()); - - const testTitles = ["Test1", "Test2"]; - - for (const title of testTitles) { - const thing = new ExposedThing(new Servient(), { - title, - }); - - await coapServer.expose(thing); - } - - const uri = `coap://localhost:${coapServer.getPort()}/.well-known/core`; - - const coapClient = new CoapClient(coapServer); - const resp = await coapClient.readResource(new TD.Form(uri)); - expect((await resp.toBuffer()).toString()).to.equal( - ';rt="wot.thing";ct="50 432",;rt="wot.thing";ct="50 432"' - ); - - await coapServer.stop(); - } - - @test async "should support TD Content-Format negotiation"() { - const port = 5683; - const coapServer = new CoapServer({ port }); - - await coapServer.start(new Servient()); - - const testThing = new ExposedThing(new Servient(), { - title: "Test", - }); - - await coapServer.expose(testThing); - - const uri = `coap://localhost:${coapServer.getPort()}/test`; - - registerFormat("application/foobar", 65000); - - const defaultContentFormat = "application/td+json"; - const unsupportedContentFormat = "application/foobar"; - const contentFormats = [ - defaultContentFormat, - "application/json", - "application/xml", - unsupportedContentFormat, - null, - ]; - - const promises = contentFormats.map( - (contentFormat) => - new Promise((resolve) => { - const req = request(uri); - - if (contentFormat != null) { - req.setHeader("Accept", contentFormat); - } - - req.on("response", async (res: IncomingMessage) => { - const requestContentFormat = res.headers["Content-Format"]; - - if (contentFormat === unsupportedContentFormat) { - expect(res.code).to.equal("4.06"); - expect(res.payload.toString()).to.equal( - `Content-Format ${unsupportedContentFormat} is not supported by this resource.` - ); - } else { - expect(requestContentFormat).to.equal(contentFormat ?? defaultContentFormat); - } - - resolve(); - }); - req.end(); - }) - ); - - await Promise.all(promises); - - await coapServer.stop(); - } - - @test async "should supply Size2 option when fetching a TD"() { - const port = 9002; - const coapServer = new CoapServer({ port }); - - await coapServer.start(new Servient()); - - const testThing = new ExposedThing(new Servient(), { - title: "Test", - description: "This is a test!".repeat(100), - }); - - await coapServer.expose(testThing); - - await new Promise((resolve) => { - const req = request({ - host: "localhost", - pathname: "test", - port: coapServer.getPort(), - }); - req.setOption("Size2", 0); - req.on("response", (res) => { - expect(res.headers.Size2).to.equal(JSON.stringify(testThing.getThingDescription()).length); - resolve(); - }); - req.end(); - }); - - await coapServer.stop(); - } - - @test async "should check uriVariables consistency"() { - const port = 9003; - const coapServer = new CoapServer({ port }); - const servient = new Servient(); - - const baseUri = `coap://localhost:${port}/test`; - - await coapServer.start(servient); - - const testThing = new ExposedThing(servient, { - title: "Test", - properties: { - test: { - type: "string", - uriVariables: { - id: { - type: "string", - }, - }, - }, - }, - actions: { - try: { - output: { type: "string" }, - uriVariables: { - step: { type: "integer" }, - }, - }, - }, - }); - - let test: DataSchemaValue; - testThing.setPropertyReadHandler("test", (options) => { - expect(options?.uriVariables).to.deep.equal({ id: "testId" }); - return new Promise((resolve, reject) => { - resolve(test); - }); - }); - testThing.setPropertyWriteHandler("test", async (value, options) => { - expect(options?.uriVariables).to.deep.equal({ id: "testId" }); - test = await value.value(); - expect(test?.valueOf()).to.deep.equal("on"); - }); - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - testThing.properties.test.forms = []; - testThing.setActionHandler("try", async (input: WoT.InteractionOutput, params?: InteractionOptions) => { - expect(params?.uriVariables).to.deep.equal({ step: 5 }); - return "TEST"; - }); - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - testThing.actions.try.forms = []; - - await coapServer.expose(testThing); - - const coapClient = new CoapClient(coapServer); - - const propertyUri = `${baseUri}/properties/test?id=testId`; - - await coapClient.writeResource(new TD.Form(propertyUri), new Content("text/plain", Readable.from("on"))); - - const response1 = await coapClient.readResource(new TD.Form(propertyUri)); - expect((await response1.toBuffer()).toString()).to.equal('"on"'); - - const response2 = await coapClient.invokeResource(new TD.Form(`${baseUri}/actions/try?step=5`)); - expect((await response2.toBuffer()).toString()).to.equal('"TEST"'); - - await coapClient.stop(); - await coapServer.stop(); - } - - @test async "should report allproperties excluding non-JSON properties"() { - const port = 5683; - const coapServer = new CoapServer({ port }); - const servient = new Servient(); - - await coapServer.start(servient); - - const tdTemplate: WoT.ExposedThingInit = { - title: "TestA", - properties: { - image: { - forms: [ - { - contentType: "image/svg+xml", - }, - ], - }, - testInteger: { - type: "integer", - }, - testBoolean: { - type: "boolean", - }, - testString: { - type: "string", - }, - testObject: { - type: "object", - }, - testArray: { - type: "array", - }, - }, - }; - const testThing = new ExposedThing(servient, tdTemplate); - - const image = "FOO"; - const integer = 123; - const boolean = true; - const string = "ABCD"; - const object = { t1: "xyz", i: 77 }; - const array = ["x", "y", "z"]; - testThing.setPropertyReadHandler("image", async (_) => image); - testThing.setPropertyReadHandler("testInteger", async (_) => integer); - testThing.setPropertyReadHandler("testBoolean", async (_) => boolean); - testThing.setPropertyReadHandler("testString", async (_) => string); - testThing.setPropertyReadHandler("testObject", async (_) => object); - testThing.setPropertyReadHandler("testArray", async (_) => array); - - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - testThing.properties.image.forms = []; - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - testThing.properties.testInteger.forms = []; - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - testThing.properties.testBoolean.forms = []; - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - testThing.properties.testString.forms = []; - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - testThing.properties.testObject.forms = []; - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - testThing.properties.testArray.forms = []; - - await coapServer.expose(testThing, tdTemplate); - - const coapClient = new CoapClient(coapServer); - - const decodeContent = async (content: Content) => JSON.parse((await content.toBuffer()).toString()); - - const baseUri = `coap://localhost:${port}/testa/properties`; - - // check values one by one first - const responseInteger = await coapClient.readResource(new TD.Form(`${baseUri}/testInteger`)); - expect(await decodeContent(responseInteger)).to.equal(integer); - const responseBoolean = await coapClient.readResource(new TD.Form(`${baseUri}/testBoolean`)); - expect(await decodeContent(responseBoolean)).to.equal(boolean); - const responseString = await coapClient.readResource(new TD.Form(`${baseUri}/testString`)); - expect(await decodeContent(responseString)).to.equal(string); - const responseObject = await coapClient.readResource(new TD.Form(`${baseUri}/testObject`)); - expect(await decodeContent(responseObject)).to.deep.equal(object); - const responseArray = await coapClient.readResource(new TD.Form(`${baseUri}/testArray`)); - expect(await decodeContent(responseArray)).to.deep.equal(array); - - // check values of readallproperties - const responseAll = await coapClient.readResource(new TD.Form(baseUri)); - expect(await decodeContent(responseAll)).to.deep.equal({ - image, - testInteger: integer, - testBoolean: boolean, - testString: string, - testObject: object, - testArray: array, - }); - - await coapServer.stop(); - await coapClient.stop(); - } - - @test async "should reject requests for undefined meta operations"() { - const coapServer = new CoapServer(); - const servient = new Servient(); - - await coapServer.start(servient); - - const testThingWithoutForms = new ExposedThing(servient, { - title: "Test", - }); - - await coapServer.expose(testThingWithoutForms); - - await new Promise((resolve) => { - const req = request({ - host: "localhost", - pathname: "test/properties", - port: coapServer.getPort(), - method: "GET", - }); - req.on("response", (res: IncomingMessage) => { - expect(res.code).to.equal("4.04"); - resolve(); - }); - req.end(); - }); - - await coapServer.stop(); - } - - @test async "should reject unsupported methods for meta operations"() { - const coapServer = new CoapServer(); - const servient = new Servient(); - - await coapServer.start(new Servient()); - - const testThingWithoutForms = new ExposedThing(servient, { - title: "Test", - properties: { - testInteger: { - type: "integer", - forms: [], - }, - }, - }); - - await coapServer.expose(testThingWithoutForms); - - await new Promise((resolve) => { - const req = request({ - host: "localhost", - pathname: "test/properties", - port: coapServer.getPort(), - method: "PUT", - }); - req.on("response", (res) => { - expect(res.code).to.equal("4.05"); - resolve(); - }); - req.end(); - }); - - await coapServer.stop(); - } - - @test async "should add the cov:observe subprotocol value to observable properties and events "() { - const coapServer = new CoapServer({ port: 5683 }); - const servient = new Servient(); - servient.addServer(coapServer); - - await coapServer.start(servient); - - const covObserveThing = new ExposedThing(servient, { - title: "Test", - properties: { - observableProperty: { - observable: true, - }, - nonObservableProperty: {}, - }, - events: { - testEvent: {}, - }, - }); - - await coapServer.expose(covObserveThing); - - await new Promise((resolve) => { - const req = request({ - host: "localhost", - pathname: "test", - port: 5683, - method: "GET", - }); - req.on("response", (res: IncomingMessage) => { - const payload = res.payload.toString(); - const td = JSON.parse(payload) as ThingDescription; - - for (const property of Object.values(td.properties!)) { - let observeOpValueFormCount = 0; - for (const form of property.forms) { - const opValues = form.op!; - expect(opValues.length).to.be.greaterThan(0); - - const observeOpValueCount = filterPropertyObserveOperations(opValues as Array).length; - const observeOpValuePresent = observeOpValueCount > 0; - - if (observeOpValuePresent) { - observeOpValueFormCount++; - expect(form.subprotocol).to.eql("cov:observe"); - } - - const readWriteOpValueCount = filterPropertyReadWriteOperations( - opValues as Array - ).length; - const readWriteOpValuePresent = readWriteOpValueCount > 0; - - // eslint-disable-next-line no-unused-expressions - expect(observeOpValuePresent && readWriteOpValuePresent).to.not.be.true; - - if (property.observable !== true) { - expect(observeOpValueCount).to.eql(0); - } - } - - if (property.observable === true) { - expect(observeOpValueFormCount).to.be.greaterThan(0); - } - } - - for (const event of Object.values(td.events!)) { - for (const form of event.forms) { - const opValues = form.op!; - expect(opValues.length > 0); - - const eventOpValueCount = filterEventOperations(opValues as Array).length; - const eventOpValueCountPresent = eventOpValueCount > 0; - - expect(eventOpValueCountPresent); - - expect(form.subprotocol === "cov:observe"); - } - } - - resolve(); - }); - req.end(); - }); - - await coapServer.stop(); - } -} diff --git a/packages/binding-coap/test/coap-types-test.ts b/packages/binding-coap/test/coap-types-test.ts deleted file mode 100644 index 378236c7a..000000000 --- a/packages/binding-coap/test/coap-types-test.ts +++ /dev/null @@ -1,35 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2022 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 - ********************************************************************************/ - -/** - * Protocol test suite for CoAP binding definitions - */ - -import { suite, test } from "@testdeck/mocha"; -import { expect } from "chai"; -import { blockSizeToOptionValue } from "../src/coap"; - -@suite("CoAP Binding Definitions") -class CoapDefinitionsTest { - @test "should map raw blocksizes to the correct option value"() { - expect(blockSizeToOptionValue(16)).to.eq(0); - expect(blockSizeToOptionValue(32)).to.eq(1); - expect(blockSizeToOptionValue(64)).to.eq(2); - expect(blockSizeToOptionValue(128)).to.eq(3); - expect(blockSizeToOptionValue(256)).to.eq(4); - expect(blockSizeToOptionValue(512)).to.eq(5); - expect(blockSizeToOptionValue(1024)).to.eq(6); - } -} diff --git a/packages/binding-coap/test/tsconfig.json b/packages/binding-coap/test/tsconfig.json deleted file mode 100644 index 786b1b616..000000000 --- a/packages/binding-coap/test/tsconfig.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "extends": "../tsconfig.json", - "compilerOptions": { - "rootDir": ".." - }, - "include": ["*.ts", "**/*.ts", "../src/**/*.ts"] -} diff --git a/packages/binding-coap/tsconfig.json b/packages/binding-coap/tsconfig.json deleted file mode 100644 index e9fa7c062..000000000 --- a/packages/binding-coap/tsconfig.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "extends": "../../tsconfig.json", - "compilerOptions": { - "outDir": "dist", - "rootDir": "src" - }, - "include": ["src/**/*"], - "references": [{ "path": "../td-tools" }, { "path": "../core" }] -} diff --git a/packages/binding-file/.eslintrc.json b/packages/binding-file/.eslintrc.json deleted file mode 100644 index c4237119e..000000000 --- a/packages/binding-file/.eslintrc.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "extends": "../../.eslintrc.js" -} diff --git a/packages/binding-file/README.md b/packages/binding-file/README.md deleted file mode 100644 index e5ffdb425..000000000 --- a/packages/binding-file/README.md +++ /dev/null @@ -1,113 +0,0 @@ -# File Binding of node-wot - -## Protocol specifier - -The protocol prefix handled by this binding is `file://`. - -## Getting Started - -In the following examples, how to use the File binding of node-wot is shown. - -### Prerequisites - -- `npm install @node-wot/core` -- `npm install @node-wot/binding-file` -- local test file `test.txt` with content - -### Example 1 - -The example tries to load an internal TestThing TD and reads the `fileContent` property, which exposes the content of the file `test.txt`. - -`node example1.js` - -```js -// example1.js -const { Servient } = require("@node-wot/core"); -const { FileClientFactory } = require("@node-wot/binding-file"); - -// create Servient and add File binding -const servient = new Servient(); -servient.addClientFactory(new FileClientFactory(null)); - -td = { - id: "urn:dev:wot:org:w3:testthing:file", - title: "TestThing", - "@type": "Thing", - security: ["nosec_sc"], - properties: { - fileContent: { - type: "string", - readOnly: true, - observable: false, - forms: [ - { - href: "file:///test.txt", - contentType: "text/plain", - op: ["readproperty"], - }, - ], - }, - }, - securityDefinitions: { - nosec_sc: { - scheme: "nosec", - }, - }, -}; - -// try to read property that exposes the content of file test.txt -try { - servient.start().then(async (WoT) => { - const thing = await WoT.consume(td); - - // read property "fileContent" and print the content - const read1 = await thing.readProperty("fileContent"); - console.log("Content of File:\n", await read1.value()); - }); -} catch (err) { - console.error("Script error:", err); -} -``` - -### Example 2 - -The example tries to load a locally stored TestThing TD and reads the `fileContent` property, which exposes the content of the file `test.txt`. - -### Prerequisites - -- local TD file `TD.jsonld` with content as in Example 1 - -`node example2.js` - -```js -// example2.js -const { Servient } = require("@node-wot/core"); -const { FileClientFactory } = require("@node-wot/binding-file"); - -// create Servient and add File binding -const servient = new Servient(); -servient.addClientFactory(new FileClientFactory(null)); - -servient - .start() - .then(async (WoT) => { - // using await for serial execution (note 'async' in then() of start()) - try { - const td = await WoT.requestThingDescription("file:///TD.jsonld"); - const thing = await WoT.consume(td); - - // read property "fileContent" and print the content - const read1 = await thing.readProperty("fileContent"); - console.log("Content of File:\n" + (await read1.value())); - } catch (err) { - console.error("Script error:", err); - } - }) - .catch((err) => { - console.error("Fetch error:", err); - }); -``` - -## More Details - -See diff --git a/packages/binding-file/package.json b/packages/binding-file/package.json deleted file mode 100644 index 885d945f8..000000000 --- a/packages/binding-file/package.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "name": "@node-wot/binding-file", - "version": "0.8.15", - "description": "File client protocol binding for node-wot", - "author": "Eclipse Thingweb (https://thingweb.io/)", - "license": "EPL-2.0 OR W3C-20150513", - "repository": "https://github.com/eclipse-thingweb/node-wot/tree/master/packages/binding-file", - "publishConfig": { - "access": "public" - }, - "files": [ - "dist/" - ], - "main": "dist/file.js", - "types": "dist/file.d.ts", - "devDependencies": { - "@node-wot/td-tools": "0.8.15" - }, - "dependencies": { - "@node-wot/core": "0.8.15" - }, - "scripts": { - "build": "tsc -b", - "test": "mocha --require ts-node/register --extension ts", - "lint": "eslint .", - "lint:fix": "eslint . --fix", - "format": "prettier --write \"src/**/*.ts\" \"**/*.json\"" - }, - "bugs": { - "url": "https://github.com/eclipse-thingweb/node-wot/issues" - }, - "homepage": "https://github.com/eclipse-thingweb/node-wot/tree/master/packages/binding-file#readme", - "directories": { - "test": "test" - } -} diff --git a/packages/binding-file/src/file-client-factory.ts b/packages/binding-file/src/file-client-factory.ts deleted file mode 100644 index 62f90ac5a..000000000 --- a/packages/binding-file/src/file-client-factory.ts +++ /dev/null @@ -1,34 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2018 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 - ********************************************************************************/ - -/** - * File protocol binding - */ -import { ProtocolClientFactory, ProtocolClient, createLoggers } from "@node-wot/core"; -import FileClient from "./file-client"; - -const { debug } = createLoggers("binding-file", "file-client-factory"); - -export default class FileClientFactory implements ProtocolClientFactory { - public readonly scheme: string = "file"; - - public getClient(): ProtocolClient { - debug(`FileClientFactory creating client for '${this.scheme}'`); - return new FileClient(); - } - - public init = (): boolean => true; - public destroy = (): boolean => true; -} diff --git a/packages/binding-file/src/file-client.ts b/packages/binding-file/src/file-client.ts deleted file mode 100644 index 47334139d..000000000 --- a/packages/binding-file/src/file-client.ts +++ /dev/null @@ -1,90 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2018 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 - ********************************************************************************/ - -/** - * File protocol binding - */ -import { Form, SecurityScheme } from "@node-wot/td-tools"; -import { ProtocolClient, Content, createLoggers, ContentSerdes } from "@node-wot/core"; -import { Subscription } from "rxjs/Subscription"; -import { promises as asyncFs } from "fs"; -import { fileURLToPath } from "node:url"; - -const { debug } = createLoggers("binding-file", "file-client"); - -export default class FileClient implements ProtocolClient { - public toString(): string { - return "[FileClient]"; - } - - private async readFromFile(uri: string, contentType: string) { - const filePath = fileURLToPath(uri); - debug(`Reading file of Content-Type ${contentType} from path ${filePath}.`); - - const fileHandle = await asyncFs.open(filePath); - const body = fileHandle.createReadStream(); - return new Content(contentType, body); - } - - public async readResource(form: Form): Promise { - const formContentType = form.contentType; - if (formContentType == null) { - debug(`Found no Content-Type for Form, defaulting to ${ContentSerdes.DEFAULT}`); - } - const contentType = formContentType ?? ContentSerdes.DEFAULT; - - return this.readFromFile(form.href, contentType); - } - - public async writeResource(form: Form, content: Content): Promise { - const filePath = fileURLToPath(form.href); - - await asyncFs.writeFile(filePath, content.body); - } - - public async invokeResource(form: Form, content: Content): Promise { - throw new Error("FileClient does not implement invoke"); - } - - public async unlinkResource(form: Form): Promise { - throw new Error("FileClient does not implement unlink"); - } - - /** - * @inheritdoc - */ - public async requestThingDescription(uri: string): Promise { - return this.readFromFile(uri, "application/td+json"); - } - - public async subscribeResource( - form: Form, - next: (value: Content) => void, - error?: (error: Error) => void, - complete?: () => void - ): Promise { - throw new Error("FileClient does not implement subscribe"); - } - - public async start(): Promise { - // do nothing - } - - public async stop(): Promise { - // do nothing - } - - public setSecurity = (metadata: Array): boolean => false; -} diff --git a/packages/binding-file/src/file.ts b/packages/binding-file/src/file.ts deleted file mode 100644 index ed1df8ad8..000000000 --- a/packages/binding-file/src/file.ts +++ /dev/null @@ -1,19 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2018 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 { default as FileClient } from "./file-client"; -export { default as FileClientFactory } from "./file-client-factory"; -export * from "./file-client"; -export * from "./file-client-factory"; diff --git a/packages/binding-file/test/.gitignore b/packages/binding-file/test/.gitignore deleted file mode 100644 index 1ba91c4c8..000000000 --- a/packages/binding-file/test/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -# Ignore auxiliary files created by the FileClient implementation tests -test*.json -test*.txt diff --git a/packages/binding-file/test/file-client-test.ts b/packages/binding-file/test/file-client-test.ts deleted file mode 100644 index 080797ed5..000000000 --- a/packages/binding-file/test/file-client-test.ts +++ /dev/null @@ -1,96 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 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 { Content, ContentSerdes } from "@node-wot/core"; - -import FileClient from "../src/file-client"; -import { Form } from "@node-wot/td-tools"; -import { expect } from "chai"; -import { promises as asyncFs } from "fs"; -import { fileURLToPath } from "node:url"; - -const jsonValue = { - foo: "bar", -}; - -function formatContentType(contentType?: string) { - if (contentType == null) { - return "no Content-Type"; - } - - return `Content-Type ${contentType}`; -} - -describe("File Client Implementation", () => { - let fileClient: FileClient; - - beforeEach(async () => { - fileClient = new FileClient(); - await fileClient.start(); - }); - - afterEach(async () => { - await fileClient.stop(); - }); - - for (const uriScheme of ["file:///", "file://"]) { - for (const [index, testData] of [ - { - value: jsonValue, - contentType: "application/json", - fileExtension: "json", - }, - { value: jsonValue, contentType: undefined, fileExtension: "json" }, - { value: "Lorem ipsum dolor sit amet.", contentType: "text/plain", fileExtension: "txt" }, - ].entries()) { - it(`should be able to write and read files using URI scheme ${uriScheme} with ${formatContentType( - testData.contentType - )}`, async () => { - const contentType = testData.contentType; - const originalValue = testData.value; - const fileName = `test${index}.${testData.fileExtension}`; - - // eslint-disable-next-line n/no-path-concat - const href = `${uriScheme}${__dirname}/${fileName}`; - const filePath = fileURLToPath(href); - - const form: Form = { - href, - contentType, - }; - - const writeContent = ContentSerdes.get().valueToContent( - originalValue, - undefined, - contentType ?? ContentSerdes.DEFAULT - ); - - await fileClient.writeResource(form, writeContent); - - const rawContent: Content = await fileClient.readResource(form); - - const readContent = { - body: await rawContent.toBuffer(), - type: writeContent.type, - }; - - const readValue = ContentSerdes.get().contentToValue(readContent, {}); - expect(readValue).to.deep.eq(originalValue); - - await asyncFs.unlink(filePath); - }); - } - } -}); diff --git a/packages/binding-file/tsconfig.json b/packages/binding-file/tsconfig.json deleted file mode 100644 index e9fa7c062..000000000 --- a/packages/binding-file/tsconfig.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "extends": "../../tsconfig.json", - "compilerOptions": { - "outDir": "dist", - "rootDir": "src" - }, - "include": ["src/**/*"], - "references": [{ "path": "../td-tools" }, { "path": "../core" }] -} diff --git a/packages/binding-http/.eslintrc.json b/packages/binding-http/.eslintrc.json deleted file mode 100644 index c4237119e..000000000 --- a/packages/binding-http/.eslintrc.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "extends": "../../.eslintrc.js" -} diff --git a/packages/binding-http/README.md b/packages/binding-http/README.md deleted file mode 100644 index 750a20168..000000000 --- a/packages/binding-http/README.md +++ /dev/null @@ -1,384 +0,0 @@ -# HTTP Protocol Binding of node-wot - -W3C WoT Binding Template specification for HTTP can be found [here](https://w3c.github.io/wot-binding-templates/bindings/protocols/http/index.html). - -Current Maintainer(s): [@relu91](https://github.com/relu91) [@danielpeintner](https://github.com/danielpeintner) - -## Protocol specifier - -The protocol prefix handled by this binding is `http://` or `https://`. - -## Getting Started - -In the following examples, how to use the HTTP binding of node-wot is shown. - -### Prerequisites - -- `npm install @node-wot/core` -- `npm install @node-wot/binding-http` - -### Client Example - -The client example tries to connect to a TestThing via HTTP and reads the `string` property. -The Thing Description is located under the following uri . - -`node example-client.js` - -```js -// example-client.js -const { Servient } = require("@node-wot/core"); -const { HttpClientFactory } = require("@node-wot/binding-http"); - -// create Servient and add HTTP binding -const servient = new Servient(); -servient.addClientFactory(new HttpClientFactory(null)); - -servient - .start() - .then(async (WoT) => { - try { - const td = await WoT.requestThingDescription("http://plugfest.thingweb.io:8083/testthing"); - const thing = await WoT.consume(td); - - // read property - const read1 = await thing.readProperty("string"); - console.log("string value is: ", await read1.value()); - } catch (err) { - console.error("Script error:", err); - } - }) - .catch((err) => { - console.error("Start error:", err); - }); -``` - -### Server Example - -The server example produces a thing that allows for setting a property `count`. The thing is reachable through HTTP. - -`node example-server.js` - -```js -// example-server.js -const { Servient } = require("@node-wot/core"); -const { HttpServer } = require("@node-wot/binding-http"); - -// create Servient add HTTP binding with port configuration -const servient = new Servient(); -servient.addServer( - new HttpServer({ - port: 8081, // (default 8080) - }) -); - -let count; - -servient.start().then((WoT) => { - WoT.produce({ - title: "MyCounter", - properties: { - count: { - type: "integer", - }, - }, - }).then((thing) => { - console.log("Produced " + thing.getThingDescription().title); - - // init property value - count = 0; - // set property handlers (using async-await) - thing.setPropertyReadHandler("count", async () => count); - thing.setPropertyWriteHandler("count", async (intOutput, options) => { - count = await intOutput.value(); - return undefined; - }); - - // expose the thing - thing.expose().then(() => { - console.info(thing.getThingDescription().title + " ready"); - console.info("TD : " + JSON.stringify(thing.getThingDescription())); - }); - }); -}); -``` - -The _secure_ server example shows how to add credentials and how to set up HTTPS. - -`node example-server-secure.js` - -```js -// example-server-secure.js -const { Servient } = require("@node-wot/core"); -const { HttpServer } = require("@node-wot/binding-http"); - -// create secure Servient with username & password credentials -const servient = new Servient(); -servient.addCredentials({ - "urn:dev:wot:org:eclipse:thingweb:my-example-secure": { - username: "node-wot", - password: "hello", - // token: "1/mZ1edKKACtPAb7zGlwSzvs72PvhAbGmB8K1ZrGxpcNM" - }, -}); -const httpConfig = { - allowSelfSigned: true, // client configuration - serverKey: "privatekey.pem", - serverCert: "certificate.pem", - security: [ - { - scheme: "basic", // (username & password) - }, - ], -}; -// add HTTPS binding with configuration -servient.addServer(new HttpServer(httpConfig)); - -let count; - -servient.start().then((WoT) => { - WoT.produce({ - title: "MyCounter", - properties: { - count: { - type: "integer", - }, - }, - }).then((thing) => { - console.log("Produced " + thing.getThingDescription().title); - - // init property value - count = 0; - // set property handlers (using async-await) - thing.setPropertyReadHandler("count", async () => count); - thing.setPropertyWriteHandler("count", async (intOutput, options) => { - count = await intOutput.value(); - return undefined; - }); - - // expose the thing - thing.expose().then(() => { - console.info(thing.getThingDescription().title + " ready"); - console.info("TD : " + JSON.stringify(thing.getThingDescription())); - }); - }); -}); -``` - -## Configuration - -The protocol binding can be configured using his constructor or trough servient config file. The `HTTPConfig` object contains a set of useful parameters: - -```ts -{ - port?: number; // TCP Port to listen on - address?: string; // IP address or hostname of local interface to bind to - proxy?: HttpProxyConfig; // proxy configuration - allowSelfSigned?: boolean; // Accept self signed certificates - serverKey?: string; // HTTPs server secret key file - serverCert?: string; // HTTPs server certificate file - security?: TD.SecurityScheme[]; // A list of possible security schemes to be used by things exposed by this servient. - baseUri?: string // A Base URI to be used in the TD in cases where the client will access a different URL than the actual machine serving the thing. [See Using BaseUri below] - urlRewrite?: Record // A record to allow for other URLs pointing to existing endpoints, e.g., { "/myroot/myUrl": "/test/properties/test" } - middleware?: MiddlewareRequestHandler; // the MiddlewareRequestHandler function. See [Adding a middleware] section below. -} -``` - -When both `serverKey` and `serverCert` are defined the server is started in `https` mode. Examples of `serverKey` and `serverCert` can be found [here](../../examples/security). Moreover, when a security schema is provided the servient must be also configured with valid credentials both client and server side. See [Security](#Security) for further details. - -### Environment Variable Overrides - -HttpServer will check the environment variables `WOT_PORT`, then `PORT`. If either are set, they will override the port the HttpServer will bind to. - -You'll see log entries indicating this: - -`[binding-http] HttpServer Port Overridden to 1337 by Environment Variable WOT_PORT` - -These are useful on Heroku, Dokku, buildpack deployment, or Docker, etc. - -> `WOT_PORT` takes higher precedence than `PORT` - -### HttpProxyConfig - -```ts -{ - href: string; // Proxy address - scheme?: "basic" | "bearer"; // Security scheme used by the proxy - token?: string; // Bearer token (valid only with scheme = "bearer") - username?: string; // Username security parameter (valid only with scheme = "basic") - password?: string; // Password security parameter (valid only with scheme = "basic") -} -``` - -## Security - -The http protocol binding supports a set of security protocols that can be enabled via servient configuration. As shown in the example section here is a configuration for a basic secure scheme: - -```js -{ - http: { - port: 8080, - allowSelfSigned: true, - serverKey: "privatekey.pem", - serverCert: "certificate.pem", - security: [{ - scheme: "basic" // (username & password) - }] - } - credentials: { - "urn:dev:wot:org:eclipse:thingweb:my-example-secure": { - username: "node-wot", - password: "hello" - } - } -} -``` - -The above configuration file, is setting up a https server with basic secure scheme. To interact with `urn:dev:wot:org:eclipse:thingweb:my-example-secure` (i.e. read a property) username and password must be provided and should be equal to _node-wot_ and _hello_. Consequently, on the client side, the same credentials should be provided: - -```js -{ - servient: { - clientOnly: true, - }, - http:{ - allowSelfSigned: true - }, - credentials: { - "urn:dev:wot:org:eclipse:thingweb:my-example-secure": { - username: "node-wot", - password: "hello" - } - } -} -``` - -### oAuth2.0 - -Currently this binding supports only oAuth2.0 `client credential` and `Resource owner credential` flows. Other flows may be implemented in future like `code` flow. Furthermore, the oAuth2.0 protocol is only implemented for the client side. - -An example of a WoT oAuth2.0 enabled client can be found [here](../examples/src/security/oauth/README.md). - -### Using baseUri - -Assume the example [WoT coffee machine](../examples/src/scripts/coffee-machine.ts) is in the W3C's office kitchen connected to the W3C's private network. It allows you to start the coffee machine before you leave home so it will be ready when you get to work. - -Inside the W3C's private network the coffee machine can found at: -
`https://internal-host:8080/smart-coffee-machine` - -From your home, it can be addressed via an Internet accessible domain name: -
`https://coffee.w3.org/things/smart-coffee-machine` - -**HttpServer Configuration** - -```js -servient.addServer( - new HttpServer({ - port: 8080, // (default 8080) - baseUri: "https://coffee.w3.org/things", - }) -); -``` - -**External Gateway Configuration** - -The DNS name`coffee.w3.org` resolves to an elastic IP on a gateway using nginx, which has this rule configured. - -``` -location /things/ { - proxy_pass https://internal-host:8080/smart-coffee-machine -} -``` - -The exposed thing on the internal server will product form URLs such as: - -```json - "actions": { - "makeDrink": { - "forms": [ - { - "href": "https://wot.w3.org/things/smart-coffee-machine/actions/makeDrink" -``` - -**baseUri vs address** - -> `baseUri` tells the producer to prefix URLs which may include hostnames, network interfaces, and URI prefixes which are not local to the machine exposing the Thing. - -> `address` tells the HttpServer a specific local network interface to bind its TCP listener. - -### Adding a middleware - -HttpServer supports the addition of **middleware** to handle the raw HTTP requests before they hit the Servient. In the middleware function, you can run some logic to filter and eventually reject HTTP requests (e.g. based on some custom headers). - -This can be done by passing a middleware function to the HttpServer constructor. - -```js -const { Servient } = require("@node-wot/core"); -const { HttpServer } = require("@node-wot/binding-http"); - -const servient = new Servient(); - -const middleware = async (req, res, next) => { - // For example, reject requests in which the X-Custom-Header header is missing - // by replying with 400 Bad Request - if (!req.headers["x-custom-header"]) { - res.statusCode = 400; - res.end("Bad Request"); - return; - } - // Pass all other requests to the WoT Servient - next(); -}; - -const httpServer = new HttpServer({ - middleware, -}); - -servient.addServer(httpServer); - -servient.start().then(async (WoT) => { - // ... -}); -``` - -## Feature matrix - -| Operation | HTTP Producer | HTTP Consumer | -| :---------------------- | :-----------: | :-----------: | -| readproperty | Y | Y | -| writeproperty | Y | Y | -| observeproperty | Y | Y | -| unobserveproperty | ? | ? | -| readallproperties | Y | Y | -| writeallproperties | Y | Y | -| readmultipleproperties | Y | Y | -| writemultipleproperties | Y | Y | -| invokeaction | Y | Y | -| subscribeevent | Y | Y | -| unsubscribeevent | ? | ? | - -| SubProtocols | HTTP Producer | HTTP Consumer | -| :----------- | :-----------: | :-----------: | -| longpoll | Y | Y | -| sse | Y | Y | -| websub | N | N | - -| Sec. Schemes | HTTP Producer | HTTP Consumer | -| :----------- | :-----------: | :-----------: | -| basic | Y | Y | -| digest | N | N | -| apikey | N | Y | -| bearer | Y | Y | -| psk | N | N | -| oauth2 | P | Y | - -**Symbols** : - -- Y implemented -- N not implement and not planned -- N.A not applicable -- ? need to be verified -- P planned - -## More Details - -see https://github.com/eclipse-thingweb/node-wot/ diff --git a/packages/binding-http/package.json b/packages/binding-http/package.json deleted file mode 100644 index 6a6a8a0f0..000000000 --- a/packages/binding-http/package.json +++ /dev/null @@ -1,55 +0,0 @@ -{ - "name": "@node-wot/binding-http", - "version": "0.8.15", - "description": "HTTP client & server protocol binding for node-wot", - "author": "Eclipse Thingweb (https://thingweb.io/)", - "license": "EPL-2.0 OR W3C-20150513", - "repository": "https://github.com/eclipse-thingweb/node-wot/tree/master/packages/binding-http", - "publishConfig": { - "access": "public" - }, - "files": [ - "dist/" - ], - "main": "dist/http.js", - "types": "dist/http.d.ts", - "browser": { - "./dist/http.js": "./dist/http-browser.js", - "./dist/http-client.js": "./dist/http-client-browser.js" - }, - "devDependencies": { - "@node-oauth/express-oauth-server": "^3.0.1", - "@node-oauth/oauth2-server": "^4.3.0", - "@types/accept-language-parser": "^1.5.2", - "@types/basic-auth": "1.1.3", - "@types/express": "^4.17.3", - "@types/node-fetch": "^2.5.10", - "ssestream": "^1.1.0", - "timekeeper": "^2.2.0" - }, - "dependencies": { - "@node-wot/core": "0.8.15", - "@node-wot/td-tools": "0.8.15", - "@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" - }, - "scripts": { - "build": "tsc -b", - "test": "mocha --require ts-node/register --extension ts", - "lint": "eslint .", - "lint:fix": "eslint . --fix", - "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\" \"**/*.json\"" - }, - "bugs": { - "url": "https://github.com/eclipse-thingweb/node-wot/issues" - }, - "homepage": "https://github.com/eclipse-thingweb/node-wot/tree/master/packages/binding-http#readme" -} diff --git a/packages/binding-http/src/codecs/tuya-codec.ts b/packages/binding-http/src/codecs/tuya-codec.ts deleted file mode 100644 index d6165cd45..000000000 --- a/packages/binding-http/src/codecs/tuya-codec.ts +++ /dev/null @@ -1,61 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2022 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 { ContentCodec } from "@node-wot/core"; -import * as TD from "@node-wot/td-tools"; -import { DataSchemaValue } from "wot-typescript-definitions"; - -interface TuyaOutput { - success?: boolean; - msg?: string; - result?: { - code?: string; - }[]; -} - -export default class HttpTuyaCodec implements ContentCodec { - getMediaType(): string { - return "application/json+tuya"; - } - - bytesToValue(bytes: Buffer, schema: TD.DataSchema, parameters: { [key: string]: string }): DataSchemaValue { - const parsedBody: TuyaOutput = JSON.parse(bytes.toString()); - const success = parsedBody.success ?? false; - - if (!success) { - throw new Error(parsedBody.msg ?? JSON.stringify(parsedBody)); - } - - for (const value of Object.values(parsedBody.result ?? {})) { - if (value.code === schema["tuya:PropertyName"]) { - return value; - } - } - throw new Error("Property not found"); - } - - valueToBytes(value: unknown, schema: TD.DataSchema, parameters?: { [key: string]: string }): Buffer { - const obj = { - commands: [ - { - code: schema["tuya:PropertyName"], - value, - }, - ], - }; - const body = JSON.stringify(obj); - return Buffer.from(body); - } -} diff --git a/packages/binding-http/src/credential.ts b/packages/binding-http/src/credential.ts deleted file mode 100644 index 540688b4b..000000000 --- a/packages/binding-http/src/credential.ts +++ /dev/null @@ -1,285 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2020 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 { Token } from "client-oauth2"; -import fetch, { Request } from "node-fetch"; -import { BasicSecurityScheme, APIKeySecurityScheme, BearerSecurityScheme } from "@node-wot/td-tools"; -import * as crypto from "crypto"; -import * as queryString from "query-string"; -import { TuyaCustomBearerSecurityScheme } from "./http"; - -export abstract class Credential { - abstract sign(request: Request): Promise; -} - -export interface BasicCredentialConfiguration { - username: string; - password: string; -} -export class BasicCredential extends Credential { - private readonly username: string; - private readonly password: string; - private readonly options?: BasicSecurityScheme; - /** - * - */ - constructor({ username, password }: BasicCredentialConfiguration, options?: BasicSecurityScheme) { - super(); - if (username === undefined || password === undefined || username === null || password === null) { - throw new Error(`No Basic credentials for Thing`); - } - - this.username = username; - this.password = password; - this.options = options; - } - - async sign(request: Request): Promise { - const result = request.clone(); - let headerName = "authorization"; - if (this.options !== undefined && this.options.in === "header" && this.options.name !== undefined) { - headerName = this.options.name; - } - result.headers.set(headerName, "Basic " + Buffer.from(this.username + ":" + this.password).toString("base64")); - return result; - } -} -export interface BearerCredentialConfiguration { - token: string; -} -export class BearerCredential extends Credential { - private readonly token: string; - private readonly options: BearerSecurityScheme; - constructor({ token }: BearerCredentialConfiguration, options: BearerSecurityScheme) { - super(); - if (token === undefined || token === null) { - throw new Error(`No Bearer credentials for Thing`); - } - - this.token = token; - this.options = options; - } - - async sign(request: Request): Promise { - const result = request.clone(); - let headerName = "authorization"; - if (this.options.in === "header" && this.options.name !== undefined) { - headerName = this.options.name; - } - result.headers.set(headerName, "Bearer " + this.token); - return result; - } -} -export interface BasicKeyCredentialConfiguration { - apiKey: string; -} -export class BasicKeyCredential extends Credential { - private readonly apiKey: string; - private readonly options: APIKeySecurityScheme; - - constructor({ apiKey }: BasicKeyCredentialConfiguration, options: APIKeySecurityScheme) { - super(); - if (apiKey === undefined || apiKey === null) { - throw new Error(`No API key credentials for Thing`); - } - - this.apiKey = apiKey; - this.options = options; - } - - async sign(request: Request): Promise { - const result = request.clone(); - - let headerName = "authorization"; - if (this.options.in === "header" && this.options.name !== undefined) { - headerName = this.options.name; - } - result.headers.append(headerName, this.apiKey); - - return result; - } -} - -export class OAuthCredential extends Credential { - private token: Token | Promise; - private readonly refresh?: () => Promise; - - /** - * - * @param tokenRequest oAuth2 token instance - * @param refresh use a custom refresh function - */ - constructor(token: Token | Promise, refresh?: () => Promise) { - super(); - this.token = token; - this.refresh = refresh; - this.token = token; - } - - async sign(request: Request): Promise { - if (this.token instanceof Promise) { - const tokenRequest = this.token as Promise; - this.token = await tokenRequest; - } - - let tempRequest = { url: request.url, headers: {} }; - - tempRequest = this.token.sign(tempRequest); - - const mergeHeaders = new Request(request, tempRequest); - - return mergeHeaders; - } - - async refreshToken(): Promise { - if (this.token instanceof Promise) { - throw new Error("Uninitialized token. You have to call sing before refresh"); - } - - let newToken; - if (this.refresh) { - newToken = await this.refresh(); - } else { - newToken = await this.token.refresh(); - } - return new OAuthCredential(newToken, this.refresh); - } -} - -export interface TuyaCustomBearerCredentialConfiguration { - key: string; - secret: string; -} - -interface TokenResponse { - success?: boolean; - result?: { - // eslint-disable-next-line camelcase - access_token?: string; - // eslint-disable-next-line camelcase - refresh_token?: string; - // eslint-disable-next-line camelcase - expire_time?: number; - }; -} - -export class TuyaCustomBearer extends Credential { - protected key: string; - protected secret: string; - protected baseUri: string; - protected token?: string; - protected refreshToken?: string; - protected expireTime?: Date; - - constructor(credentials: TuyaCustomBearerCredentialConfiguration, scheme: TuyaCustomBearerSecurityScheme) { - super(); - this.key = credentials.key; - this.secret = credentials.secret; - this.baseUri = scheme.baseUri; - } - - async sign(request: Request): Promise { - const isTokenExpired: boolean = this.isTokenExpired(); - if (this.token === undefined || this.token === "" || isTokenExpired) - await this.requestAndRefreshToken(isTokenExpired); - - const url: string = request.url; - const body = request.body?.read().toString(); - const method = request.method; - const headers = this.getHeaders(true, request.headers.raw(), body, url, method); - Object.assign(headers, request.headers.raw()); - return new Request(url, { method, body: body !== "" ? body : undefined, headers }); - } - - protected async requestAndRefreshToken(refresh: boolean): Promise { - const headers = this.getHeaders(false, {}); - const request = { - headers, - method: "GET", - }; - let url = `${this.baseUri}/token?grant_type=1`; - if (refresh) { - url = `${this.baseUri}/token/${this.refreshToken}`; - } - const data: TokenResponse = await (await fetch(url, request)).json(); - const success = data.success ?? false; - - if (success) { - this.token = data.result?.access_token; - this.refreshToken = data.result?.refresh_token; - - const expireTime = data.result?.expire_time; - if (expireTime != null) { - this.expireTime = new Date(Date.now() + expireTime * 1000); - } - } else { - throw new Error("token fetch failed"); - } - } - - private getHeaders(NormalRequest: boolean, headers: unknown, body?: string, url?: string, method?: string) { - const requestTime = Date.now().toString(); - const replaceUri = this.baseUri.replace("/v1.0", ""); - const _url = url?.replace(replaceUri, ""); - const sign = this.requestSign(NormalRequest, requestTime, body, _url, method); - return { - t: requestTime, - client_id: this.key, - sign_method: "HMAC-SHA256", - sign, - access_token: this.token ?? "", - }; - } - - private requestSign( - NormalRequest: boolean, - requestTime: string, - body?: string, - path = "", - method?: string - ): string { - const bodyHash = crypto - .createHash("sha256") - .update(body ?? "") - .digest("hex"); - let signUrl = "/v1.0/token?grant_type=1"; - const headerString = ""; - let useToken = ""; - const _method = method ?? "GET"; - if (NormalRequest) { - useToken = this.token ?? ""; - const pathQuery = queryString.parse(path.split("?")[1]); - let query: Record = {}; - query = Object.assign(query, pathQuery); - const sortedQuery: { [k: string]: string } = {}; - Object.keys(query) - .sort() - .forEach((i) => { - sortedQuery[i] = query[i]; - }); - const qs = queryString.stringify(sortedQuery); - signUrl = decodeURIComponent(qs ? `${path.split("?")[0]}?${qs}` : path); - } - const endStr = [this.key, useToken, requestTime, [_method, bodyHash, headerString, signUrl].join("\n")].join( - "" - ); - const sign = crypto.createHmac("sha256", this.secret).update(endStr).digest("hex").toUpperCase(); - return sign; - } - - private isTokenExpired(): boolean { - return this.expireTime ? Date.now() > this.expireTime.getTime() : false; - } -} diff --git a/packages/binding-http/src/http-browser.ts b/packages/binding-http/src/http-browser.ts deleted file mode 100644 index 712f325c3..000000000 --- a/packages/binding-http/src/http-browser.ts +++ /dev/null @@ -1,65 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2019 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 TD from "@node-wot/td-tools"; - -import { Headers, Response } from "node-fetch"; - -export { default as HttpClient } from "./http-client"; -export { default as HttpClientFactory } from "./http-client-factory"; -export { default as HttpsClientFactory } from "./https-client-factory"; -export * from "./http-client"; -export * from "./http-client-factory"; -export * from "./https-client-factory"; - -export interface HttpProxyConfig { - href: string; - scheme?: string; - token?: string; - username?: string; - password?: string; -} - -export interface HttpConfig { - port?: number; - address?: string; - proxy?: HttpProxyConfig; - allowSelfSigned?: boolean; - serverKey?: string; - serverCert?: string; - security?: TD.SecurityScheme; -} - -export class HttpHeader { - public "http:fieldName": number; - public "http:fieldValue": unknown; -} - -export class HttpForm extends TD.Form { - public "http:methodName"?: string; // "GET", "PUT", "POST", "DELETE" - public "http:headers"?: Array | HttpHeader; -} - -Headers.prototype.raw = function () { - const result: { [key: string]: string[] } = {}; - for (const h in this.entries()) { - result[h[0]] = h[1].split(","); - } - return result; -}; - -Response.prototype.buffer = async function () { - return Buffer.from(await this.arrayBuffer()); -}; diff --git a/packages/binding-http/src/http-client-browser.ts b/packages/binding-http/src/http-client-browser.ts deleted file mode 100644 index 24e24a8ad..000000000 --- a/packages/binding-http/src/http-client-browser.ts +++ /dev/null @@ -1,33 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2022 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 { ProtocolHelpers } from "@node-wot/core"; -import { RequestInit, Request } from "node-fetch"; -import { Readable } from "stream"; -import { HttpForm, HTTPMethodName } from "./http"; -import HttpClient from "./http-client-impl"; - -export default class BrowserHttpClient extends HttpClient { - protected async generateFetchRequest( - form: HttpForm, - defaultMethod: HTTPMethodName, - additionalOptions?: RequestInit - ): Promise { - if (additionalOptions?.body instanceof Readable) { - const buffer = await ProtocolHelpers.readStreamFully(additionalOptions.body); - additionalOptions.body = buffer; - } - return super.generateFetchRequest(form, defaultMethod, additionalOptions); - } -} diff --git a/packages/binding-http/src/http-client-factory.ts b/packages/binding-http/src/http-client-factory.ts deleted file mode 100644 index d30e682a5..000000000 --- a/packages/binding-http/src/http-client-factory.ts +++ /dev/null @@ -1,58 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2018 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 - ********************************************************************************/ - -/** - * HTTP client Factory - */ - -import { ProtocolClientFactory, ProtocolClient, createLoggers } from "@node-wot/core"; -import { HttpConfig } from "./http"; -import HttpClient from "./http-client"; -import OAuthManager from "./oauth-manager"; - -const { debug, warn } = createLoggers("binding-http", "http-client-factory"); - -export default class HttpClientFactory implements ProtocolClientFactory { - public readonly scheme: string = "http"; - private config: HttpConfig | null = null; - private oAuthManager: OAuthManager = new OAuthManager(); - - constructor(config: HttpConfig | null = null) { - this.config = config; - } - - public getClient(): ProtocolClient { - // HTTP over HTTPS proxy requires HttpsClient - if (this.config && this.config.proxy && this.config.proxy.href && this.config.proxy.href.startsWith("https:")) { - warn("HttpClientFactory creating client for 'https' due to secure proxy configuration"); - return new HttpClient(this.config, true, this.oAuthManager); - } else { - debug(`HttpClientFactory creating client for '${this.scheme}'`); - return new HttpClient(this.config); - } - } - - public init(): boolean { - // info(`HttpClientFactory for '${HttpClientFactory.scheme}' initializing`); - // TODO uncomment info if something is executed here - return true; - } - - public destroy(): boolean { - // info(`HttpClientFactory for '${HttpClientFactory.scheme}' destroyed`); - // TODO uncomment info if something is executed here - return true; - } -} diff --git a/packages/binding-http/src/http-client-impl.ts b/packages/binding-http/src/http-client-impl.ts deleted file mode 100644 index 14c7ec9ed..000000000 --- a/packages/binding-http/src/http-client-impl.ts +++ /dev/null @@ -1,450 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2022 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 - ********************************************************************************/ - -/** - * HTTP client based on http - */ - -import * as http from "http"; -import * as https from "https"; - -import { Subscription } from "rxjs/Subscription"; - -import * as TD from "@node-wot/td-tools"; -// for Security definition - -import { ProtocolClient, Content, ProtocolHelpers, createLoggers, ContentSerdes } from "@node-wot/core"; -import { HttpForm, HttpHeader, HttpConfig, HTTPMethodName, TuyaCustomBearerSecurityScheme } from "./http"; -import fetch, { Request, RequestInit, Response } from "node-fetch"; -import { Buffer } from "buffer"; -import OAuthManager, { OAuthClientConfiguration, OAuthResourceOwnerConfiguration } from "./oauth-manager"; -import { - BasicCredential, - Credential, - BearerCredential, - BasicKeyCredential, - OAuthCredential, - BasicCredentialConfiguration, - BearerCredentialConfiguration, - BasicKeyCredentialConfiguration, - TuyaCustomBearer, - TuyaCustomBearerCredentialConfiguration, -} from "./credential"; -import { LongPollingSubscription, SSESubscription, InternalSubscription } from "./subscription-protocols"; -import { Readable } from "stream"; - -const { debug, warn, error } = createLoggers("binding-http", "http-client-impl"); - -export default class HttpClient implements ProtocolClient { - private readonly agent: http.Agent; - private readonly provider: "https" | "http"; - private proxyRequest: Request | null = null; - private allowSelfSigned = false; - private oauth: OAuthManager; - - private credential: Credential | null = null; - - private activeSubscriptions = new Map(); - - constructor(config: HttpConfig | null = null, secure = false, oauthManager: OAuthManager = new OAuthManager()) { - // config proxy by client side (not from TD) - if (config !== null && config.proxy && config.proxy.href) { - this.proxyRequest = new Request(HttpClient.fixLocalhostName(config.proxy.href)); - - if (config.proxy.scheme === "basic") { - if ( - !Object.prototype.hasOwnProperty.call(config.proxy, "username") || - !Object.prototype.hasOwnProperty.call(config.proxy, "password") - ) - warn("HttpClient client configured for basic proxy auth, but no username/password given"); - this.proxyRequest.headers.set( - "proxy-authorization", - "Basic " + Buffer.from(config.proxy.username + ":" + config.proxy.password).toString("base64") - ); - } else if (config.proxy.scheme === "bearer") { - if (!Object.prototype.hasOwnProperty.call(config.proxy, "token")) - warn("HttpClient client configured for bearer proxy auth, but no token given"); - this.proxyRequest.headers.set("proxy-authorization", "Bearer " + config.proxy.token); - } - // security for hop to proxy - if (this.proxyRequest.protocol === "https") { - secure = true; - } - - debug( - `HttpClient using ${secure ? "secure " : ""}proxy ${this.proxyRequest.hostname}:${ - this.proxyRequest.port - }` - ); - } - - // config certificate checks - if (config !== null && config.allowSelfSigned !== undefined) { - this.allowSelfSigned = config.allowSelfSigned; - warn(`HttpClient allowing self-signed/untrusted certificates -- USE FOR TESTING ONLY`); - } - - // using one client impl for both HTTP and HTTPS - this.agent = secure - ? new https.Agent({ - rejectUnauthorized: !this.allowSelfSigned, - }) - : new http.Agent(); - - this.provider = secure ? "https" : "http"; - this.oauth = oauthManager; - } - - public toString(): string { - return `[HttpClient]`; - } - - public async readResource(form: HttpForm): Promise { - // See https://www.w3.org/TR/wot-thing-description11/#contentType-usage - // Case: 1B - const headers = form.contentType != null ? [["accept", form.contentType]] : [["accept", ContentSerdes.DEFAULT]]; - const request = await this.generateFetchRequest(form, "GET", { headers }); - debug(`HttpClient (readResource) sending ${request.method} to ${request.url}`); - - const result = await this.fetch(request); - - this.checkFetchResponse(result); - - debug(`HttpClient received headers: ${JSON.stringify(result.headers.raw())}`); - debug(`HttpClient received Content-Type: ${result.headers.get("content-type")}`); - - // in browsers node-fetch uses the native fetch, which returns a ReadableStream - // not complaint with node. Therefore we have to force the conversion here. - const body = ProtocolHelpers.toNodeStream(result.body as Readable); - return new Content(result.headers.get("content-type") ?? ContentSerdes.DEFAULT, body); - } - - public async writeResource(form: HttpForm, content: Content): Promise { - const request = await this.generateFetchRequest(form, "PUT", { - headers: [["content-type", content.type]], - body: content.body, - }); - - debug( - `HttpClient (writeResource) sending ${request.method} with '${request.headers.get("Content-Type")}' to ${ - request.url - }` - ); - const result = await this.fetch(request); - - debug(`HttpClient received ${result.status} from ${result.url}`); - - this.checkFetchResponse(result); - - debug(`HttpClient received headers: ${JSON.stringify(result.headers.raw())}`); - } - - public async subscribeResource( - form: HttpForm, - next: (value: Content) => void, - error?: (error: Error) => void, - complete?: () => void - ): Promise { - const defaultSubprotocol = "longpoll"; - let subprotocol = form.subprotocol; - - if (subprotocol == null) { - warn(`Subscribing to ${form.href} using long polling for form without subprotocol`); - subprotocol = defaultSubprotocol; - } - - let internalSubscription: InternalSubscription; - if (subprotocol === defaultSubprotocol) { - internalSubscription = new LongPollingSubscription(form, this); - } else if (form.subprotocol === "sse") { - // server sent events - internalSubscription = new SSESubscription(form); - } else { - throw new Error(`HttpClient does not support subprotocol ${form.subprotocol}`); - } - - await internalSubscription.open(next, error, complete); - this.activeSubscriptions.set(form.href, internalSubscription); - return new Subscription(() => { - internalSubscription.close(); - }); - } - - public async invokeResource(form: HttpForm, content?: Content): Promise { - const headers = content != null ? [["content-type", content.type]] : []; - // See https://www.w3.org/TR/wot-thing-description11/#contentType-usage - // Cases: 1C and 2A - if (form.response?.contentType != null) { - headers.push(["accept", form.response?.contentType]); - } else if (form.contentType != null) { - headers.push(["accept", form.contentType]); - } else { - headers.push(["accept", ContentSerdes.DEFAULT]); - } - - const request = await this.generateFetchRequest(form, "POST", { - headers, - body: content?.body, - }); - - debug( - `HttpClient (invokeResource) sending ${request.method} ${ - content != null ? `with '"${request.headers.get("Content-Type")}"` : "" - } to ${request.url}` - ); - - const result = await this.fetch(request); - - debug(`HttpClient received ${result.status} from ${request.url}`); - debug(`HttpClient received Content-Type: ${result.headers.get("content-type")}`); - - this.checkFetchResponse(result); - - // in browsers node-fetch uses the native fetch, which returns a ReadableStream - // not complaint with node. Therefore we have to force the conversion here. - const body = ProtocolHelpers.toNodeStream(result.body as Readable); - return new Content(result.headers.get("content-type") ?? ContentSerdes.DEFAULT, body); - } - - public async unlinkResource(form: HttpForm): Promise { - debug(`HttpClient (unlinkResource) ${form.href}`); - const internalSub = this.activeSubscriptions.get(form.href); - - if (internalSub) { - internalSub.close(); - } else { - warn(`HttpClient cannot unlink ${form.href} no subscription found`); - } - } - - /** - * @inheritdoc - */ - public async requestThingDescription(uri: string): Promise { - const headers: HeadersInit = { - Accept: "application/td+json", - }; - const request = await this.generateFetchRequest({ href: uri }, "GET", headers); - const response = await this.fetch(request); - const body = ProtocolHelpers.toNodeStream(response.body as Readable); - return new Content(response.headers.get("content-type") ?? "application/td+json", body); - } - - public async start(): Promise { - // do nothing - } - - public async stop(): Promise { - // When running in browser mode, Agent.destroy() might not exist. - this.agent?.destroy?.(); - } - - public setSecurity(metadata: Array, credentials?: unknown): boolean { - if (metadata === undefined || !Array.isArray(metadata) || metadata.length === 0) { - warn("HttpClient without security"); - return false; - } - - // TODO support for multiple security schemes - const security: TD.SecurityScheme = metadata[0]; - switch (security.scheme) { - case "basic": { - const securityBasic: TD.BasicSecurityScheme = security; - - this.credential = new BasicCredential(credentials as BasicCredentialConfiguration, securityBasic); - break; - } - case "bearer": { - const securityBearer: TD.BearerSecurityScheme = security; - - this.credential = new BearerCredential(credentials as BearerCredentialConfiguration, securityBearer); - break; - } - case "apikey": { - const securityAPIKey: TD.APIKeySecurityScheme = security; - - this.credential = new BasicKeyCredential( - credentials as BasicKeyCredentialConfiguration, - securityAPIKey - ); - break; - } - case "oauth2": { - const securityOAuth: TD.OAuth2SecurityScheme = security; - - if (securityOAuth.flow === "client") { - securityOAuth.flow = "client_credentials"; - this.credential = this.oauth.handleClient(securityOAuth, credentials as OAuthClientConfiguration); - } else if (securityOAuth.flow === "password") { - this.credential = this.oauth.handleResourceOwnerCredential( - securityOAuth, - credentials as OAuthResourceOwnerConfiguration - ); - } - - break; - } - case "TuyaCustomBearer": { - this.credential = new TuyaCustomBearer( - credentials as TuyaCustomBearerCredentialConfiguration, - security as TuyaCustomBearerSecurityScheme - ); - break; - } - case "nosec": - break; - default: - error(`HttpClient cannot set security scheme '${security.scheme}'. ${metadata}`); - return false; - } - - if (security.proxy != null) { - if (this.proxyRequest !== null) { - debug(`HttpClient overriding client-side proxy with security proxy '${security.proxy}`); - } - - this.proxyRequest = new Request(HttpClient.fixLocalhostName(security.proxy)); - - // TODO support for different credentials at proxy and server (e.g., credentials.username vs credentials.proxy.username) - if (security.scheme === "basic") { - const basicCredential: BasicCredentialConfiguration = credentials as BasicCredentialConfiguration; - if ( - basicCredential === undefined || - basicCredential.username === undefined || - basicCredential.password === undefined - ) { - throw new Error(`No Basic credentials for Thing`); - } - - this.proxyRequest.headers.set( - "proxy-authorization", - "Basic " + Buffer.from(basicCredential.username + ":" + basicCredential.password).toString("base64") - ); - } else if (security.scheme === "bearer") { - const tokenCredentials: BearerCredentialConfiguration = credentials as BearerCredentialConfiguration; - if (credentials === undefined || tokenCredentials.token === undefined) { - throw new Error(`No Bearer credentials for Thing`); - } - this.proxyRequest.headers.set("proxy-authorization", "Bearer " + tokenCredentials.token); - } - } - - debug(`HttpClient using security scheme '${security.scheme}'`); - return true; - } - - protected async generateFetchRequest( - form: HttpForm, - defaultMethod: HTTPMethodName, - additionalOptions: RequestInit = {} - ): Promise { - const requestInit: RequestInit = additionalOptions; - - const url = HttpClient.fixLocalhostName(form.href); - - requestInit.method = form["htv:methodName"] ? form["htv:methodName"] : defaultMethod; - - requestInit.headers = requestInit.headers ?? []; - requestInit.headers = requestInit.headers as string[][]; - - if (Array.isArray(form["htv:headers"])) { - debug(`HttpClient got Form 'headers' ${form["htv:headers"]}`); - - const headers = form["htv:headers"] as Array; - for (const option of headers) { - // override defaults - requestInit.headers = requestInit.headers.filter( - (header) => header[0].toLowerCase() !== option["htv:fieldName"].toLowerCase() - ); - requestInit.headers.push([option["htv:fieldName"], option["htv:fieldValue"]]); - } - } else if (typeof form["htv:headers"] === "object") { - debug(`HttpClient got Form SINGLE-ENTRY 'headers' ${form["htv:headers"]}`); - - const option = form["htv:headers"] as HttpHeader; - // override defaults - requestInit.headers = requestInit.headers.filter( - (header) => header[0].toLowerCase() !== option["htv:fieldName"].toLowerCase() - ); - requestInit.headers.push([option["htv:fieldName"], option["htv:fieldValue"]]); - } - - requestInit.agent = this.agent; - - let request = this.proxyRequest ? new Request(this.proxyRequest, requestInit) : new Request(url, requestInit); - - // Sign the request using client credentials - if (this.credential) { - request = await this.credential.sign(request); - } - - if (this.proxyRequest) { - const parsedBaseURL = new URL(url); - request.url = request.url + parsedBaseURL.pathname; - - debug(`HttpClient proxy request URL: ${request.url}`); - - request.headers.set("host", parsedBaseURL.hostname); - } - - return request; - } - - private async fetch(request: Request, content?: Content) { - const result = await fetch(request, { body: content?.body }); - - if (HttpClient.isOAuthTokenExpired(result, this.credential)) { - this.credential = await (this.credential as OAuthCredential).refreshToken(); - return await fetch(await this.credential.sign(request)); - } - - return result; - } - - private checkFetchResponse(response: Response) { - const statusCode = response.status; - - if (statusCode < 200) { - throw new Error( - `HttpClient received ${statusCode} and cannot continue (not implemented, open GitHub Issue)` - ); - } else if (statusCode < 300) { - // No error - } else if (statusCode < 400) { - throw new Error( - `HttpClient received ${statusCode} and cannot continue (not implemented, open GitHub Issue)` - ); - } else if (statusCode < 500) { - throw new Error(`Client error: ${response.statusText}`); - } else { - throw new Error(`Server error: ${response.statusText}`); - } - } - - private static isOAuthTokenExpired(result: Response, credential: Credential | null) { - return result.status === 401 && credential instanceof OAuthCredential; - } - - private static fixLocalhostName(url: string) { - const localhostPresent = /^(https?:)?(\/\/)?(?:[^@\n]+@)?(www\.)?(localhost)/gm; - - if (localhostPresent.test(url)) { - warn("LOCALHOST FIX"); - return url.replace(localhostPresent, "$1$2127.0.0.1"); - } - - return url; - } -} diff --git a/packages/binding-http/src/http-client.ts b/packages/binding-http/src/http-client.ts deleted file mode 100644 index 0c1da00e5..000000000 --- a/packages/binding-http/src/http-client.ts +++ /dev/null @@ -1,16 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2018 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 { default } from "./http-client-impl"; diff --git a/packages/binding-http/src/http-server-middleware.ts b/packages/binding-http/src/http-server-middleware.ts deleted file mode 100644 index aad8c2e5d..000000000 --- a/packages/binding-http/src/http-server-middleware.ts +++ /dev/null @@ -1,62 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 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 - ********************************************************************************/ - -/** - * HTTP Middleware for the HTTP Server - */ - -import * as http from "http"; - -/** - * A middleware function for the HTTP server, which can be used to intercept requests before they are handled by the WoT Servient. - * - * Example: - * ```javascript - * import { Servient } from "@node-wot/core"; - * import { HttpServer, MiddlewareRequestHandler } from "@node-wot/binding-http"; - * - * const servient = new Servient(); - * - * const middleware: MiddlewareRequestHandler = async (req, res, next) => { - * // For example, reject requests in which the X-Custom-Header header is missing -* // by replying with 400 Bad Request - * if (!req.headers["x-custom-header"]) { - * res.statusCode = 400; - * res.end("Bad Request"); - * return; - * } - * // Pass all other requests to the WoT Servient - * next(); - * }; - - * const httpServer = new HttpServer({ - * middleware, - * }); - * - * servient.addServer(httpServer); - * - * servient.start().then(async (WoT) => { - * // ... - * }); - * ``` - * @param req The HTTP request. - * @param res The HTTP response. - * @param next Call this function to pass the request to the WoT Servient. - */ -export type MiddlewareRequestHandler = ( - req: http.IncomingMessage, - res: http.ServerResponse, - next: () => void -) => Promise; diff --git a/packages/binding-http/src/http-server.ts b/packages/binding-http/src/http-server.ts deleted file mode 100644 index 414bd4ba6..000000000 --- a/packages/binding-http/src/http-server.ts +++ /dev/null @@ -1,642 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2018 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 - ********************************************************************************/ - -/** - * HTTP Server based on http - */ - -import * as fs from "fs"; -import * as http from "http"; -import * as https from "https"; -import bauth from "basic-auth"; - -import * as TD from "@node-wot/td-tools"; -import Servient, { - ProtocolServer, - ContentSerdes, - Helpers, - ExposedThing, - ProtocolHelpers, - createLoggers, -} from "@node-wot/core"; -import { HttpConfig, HttpForm, OAuth2ServerConfig } from "./http"; -import createValidator, { Validator } from "./oauth-token-validation"; -import { OAuth2SecurityScheme } from "@node-wot/td-tools"; -import slugify from "slugify"; -import { ActionElement, EventElement, PropertyElement } from "wot-thing-description-types"; -import { MiddlewareRequestHandler } from "./http-server-middleware"; -import Router from "find-my-way"; -import thingsRoute from "./routes/things"; -import thingDescriptionRoute from "./routes/thing-description"; -import propertyRoute from "./routes/property"; -import actionRoute from "./routes/action"; -import eventRoute from "./routes/event"; -import propertiesRoute from "./routes/properties"; -import propertyObserveRoute from "./routes/property-observe"; - -const { debug, info, warn, error } = createLoggers("binding-http", "http-server"); - -export default class HttpServer implements ProtocolServer { - public readonly scheme: "http" | "https"; - - private readonly PROPERTY_DIR = "properties"; - private readonly ACTION_DIR = "actions"; - private readonly EVENT_DIR = "events"; - - private readonly OBSERVABLE_DIR = "observable"; - - // private readonly OPTIONS_URI_VARIABLES ='uriVariables'; - // private readonly OPTIONS_BODY_VARIABLES ='body'; - - private readonly port: number; - private readonly address?: string; - private readonly baseUri?: string; - private readonly urlRewrite?: Record; - private readonly supportedSecuritySchemes: string[] = ["nosec"]; - private readonly validOAuthClients: RegExp = /.*/g; - private readonly server: http.Server | https.Server; - private readonly middleware?: MiddlewareRequestHandler; - private readonly things: Map = new Map(); - private servient: Servient | null = null; - private oAuthValidator?: Validator = undefined; - private router: Router.Instance; - - constructor(config: HttpConfig = {}) { - if (typeof config !== "object") { - throw new Error(`HttpServer requires config object (got ${typeof config})`); - } - - this.port = this.obtainEnvironmentPortNumber() ?? config.port ?? 8080; - this.address = config.address; - this.baseUri = config.baseUri; - this.urlRewrite = config.urlRewrite; - this.middleware = config.middleware; - - const router = Router({ - ignoreTrailingSlash: true, - defaultRoute(req, res) { - // url-rewrite feature in use ? - const pathname = req.url; - if (config.urlRewrite) { - const entryUrl = pathname; - const internalUrl = config.urlRewrite[entryUrl ?? "/"]; - if (internalUrl) { - req.url = internalUrl; - router.lookup(req, res, this); - debug("[binding-http]", `URL "${entryUrl}" has been rewritten to "${pathname}"`); - return; - } - } - - // No url-rewrite mapping found -> resource not found - res.writeHead(404); - res.end("Not Found"); - }, - }); - - this.router = router; - - this.router.get("/", thingsRoute); - this.router.get("/:thing", thingDescriptionRoute); - this.router.on(["GET", "HEAD", "OPTIONS"], "/:thing/" + this.PROPERTY_DIR, propertiesRoute); - this.router.on(["GET", "PUT", "HEAD", "OPTIONS"], "/:thing/" + this.PROPERTY_DIR + "/:property", propertyRoute); - this.router.on( - ["GET", "HEAD", "OPTIONS"], - "/:thing/" + this.PROPERTY_DIR + "/:property/" + this.OBSERVABLE_DIR, - propertyObserveRoute - ); - this.router.on(["POST", "OPTIONS"], "/:thing/" + this.ACTION_DIR + "/:action", actionRoute); - this.router.on(["GET", "HEAD", "OPTIONS"], "/:thing/" + this.EVENT_DIR + "/:event", eventRoute); - - // TLS - if (config.serverKey != null && config.serverCert != null) { - const options: https.ServerOptions = {}; - options.key = fs.readFileSync(config.serverKey); - options.cert = fs.readFileSync(config.serverCert); - this.scheme = "https"; - this.server = https.createServer(options, (req, res) => { - if (this.middleware) { - this.middleware(req, res, () => { - this.handleRequest(req, res); - }); - } else { - this.handleRequest(req, res); - } - }); - } else { - this.scheme = "http"; - this.server = http.createServer((req, res) => { - if (this.middleware) { - this.middleware(req, res, () => { - this.handleRequest(req, res); - }); - } else { - this.handleRequest(req, res); - } - }); - } - - // Auth - if (config.security) { - if (config.security.length > 1) { - // clear the default - this.supportedSecuritySchemes = []; - } - for (const securityScheme of config.security) { - switch (securityScheme.scheme) { - case "nosec": - case "basic": - case "digest": - case "bearer": - break; - case "oauth2": - { - const oAuthConfig = securityScheme as OAuth2ServerConfig; - this.validOAuthClients = new RegExp(oAuthConfig.allowedClients ?? ".*"); - this.oAuthValidator = createValidator(oAuthConfig.method); - } - break; - default: - throw new Error(`HttpServer does not support security scheme '${securityScheme.scheme}`); - } - this.supportedSecuritySchemes.push(securityScheme.scheme); - } - } - } - - private obtainEnvironmentPortNumber(): number | undefined { - for (const portVariable of ["WOT_PORT", "PORT"]) { - const environmentValue = process.env[portVariable]; - - if (environmentValue == null) { - continue; - } - - const parsedPort = parseInt(environmentValue); - - if (isNaN(parsedPort)) { - debug(`Ignoring environment variable ${portVariable} because it is not an integer.`); - continue; - } - - info(`HttpServer Port Overridden to ${parsedPort} by Environment Variable ${portVariable}`); - return parsedPort; - } - - return undefined; - } - - public start(servient: Servient): Promise { - info(`HttpServer starting on ${this.address !== undefined ? this.address + " " : ""}port ${this.port}`); - return new Promise((resolve, reject) => { - // store servient to get credentials - this.servient = servient; - - // long timeout for long polling - this.server.setTimeout(60 * 60 * 1000, () => { - debug(`HttpServer on port ${this.getPort()} timed out connection`); - }); - // no keep-alive because NodeJS HTTP clients do not properly use same socket due to pooling - this.server.keepAliveTimeout = 0; - - // start promise handles all errors until successful start - this.server.once("error", (err: Error) => { - reject(err); - }); - this.server.once("listening", () => { - // once started, console "handles" errors - this.server.on("error", (err: Error) => { - error(`HttpServer on port ${this.port} failed: ${err.message}`); - }); - resolve(); - }); - this.server.listen(this.port, this.address); - }); - } - - public stop(): Promise { - info(`HttpServer stopping on port ${this.getPort()}`); - return new Promise((resolve, reject) => { - // stop promise handles all errors from now on - this.server.once("error", (err: Error) => { - reject(err); - }); - this.server.once("close", () => { - resolve(); - }); - this.server.close(); - }); - } - - /** returns http.Server to be re-used by other HTTP-based bindings (e.g., WebSockets) */ - public getServer(): http.Server | https.Server { - return this.server; - } - - public getThings(): Map { - return this.things; - } - - /** returns server port number and indicates that server is running when larger than -1 */ - public getPort(): number { - const address = this.server?.address(); - - if (typeof address === "object") { - return address?.port ?? -1; - } - - const port = parseInt(address); - - if (isNaN(port)) { - return -1; - } - - return port; - } - - public async expose(thing: ExposedThing, tdTemplate: WoT.ExposedThingInit = {}): Promise { - let urlPath = slugify(thing.title, { lower: true }); - - if (this.things.has(urlPath)) { - urlPath = Helpers.generateUniqueName(urlPath); - } - - if (this.getPort() !== -1) { - debug(`HttpServer on port ${this.getPort()} exposes '${thing.title}' as unique '/${urlPath}'`); - this.things.set(urlPath, thing); - - if (this.scheme === "http" && Object.keys(thing.securityDefinitions).length !== 0) { - warn(`HTTP Server will attempt to use your security schemes even if you are not using HTTPS.`); - } - - this.fillSecurityScheme(thing); - - if (this.baseUri !== undefined) { - const base: string = this.baseUri.concat("/", encodeURIComponent(urlPath)); - info("HttpServer TD hrefs using baseUri " + this.baseUri); - this.addEndpoint(thing, tdTemplate, base); - } else { - // fill in binding data - for (const address of Helpers.getAddresses()) { - const base: string = - this.scheme + "://" + address + ":" + this.getPort() + "/" + encodeURIComponent(urlPath); - - this.addEndpoint(thing, tdTemplate, base); - } - } - } - } - - public async destroy(thingId: string): Promise { - debug(`HttpServer on port ${this.getPort()} destroying thingId '${thingId}'`); - - for (const [name, thing] of this.things.entries()) { - if (thing.id === thingId) { - this.things.delete(name); - info(`HttpServer successfully destroyed '${thing.title}'`); - - return true; - } - } - - info(`HttpServer failed to destroy thing with thingId '${thingId}'`); - return false; - } - - private addUrlRewriteEndpoints(form: TD.Form, forms: Array): void { - if (this.urlRewrite != null) { - for (const [inUri, toUri] of Object.entries(this.urlRewrite)) { - const endsWithToUri: boolean = form.href.endsWith(toUri); - if (endsWithToUri) { - const form2 = Helpers.structuredClone(form); - form2.href = form2.href.substring(0, form.href.lastIndexOf(toUri)) + inUri; - forms.push(form2); - debug(`HttpServer on port ${this.getPort()} assigns urlRewrite '${form2.href}' for '${form.href}'`); - } - } - } - } - - public addEndpoint(thing: ExposedThing, tdTemplate: WoT.ExposedThingInit, base: string): void { - for (const type of ContentSerdes.get().getOfferedMediaTypes()) { - const properties = Object.values(thing.properties); - - let allReadOnly = true; - let allWriteOnly = true; - - for (const property of properties) { - const readOnly: boolean = property.readOnly ?? false; - if (!readOnly) { - allReadOnly = false; - } - - const writeOnly: boolean = property.writeOnly ?? false; - if (!writeOnly) { - allWriteOnly = false; - } - } - - if (properties.length > 0) { - const href = base + "/" + this.PROPERTY_DIR; - const form = new TD.Form(href, type); - if (allReadOnly && !allWriteOnly) { - form.op = ["readallproperties", "readmultipleproperties"]; - } else if (allWriteOnly && !allReadOnly) { - form.op = ["writeallproperties", "writemultipleproperties"]; - } else { - form.op = [ - "readallproperties", - "readmultipleproperties", - "writeallproperties", - "writemultipleproperties", - ]; - } - if (thing.forms == null) { - thing.forms = []; - } - thing.forms.push(form); - this.addUrlRewriteEndpoints(form, thing.forms); - } - - for (const [propertyName, property] of Object.entries(thing.properties)) { - const propertyNamePattern = Helpers.updateInteractionNameWithUriVariablePattern( - propertyName, - property.uriVariables, - thing.uriVariables - ); - const href = base + "/" + this.PROPERTY_DIR + "/" + propertyNamePattern; - const form = new TD.Form(href, type); - ProtocolHelpers.updatePropertyFormWithTemplate( - form, - (tdTemplate.properties?.[propertyName] ?? {}) as PropertyElement - ); - - const readOnly: boolean = property.readOnly ?? false; - const writeOnly: boolean = property.writeOnly ?? false; - - if (readOnly) { - form.op = ["readproperty"]; - const hform: HttpForm = form; - hform["htv:methodName"] ??= "GET"; - } else if (writeOnly) { - form.op = ["writeproperty"]; - const hform: HttpForm = form; - hform["htv:methodName"] ??= "PUT"; - } else { - form.op = ["readproperty", "writeproperty"]; - } - - property.forms.push(form); - debug(`HttpServer on port ${this.getPort()} assigns '${href}' to Property '${propertyName}'`); - this.addUrlRewriteEndpoints(form, property.forms); - - // if property is observable add an additional form with a observable href - if (property.observable === true) { - const href = - base + - "/" + - this.PROPERTY_DIR + - "/" + - encodeURIComponent(propertyName) + - "/" + - this.OBSERVABLE_DIR; - const form = new TD.Form(href, type); - form.op = ["observeproperty", "unobserveproperty"]; - form.subprotocol = "longpoll"; - property.forms.push(form); - debug( - `HttpServer on port ${this.getPort()} assigns '${href}' to observable Property '${propertyName}'` - ); - this.addUrlRewriteEndpoints(form, property.forms); - } - } - - for (const [actionName, action] of Object.entries(thing.actions)) { - const actionNamePattern = Helpers.updateInteractionNameWithUriVariablePattern( - actionName, - action.uriVariables, - thing.uriVariables - ); - const href = base + "/" + this.ACTION_DIR + "/" + actionNamePattern; - const form = new TD.Form(href, type); - ProtocolHelpers.updateActionFormWithTemplate( - form, - (tdTemplate.actions?.[actionName] ?? {}) as ActionElement - ); - form.op = ["invokeaction"]; - const hform: HttpForm = form; - - hform["htv:methodName"] ??= "POST"; - action.forms.push(form); - debug(`HttpServer on port ${this.getPort()} assigns '${href}' to Action '${actionName}'`); - this.addUrlRewriteEndpoints(form, action.forms); - } - - for (const [eventName, event] of Object.entries(thing.events)) { - const eventNamePattern = Helpers.updateInteractionNameWithUriVariablePattern( - eventName, - event.uriVariables, - thing.uriVariables - ); - const href = base + "/" + this.EVENT_DIR + "/" + eventNamePattern; - const form = new TD.Form(href, type); - ProtocolHelpers.updateEventFormWithTemplate( - form, - (tdTemplate.events?.[eventName] ?? {}) as EventElement - ); - form.subprotocol = "longpoll"; - form.op = ["subscribeevent", "unsubscribeevent"]; - event.forms.push(form); - debug(`HttpServer on port ${this.getPort()} assigns '${href}' to Event '${eventName}'`); - this.addUrlRewriteEndpoints(form, event.forms); - } - } - } - - public async checkCredentials(thing: ExposedThing, req: http.IncomingMessage): Promise { - debug(`HttpServer on port ${this.getPort()} checking credentials for '${thing.id}'`); - - if (this.servient === null) { - throw new Error("Servient not set"); - } - - const credentials = this.servient.retrieveCredentials(thing.id); - // Multiple security schemes are deprecated we are not supporting them. We are only supporting one security value. - const selected = Helpers.toStringArray(thing.security)[0]; - const thingSecurityScheme = thing.securityDefinitions[selected]; - debug(`Verifying credentials with security scheme '${thingSecurityScheme.scheme}'`); - switch (thingSecurityScheme.scheme) { - case "nosec": - return true; - case "basic": { - const basic = bauth(req); - if (basic === undefined) return false; - if (credentials == null || credentials.length === 0) return false; - - const basicCredentials = credentials as { username: string; password: string }[]; - return basicCredentials.some((cred) => basic.name === cred.username && basic.pass === cred.password); - } - case "digest": - return false; - case "oauth2": { - const oAuthScheme = thing.securityDefinitions[thing.security[0] as string] as OAuth2SecurityScheme; - - // TODO: Support security schemes defined at affordance level - const scopes = Helpers.toStringArray(oAuthScheme.scopes); // validate call requires array of strings while oAuthScheme.scopes can be string or array of strings - let valid = false; - - if (!this.oAuthValidator) { - throw new Error("OAuth validator not set. Cannot validate request."); - } - - try { - valid = await this.oAuthValidator.validate(req, scopes, this.validOAuthClients); - } catch (err) { - // TODO: should we answer differently to the client if something went wrong? - error("OAuth authorization error; sending unauthorized response error"); - error("this was possibly caused by a misconfiguration of the server"); - error(`${err}`); - } - - return valid; - } - case "Bearer": { - if (req.headers.authorization === undefined) return false; - // TODO proper token evaluation - const auth = req.headers.authorization.split(" "); - - if (auth.length !== 2 || auth[0] !== "Bearer") return false; - if (credentials == null || credentials.length === 0) return false; - - const bearerCredentials = credentials as { token: string }[]; - return bearerCredentials.some((cred) => cred.token === auth[1]); - } - default: - return false; - } - } - - private fillSecurityScheme(thing: ExposedThing) { - // User selected one security scheme - if (thing.security.length > 0) { - // multiple security schemes are deprecated we are not supporting them - const securityScheme = Helpers.toStringArray(thing.security)[0]; - const secCandidate = Object.keys(thing.securityDefinitions).find((key) => { - return key === securityScheme; - }); - - if (secCandidate == null) { - throw new Error( - "Security scheme not found in thing security definitions. Thing security definitions: " + - Object.keys(thing.securityDefinitions).join(", ") - ); - } - - const isSupported = this.supportedSecuritySchemes.find((supportedScheme) => { - const thingScheme = thing.securityDefinitions[secCandidate].scheme; - return thingScheme === supportedScheme.toLocaleLowerCase(); - }); - - if (isSupported == null) { - throw new Error( - "Servient does not support thing security schemes. Current scheme supported: " + - this.supportedSecuritySchemes.join(", ") - ); - } - // We don't need to do anything else, the user has selected one supported security scheme. - return; - } - - // The security array is empty – the user lets the servient choose the - // security scheme. - if (Object.keys(thing.securityDefinitions ?? {}).length === 0) { - // We are using the first supported security scheme as default - thing.securityDefinitions = { - [this.supportedSecuritySchemes[0]]: { scheme: this.supportedSecuritySchemes[0] }, - }; - thing.security = [this.supportedSecuritySchemes[0]]; - return; - } - - // User provided a bunch of security schemes but no thing.security - // we select one for him. We select the first supported scheme. - const secCandidate = Object.keys(thing.securityDefinitions).find((key) => { - let scheme = thing.securityDefinitions[key].scheme; - // HTTP Authentication Scheme for OAuth does not contain the version number - // see https://www.iana.org/assignments/http-authschemes/http-authschemes.xhtml - // remove version number for oauth2 schemes - scheme = scheme === "oauth2" ? scheme.split("2")[0] : scheme; - return this.supportedSecuritySchemes.includes(scheme.toLocaleLowerCase()); - }); - - if (secCandidate == null) { - throw new Error( - "Servient does not support any of thing security schemes. Current scheme supported: " + - this.supportedSecuritySchemes.join(",") + - " thing security schemes: " + - Object.values(thing.securityDefinitions) - .map((schemeDef) => schemeDef.scheme) - .join(", ") - ); - } - - const selectedSecurityScheme = thing.securityDefinitions[secCandidate]; - thing.securityDefinitions = {}; - thing.securityDefinitions[secCandidate] = selectedSecurityScheme; - - thing.security = [secCandidate]; - } - - private async handleRequest(req: http.IncomingMessage, res: http.ServerResponse) { - const requestUri = new URL(req.url ?? "", `${this.scheme}://${req.headers.host}`); - - debug( - `HttpServer on port ${this.getPort()} received '${req.method} ${ - requestUri.pathname - }' from ${Helpers.toUriLiteral(req.socket.remoteAddress)}:${req.socket.remotePort}` - ); - res.on("finish", () => { - debug( - `HttpServer on port ${this.getPort()} replied with '${res.statusCode}' to ${Helpers.toUriLiteral( - req.socket.remoteAddress - )}:${req.socket.remotePort}` - ); - }); - - const contentTypeHeader = req.headers["content-type"]; - let contentType: string = Array.isArray(contentTypeHeader) ? contentTypeHeader[0] : contentTypeHeader; - - if (req.method === "PUT" || req.method === "POST") { - if (!contentType) { - // FIXME should be rejected with 400 Bad Request, as guessing is not good in M2M -> debug/testing flag to allow - // FIXME would need to check if payload is present - warn( - `HttpServer on port ${this.getPort()} received no Content-Type from ${Helpers.toUriLiteral( - req.socket.remoteAddress - )}:${req.socket.remotePort}` - ); - contentType = ContentSerdes.DEFAULT; - } else if ( - ContentSerdes.get().getSupportedMediaTypes().indexOf(ContentSerdes.getMediaType(contentType)) < 0 - ) { - res.writeHead(415); - res.end("Unsupported Media Type"); - return; - } - } - - this.router.lookup(req, res, this); - } -} diff --git a/packages/binding-http/src/http.ts b/packages/binding-http/src/http.ts deleted file mode 100644 index a44ffc20a..000000000 --- a/packages/binding-http/src/http.ts +++ /dev/null @@ -1,78 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2018 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 TD from "@node-wot/td-tools"; -import { Method } from "./oauth-token-validation"; -import { MiddlewareRequestHandler } from "./http-server-middleware"; - -export { default as HttpServer } from "./http-server"; -export { default as HttpClient } from "./http-client"; -export { default as HttpClientFactory } from "./http-client-factory"; -export { default as HttpsClientFactory } from "./https-client-factory"; -export { MiddlewareRequestHandler } from "./http-server-middleware"; -export * from "./http-server"; -export * from "./http-client"; -export * from "./http-client-factory"; -export * from "./https-client-factory"; - -export interface HttpProxyConfig { - href: string; - scheme?: "basic" | "bearer"; - token?: string; - username?: string; - password?: string; -} - -export interface HttpConfig { - port?: number; - address?: string; - baseUri?: string; - urlRewrite?: Record; - proxy?: HttpProxyConfig; - allowSelfSigned?: boolean; - serverKey?: string; - serverCert?: string; - security?: TD.SecurityScheme[]; - middleware?: MiddlewareRequestHandler; -} - -export interface OAuth2ServerConfig extends TD.SecurityScheme { - method: Method; - /** - * Regex to select the valid clients ids. Default: .* - */ - allowedClients?: string; -} - -export interface TuyaCustomBearerSecurityScheme extends TD.SecurityScheme { - /** - * This scheme is necessary because of the Tuya binding. - * The Tuya Apis are implementing a custom security protocol that needs a custom handling - */ - scheme: "TuyaCustomBearer"; - baseUri: string; -} - -export type HTTPMethodName = "GET" | "PUT" | "POST" | "DELETE" | "PATCH" | "HEAD"; - -export class HttpHeader { - public "htv:fieldName": string; - public "htv:fieldValue": string; -} - -export class HttpForm extends TD.Form { - public "htv:methodName"?: HTTPMethodName; - public "htv:headers"?: Array | HttpHeader; -} diff --git a/packages/binding-http/src/https-client-factory.ts b/packages/binding-http/src/https-client-factory.ts deleted file mode 100644 index 8c7e6dca3..000000000 --- a/packages/binding-http/src/https-client-factory.ts +++ /dev/null @@ -1,56 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2018 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 - ********************************************************************************/ - -/** - * HTTPS client Factory - */ - -import { ProtocolClientFactory, ProtocolClient, createLoggers } from "@node-wot/core"; -import { HttpConfig } from "./http"; -import HttpClient from "./http-client"; - -const { debug, warn } = createLoggers("binding-http", "https-client-factory"); - -export default class HttpsClientFactory implements ProtocolClientFactory { - public readonly scheme: string = "https"; - private config: HttpConfig | null = null; - - constructor(config: HttpConfig | null = null) { - this.config = config; - } - - public getClient(): ProtocolClient { - // HTTPS over HTTP proxy requires HttpClient - if (this.config && this.config.proxy && this.config.proxy.href && this.config.proxy.href.startsWith("http:")) { - warn("HttpsClientFactory creating client for 'http' due to insecure proxy configuration"); - return new HttpClient(this.config); - } else { - debug(`HttpsClientFactory creating client for '${this.scheme}'`); - return new HttpClient(this.config, true); - } - } - - public init(): boolean { - // info(`HttpsClientFactory for '${HttpsClientFactory.scheme}' initializing`); - // TODO uncomment info if something is executed here - return true; - } - - public destroy(): boolean { - // info(`HttpsClientFactory for '${HttpsClientFactory.scheme}' destroyed`); - // TODO uncomment info if something is executed here - return true; - } -} diff --git a/packages/binding-http/src/oauth-manager.ts b/packages/binding-http/src/oauth-manager.ts deleted file mode 100644 index 9f6e032f8..000000000 --- a/packages/binding-http/src/oauth-manager.ts +++ /dev/null @@ -1,115 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2020 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 { OAuth2SecurityScheme } from "@node-wot/td-tools"; -import { Helpers } from "@node-wot/core"; -import ClientOAuth2 from "client-oauth2"; -import { request, RequestOptions } from "https"; -import { OAuthCredential } from "./credential"; - -function createRequestFunction(rejectUnauthorized: boolean) { - // TODO: Does not work inside a browser - return ( - method: string, - url: string, - body: string, - headers: { [key: string]: string | string[] } - ): Promise<{ status: number; body: string }> => { - return new Promise((resolve, reject) => { - const parsedURL = new URL(url); - - const options: RequestOptions = { - method, - host: parsedURL.hostname, - port: parseInt(parsedURL.port), - path: parsedURL.pathname + parsedURL.search, - headers, - }; - - options.rejectUnauthorized = rejectUnauthorized; - const req = request(options); - - req.on("response", (response) => { - response.setEncoding("utf8"); - const body: Array = []; - response.on("data", (data) => { - body.push(data); - }); - response.on("end", () => { - resolve({ - status: response.statusCode ?? 500, // we are not expecting undefined status codes - body: body.toString(), - }); - }); - }); - req.on("error", (er) => { - reject(er); - }); - - req.write(body); - - req.end(); - }); - }; -} -export interface OAuthClientConfiguration { - clientId: string; - clientSecret: string; -} -export interface OAuthResourceOwnerConfiguration extends OAuthClientConfiguration { - username: string; - password: string; -} - -export default class OAuthManager { - private tokenStore: Map = new Map(); - handleClient(securityScheme: OAuth2SecurityScheme, credentials: OAuthClientConfiguration): OAuthCredential { - const clientFlow: ClientOAuth2 = new ClientOAuth2( - { - clientId: credentials.clientId, - clientSecret: credentials.clientSecret, - accessTokenUri: securityScheme.token, - scopes: Helpers.toStringArray(securityScheme.scopes), - body: { - // TODO: some server implementation may require client_id and secret inside - // the request body - // client_id: credentials.clientId, - // client_secret: credentials.clientSecret - }, - }, - createRequestFunction(false) - ); - const token = clientFlow.credentials.getToken(); - return new OAuthCredential(token, clientFlow.credentials.getToken.bind(clientFlow.credentials)); - } - - handleResourceOwnerCredential( - securityScheme: OAuth2SecurityScheme, - credentials: OAuthResourceOwnerConfiguration - ): OAuthCredential { - const clientFlow: ClientOAuth2 = new ClientOAuth2( - { - clientId: credentials.clientId, - clientSecret: credentials.clientSecret, - accessTokenUri: securityScheme.token, - scopes: Helpers.toStringArray(securityScheme.scopes), - }, - createRequestFunction(false) - ); - const token = clientFlow.owner.getToken(credentials.username, credentials.password); - - return new OAuthCredential(token); - } -} diff --git a/packages/binding-http/src/oauth-token-validation.ts b/packages/binding-http/src/oauth-token-validation.ts deleted file mode 100644 index 29638391e..000000000 --- a/packages/binding-http/src/oauth-token-validation.ts +++ /dev/null @@ -1,172 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2020 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 fetch, { Request } from "node-fetch"; -import { BasicCredential } from "./credential"; -import * as http from "http"; -import { Agent as SecureAgent } from "https"; - -export interface Method { - name: string; -} - -export interface IntrospectionEndpoint extends Method { - endpoint: string; - allowSelfSigned?: boolean; - - credentials?: { username: string; password: string }; -} -/** - * Identify an introspection result. It contains some of the - * properties defined in - * https://tools.ietf.org/html/rfc7662#section-2.2 - */ -interface TokenInformation { - /** - * Boolean indicator of whether or not the presented token - * is currently active. - */ - active: boolean; - /** - * A JSON string containing a space-separated list of - * scopes associated with this token - */ - scope?: string; - /** - * Client identifier for the OAuth 2.0 client that - * requested this token - */ - // eslint-disable-next-line camelcase -- the name is defined in the spec - client_id?: string; -} - -export abstract class Validator { - abstract validate(tokenRequest: http.IncomingMessage, scopes: Array, clients: RegExp): Promise; -} - -function extractTokenFromRequest(request: http.IncomingMessage) { - const headerToken = request.headers.authorization; - const url = new URL(request.url ?? "", `http://${request.headers.host}`); - const queryToken = url.searchParams.get("access_token"); - - if (headerToken != null && queryToken != null) { - throw new Error("Invalid request: only one authentication method is allowed"); - } - - if (queryToken != null) { - return queryToken; - } - - const matches = headerToken?.match(/Bearer\s(\S+)/); - - if (!matches) { - throw new Error("Invalid request: malformed authorization header"); - } - - return matches[1]; -} - -export class EndpointValidator extends Validator { - private config: IntrospectionEndpoint; - private agent: http.Agent; - constructor(config: IntrospectionEndpoint) { - super(); - this.config = config; - const endpoint = config.endpoint; - const allowSelfSigned = config?.allowSelfSigned ?? false; - this.agent = endpoint.startsWith("https") - ? new SecureAgent({ - rejectUnauthorized: !allowSelfSigned, - }) - : new http.Agent(); - } - - async validate(tokenRequest: http.IncomingMessage, scopes: Array, clients: RegExp): Promise { - const token = extractTokenFromRequest(tokenRequest); - const request = new Request(this.config.endpoint, { - method: "POST", - body: `token=${token}`, - headers: { - "content-type": "application/x-www-form-urlencoded", - }, - agent: this.agent, - }); - - if (this.config.credentials) { - await new BasicCredential(this.config.credentials).sign(request); - } - - const response = await fetch(request); - - if (response.status !== 200) { - throw new Error("Introspection endpoint error: " + response.statusText); - } - - const contentType = response.headers.get("content-type")?.split(";")[0]; - - if (contentType !== "application/json") { - throw new Error( - "Introspection response is not a json file. Content-Type: " + response.headers.get("content-type") - ); - } - - const validationResult = (await response.json()) as TokenInformation; - - if (validationResult.active === undefined) { - throw new Error("Malformed token introspection response: active is undefined"); - } - // Endpoint validation - if (!validationResult.active) { - return false; - } - if (scopes.length === 0) { - // We don't require any particular scope; accept the token - return true; - } - - if (validationResult.scope == null) { - // If the token doesn't have any scope and we already know that scopes.length > 0, - // then the token is not valid - return false; - } - - // Check if the token's scopes are allowed by the Thing Descriptor - const tokenScopes = validationResult.scope.split(" "); - const validScope = tokenScopes.some((tokenScope) => { - return scopes.some((thingScope) => tokenScope === thingScope); - }); - - if (!validScope) return false; - - // Check if the client was allowed in the servient configuration file - if (!validationResult.client_id?.match(clients)) { - return false; - } - - return true; - } -} - -export default function (method?: Method): EndpointValidator { - if (!method || !method?.name) { - throw new Error("Undefined oauth token validation method"); - } - - switch (method.name) { - case "introspection_endpoint": - return new EndpointValidator(method as IntrospectionEndpoint); - default: - throw new Error("Unsupported oauth token validation method " + method.name); - } -} diff --git a/packages/binding-http/src/routes/action.ts b/packages/binding-http/src/routes/action.ts deleted file mode 100644 index 2ed663e43..000000000 --- a/packages/binding-http/src/routes/action.ts +++ /dev/null @@ -1,115 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 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 { IncomingMessage, ServerResponse } from "http"; -import { Content, Helpers, ProtocolHelpers, createLoggers } from "@node-wot/core"; -import { - isEmpty, - respondUnallowedMethod, - securitySchemeToHttpHeader, - setCorsForThing, - validOrDefaultRequestContentType, -} from "./common"; -import HttpServer from "../http-server"; - -const { error, warn } = createLoggers("binding-http", "routes", "action"); - -export default async function actionRoute( - this: HttpServer, - req: IncomingMessage, - res: ServerResponse, - _params: { [k: string]: string | undefined } -): Promise { - if (_params.thing === undefined || _params.action === undefined) { - res.writeHead(400); - res.end(); - return; - } - const thing = this.getThings().get(_params.thing); - - if (thing == null) { - res.writeHead(404); - res.end(); - return; - } - - const contentTypeHeader = req.headers["content-type"]; - let contentType: string = Array.isArray(contentTypeHeader) ? contentTypeHeader[0] : contentTypeHeader; - try { - contentType = validOrDefaultRequestContentType(req, res, contentType); - } catch (error) { - warn( - `HttpServer received unsupported Content-Type from ${Helpers.toUriLiteral(req.socket.remoteAddress)}:${ - req.socket.remotePort - }` - ); - res.writeHead(415); - res.end("Unsupported Media Type"); - return; - } - - const action = thing.actions[_params.action]; - - if (action == null) { - res.writeHead(404); - res.end(); - return; - } - // TODO: refactor this part to move into a common place - setCorsForThing(req, res, thing); - let corsPreflightWithCredentials = false; - const securityScheme = thing.securityDefinitions[Helpers.toStringArray(thing.security)[0]].scheme; - - if (securityScheme !== "nosec" && !(await this.checkCredentials(thing, req))) { - if (req.method === "OPTIONS" && req.headers.origin != null) { - corsPreflightWithCredentials = true; - } else { - res.setHeader("WWW-Authenticate", `${securitySchemeToHttpHeader(securityScheme)} realm="${thing.id}"`); - res.writeHead(401); - res.end(); - return; - } - } - - if (req.method === "POST") { - const options: WoT.InteractionOptions & { formIndex: number } = { - formIndex: ProtocolHelpers.findRequestMatchingFormIndex(action.forms, this.scheme, req.url, contentType), - }; - const uriVariables = Helpers.parseUrlParameters(req.url, thing.uriVariables, action.uriVariables); - if (!isEmpty(uriVariables)) { - options.uriVariables = uriVariables; - } - try { - const output = await thing.handleInvokeAction(_params.action, new Content(contentType, req), options); - if (output != null) { - res.setHeader("Content-Type", output.type); - res.writeHead(200); - output.body.pipe(res); - } else { - res.writeHead(204); - res.end(); - } - } catch (err) { - const message = err instanceof Error ? err.message : JSON.stringify(err); - - error(`HttpServer on port ${this.getPort()} got internal error on invoke '${req.url}': ${message}`); - res.writeHead(500); - res.end(message); - } - } else { - // may have been OPTIONS that failed the credentials check - // as a result, we pass corsPreflightWithCredentials - respondUnallowedMethod(req, res, "POST", corsPreflightWithCredentials); - } -} diff --git a/packages/binding-http/src/routes/common.ts b/packages/binding-http/src/routes/common.ts deleted file mode 100644 index b4d164fad..000000000 --- a/packages/binding-http/src/routes/common.ts +++ /dev/null @@ -1,105 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 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 { ContentSerdes, ExposedThing, Helpers, createLoggers } from "@node-wot/core"; -import { IncomingMessage, ServerResponse } from "http"; - -const { debug, warn } = createLoggers("binding-http", "routes", "common"); - -export function respondUnallowedMethod( - req: IncomingMessage, - res: ServerResponse, - allowed: string, - corsPreflightWithCredentials = false -): void { - // Always allow OPTIONS to handle CORS pre-flight requests - if (!allowed.includes("OPTIONS")) { - allowed += ", OPTIONS"; - } - - const headers = req.headers; - const origin = headers.origin; - if (req.method === "OPTIONS" && origin != null && headers["access-control-request-method"] != null) { - debug( - `HttpServer received an CORS preflight request from ${Helpers.toUriLiteral(req.socket.remoteAddress)}:${ - req.socket.remotePort - }` - ); - if (corsPreflightWithCredentials) { - res.setHeader("Access-Control-Allow-Origin", origin); - res.setHeader("Access-Control-Allow-Credentials", "true"); - } else { - res.setHeader("Access-Control-Allow-Origin", "*"); - } - res.setHeader("Access-Control-Allow-Methods", allowed); - res.setHeader("Access-Control-Allow-Headers", "content-type, authorization, *"); - res.writeHead(200); - res.end(); - } else { - res.setHeader("Allow", allowed); - res.writeHead(405); - res.end("Method Not Allowed"); - } -} - -export function validOrDefaultRequestContentType( - req: IncomingMessage, - res: ServerResponse, - contentType: string -): string { - if (req.method === "PUT" || req.method === "POST") { - if (!contentType) { - // FIXME should be rejected with 400 Bad Request, as guessing is not good in M2M -> debug/testing flag to allow - // FIXME would need to check if payload is present - warn( - `HttpServer received no Content-Type from ${Helpers.toUriLiteral(req.socket.remoteAddress)}:${ - req.socket.remotePort - }` - ); - return ContentSerdes.DEFAULT; - } else if (ContentSerdes.get().getSupportedMediaTypes().indexOf(ContentSerdes.getMediaType(contentType)) < 0) { - throw new Error("Unsupported Media Type"); - } - return contentType; - } - return contentType; -} - -export function isEmpty(obj: Record): boolean { - for (const key in obj) { - if (Object.prototype.hasOwnProperty.call(obj, key)) return false; - } - return true; -} - -export function securitySchemeToHttpHeader(scheme: string): string { - const [first, ...rest] = scheme; - // HTTP Authentication Scheme for OAuth does not contain the version number - // see https://www.iana.org/assignments/http-authschemes/http-authschemes.xhtml - if (scheme === "oauth2") return "OAuth"; - return first.toUpperCase() + rest.join("").toLowerCase(); -} - -export function setCorsForThing(req: IncomingMessage, res: ServerResponse, thing: ExposedThing): void { - const securityScheme = thing.securityDefinitions[Helpers.toStringArray(thing.security)[0]].scheme; - // Set CORS headers - - const origin = req.headers.origin; - if (securityScheme !== "nosec" && origin != null) { - res.setHeader("Access-Control-Allow-Origin", origin); - res.setHeader("Access-Control-Allow-Credentials", "true"); - } else { - res.setHeader("Access-Control-Allow-Origin", "*"); - } -} diff --git a/packages/binding-http/src/routes/event.ts b/packages/binding-http/src/routes/event.ts deleted file mode 100644 index 91a29de3b..000000000 --- a/packages/binding-http/src/routes/event.ts +++ /dev/null @@ -1,115 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 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 { IncomingMessage, ServerResponse } from "http"; -import { Content, Helpers, ProtocolHelpers, createLoggers } from "@node-wot/core"; -import { isEmpty, respondUnallowedMethod, securitySchemeToHttpHeader, setCorsForThing } from "./common"; -import HttpServer from "../http-server"; - -const { warn, debug } = createLoggers("binding-http", "routes", "event"); -export default async function eventRoute( - this: HttpServer, - req: IncomingMessage, - res: ServerResponse, - _params: { [k: string]: string | undefined } -): Promise { - if (_params.thing === undefined || _params.event === undefined) { - res.writeHead(400); - res.end(); - return; - } - const thing = this.getThings().get(_params.thing); - - if (thing == null) { - res.writeHead(404); - res.end(); - return; - } - - const contentTypeHeader = req.headers["content-type"]; - const contentType: string = Array.isArray(contentTypeHeader) ? contentTypeHeader[0] : contentTypeHeader; - - const event = thing.events[_params.event]; - if (event == null) { - res.writeHead(404); - res.end(); - return; - } - // TODO: refactor this part to move into a common place - setCorsForThing(req, res, thing); - let corsPreflightWithCredentials = false; - const securityScheme = thing.securityDefinitions[Helpers.toStringArray(thing.security)[0]].scheme; - - if (securityScheme !== "nosec" && !(await this.checkCredentials(thing, req))) { - if (req.method === "OPTIONS" && req.headers.origin != null) { - corsPreflightWithCredentials = true; - } else { - res.setHeader("WWW-Authenticate", `${securitySchemeToHttpHeader(securityScheme)} realm="${thing.id}"`); - res.writeHead(401); - res.end(); - return; - } - } - - if (req.method === "GET") { - const options: WoT.InteractionOptions & { formIndex: number } = { - formIndex: ProtocolHelpers.findRequestMatchingFormIndex(event.forms, this.scheme, req.url, contentType), - }; - const uriVariables = Helpers.parseUrlParameters(req.url, thing.uriVariables, event.uriVariables); - if (!isEmpty(uriVariables)) { - options.uriVariables = uriVariables; - } - const eventName = _params.event; - const listener = async (value: Content) => { - try { - // send event data - if (!res.headersSent) { - // We are polite and use the same request as long as the client - // does not close the connection (or we hit the timeout; see below). - // Therefore we are sending the headers - // only if we didn't have sent them before. - res.setHeader("Content-Type", value.type); - res.writeHead(200); - } - value.body.pipe(res); - } catch (err) { - // Safe cast to NodeJS.ErrnoException we are checking if it is equal to ERR_HTTP_HEADERS_SENT - if ((err as NodeJS.ErrnoException)?.code === "ERR_HTTP_HEADERS_SENT") { - thing.handleUnsubscribeEvent(eventName, listener, options); - return; - } - const message = err instanceof Error ? err.message : JSON.stringify(err); - warn(`HttpServer on port ${this.getPort()} cannot process data for Event '${eventName}: ${message}'`); - res.writeHead(500); - res.end("Invalid Event Data"); - } - }; - - await thing.handleSubscribeEvent(eventName, listener, options); - res.on("close", () => { - debug(`HttpServer on port ${this.getPort()} closed Event connection`); - thing.handleUnsubscribeEvent(eventName, listener, options); - }); - res.setTimeout(60 * 60 * 1000, () => thing.handleUnsubscribeEvent(eventName, listener, options)); - } else if (req.method === "HEAD") { - // HEAD support for long polling subscription - res.writeHead(202); - res.end(); - } else { - // may have been OPTIONS that failed the credentials check - // as a result, we pass corsPreflightWithCredentials - respondUnallowedMethod(req, res, "GET", corsPreflightWithCredentials); - } - // resource found and response sent -} diff --git a/packages/binding-http/src/routes/properties.ts b/packages/binding-http/src/routes/properties.ts deleted file mode 100644 index a9bc34993..000000000 --- a/packages/binding-http/src/routes/properties.ts +++ /dev/null @@ -1,91 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 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 { IncomingMessage, ServerResponse } from "http"; -import { ContentSerdes, Helpers, PropertyContentMap, createLoggers } from "@node-wot/core"; -import { respondUnallowedMethod, securitySchemeToHttpHeader, setCorsForThing } from "./common"; -import HttpServer from "../http-server"; - -const { error } = createLoggers("binding-http", "routes", "properties"); -export default async function propertiesRoute( - this: HttpServer, - req: IncomingMessage, - res: ServerResponse, - _params: { [k: string]: string | undefined } -): Promise { - if (_params.thing === undefined) { - res.writeHead(400); - res.end(); - return; - } - - const thing = this.getThings().get(_params.thing); - - if (thing == null) { - res.writeHead(404); - res.end(); - return; - } - - // TODO: refactor this part to move into a common place - setCorsForThing(req, res, thing); - let corsPreflightWithCredentials = false; - const securityScheme = thing.securityDefinitions[Helpers.toStringArray(thing.security)[0]].scheme; - - if (securityScheme !== "nosec" && !(await this.checkCredentials(thing, req))) { - if (req.method === "OPTIONS" && req.headers.origin != null) { - corsPreflightWithCredentials = true; - } else { - res.setHeader("WWW-Authenticate", `${securitySchemeToHttpHeader(securityScheme)} realm="${thing.id}"`); - res.writeHead(401); - res.end(); - return; - } - } - - // all properties - if (req.method === "GET") { - try { - const propMap: PropertyContentMap = await thing.handleReadAllProperties({ - formIndex: 0, - }); - res.setHeader("Content-Type", ContentSerdes.DEFAULT); // contentType handling? - res.writeHead(200); - const recordResponse: Record = {}; - for (const key of propMap.keys()) { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- map key is always present as checked above - const content = propMap.get(key)!; - const value = ContentSerdes.get().contentToValue( - { type: ContentSerdes.DEFAULT, body: await content.toBuffer() }, - {} - ); - recordResponse[key] = value; - } - res.end(JSON.stringify(recordResponse)); - } catch (err) { - const message = err instanceof Error ? err.message : JSON.stringify(err); - - error(`HttpServer on port ${this.getPort()} got internal error on invoke '${req.url}': ${message}`); - res.writeHead(500); - res.end(message); - } - } else if (req.method === "HEAD") { - res.writeHead(202); - res.end(); - } else { - // may have been OPTIONS that failed the credentials check - // as a result, we pass corsPreflightWithCredentials - respondUnallowedMethod(req, res, "GET", corsPreflightWithCredentials); - } -} diff --git a/packages/binding-http/src/routes/property-observe.ts b/packages/binding-http/src/routes/property-observe.ts deleted file mode 100644 index 1c2aac2cc..000000000 --- a/packages/binding-http/src/routes/property-observe.ts +++ /dev/null @@ -1,118 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 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 { IncomingMessage, ServerResponse } from "http"; -import { Content, Helpers, ProtocolHelpers, createLoggers } from "@node-wot/core"; -import { isEmpty, respondUnallowedMethod, securitySchemeToHttpHeader, setCorsForThing } from "./common"; -import HttpServer from "../http-server"; - -const { debug, warn } = createLoggers("binding-http", "routes", "property", "observe"); -export default async function propertyObserveRoute( - this: HttpServer, - req: IncomingMessage, - res: ServerResponse, - _params: { [k: string]: string | undefined } -): Promise { - if (_params.thing === undefined || _params.property === undefined) { - res.writeHead(400); - res.end(); - return; - } - - const thing = this.getThings().get(_params.thing); - - if (thing == null) { - res.writeHead(404); - res.end(); - return; - } - - const contentTypeHeader = req.headers["content-type"]; - const contentType: string = Array.isArray(contentTypeHeader) ? contentTypeHeader[0] : contentTypeHeader; - const property = thing.properties[_params.property]; - - if (property == null) { - res.writeHead(404); - res.end(); - return; - } - - const options: WoT.InteractionOptions & { formIndex: number } = { - formIndex: ProtocolHelpers.findRequestMatchingFormIndex(property.forms, this.scheme, req.url, contentType), - }; - const uriVariables = Helpers.parseUrlParameters(req.url, thing.uriVariables, property.uriVariables); - if (!isEmpty(uriVariables)) { - options.uriVariables = uriVariables; - } - - // TODO: refactor this part to move into a common place - setCorsForThing(req, res, thing); - let corsPreflightWithCredentials = false; - const securityScheme = thing.securityDefinitions[Helpers.toStringArray(thing.security)[0]].scheme; - - if (securityScheme !== "nosec" && !(await this.checkCredentials(thing, req))) { - if (req.method === "OPTIONS" && req.headers.origin != null) { - corsPreflightWithCredentials = true; - } else { - res.setHeader("WWW-Authenticate", `${securitySchemeToHttpHeader(securityScheme)} realm="${thing.id}"`); - res.writeHead(401); - res.end(); - return; - } - } - const propertyName = _params.property; - if (req.method === "GET") { - const listener = async (value: Content) => { - try { - if (!res.headersSent) { - // We are polite and use the same request as long as the client - // does not close the connection (or we hit the timeout; see below). - // Therefore we are sending the headers - // only if we didn't have sent them before. - res.setHeader("Content-Type", value.type); - res.writeHead(200); - } - // send property data - value.body.pipe(res); - } catch (err) { - // Safe cast to NodeJS.ErrnoException we are checking if it is equal to ERR_HTTP_HEADERS_SENT - if ((err as NodeJS.ErrnoException)?.code === "ERR_HTTP_HEADERS_SENT") { - thing.handleUnobserveProperty(propertyName, listener, options); - return; - } - const message = err instanceof Error ? err.message : JSON.stringify(err); - warn( - `HttpServer on port ${this.getPort()} cannot process data for Property '${ - _params.property - }: ${message}'` - ); - res.writeHead(500); - res.end("Invalid Property Data"); - } - }; - await thing.handleObserveProperty(_params.property, listener, options); - res.on("finish", () => { - debug(`HttpServer on port ${this.getPort()} closed connection`); - thing.handleUnobserveProperty(propertyName, listener, options); - }); - res.setTimeout(60 * 60 * 1000, () => thing.handleUnobserveProperty(propertyName, listener, options)); - } else if (req.method === "HEAD") { - // HEAD support for long polling subscription - // TODO: set the Content-Type header to the type of the property - res.writeHead(202); - res.end(); - } else { - respondUnallowedMethod(req, res, "GET", corsPreflightWithCredentials); - } -} diff --git a/packages/binding-http/src/routes/property.ts b/packages/binding-http/src/routes/property.ts deleted file mode 100644 index 916bc6a9b..000000000 --- a/packages/binding-http/src/routes/property.ts +++ /dev/null @@ -1,133 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 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 { IncomingMessage, ServerResponse } from "http"; -import { Content, Helpers, ProtocolHelpers, createLoggers } from "@node-wot/core"; -import { - isEmpty, - respondUnallowedMethod, - securitySchemeToHttpHeader, - setCorsForThing, - validOrDefaultRequestContentType, -} from "./common"; -import HttpServer from "../http-server"; - -const { error, warn } = createLoggers("binding-http", "routes", "property"); -export default async function propertyRoute( - this: HttpServer, - req: IncomingMessage, - res: ServerResponse, - _params: { [k: string]: string | undefined } -): Promise { - if (_params.thing === undefined || _params.property === undefined) { - res.writeHead(400); - res.end(); - return; - } - - const thing = this.getThings().get(_params.thing); - - if (thing == null) { - res.writeHead(404); - res.end(); - return; - } - - const contentTypeHeader = req.headers["content-type"]; - let contentType: string = Array.isArray(contentTypeHeader) ? contentTypeHeader[0] : contentTypeHeader; - try { - contentType = validOrDefaultRequestContentType(req, res, contentType); - } catch (error) { - warn( - `HttpServer received unsupported Content-Type from ${Helpers.toUriLiteral(req.socket.remoteAddress)}:${ - req.socket.remotePort - }` - ); - res.writeHead(415); - res.end("Unsupported Media Type"); - return; - } - - const property = thing.properties[_params.property]; - - if (property == null) { - res.writeHead(404); - res.end(); - return; - } - - const options: WoT.InteractionOptions & { formIndex: number } = { - formIndex: ProtocolHelpers.findRequestMatchingFormIndex(property.forms, this.scheme, req.url, contentType), - }; - const uriVariables = Helpers.parseUrlParameters(req.url, thing.uriVariables, property.uriVariables); - - if (!isEmpty(uriVariables)) { - options.uriVariables = uriVariables; - } - - // TODO: refactor this part to move into a common place - setCorsForThing(req, res, thing); - let corsPreflightWithCredentials = false; - const securityScheme = thing.securityDefinitions[Helpers.toStringArray(thing.security)[0]].scheme; - - if (securityScheme !== "nosec" && !(await this.checkCredentials(thing, req))) { - if (req.method === "OPTIONS" && req.headers.origin != null) { - corsPreflightWithCredentials = true; - } else { - res.setHeader("WWW-Authenticate", `${securitySchemeToHttpHeader(securityScheme)} realm="${thing.id}"`); - res.writeHead(401); - res.end(); - return; - } - } - - if (req.method === "GET") { - try { - const content = await thing.handleReadProperty(_params.property, options); - res.setHeader("Content-Type", content.type); - res.writeHead(200); - content.body.pipe(res); - } catch (err) { - const message = err instanceof Error ? err.message : JSON.stringify(err); - - error(`HttpServer on port ${this.getPort()} got internal error on read '${req.url}': ${message}`); - res.writeHead(500); - res.end(message); - } - } else if (req.method === "PUT") { - const readOnly: boolean = property.readOnly ?? false; - if (readOnly) { - respondUnallowedMethod(req, res, "GET, PUT"); - return; - } - - try { - await thing.handleWriteProperty(_params.property, new Content(contentType, req), options); - - res.writeHead(204); - res.end("Changed"); - } catch (err) { - const message = err instanceof Error ? err.message : JSON.stringify(err); - - error(`HttpServer on port ${this.getPort()} got internal error on invoke '${req.url}': ${message}`); - res.writeHead(500); - res.end(message); - } - // resource found and response sent - } else { - // may have been OPTIONS that failed the credentials check - // as a result, we pass corsPreflightWithCredentials - respondUnallowedMethod(req, res, "GET, PUT", corsPreflightWithCredentials); - } // Property exists? -} diff --git a/packages/binding-http/src/routes/thing-description.ts b/packages/binding-http/src/routes/thing-description.ts deleted file mode 100644 index ef1c6618a..000000000 --- a/packages/binding-http/src/routes/thing-description.ts +++ /dev/null @@ -1,188 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 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 { ContentSerdes, createLoggers } from "@node-wot/core"; -import { IncomingMessage, ServerResponse } from "http"; -import { ExposedThing, ThingDescription } from "wot-typescript-definitions"; -import * as acceptLanguageParser from "accept-language-parser"; -import * as TD from "@node-wot/td-tools"; -import HttpServer from "../http-server"; - -const { debug } = createLoggers("binding-http", "routes", "thing-description"); - -function resetMultiLangInteraction( - interactions: ThingDescription["properties"] | ThingDescription["actions"] | ThingDescription["events"], - prefLang: string -) { - if (interactions) { - for (const interName in interactions) { - // unset any current title and/or description - delete interactions[interName].title; - delete interactions[interName].description; - - // use new language title - const titles = interactions[interName].titles; - if (titles) { - for (const titleLang in titles) { - if (titleLang.startsWith(prefLang)) { - interactions[interName].title = titles[titleLang]; - } - } - } - - // use new language description - const descriptions = interactions[interName].descriptions; - if (descriptions) { - for (const descLang in descriptions) { - if (descLang.startsWith(prefLang)) { - interactions[interName].description = descriptions[descLang]; - } - } - } - - // unset any multilanguage titles and/or descriptions - delete interactions[interName].titles; - delete interactions[interName].descriptions; - } - } -} - -function resetMultiLangThing(thing: ThingDescription, prefLang: string) { - // TODO can we reset "title" to another name given that title is used in URI creation? - - // set @language in @context - TD.setContextLanguage(thing, prefLang, true); - - // use new language title - if (thing.titles) { - for (const titleLang in thing.titles) { - if (titleLang.startsWith(prefLang)) { - thing.title = thing.titles[titleLang]; - } - } - } - - // use new language description - if (thing.descriptions) { - for (const titleLang in thing.descriptions) { - if (titleLang.startsWith(prefLang)) { - thing.description = thing.descriptions[titleLang]; - } - } - } - - // remove any titles or descriptions and update title / description accordingly - delete thing.titles; - delete thing.descriptions; - - // reset multi-language terms for interactions - resetMultiLangInteraction(thing.properties, prefLang); - resetMultiLangInteraction(thing.actions, prefLang); - resetMultiLangInteraction(thing.events, prefLang); -} - -/** - * Look for language negotiation through the Accept-Language header field of HTTP (e.g., "de", "de-CH", "en-US,en;q=0.5") - * Note: "title" on thing level is mandatory term --> check whether "titles" exists for multi-languages - * Note: HTTP header names are case-insensitive and req.headers seems to contain them in lowercase - * - * - * @param td - * @param thing - * @param req - */ -function negotiateLanguage(td: ThingDescription, thing: ExposedThing, req: IncomingMessage) { - const acceptLanguage = req.headers["accept-language"]; - const noPreference = acceptLanguage == null || acceptLanguage === "*"; - - if (noPreference) { - return; - } - - if (td.titles != null) { - const supportedLanguages = Object.keys(td.titles); // e.g., ['fr', 'en'] - // the loose option allows partial matching on supported languages (e.g., returns "de" for "de-CH") - const prefLang = acceptLanguageParser.pick(supportedLanguages, acceptLanguage, { - loose: true, - }); - if (prefLang != null) { - // if a preferred language can be found use it - debug(`TD language negotiation through the Accept-Language header field of HTTP leads to "${prefLang}"`); - // TODO: reset titles and descriptions to only contain the preferred language - resetMultiLangThing(td, prefLang); - } - } -} - -export default async function thingDescriptionRoute( - this: HttpServer, - req: IncomingMessage, - res: ServerResponse, - _params: { [k: string]: string | undefined } -): Promise { - if (_params.thing === undefined) { - res.writeHead(400); - res.end(); - return; - } - - const thing = this.getThings().get(_params.thing); - if (thing == null) { - res.writeHead(404); - res.end(); - return; - } - - const td = thing.getThingDescription(); - const contentSerdes = ContentSerdes.get(); - - // TODO: Parameters need to be considered here as well - const acceptValues = req.headers.accept?.split(",").map((acceptValue) => acceptValue.split(";")[0]) ?? [ - ContentSerdes.TD, - ]; - // TODO: Better handling of wildcard values - const filteredAcceptValues = acceptValues - .map((acceptValue) => { - if (acceptValue === "*/*") { - return ContentSerdes.TD; - } - return acceptValue; - }) - .filter((acceptValue) => contentSerdes.isSupported(acceptValue)) - .sort((a, b) => { - // weight function last places weight more than first: application/td+json > application/json > text/html - const aWeight = ["text/html", "application/json", "application/td+json"].findIndex((value) => value === a); - const bWeight = ["text/html", "application/json", "application/td+json"].findIndex((value) => value === b); - return bWeight - aWeight; - }); - - if (filteredAcceptValues.length > 0) { - const contentType = filteredAcceptValues[0]; - const content = contentSerdes.valueToContent(td, undefined, contentType); - const payload = await content.toBuffer(); - - negotiateLanguage(td, thing, req); - res.setHeader("Access-Control-Allow-Origin", "*"); - res.setHeader("Content-Type", contentType); - res.writeHead(200); - debug(`Sending HTTP response for TD with Content-Type ${contentType}.`); - res.end(payload); - return; - } - - debug(`Request contained an accept header with the values ${acceptValues}, none of which are supported.`); - res.writeHead(406); - res.end(`Accept header contained no Content-Types supported by this resource. (Was ${acceptValues})`); -} diff --git a/packages/binding-http/src/routes/things.ts b/packages/binding-http/src/routes/things.ts deleted file mode 100644 index 93d4aa162..000000000 --- a/packages/binding-http/src/routes/things.ts +++ /dev/null @@ -1,46 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 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 { ContentSerdes, Helpers } from "@node-wot/core"; -import { IncomingMessage, ServerResponse } from "http"; -import HttpServer from "../http-server"; - -export default function thingsRoute( - this: HttpServer, - req: IncomingMessage, - res: ServerResponse, - _params: unknown -): void { - res.setHeader("Access-Control-Allow-Origin", "*"); - res.setHeader("Content-Type", ContentSerdes.DEFAULT); - res.writeHead(200); - const list = []; - for (const address of Helpers.getAddresses()) { - for (const name in this.getThings()) { - // FIXME the undefined check should NOT be necessary (however there seems to be null in it) - if (name) { - list.push( - this.scheme + - "://" + - Helpers.toUriLiteral(address) + - ":" + - this.getPort() + - "/" + - encodeURIComponent(name) - ); - } - } - } - res.end(JSON.stringify(list)); -} diff --git a/packages/binding-http/src/subscription-protocols.ts b/packages/binding-http/src/subscription-protocols.ts deleted file mode 100644 index 024447be9..000000000 --- a/packages/binding-http/src/subscription-protocols.ts +++ /dev/null @@ -1,134 +0,0 @@ -/* eslint-disable dot-notation -- we are using private functions from HttpClient */ -/******************************************************************************** - * Copyright (c) 2020 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 { HttpClient, HttpForm } from "./http"; -import EventSource from "eventsource"; -import { Content, ContentSerdes, ProtocolHelpers, createLoggers } from "@node-wot/core"; -import { Readable } from "stream"; - -const { debug } = createLoggers("binding-http", "subscription-protocols"); - -export interface InternalSubscription { - open(next: (value: Content) => void, error?: (error: Error) => void, complete?: () => void): Promise; - close(): void; -} -export class LongPollingSubscription implements InternalSubscription { - private form: HttpForm; - private client: HttpClient; - - private closed: boolean; - private abortController: AbortController; - /** - * - */ - constructor(form: HttpForm, client: HttpClient) { - this.form = form; - this.client = client; - this.closed = false; - this.abortController = new AbortController(); - } - - open(next: (value: Content) => void, error?: (error: Error) => void, complete?: () => void): Promise { - return new Promise((resolve, reject) => { - const polling = async (handshake: boolean) => { - try { - if (handshake) { - const headRequest = await this.client["generateFetchRequest"](this.form, "HEAD", { - timeout: 1000, - signal: this.abortController.signal, - }); - const result = await this.client["fetch"](headRequest); - if (result.ok) resolve(); - } - - // long timeout for long polling - const request = await this.client["generateFetchRequest"](this.form, "GET", { - timeout: 60 * 60 * 1000, - signal: this.abortController.signal, - }); - debug(`HttpClient (subscribeResource) sending ${request.method} to ${request.url}`); - - const result = await this.client["fetch"](request); - - this.client["checkFetchResponse"](result); - - debug(`HttpClient received ${result.status} from ${request.url}`); - - debug(`HttpClient received headers: ${JSON.stringify(result.headers.raw())}`); - debug(`HttpClient received Content-Type: ${result.headers.get("content-type")}`); - - if (!this.closed) { - // in browsers node-fetch uses the native fetch, which returns a ReadableStream - // not complaint with node. Therefore we have to force the conversion here. - const body = ProtocolHelpers.toNodeStream(result.body as Readable); - next(new Content(result.headers.get("content-type") ?? ContentSerdes.DEFAULT, body)); - polling(false); - } - - complete && complete(); - } catch (e) { - const err = e instanceof Error ? e : new Error(JSON.stringify(e)); - error && error(err); - complete && complete(); - reject(e); - } - }; - polling(true); - }); - } - - close(): void { - this.abortController.abort(); - this.closed = true; - } -} - -export class SSESubscription implements InternalSubscription { - private form: HttpForm; - private eventSource: EventSource | undefined; - private closed: boolean; - /** - * - */ - constructor(form: HttpForm) { - this.form = form; - this.closed = false; - } - - open(next: (value: Content) => void, error?: (error: Error) => void, complete?: () => void): Promise { - return new Promise((resolve, reject) => { - this.eventSource = new EventSource(this.form.href); - - this.eventSource.onopen = (event) => { - debug(`HttpClient (subscribeResource) Server-Sent Event connection is opened to ${this.form.href}`); - resolve(); - }; - this.eventSource.onmessage = (event) => { - debug(`HttpClient received ${JSON.stringify(event)} from ${this.form.href}`); - const output = new Content(this.form.contentType ?? ContentSerdes.DEFAULT, Readable.from(event.data)); - next(output); - }; - this.eventSource.onerror = function (event) { - error?.(new Error(event.toString())); - complete && complete(); - reject(event.toString()); - }; - }); - } - - close(): void { - this.eventSource?.close(); - } -} diff --git a/packages/binding-http/test/credential-test.ts b/packages/binding-http/test/credential-test.ts deleted file mode 100644 index d490edc29..000000000 --- a/packages/binding-http/test/credential-test.ts +++ /dev/null @@ -1,126 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2021 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 { suite, test } from "@testdeck/mocha"; -import { APIKeySecurityScheme, BasicSecurityScheme, BearerSecurityScheme } from "@node-wot/td-tools"; -import * as chai from "chai"; -import chaiAsPromised from "chai-as-promised"; -import { Request } from "node-fetch"; -import { - BasicCredential, - BasicCredentialConfiguration, - BearerCredential, - BearerCredentialConfiguration, - BasicKeyCredential, - BasicKeyCredentialConfiguration, - TuyaCustomBearer, - TuyaCustomBearerCredentialConfiguration, -} from "./../src/credential"; -import timekeeper from "timekeeper"; -import { TuyaCustomBearerSecurityScheme } from "../src/http"; - -chai.should(); -chai.use(chaiAsPromised); - -@suite("Credentials auth test suite") -class CredentialTest { - @test async "should sign in with basic"(): Promise { - const scheme: BasicSecurityScheme & { name: string } = { - scheme: "basic", - in: "header", - name: "testHeader", - }; - - const config: BasicCredentialConfiguration = { - username: "admin", - password: "password", - }; - - const request = new Request("http://test.com/"); - - const basic = new BasicCredential(config, scheme); - const response = await basic.sign(request); - - chai.expect(response.headers?.get(scheme.name)).be.equal( - "Basic " + Buffer.from(config.username + ":" + config.password).toString("base64") - ); - } - - @test async "should sign in with bearer"(): Promise { - const scheme: BearerSecurityScheme & { name: string } = { - scheme: "bearer", - in: "header", - name: "testHeader", - }; - - const config: BearerCredentialConfiguration = { - token: "token", - }; - - const request = new Request("http://test.com/"); - - const bearer = new BearerCredential(config, scheme); - const response = await bearer.sign(request); - - chai.expect(response.headers?.get(scheme.name)).be.equal("Bearer " + config.token); - } - - @test async "should sign in with basic key"(): Promise { - const scheme: APIKeySecurityScheme & { name: string } = { - scheme: "apikey", - in: "header", - name: "testHeader", - }; - - const config: BasicKeyCredentialConfiguration = { - apiKey: "apiKey", - }; - - const request = new Request("http://test.com/"); - - const basic = new BasicKeyCredential(config, scheme); - const response = await basic.sign(request); - - chai.expect(response.headers?.get(scheme.name)).be.equal(config.apiKey); - } - - @test async "should sign in with TuyaCustomBearer"(): Promise { - timekeeper.freeze(new Date("2021-12-15T00:00:00.000Z")); - - class MockTuyaCustomBearer extends TuyaCustomBearer { - protected async requestAndRefreshToken(refresh: boolean) { - this.token = "AccessToken"; - this.refreshToken = "RefreshToken"; - this.expireTime = new Date(Date.now() + Date.now() * 1000); - } - } - - const sign = "B052CABF5F47E7AD3E1EDFFFD920E27FA7D44969F8082CA9FB94DE2315CC74EA"; - const credentialsConfig: TuyaCustomBearerCredentialConfiguration = { - key: "key", - secret: "secret", - }; - const scheme: TuyaCustomBearerSecurityScheme = { - scheme: "TuyaCustomBearer", - baseUri: "base", - }; - const request = new Request("http://test.com"); - const credentials = new MockTuyaCustomBearer(credentialsConfig, scheme); - - const response = await credentials.sign(request); - timekeeper.reset(); - chai.expect(response.headers.get("sign")).be.equal(sign); - } -} diff --git a/packages/binding-http/test/http-client-basic-test.ts b/packages/binding-http/test/http-client-basic-test.ts deleted file mode 100644 index e57424bbd..000000000 --- a/packages/binding-http/test/http-client-basic-test.ts +++ /dev/null @@ -1,123 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2020 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 { suite, test } from "@testdeck/mocha"; -import express from "express"; -import { HttpClient } from "../src/http"; -import * as https from "https"; -import { BasicSecurityScheme } from "@node-wot/td-tools"; -import * as chai from "chai"; -import chaiAsPromised from "chai-as-promised"; -import { fail } from "assert"; -import { readFileSync } from "fs"; - -chai.should(); -chai.use(chaiAsPromised); - -function mockService(req: express.Request, res: express.Response) { - // ----------------------------------------------------------------------- - // authentication middleware - - const auth = { login: "admin", password: "password" }; // change this - - // parse login and password from headers - const b64auth = (req.headers.authorization ?? "").split(" ")[1] ?? ""; - const [login, password] = Buffer.from(b64auth, "base64").toString().split(":"); - - // Verify login and password are set and correct - if (login && password && login === auth.login && password === auth.password) { - // Access granted... - res.write("Access granted"); - res.end(); - return; - } - - // Access denied... - res.set("WWW-Authenticate", 'Basic realm="401"'); // change this - res.status(401).send("Authentication required."); // custom message - - // ----------------------------------------------------------------------- -} -@suite("HTTP auth basic client implementation") -class HttpClientBasicTest { - private client!: HttpClient; - - private static server: https.Server; - - static async before(): Promise { - const app = express(); - app.use(mockService); - - return new Promise((resolve) => { - HttpClientBasicTest.server = https - .createServer( - { - key: readFileSync("./test/server.key"), - cert: readFileSync("./test/server.cert"), - }, - app - ) - .listen(3001, "127.0.0.1", resolve); - }); - } - - static async after(): Promise { - return new Promise((resolve) => { - HttpClientBasicTest.server.close(() => resolve()); - }); - } - - before() { - this.client = new HttpClient({ allowSelfSigned: true }, true); - } - - async after() { - await this.client.stop(); - } - - @test async "should authorize client with basic"(): Promise { - const scheme: BasicSecurityScheme = { - scheme: "basic", - in: "header", - }; - - this.client.setSecurity([scheme], { username: "admin", password: "password" }); - const resource = await this.client.readResource({ - href: "https://127.0.0.1:3001", - }); - const body = await resource.toBuffer(); - body.toString("ascii").should.eql("Access granted"); - } - - @test async "should fail to authorize client with basic"(): Promise { - const scheme: BasicSecurityScheme = { - scheme: "basic", - in: "header", - }; - - this.client.setSecurity([scheme], { username: "other", password: "other" }); - try { - await this.client.readResource({ href: "https://127.0.0.1:3001" }); - } catch (error) { - if (error instanceof Error) { - error.message.should.eql("Client error: Unauthorized"); - } else { - fail("No proper error instance object"); - } - return; - } - fail("should fail to authorize client with basic"); - } -} diff --git a/packages/binding-http/test/http-client-oauth-tests.ts b/packages/binding-http/test/http-client-oauth-tests.ts deleted file mode 100644 index 9ec3ca56c..000000000 --- a/packages/binding-http/test/http-client-oauth-tests.ts +++ /dev/null @@ -1,152 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2020 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 https from "https"; -import { suite, test } from "@testdeck/mocha"; -import express from "express"; -import { HttpClient } from "../src/http"; -import { OAuth2SecurityScheme } from "@node-wot/td-tools"; - -import InMemoryModel from "./memory-model"; -import { promisify } from "util"; -import { readFileSync } from "fs"; - -import OAuthServer from "@node-oauth/express-oauth-server"; -import bodyParser from "body-parser"; - -@suite("HTTP oauth client implementation") -class HttpClientOAuthTest { - private client!: HttpClient; - static model: InMemoryModel; - static server: https.Server; - - static before(): Promise { - const app: express.Express = express(); - HttpClientOAuthTest.model = new InMemoryModel(); - - const oauth = new OAuthServer({ - model: HttpClientOAuthTest.model, - accessTokenLifetime: 1, // One minute; less is not possible - }); - - app.use(bodyParser.json()); - app.use("/resource", oauth.authenticate()); - app.use("/token", bodyParser.urlencoded({ extended: false })); - app.use("/token", oauth.token()); - - app.use("/resource", (req: express.Request, res: express.Response) => { - res.send("Ok!"); - }); - - return new Promise((resolve) => { - HttpClientOAuthTest.server = https - .createServer( - { - key: readFileSync("./test/server.key"), - cert: readFileSync("./test/server.cert"), - }, - app - ) - .listen(3000, "127.0.0.1", resolve); - }); - } - - static async after() { - return await promisify(this.server.close.bind(this.server))(); - } - - before() { - this.client = new HttpClient({ allowSelfSigned: true }, true); - } - - async after() { - await this.client.stop(); - } - - @test async "should authorize client with client flow"() { - const scheme: OAuth2SecurityScheme = { - scheme: "oauth2", - flow: "client", - token: "https://127.0.0.1:3000/token", - scopes: ["test"], - }; - this.client.setSecurity([scheme], { clientId: "thom", clientSecret: "nightworld" }); - const resource = await this.client.readResource({ - href: "https://127.0.0.1:3000/resource", - }); - const body = await resource.toBuffer(); - body.toString("ascii").should.eql("Ok!"); - } - - @test async "should authorize client with resource owener flow"() { - const scheme: OAuth2SecurityScheme = { - scheme: "oauth2", - flow: "password", - token: "https://127.0.0.1:3000/token", - scopes: ["test"], - }; - this.client.setSecurity([scheme], { - clientId: "thom", - clientSecret: "nightworld", - username: "thomseddon", - password: "nightworld", - }); - const resource = await this.client.readResource({ - href: "https://127.0.0.1:3000/resource", - }); - const body = await resource.toBuffer(); - body.toString("ascii").should.eql("Ok!"); - } - - @test async "should refresh token"() { - const scheme: OAuth2SecurityScheme = { - scheme: "oauth2", - flow: "client", - token: "https://127.0.0.1:3000/token", - scopes: ["test"], - }; - const model = HttpClientOAuthTest.model; - await model.expireAllTokens(); - this.client.setSecurity([scheme], { clientId: "thom", clientSecret: "nightworld" }); - const resource = await this.client.readResource({ - href: "https://127.0.0.1:3000/resource", - }); - const body = await resource.toBuffer(); - body.toString("ascii").should.eql("Ok!"); - } - - @test async "should refresh token with resource owener flow"() { - const scheme: OAuth2SecurityScheme = { - scheme: "oauth2", - flow: "password", - token: "https://127.0.0.1:3000/token", - scopes: ["test"], - }; - const model = HttpClientOAuthTest.model; - - model.expireAllTokens(); - this.client.setSecurity([scheme], { - clientId: "thom", - clientSecret: "nightworld", - username: "thomseddon", - password: "nightworld", - }); - const resource = await this.client.readResource({ - href: "https://127.0.0.1:3000/resource", - }); - const body = await resource.toBuffer(); - body.toString("ascii").should.eql("Ok!"); - } -} diff --git a/packages/binding-http/test/http-client-test.ts b/packages/binding-http/test/http-client-test.ts deleted file mode 100644 index 31ce88cfb..000000000 --- a/packages/binding-http/test/http-client-test.ts +++ /dev/null @@ -1,642 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2018 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 - ********************************************************************************/ - -/** - * Protocol test suite to test protocol implementations - */ - -import { suite, test } from "@testdeck/mocha"; -import chai, { expect, should } from "chai"; - -import * as http from "http"; - -import { Content, DefaultContent, ContentSerdes, createLoggers, ProtocolServer } from "@node-wot/core"; - -import { Readable } from "stream"; -import { text } from "node:stream/consumers"; - -import HttpClient from "../src/http-client"; -import { HttpForm } from "../src/http"; - -import express from "express"; -import serveStatic from "serve-static"; -import { DataSchema, DataSchemaValue, Form } from "wot-typescript-definitions"; -import SseStream from "ssestream"; -import FakeTimers from "@sinonjs/fake-timers"; - -// Add spies -import spies from "chai-spies"; -import { fail } from "assert"; - -const { debug } = createLoggers("binding-http", "http-client-test"); - -// should must be called to augment all variables -should(); - -chai.use(spies); - -interface TestVector { - op: Array; - method?: string; - schema?: DataSchema; - payload?: DataSchemaValue; - headers?: Record; - form: Form; -} - -const port1 = 30001; -const port2 = 30002; -const port3 = 30001; -const port4 = 30003; - -class TestHttpServer implements ProtocolServer { - public readonly scheme: string = "test"; - - private testVector: TestVector | undefined; - - private readonly port: number = 60606; - private readonly address: string | undefined = undefined; - private readonly server: http.Server; - - constructor(port?: number, address?: string) { - if (port !== undefined) { - this.port = port; - } - if (address !== undefined) { - this.address = address; - } - this.server = http.createServer((req, res) => { - this.checkRequest(req, res); - }); - } - - public async start(): Promise { - return new Promise((resolve) => { - this.server.listen(this.port, this.address, resolve); - }); - } - - public async stop(): Promise { - return new Promise((resolve) => { - this.server.close(() => { - resolve(); - }); - }); - } - - /** returns server port number and indicates that server is running when larger than -1 */ - public getPort(): number { - const address = this.server?.address(); - - if (typeof address === "object") { - return address?.port ?? -1; - } - - const port = parseInt(address); - - if (isNaN(port)) { - return -1; - } - - return port; - } - - public async expose(thing: unknown): Promise {} - - public async destroy(thingId: string): Promise { - return false; - } - - public setTestVector(vector: TestVector) { - if (vector.op == null) { - throw new Error("No vector op given"); - } - if (vector.form["htv:methodName"] == null) { - // TODO also check all array entries - switch (vector.op[0]) { - case "readproperty": - vector.method = "GET"; - break; - case "writeproperty": - vector.method = "PUT"; - break; - case "observeproperty": - vector.method = "GET"; - break; - case "invokeaction": - vector.method = "POST"; - break; - case "subscribeevent": - vector.method = "GET"; - break; - default: - throw new Error("Unknown op " + vector.op); - } - } else { - vector.method = vector.form["htv:methodName"]; - } - this.testVector = vector; - } - - private checkRequest(req: http.IncomingMessage, res: http.ServerResponse) { - if (!this.testVector) throw new Error("No test vector given"); - - expect(req.method).to.equal(this.testVector.method); - expect(req.url).to.equal(new URL(this.testVector.form.href).pathname); - - if (this.testVector.headers) { - expect(req.headers).to.include(this.testVector.headers); - } - - if (this.testVector.payload !== undefined) { - // load payload - const body: Array = []; - req.on("data", (data) => { - body.push(data); - }); - req.on("end", () => { - if (!this.testVector) { - chai.assert.fail("No test vector given"); - } - let value; - try { - value = ContentSerdes.get().contentToValue( - { type: ContentSerdes.DEFAULT, body: Buffer.concat(body) }, - this.testVector.schema ?? { type: "string" } - ); - } catch (err) { - throw new Error("Cannot deserialize client payload"); - } - expect(value).to.equal(this.testVector?.payload); - res.end(); - }); - } else { - res.end(); - } - } -} - -@suite("HTTP client basic operations") -class HttpClientTest1 { - static httpServer: TestHttpServer; - static async before(): Promise { - HttpClientTest1.httpServer = new TestHttpServer(port1); - await HttpClientTest1.httpServer.start(); - expect(HttpClientTest1.httpServer.getPort()).to.equal(port1); - } - - static async after(): Promise { - HttpClientTest1.httpServer.stop(); - } - - private client!: HttpClient; - - before() { - this.client = new HttpClient(); - } - - async after() { - await this.client.stop(); - } - - @test async "should apply defaults : read with default"() { - // read with defaults - const inputVector1 = { - op: ["readproperty"], - form: { - href: `http://localhost:${port1}/`, - }, - headers: { - accept: "application/json", - }, - }; - HttpClientTest1.httpServer.setTestVector(inputVector1); - const resource = await this.client.readResource(inputVector1.form); - const body = await resource.toBuffer(); - body.toString("ascii").should.eql(""); - } - - @test async "should apply defaults - write with default"() { - // write with defaults - const inputVector2 = { - op: ["writeproperty"], - form: { - href: `http://localhost:${port1}/`, - }, - headers: { - "content-type": "application/json", - }, - payload: "test", - }; - HttpClientTest1.httpServer.setTestVector(inputVector2); - - await this.client.writeResource(inputVector2.form, new DefaultContent(Readable.from(inputVector2.payload))); - } - - @test async "should apply defaults - invoke with default"() { - // invoke with defaults - const inputVector3 = { - op: ["invokeaction"], - form: { - href: `http://localhost:${port1}/`, - }, - headers: { - "content-type": "application/json", - accept: "application/json", - }, - payload: "test", - }; - HttpClientTest1.httpServer.setTestVector(inputVector3); - - const resource = await this.client.invokeResource( - inputVector3.form, - new DefaultContent(Readable.from(inputVector3.payload)) - ); - - expect(resource.type).eql(ContentSerdes.DEFAULT); - const body = await resource.toBuffer(); - body.toString("ascii").should.eql(""); - } - - @test async "should apply form information - read with POST instead of GET"() { - // read with POST instead of GET - const inputVector1 = { - op: ["readproperty"], - form: { - href: `http://localhost:${port1}/`, - "htv:methodName": "POST", - }, - }; - HttpClientTest1.httpServer.setTestVector(inputVector1); - const resource = await this.client.readResource(inputVector1.form); - expect(resource.type).eql(ContentSerdes.DEFAULT); - const body = await resource.toBuffer(); - body.toString("ascii").should.eql(""); - } - - @test async "should apply form information - read with not default content-type"() { - // read with defaults - const inputVector1 = { - op: ["readproperty"], - form: { - href: `http://localhost:${port1}/`, - contentType: "text/plain", - }, - headers: { - accept: "text/plain", - }, - }; - HttpClientTest1.httpServer.setTestVector(inputVector1); - const resource = await this.client.readResource(inputVector1.form); - const body = await resource.toBuffer(); - body.toString("ascii").should.eql(""); - } - - @test async "should apply form information - read with header override"() { - // read with defaults - const inputVector1 = { - op: ["readproperty"], - form: { - href: `http://localhost:${port1}/`, - "htv:headers": { - "htv:fieldName": "accept", - "htv:fieldValue": "text/plain", - }, - }, - headers: { - accept: "text/plain", - }, - }; - HttpClientTest1.httpServer.setTestVector(inputVector1); - const resource = await this.client.readResource(inputVector1.form); - const body = await resource.toBuffer(); - body.toString("ascii").should.eql(""); - } - - @test async "should apply form information - write with POST instead of PUT"() { - // write with POST instead of PUT - const inputVector2 = { - op: ["writeproperty"], - form: { - href: `http://localhost:${port1}/`, - "htv:methodName": "POST", - }, - payload: "test", - }; - HttpClientTest1.httpServer.setTestVector(inputVector2); - await this.client.writeResource(inputVector2.form, new DefaultContent(Readable.from(inputVector2.payload))); - } - - @test async "should apply form information - invoke with PUT instead of GET"() { - // invoke with PUT instead of POST - const inputVector3 = { - op: ["invokeaction"], - form: { - href: `http://localhost:${port1}/`, - "htv:methodName": "PUT", - }, - payload: "test", - }; - HttpClientTest1.httpServer.setTestVector(inputVector3); - await this.client.invokeResource(inputVector3.form, new DefaultContent(Readable.from(inputVector3.payload))); - } - - @test async "should apply form information - invoke with DELETE instead of POST"() { - // invoke with DELETE instead of POST - const inputVector4 = { - op: ["invokeaction"], - form: { - href: `http://localhost:${port1}/`, - "htv:methodName": "DELETE", - }, - }; - HttpClientTest1.httpServer.setTestVector(inputVector4); - await this.client.invokeResource(inputVector4.form); - } - - @test async "should apply form information - invoke with not default content-type and no inputs"() { - // invoke with DELETE instead of POST - const inputVector4 = { - op: ["invokeaction"], - form: { - href: `http://localhost:${port1}/`, - contentType: "text/plain", - }, - headers: { - accept: "text/plain", - }, - }; - HttpClientTest1.httpServer.setTestVector(inputVector4); - await this.client.invokeResource(inputVector4.form); - } - - @test async "should apply form information - invoke with not default content-type"() { - // invoke with DELETE instead of POST - const inputVector4 = { - op: ["invokeaction"], - form: { - href: `http://localhost:${port1}/`, - contentType: "text/plain", - }, - headers: { - "content-type": "text/plain", - accept: "text/plain", - }, - payload: "test", - }; - HttpClientTest1.httpServer.setTestVector(inputVector4); - await this.client.invokeResource( - inputVector4.form, - new Content("text/plain", Readable.from(inputVector4.payload)) - ); - } - - @test async "should apply form information - invoke with default content-type and response content-type"() { - // invoke with DELETE instead of POST - const inputVector4 = { - op: ["invokeaction"], - form: { - href: `http://localhost:${port1}/`, - response: { - contentType: "text/plain", - }, - }, - headers: { - "content-type": "application/json", - accept: "text/plain", - }, - payload: "test", - }; - HttpClientTest1.httpServer.setTestVector(inputVector4); - await this.client.invokeResource(inputVector4.form, new DefaultContent(Readable.from(inputVector4.payload))); - } -} - -@suite("HTTP client subscriptions") -class HttpClientTest2 { - @test "should register to sse server and get server sent event"(done: Mocha.Done) { - let dataCheckError: string | undefined; - - // create sse server - const clock = FakeTimers.install(); - const app = express(); - app.use(serveStatic(__dirname)); - app.get("/sse", function (req: express.Request, res: express.Response) { - debug("new connection"); - - const sseStream = new SseStream(req); - sseStream.pipe(res); - const pusher = setInterval(() => { - sseStream.write({ - data: "Test event", - }); - }, 300); - - res.on("close", () => { - debug("lost connection"); - clearInterval(pusher); - sseStream.unpipe(res); - done(); - if (dataCheckError !== undefined) { - fail(dataCheckError); - } - }); - }); - - const server = app.listen(port1, () => { - debug(`server ready on http://localhost:${port1}`); - }); - debug("client created"); - const client = new HttpClient(); - - // Subscribe to a resource with sse - const form: HttpForm = { - op: ["observeproperty"], - subprotocol: "sse", - contentType: "application/json", - href: `http://localhost:${port1}/sse`, - }; - - client - .subscribeResource(form, async (data) => { - if ((await text(data.body)) !== "Test event") { - dataCheckError = "Data should report 'Test event'"; - } - client.unlinkResource(form); - server.close(); - clock.uninstall(); - }) - .then(() => { - // subscription is active we can tick the clock - clock.tick(400); - }); - } - - @test "should call error() and complete() on subscription with no connection"(done: () => void) { - const client = new HttpClient(); - - // Subscribe to an event - const form: HttpForm = { - op: ["subscribeevent"], - href: "http://404.localhost", - }; - - const errorSpy = chai.spy(); - const completeSpy = chai.spy(() => { - // eslint-disable-next-line no-unused-expressions - errorSpy.should.have.been.called.once; - // eslint-disable-next-line no-unused-expressions - completeSpy.should.have.been.called.once; - done(); - }); - - client.subscribeResource( - form, - (data) => { - /** */ - }, - errorSpy, - completeSpy - ); - } - - @test "should call error() and complete() on subscription with wrong URL"(done: Mocha.Done) { - const client = new HttpClient(); - - // Subscribe to an event - const form: HttpForm = { - op: ["subscribeevent"], - href: `http://localhost:${port2}/`, - }; - - const server = http.createServer((req, res) => { - res.writeHead(404); - res.end(); - }); - - const errorSpy = chai.spy(); - const completeSpy = chai.spy(function () { - // eslint-disable-next-line no-unused-expressions - errorSpy.should.have.been.called.once; - // eslint-disable-next-line no-unused-expressions - completeSpy.should.have.been.called.once; - done(); - server.close(); - }); - - server.listen(port2, "0.0.0.0"); - server.once("listening", () => { - client.subscribeResource( - form, - (data) => { - /** */ - }, - errorSpy, - completeSpy - ); - }); - } - - @test "should subscribe successfully"(done: Mocha.Done) { - const client = new HttpClient(); - - // Subscribe to an event - const form: HttpForm = { - op: ["subscribeevent"], - href: `http://localhost:${port3}/`, - }; - - const server = http.createServer((req, res) => { - res.writeHead(200); - res.end(); - }); - - const subscribeSpy = chai.spy(); - - const eventSpy = chai.spy(async function (data: Content) { - // eslint-disable-next-line no-unused-expressions - eventSpy.should.have.been.called.once; - // eslint-disable-next-line no-unused-expressions - subscribeSpy.should.have.been.called.once; - server.close(); - done(); - }); - - server.listen(port3, "0.0.0.0"); - server.once("listening", () => { - client - .subscribeResource( - form, - eventSpy, - () => { - /** */ - }, - () => { - /** */ - } - ) - .then(subscribeSpy); - }); - } - - @test "should unsubscribe successfully"(done: Mocha.Done) { - const client = new HttpClient(); - - // Subscribe to an event - const form: HttpForm = { - op: ["subscribeevent"], - href: `http://localhost:${port4}/`, - }; - - const app = express(); - const server = http.createServer({}, app); - - app.get("/", async (req, res) => { - res.send("Emitted Event!"); - }); - - const eventSpy = chai.spy(); - - server.listen(port4, "0.0.0.0"); - server.once("listening", async () => { - let counter = 0; - const sub = await client.subscribeResource( - form, - async () => { - counter++; - eventSpy(); - if (counter === 2) { - sub.unsubscribe(); - // wait 100 ms, so that tests fail if unsubscribe didn't work - await new Promise((resolve) => setTimeout(resolve, 100)); - // eslint-disable-next-line no-unused-expressions - eventSpy.should.have.been.called.twice; - server.close(); - done(); - } else if (counter > 2) { - server.close(); - done(new Error("unsubscribe didn't work as expected")); - } - }, - () => { - /** */ - }, - () => { - /** */ - } - ); - }); - } -} diff --git a/packages/binding-http/test/http-server-oauth-tests.ts b/packages/binding-http/test/http-server-oauth-tests.ts deleted file mode 100644 index 5ccc206b7..000000000 --- a/packages/binding-http/test/http-server-oauth-tests.ts +++ /dev/null @@ -1,138 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2020 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 { suite, test } from "@testdeck/mocha"; -import { should, expect } from "chai"; -import { HttpServer, OAuth2ServerConfig } from "../src/http"; -import { IntrospectionEndpoint, EndpointValidator } from "../src/oauth-token-validation"; -import Servient, { ExposedThing } from "@node-wot/core"; -import fetch from "node-fetch"; - -class MockServient extends Servient {} - -should(); -@suite("OAuth server token validation tests") -class OAuthServerTests { - private server!: HttpServer; - async before() { - // eslint-disable-next-line @typescript-eslint/no-empty-function - console.debug = () => {}; - // eslint-disable-next-line @typescript-eslint/no-empty-function - console.warn = () => {}; - // eslint-disable-next-line @typescript-eslint/no-empty-function - console.info = () => {}; - - const method: IntrospectionEndpoint = { - name: "introspection_endpoint", - endpoint: "http://localhost:4242", - }; - const authConfig: OAuth2ServerConfig = { - scheme: "oauth2", - method, - }; - this.server = new HttpServer({ - security: [authConfig], - }); - - await this.server.start(new MockServient()); - - const testThing = new ExposedThing(new Servient(), { - title: "TestOAuth", - id: "test", - securityDefinitions: { - oauth2_sc: { - scheme: "oauth2", - flow: "code", - authorization: "https://example.com/authorization", - token: "https://example.com/token", - scopes: ["limited", "special"], - }, - }, - security: ["oauth2_sc"], - properties: { - test: { - type: "string", - }, - }, - }); - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - testThing.properties.test.forms = []; - - await this.server.expose(testThing); - } - - async after() { - await this.server.stop(); - } - - @test async "should configure oauth"() { - /* eslint-disable dot-notation */ - this.server["supportedSecuritySchemes"].should.contain("oauth2"); - expect(this.server["oAuthValidator"]).to.be.instanceOf(EndpointValidator); - /* eslint-enable dot-notation */ - } - - @test async "should call oauth validation"() { - let called = false; - - /* eslint-disable dot-notation */ - expect(this.server["oAuthValidator"]).to.not.be.equal(undefined, "OAuth validator not set"); - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - this.server["oAuthValidator"]!.validate = async (token, scopes, clients) => { - called = true; - return true; - }; - /* eslint-enable dot-notation */ - await fetch("http://localhost:8080/testoauth/properties/test"); - - called.should.eql(true); - } - - @test async "should send unauthorized if oauth validation fails"() { - let called = false; - - /* eslint-disable dot-notation */ - expect(this.server["oAuthValidator"]).to.not.be.equal(undefined, "OAuth validator not set"); - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - this.server["oAuthValidator"]!.validate = async (token, scopes, clients) => { - called = true; - return false; - }; - /* eslint-enable dot-notation */ - const response = await fetch("http://localhost:8080/testoauth/properties/test"); - - called.should.eql(true); - - response.status.should.be.equal(401); - } - - @test async "should send error if oauth validation throws"() { - let called = false; - - /* eslint-disable dot-notation */ - expect(this.server["oAuthValidator"]).to.not.be.equal(undefined, "OAuth validator not set"); - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - this.server["oAuthValidator"]!.validate = async (token, scopes, clients) => { - called = true; - return false; - }; - /* eslint-enable dot-notation */ - const response = await fetch("http://localhost:8080/testoauth/properties/test"); - - called.should.eql(true); - - response.status.should.be.equal(401); - } -} diff --git a/packages/binding-http/test/http-server-test.ts b/packages/binding-http/test/http-server-test.ts deleted file mode 100644 index 974cf263b..000000000 --- a/packages/binding-http/test/http-server-test.ts +++ /dev/null @@ -1,1023 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2018 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 - ********************************************************************************/ - -/** - * Protocol test suite to test protocol implementations - */ - -import { suite, test } from "@testdeck/mocha"; -import { expect, should, assert } from "chai"; -import * as chai from "chai"; -import fetch from "node-fetch"; - -import HttpServer from "../src/http-server"; -import Servient, { Content, createLoggers, ExposedThing, Helpers } from "@node-wot/core"; -import { DataSchemaValue, InteractionOptions } from "wot-typescript-definitions"; -import chaiAsPromised from "chai-as-promised"; -import { Readable } from "stream"; -import { MiddlewareRequestHandler } from "../src/http-server-middleware"; - -const { debug, error } = createLoggers("binding-http", "http-server-test"); - -chai.use(chaiAsPromised); - -// should must be called to augment all variables -should(); - -const port = 32080; -const port2 = 32081; -@suite("HTTP server implementation") -class HttpServerTest { - @test async "should start and stop a server"() { - const httpServer = new HttpServer({ port }); - - await httpServer.start(new Servient()); - expect(httpServer.getPort()).to.eq(port); // from test - - await httpServer.stop(); - expect(httpServer.getPort()).to.eq(-1); // from getPort() when not listening - } - - @test async "should use middleware if provided"() { - const middleware: MiddlewareRequestHandler = async (req, res, next) => { - if (req.url?.endsWith("testMiddleware") ?? false) { - res.statusCode = 401; - res.end("Unauthorized"); - } else { - next(); - } - }; - - const httpServer = new HttpServer({ - port, - middleware, - }); - - await httpServer.start(new Servient()); - - const testThing = new ExposedThing(new Servient(), { - title: "Test", - properties: { - testMiddleware: { - forms: [], - }, - testPassthrough: { - forms: [], - }, - }, - actions: {}, - }); - - let test: DataSchemaValue; - testThing.setPropertyReadHandler("testMiddleware", () => Promise.resolve(test)); - testThing.setPropertyReadHandler("testPassthrough", () => Promise.resolve(test)); - - await httpServer.expose(testThing); - - const uri = `http://localhost:${httpServer.getPort()}/test/`; - let resp; - - debug(`Testing ${uri}`); - - resp = await fetch(uri + "properties/testMiddleware"); - expect(resp.status).to.equal(401); - - resp = await fetch(uri + "properties/testPassthrough"); - expect(resp.status).to.equal(200); - - return httpServer.stop(); - } - - @test async "should be able to destroy a thing"() { - const httpServer = new HttpServer({ port: 0 }); - - await httpServer.start(new Servient()); - - let testThing = new ExposedThing(new Servient()); - testThing = Helpers.extend( - { - title: "Test", - id: "56789", - properties: { - test: { - type: "string", - }, - }, - }, - testThing - ); - // testThing.extendInteractions(); - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - testThing.properties.test.forms = []; - - await httpServer.expose(testThing); - let result = await httpServer.destroy("56789"); - expect(result); - result = await httpServer.destroy("56789"); - expect(!result); - - await httpServer.stop(); - } - - @test async "should change resource from 'off' to 'on' and try to invoke"() { - const httpServer = new HttpServer({ port: 0 }); - - await httpServer.start(new Servient()); - - const testThing = new ExposedThing(new Servient(), { - title: "Test", - properties: { - test: { - forms: [], - }, - }, - events: { - eventTest: { - forms: [], - }, - }, - actions: { - try: { - output: { type: "string" }, - forms: [], - }, - }, - }); - - let test: DataSchemaValue; - testThing.setPropertyReadHandler("test", (_) => Promise.resolve(test)); - testThing.setPropertyWriteHandler("test", async (value) => { - test = Buffer.from(await value.arrayBuffer()).toString("utf-8"); - }); - - testThing.setActionHandler("try", async (input: WoT.InteractionOutput) => { - return "TEST"; - }); - - await httpServer.expose(testThing); - - testThing.handleSubscribeEvent( - "eventTest", - async (input: Content) => { - const data = await input.toBuffer(); - expect(data.toString()).to.equal("'test''"); - }, - { formIndex: 0 } - ); - testThing.emitEvent("eventTest", "test"); - - await testThing.handleWriteProperty( - "test", - new Content("text/plain", Readable.from(Buffer.from("off", "utf-8"))), - { formIndex: 0 } - ); - - const uri = `http://localhost:${httpServer.getPort()}/test/`; - let resp; - - debug(`Testing ${uri}`); - - resp = await (await fetch(uri + "properties/test")).text(); - expect(resp).to.equal('"off"'); - - resp = await (await fetch(uri + "properties")).json(); - expect(resp).to.deep.equal({ test: "off" }); - - resp = await (await fetch(uri + "properties/test", { method: "PUT", body: "on" })).text(); - expect(resp).to.equal(""); - - resp = await (await fetch(uri + "properties/test")).text(); - expect(resp).to.equal('"on"'); - - let actionHttpResponse = await fetch(uri + "actions/try", { method: "POST", body: "toggle" }); - resp = await actionHttpResponse.text(); - - expect(actionHttpResponse.status).to.equal(200); - expect(resp).to.equal('"TEST"'); - - actionHttpResponse = await fetch(uri + "actions/try", { method: "POST", body: undefined }); - resp = await (await fetch(uri + "actions/try", { method: "POST", body: undefined })).text(); - - expect(actionHttpResponse.status).to.equal(200); - expect(resp).to.equal('"TEST"'); - - return httpServer.stop(); - } - - @test async "should return 204 when action has not output"() { - const httpServer = new HttpServer({ port: 0 }); - - await httpServer.start(new Servient()); - - const testThing = new ExposedThing(new Servient(), { - title: "Test", - actions: { - noOutput: { - output: { type: "string" }, - forms: [], - }, - }, - }); - - testThing.setActionHandler("noOutput", async () => undefined); - - await httpServer.expose(testThing); - - const uri = `http://localhost:${httpServer.getPort()}/test/`; - - debug(`Testing ${uri}`); - - const resp = await fetch(uri + "actions/noOutput", { method: "POST" }); - - expect(resp.status).to.equal(204); - - return httpServer.stop(); - } - - @test async "should check uriVariables consistency"() { - const httpServer = new HttpServer({ port: 0 }); - - await httpServer.start(new Servient()); - - const testThing = new ExposedThing(new Servient(), { - title: "Test", - properties: { - test: { - type: "string", - uriVariables: { - id: { - type: "string", - }, - }, - }, - }, - actions: { - try: { - output: { type: "string" }, - uriVariables: { - step: { type: "integer" }, - }, - }, - }, - }); - let test: DataSchemaValue; - testThing.setPropertyReadHandler("test", async (options) => { - expect(options?.uriVariables).to.deep.equal({ id: "testId" }); - return test; - }); - testThing.setPropertyWriteHandler("test", async (value, options) => { - expect(options?.uriVariables).to.deep.equal({ id: "testId" }); - test = await value.value(); - }); - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - testThing.properties.test.forms = []; - testThing.setActionHandler("try", async (input: WoT.InteractionOutput, params?: InteractionOptions) => { - expect(params?.uriVariables).to.deep.equal({ step: 5 }); - return "TEST"; - }); - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - testThing.actions.try.forms = []; - - await httpServer.expose(testThing); - - const uri = `http://localhost:${httpServer.getPort()}/test/`; - let resp; - - resp = await (await fetch(uri + "properties/test?id=testId", { method: "PUT", body: "on" })).text(); - expect(resp).to.equal(""); - - resp = await (await fetch(uri + "actions/try?step=5", { method: "POST", body: "toggle" })).text(); - expect(resp).to.equal('"TEST"'); - - return httpServer.stop(); - } - - @test async "should serialize objects for actions and properties"() { - const httpServer = new HttpServer({ port: 0 }); - - await httpServer.start(new Servient()); - - const testThing = new ExposedThing(new Servient(), { - title: "Test", - properties: { - test: { - type: "object", - }, - }, - actions: { - try: { - output: { type: "object" }, - }, - }, - }); - let test: DataSchemaValue = {}; - testThing.setPropertyReadHandler("test", (_) => Promise.resolve(test)); - testThing.setPropertyWriteHandler("test", async (value) => { - test = await value.value(); - }); - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - testThing.properties.test.forms = []; - testThing.setActionHandler("try", async (input: WoT.InteractionOutput) => { - return "TEST"; - }); - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - testThing.actions.try.forms = []; - - await httpServer.expose(testThing); - - const uri = `http://localhost:${httpServer.getPort()}/test/`; - let resp; - - debug(`Testing ${uri}`); - - resp = await (await fetch(uri + "properties/test")).text(); - expect(resp).to.equal("{}"); - - resp = await (await fetch(uri + "properties")).json(); - expect(resp).to.deep.equal({ test: {} }); - - resp = await ( - await fetch(uri + "properties/test", { - method: "PUT", - body: JSON.stringify({ new: true }), - headers: { "Content-Type": "application/json" }, - }) - ).text(); - expect(resp).to.equal(""); - - resp = await (await fetch(uri + "properties/test")).text(); - expect(resp).to.equal('{"new":true}'); - - resp = await (await fetch(uri + "actions/try", { method: "POST", body: "toggle" })).text(); - expect(resp).to.equal('"TEST"'); - - resp = await (await fetch(uri + "actions/try", { method: "POST", body: undefined })).text(); - expect(resp).to.equal('"TEST"'); - - return httpServer.stop(); - } - - @test async "should cause EADDRINUSE error when already running"() { - const httpServer1 = new HttpServer({ port: 0 }); - - await httpServer1.start(new Servient()); - expect(httpServer1.getPort()).to.be.above(0); - - const httpServer2 = new HttpServer({ port: httpServer1.getPort() }); - - try { - await httpServer2.start(new Servient()); // should fail - } catch (err) { - error(`HttpServer failed correctly on EADDRINUSE. ${err}`); - assert(true); - } - - expect(httpServer2.getPort()).to.eq(-1); - - const uri = `http://localhost:${httpServer1.getPort()}/`; - - return fetch(uri).then(async (body) => { - expect(await body.text()).to.equal("[]"); - - await httpServer1.stop(); - await httpServer2.stop(); - }); - } - - // https://github.com/eclipse-thingweb/node-wot/issues/181 - @test async "should start and stop a server with no security"() { - const httpServer = new HttpServer({ port, security: [{ scheme: "nosec" }] }); - - await httpServer.start(new Servient()); - expect(httpServer.getPort()).to.eq(port); // port test - // eslint-disable-next-line dot-notation - expect(httpServer["supportedSecuritySchemes"][0]).to.eq("nosec"); - await httpServer.stop(); - } - - // https://github.com/eclipse-thingweb/node-wot/issues/181 - @test async "should not override a valid security scheme"() { - const httpServer = new HttpServer({ - port: port2, - serverKey: "./test/server.key", - serverCert: "./test/server.cert", - security: [ - { - scheme: "bearer", - }, - ], - }); - await httpServer.start(new Servient()); - const testThing = new ExposedThing(new Servient()); - testThing.title = "Test"; - testThing.securityDefinitions = { - bearer: { - scheme: "bearer", - }, - }; - httpServer.expose(testThing); - await httpServer.stop(); - - expect(testThing.securityDefinitions.bearer).not.eql(undefined); - } - - @test async "should fill default security scheme even with baseURI defined"() { - const httpServer = new HttpServer({ - port: port2, - baseUri: "https://example.com", - }); - await httpServer.start(new Servient()); - const testThing = new ExposedThing(new Servient()); - testThing.title = "Test"; - httpServer.expose(testThing); - await httpServer.stop(); - - expect(testThing.securityDefinitions.nosec).to.not.eql(undefined); - expect(testThing.securityDefinitions.nosec.scheme).to.be.eql("nosec"); - } - - @test async "should not accept an unsupported scheme"() { - debug("START SHOULD"); - const httpServer = new HttpServer({ - port: port2, - serverKey: "./test/server.key", - serverCert: "./test/server.cert", - security: [ - { - scheme: "bearer", - }, - ], - }); - await httpServer.start(new Servient()); - - try { - const testThing = new ExposedThing(new Servient()); - testThing.title = "Test"; - testThing.securityDefinitions = { - oauth2: { - scheme: "oauth2", - }, - }; - await expect(httpServer.expose(testThing)).to.be.eventually.rejectedWith(Error); - } finally { - await httpServer.stop(); - } - } - - @test async "config.port is overridden by WOT_PORT or PORT"() { - // Works when none set - let httpServer = new HttpServer({ port }); - await httpServer.start(new Servient()); - expect(httpServer.getPort()).to.eq(port); // WOT PORT from test - await httpServer.stop(); - - // Check PORT - process.env.PORT = "2222"; - httpServer = new HttpServer({ port }); - await httpServer.start(new Servient()); - expect(httpServer.getPort()).to.eq(2222); // from PORT - await httpServer.stop(); - - // CHECK WOT_PORT - process.env.PORT = undefined; - process.env.WOT_PORT = "3333"; - httpServer = new HttpServer({ port }); - await httpServer.start(new Servient()); - expect(httpServer.getPort()).to.eq(3333); // WOT PORT from test - await httpServer.stop(); - - // Check WOT_PORT has higher priority than PORT - process.env.PORT = "2600"; - process.env.WOT_PORT = "1337"; - httpServer = new HttpServer({ port }); - await httpServer.start(new Servient()); - expect(httpServer.getPort()).to.eq(1337); // WOT PORT from test - await httpServer.stop(); - delete process.env.PORT; - delete process.env.WOT_PORT; - } - - @test async "should allow HttpServer baseUri to specify url prefix for proxied/gateswayed/buildpack etc "() { - const theHostname = "wot.w3c.loopback.site:8080"; - const theBasePath = "/things"; - const theBaseUri = `http://${theHostname}${theBasePath}`; - const httpServer = new HttpServer({ - baseUri: theBaseUri, - port: 8080, - }); - - await httpServer.start(new Servient()); - - const testThing = new ExposedThing(new Servient(), { - title: "Smart Coffee Machine", - properties: { - maintenanceNeeded: { - type: "string", - }, - }, - actions: { - makeDrink: { - output: { type: "string" }, - }, - }, - }); - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - testThing.properties.maintenanceNeeded.forms = []; - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - testThing.actions.makeDrink.forms = []; - - await httpServer.expose(testThing); - - const uri = "http://localhost:8080/smart-coffee-machine"; - const body = await (await fetch(uri)).text(); - - const expectedUrl = `${theBaseUri}/smart-coffee-machine/actions/makeDrink`; - - expect(body).to.include(expectedUrl); - debug(`Found URL ${expectedUrl} in TD`); - await httpServer.stop(); - } - - @test async "should take in account global uriVariables"() { - const httpServer = new HttpServer({ port: 0 }); - - await httpServer.start(new Servient()); - - const testThing = new ExposedThing(new Servient(), { - title: "Test", - uriVariables: { - globalVarTest: { - type: "string", - enum: ["test1", "test2", "test3"], - description: "test", - }, - id: { - type: "string", - enum: ["test1", "test2", "test3"], - }, - }, - properties: { - test: { - type: "string", - uriVariables: { - id: { - type: "string", - }, - }, - }, - }, - }); - let test: DataSchemaValue; - testThing.setPropertyReadHandler("test", async (options) => { - expect(options?.uriVariables).to.deep.equal({ id: "testId", globalVarTest: "test1" }); - return test; - }); - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - testThing.properties.test.forms = []; - - await httpServer.expose(testThing); - - const uri = `http://localhost:${httpServer.getPort()}/test/`; - - const resp = await ( - await fetch(uri + "properties/test?id=testId&globalVarTest=test1", { method: "GET" }) - ).text(); - expect(resp).to.equal(""); - - return httpServer.stop(); - } - - @test async "should allow url rewrite"() { - const httpServer = new HttpServer({ port: 0, urlRewrite: { "/myroot/foo": "/test/properties/test" } }); - - await httpServer.start(new Servient()); - - const testThing = new ExposedThing(new Servient(), { - title: "Test", - properties: { - test: { - type: "object", - }, - }, - }); - let test: DataSchemaValue = {}; - testThing.setPropertyReadHandler("test", (_) => Promise.resolve(test)); - testThing.setPropertyWriteHandler("test", async (value) => { - test = await value.value(); - }); - - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - testThing.properties.test.forms = []; - await httpServer.expose(testThing); - - const uriWithoutThing = `http://localhost:${httpServer.getPort()}/`; - let resp; - - resp = await (await fetch(uriWithoutThing + "test/properties/test")).text(); - expect(resp).to.equal("{}"); - - resp = await (await fetch(uriWithoutThing + "myroot/foo")).text(); - expect(resp).to.equal("{}"); - - resp = await (await fetch(uriWithoutThing + "my-entry/does-not-exist")).text(); - expect(resp).to.not.equal("{}"); // i.e., returns 'Not Found' - - return httpServer.stop(); - } - - @test async "should report allproperties excluding non-JSON properties"() { - const httpServer = new HttpServer({ port: 0 }); - - await httpServer.start(new Servient()); - - const tdTemplate: WoT.ExposedThingInit = { - title: "TestA", - properties: { - image: { - forms: [ - { - contentType: "image/svg+xml", - }, - ], - }, - testInteger: { - type: "integer", - }, - testBoolean: { - type: "boolean", - }, - testString: { - type: "string", - }, - testObject: { - type: "object", - }, - testArray: { - type: "array", - }, - }, - }; - const testThing = new ExposedThing(new Servient(), tdTemplate); - - const image = "FOO"; - const integer = 123; - const boolean = true; - const string = "ABCD"; - const object = { t1: "xyz", i: 77 }; - const array = ["x", "y", "z"]; - testThing.setPropertyReadHandler("image", (_) => Promise.resolve(image)); - testThing.setPropertyReadHandler("testInteger", (_) => Promise.resolve(integer)); - testThing.setPropertyReadHandler("testBoolean", (_) => Promise.resolve(boolean)); - testThing.setPropertyReadHandler("testString", (_) => Promise.resolve(string)); - testThing.setPropertyReadHandler("testObject", (_) => Promise.resolve(object)); - testThing.setPropertyReadHandler("testArray", (_) => Promise.resolve(array)); - - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - testThing.properties.image.forms = []; - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - testThing.properties.testInteger.forms = []; - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - testThing.properties.testBoolean.forms = []; - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - testThing.properties.testString.forms = []; - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - testThing.properties.testObject.forms = []; - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - testThing.properties.testArray.forms = []; - - await httpServer.expose(testThing, tdTemplate); - - // check values one by one first - const responseInteger = await fetch(`http://localhost:${httpServer.getPort()}/testa/properties/testInteger`); - expect(await responseInteger.json()).to.equal(integer); - const responseBoolean = await fetch(`http://localhost:${httpServer.getPort()}/testa/properties/testBoolean`); - expect(await responseBoolean.json()).to.equal(boolean); - const responseString = await fetch(`http://localhost:${httpServer.getPort()}/testa/properties/testString`); - expect(await responseString.json()).to.equal(string); - const responseObject = await fetch(`http://localhost:${httpServer.getPort()}/testa/properties/testObject`); - expect(await responseObject.json()).to.deep.equal(object); - const responseArray = await fetch(`http://localhost:${httpServer.getPort()}/testa/properties/testArray`); - expect(await responseArray.json()).to.deep.equal(array); - - // check values of readallproperties - const responseAll = await fetch(`http://localhost:${httpServer.getPort()}/testa/properties`); - expect(await responseAll.json()).to.deep.equal({ - // "image": image, // Note: No support for contentTypes other than JSON -> not included - testInteger: integer, - testBoolean: boolean, - testString: string, - testObject: object, - testArray: array, - }); - - return httpServer.stop(); - } - - @test async "should support setting SVG contentType"() { - const httpServer = new HttpServer({ port: 0 }); - - await httpServer.start(new Servient()); - - const tdTemplate = { - title: "Test", - properties: { - image: { - forms: [ - { - contentType: "image/svg+xml", - }, - ], - }, - }, - }; - const testThing = new ExposedThing(new Servient(), tdTemplate); - - const image = "FOO"; - testThing.setPropertyReadHandler("image", (_) => Promise.resolve(image)); - - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - testThing.properties.image.forms = []; - await httpServer.expose(testThing, tdTemplate); - - const uri = `http://localhost:${httpServer.getPort()}/test/properties/image`; - - const contentTypeResponse = await fetch(uri); - expect(contentTypeResponse.headers.get("Content-Type")).to.equal("image/svg+xml"); - - // check value (e.g., SVG text without quotes) - expect(await contentTypeResponse.text()).to.equal(image); - - return httpServer.stop(); - } - - @test async "should support setting PNG contentType"() { - const httpServer = new HttpServer({ port: 0 }); - - await httpServer.start(new Servient()); - - const tdTemplate = { - title: "Test", - properties: { - image: { - forms: [ - { - contentType: "image/png", - }, - ], - }, - }, - }; - const testThing = new ExposedThing(new Servient(), tdTemplate); - - const image = - "iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=="; - testThing.setPropertyReadHandler("image", (_) => Promise.resolve(image)); - - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - testThing.properties.image.forms = []; - await httpServer.expose(testThing, tdTemplate); - - const uri = `http://localhost:${httpServer.getPort()}/test/properties/image`; - - const contentTypeResponse = await fetch(uri); - expect(contentTypeResponse.headers.get("Content-Type")).to.equal("image/png"); - - return httpServer.stop(); - } - - @test async "should support TD content negotiation"() { - const httpServer = new HttpServer({ port: 0 }); - - await httpServer.start(new Servient()); - - const testThing = new ExposedThing(new Servient(), { - title: "Test", - }); - - await httpServer.expose(testThing); - - const uri = `http://localhost:${httpServer.getPort()}/test`; - - const testCases = [ - { - inputHeaders: undefined, - expected: "application/td+json", - expectedResponseCode: 200, - }, - { - inputHeaders: { Accept: "application/json" }, - expected: "application/json", - expectedResponseCode: 200, - }, - { - // Typical browser request (e.g., Chrome) - inputHeaders: { - Accept: "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8", - }, - // We should favor application/td+json over text/html - expected: "application/td+json", - expectedResponseCode: 200, - }, - { - inputHeaders: { - Accept: "image/svg+xml,text/html", - }, - // We should favor text/html over image/svg+xml - expected: "text/html", - expectedResponseCode: 200, - }, - { - inputHeaders: { Accept: "*/*,application/json" }, - expected: "application/td+json", - expectedResponseCode: 200, - }, - { - inputHeaders: { Accept: "*/*" }, - expected: "application/td+json", - expectedResponseCode: 200, - }, - { - inputHeaders: { Accept: "foo/cbar;baz=fuzz,application/json,foo/cbar" }, - expected: "application/json", - expectedResponseCode: 200, - }, - { - inputHeaders: { Accept: "foo/cbar;baz=fuzz,foo/cbar" }, - expected: null, - expectedResponseCode: 406, - }, - ]; - - for (const testCase of testCases) { - const negotiatedContentTypeResponse = await fetch(uri, { - headers: testCase.inputHeaders, - }); - expect(negotiatedContentTypeResponse.headers.get("Content-Type")).to.equal(testCase.expected); - expect(negotiatedContentTypeResponse.status).to.equal(testCase.expectedResponseCode); - } - - return httpServer.stop(); - } - - @test async "should not support unknown Content-Types during TD content negotiation"() { - const httpServer = new HttpServer({ port: 0 }); - - await httpServer.start(new Servient()); - - const testThing = new ExposedThing(new Servient(), { - title: "Test", - }); - - await httpServer.expose(testThing); - - const uri = `http://localhost:${httpServer.getPort()}/test`; - - const failedNegotiationResponse = await fetch(uri, { - headers: { - Accept: "foo/bar", - }, - }); - expect(failedNegotiationResponse.headers.get("Content-Type")).to.equal(null); - expect(failedNegotiationResponse.status).to.equal(406); - - return httpServer.stop(); - } - - @test async "TD should have form with readallproperties"() { - const httpServer = new HttpServer({ port: 0 }); - - await httpServer.start(new Servient()); - - const tdTemplate: WoT.ExposedThingInit = { - title: "Test", - properties: { - testReadOnly: { - type: "number", - readOnly: true, - }, - }, - }; - const testThing = new ExposedThing(new Servient(), tdTemplate); - - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - testThing.properties.testReadOnly.forms = []; - - await httpServer.expose(testThing, tdTemplate); - - const uriTD = `http://localhost:${httpServer.getPort()}/test`; - - const tdResponse = await fetch(uriTD); - const td = await tdResponse.json(); - - expect(td).to.have.property("forms").to.be.an("array"); - expect(JSON.stringify(td.forms)).to.deep.contain.oneOf(["readallproperties", "readmultipleproperties"]); - expect(JSON.stringify(td.forms)).to.not.deep.contain.oneOf(["writeallproperties", "writemultipleproperties"]); - - return httpServer.stop(); - } - - @test async "TD should have form with writeallproperties"() { - const httpServer = new HttpServer({ port: 0 }); - - await httpServer.start(new Servient()); - - const tdTemplate: WoT.ExposedThingInit = { - title: "Test", - properties: { - testWriteOnly: { - type: "number", - writeOnly: true, - }, - }, - }; - const testThing = new ExposedThing(new Servient(), tdTemplate); - - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - testThing.properties.testWriteOnly.forms = []; - - await httpServer.expose(testThing, tdTemplate); - - const uriTD = `http://localhost:${httpServer.getPort()}/test`; - - const tdResponse = await fetch(uriTD); - const td = await tdResponse.json(); - - expect(td).to.have.property("forms").to.be.an("array"); - expect(JSON.stringify(td.forms)).to.not.deep.contain.oneOf(["readallproperties", "readmultipleproperties"]); - expect(JSON.stringify(td.forms)).to.deep.contain.oneOf(["writeallproperties", "writemultipleproperties"]); - - return httpServer.stop(); - } - - @test async "TD should have form with readallproperties and writeallproperties"() { - const httpServer = new HttpServer({ port: 0 }); - - await httpServer.start(new Servient()); - - const tdTemplate: WoT.ExposedThingInit = { - title: "Test", - properties: { - testReadOnly: { - type: "number", - readOnly: true, - }, - testWriteOnly: { - type: "number", - writeOnly: true, - }, - }, - }; - const testThing = new ExposedThing(new Servient(), tdTemplate); - - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - testThing.properties.testReadOnly.forms = []; - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - testThing.properties.testWriteOnly.forms = []; - - await httpServer.expose(testThing, tdTemplate); - - const uriTD = `http://localhost:${httpServer.getPort()}/test`; - - const tdResponse = await fetch(uriTD); - const td = await tdResponse.json(); - - expect(td).to.have.property("forms").to.be.an("array"); - expect(JSON.stringify(td.forms)).to.deep.contain.oneOf(["readallproperties", "readmultipleproperties"]); - expect(JSON.stringify(td.forms)).to.deep.contain.oneOf(["writeallproperties", "writemultipleproperties"]); - - return httpServer.stop(); - } -} diff --git a/packages/binding-http/test/memory-model.ts b/packages/binding-http/test/memory-model.ts deleted file mode 100644 index 81034a7cb..000000000 --- a/packages/binding-http/test/memory-model.ts +++ /dev/null @@ -1,184 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2020 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 { createDebugLogger } from "@node-wot/core"; -import { - PasswordModel, - ClientCredentialsModel, - Callback, - Token, - Falsey, - Client, - User, -} from "@node-oauth/oauth2-server"; - -const debug = createDebugLogger("binding-http", "memory-model"); - -/** - * oAuth server logic. See https://oauth2-server.readthedocs.io/en/latest/model/overview.html - */ -export default class InMemoryModel implements ClientCredentialsModel, PasswordModel { - clients: Client[]; - tokens: Token[]; - users: User[]; - tokenGen = 0; - /** - * - */ - constructor() { - this.clients = [ - { - id: "thom", - clientSecret: "nightworld", - redirectUris: [""], - grants: ["client_credentials", "password", "refresh_token"], - }, - ]; - this.tokens = []; - this.users = [{ id: "123", username: "thomseddon", password: "nightworld" }]; - } - - async validateScope?( - user: User, - client: Client, - scope: string | string[], - callback?: Callback - ): Promise { - if (callback) { - callback(null, scope.toString()); - } - - return scope; - } - - async generateAccessToken?( - client: Client, - user: User, - scope: string | string[], - callback?: Callback - ): Promise { - if (callback) { - callback(null, Buffer.from(Math.random().toString()).toString("base64").substr(10, 5)); - } - return Buffer.from(Math.random().toString()).toString("base64").substr(10, 5); - } - - async verifyScope(token: Token, scope: string | string[], callback?: Callback): Promise { - if (callback) { - callback(null, true); - } - return true; - } - - dump(): void { - debug(`Clients: ${this.clients}`); - debug(`Tokens: ${this.tokens}`); - debug(`Users: ${this.users}`); - } - - /* - * Get access token. - */ - - async getAccessToken(bearerToken: string, callback?: Callback): Promise { - const tokens = this.tokens.filter(function (token) { - return token.accessToken === bearerToken; - }); - if (callback != null) { - callback(null, tokens[0]); - } - - return tokens.length ? tokens[0] : false; - } - - /** - * Get refresh token. - */ - - getRefreshToken(bearerToken: string): Token | false { - const tokens = this.tokens.filter(function (token) { - return token.refreshToken === bearerToken; - }); - - return tokens.length ? tokens[0] : false; - } - - /** - * Get client. - */ - - async getClient( - clientId: string, - clientSecret: string, - callback?: Callback - ): Promise { - const clients = this.clients.filter(function (client) { - return client.id === clientId && (!clientSecret || client.clientSecret === clientSecret); - }); - if (callback) { - callback(null, clients.length ? clients[0] : false); - } - return clients.length ? clients[0] : false; - } - - /** - * Save token. - */ - - async saveToken(token: Token, client: Client, user: User, callback?: Callback): Promise { - const { accessToken, accessTokenExpiresAt, refreshTokenExpiresAt, refreshToken } = token; - this.tokens.push({ - accessToken, - accessTokenExpiresAt, - client, - refreshToken, - refreshTokenExpiresAt, - user, - }); - if (callback) { - callback(null, this.tokens[this.tokens.length - 1]); - } - return this.tokens[this.tokens.length - 1]; - } - - /* - * Get user. - */ - - async getUser(username: string, password: string): Promise { - const users = this.users.filter(function (user) { - return user.username === username && user.password === password; - }); - - return users.length ? users[0] : false; - } - - async getUserFromClient(client: Client, callback?: Callback): Promise { - if (callback) { - callback(null, this.users[0]); - } - return this.users[0]; - } - - revokeToken(token: Token): boolean { - return true; - } - - expireAllTokens(): void { - for (const token of this.tokens) { - token.accessTokenExpiresAt = new Date(new Date().setHours(-1)); - } - } -} diff --git a/packages/binding-http/test/oauth-token-validation-tests.ts b/packages/binding-http/test/oauth-token-validation-tests.ts deleted file mode 100644 index aaa94e304..000000000 --- a/packages/binding-http/test/oauth-token-validation-tests.ts +++ /dev/null @@ -1,396 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2020 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 { suite, test } from "@testdeck/mocha"; -import express from "express"; -import { should } from "chai"; -import create, { IntrospectionEndpoint, Validator, EndpointValidator } from "../src/oauth-token-validation"; -import * as http from "http"; -import * as https from "https"; -import * as fs from "fs"; -import { assert } from "console"; -import { promisify } from "util"; - -should(); - -describe("OAuth2.0 Validator tests", () => { - it("should create an introspection validator", () => { - const config: IntrospectionEndpoint = { - name: "introspection_endpoint", - endpoint: "http://localhost:7777", - }; - create(config).should.be.instanceOf(EndpointValidator); - }); - - it("should throw for invalid method", () => { - const test = () => create({ name: "unknown" }); - - test.should.throw(); - }); - @suite - class IntrospectProtocolTests { - private validator!: Validator; - static server: http.Server; - static before() { - // eslint-disable-next-line @typescript-eslint/no-empty-function - console.debug = () => {}; - // eslint-disable-next-line @typescript-eslint/no-empty-function - console.warn = () => {}; - // eslint-disable-next-line @typescript-eslint/no-empty-function - console.info = () => {}; - - const tokens = ["active", "noScopes", "notActive"]; - - const introspectEndpoint: express.Express = express(); - introspectEndpoint.use(express.urlencoded({ extended: true })); - - introspectEndpoint.use("/invalid", (req, res) => { - return res.status(400).end(); - }); - - introspectEndpoint.use("/invalidResponse", (req, res) => { - return res - .status(200) - .json({ - scope: "1 2", - client_id: "coolClient", - }) - .end(); - }); - - introspectEndpoint.use("/invalidContent", (req, res) => { - return res.status(200).end(); - }); - - introspectEndpoint.use((req, res) => { - if (req.method !== "POST" || req.is("application/x-www-form-urlencoded") == null) { - return res.status(400).end(); - } - - const token = req.body.token; - - if (token == null) { - return res.status(400).end(); - } - switch (token) { - case tokens[0]: - return res - .status(200) - .json({ - active: true, - scope: "1 2", - client_id: "coolClient", - }) - .end(); - case tokens[1]: - return res - .status(200) - .json({ - active: true, - client_id: "coolClient", - }) - .end(); - default: - return res - .status(200) - .json({ - active: false, - }) - .end(); - } - }); - - this.server = introspectEndpoint.listen(7777); - } - - static after() { - return promisify(this.server.close.bind(this.server))(); - } - - before() { - const config: IntrospectionEndpoint = { - name: "introspection_endpoint", - endpoint: "http://localhost:7777", - }; - this.validator = create(config); - } - - @test async "should validate token from headers"() { - const req = { - headers: { - authorization: "Bearer active", - }, - url: "http://test", - }; - - const valid = await this.validator.validate(req as http.IncomingMessage, ["1", "2"], /.*/g); - valid.should.eql(true); - } - - @test async "should validate token from query string"() { - const req = { - headers: {}, - url: "http://test?access_token=active", - }; - - const valid = await this.validator.validate(req as http.IncomingMessage, ["1", "2"], /.*/g); - valid.should.eql(true); - } - - @test async "should validate a single scope"() { - const req = { - headers: {}, - url: "http://test?access_token=active", - }; - - const valid = await this.validator.validate(req as http.IncomingMessage, ["1"], /.*/g); - valid.should.eql(true); - } - - @test async "should validate a single scope mixed with invalid scopes"() { - const req = { - headers: {}, - url: "http://test?access_token=active", - }; - - const valid = await this.validator.validate(req as http.IncomingMessage, ["1", "3", "4"], /.*/g); - valid.should.eql(true); - } - - @test async "should validate if no scopes are required"() { - const req = { - headers: {}, - url: "http://test?access_token=active", - }; - - const valid = await this.validator.validate(req as http.IncomingMessage, [], /.*/g); - valid.should.eql(true); - } - - @test async "should validate if no scopes are required and no scopes are returned"() { - const req = { - headers: {}, - url: "http://test?access_token=noScopes", - }; - - const valid = await this.validator.validate(req as http.IncomingMessage, [], /.*/g); - valid.should.eql(true); - } - - @test async "should validate cliedId"() { - const req = { - headers: {}, - url: "http://test?access_token=active", - }; - - const valid = await this.validator.validate(req as http.IncomingMessage, ["1", "3", "4"], /coolClient/g); - valid.should.eql(true); - } - - @test async "should validate cliedId using regex"() { - const req = { - headers: {}, - url: "http://test?access_token=active", - }; - - const valid = await this.validator.validate(req as http.IncomingMessage, ["1", "3", "4"], /cool.*/g); - valid.should.eql(true); - } - - @test async "should reject invalid cliedId"() { - const req = { - headers: {}, - url: "http://test?access_token=active", - }; - - const valid = await this.validator.validate(req as http.IncomingMessage, ["1", "3", "4"], /otherClient/g); - valid.should.eql(false); - } - - @test async "should reject invalid scopes"() { - const req = { - headers: {}, - url: "http://test?access_token=active", - }; - - const valid = await this.validator.validate(req as http.IncomingMessage, ["3"], /.*/g); - valid.should.eql(false); - } - - @test async "should reject invalid token from headers"() { - const req = { - headers: { - authorization: "Bearer notActive", - }, - url: "http://test", - }; - - const valid = await this.validator.validate(req as http.IncomingMessage, [], /.*/g); - valid.should.eql(false); - } - - @test async "should reject if no scopes are returned"() { - const req = { - headers: { - authorization: "Bearer noScopes", - }, - url: "http://test", - }; - - const valid = await this.validator.validate(req as http.IncomingMessage, ["1", "2"], /.*/g); - valid.should.eql(false); - } - - @test async "should reject invalid token from query string"() { - const req = { - headers: {}, - url: "http://test?access_token=notActive", - }; - - const valid = await this.validator.validate(req as http.IncomingMessage, [], /.*/g); - valid.should.eql(false); - } - - @test async "should throw invalid incoming message"() { - const req = { - headers: {}, - url: "http://test", - }; - - try { - await this.validator.validate(req as http.IncomingMessage, [], /.*/g); - assert(false, "method did not throw"); - } catch (error) { - assert(true); - } - } - - @test async "should throw invalid introspection http response"() { - const config: IntrospectionEndpoint = { - name: "introspection_endpoint", - endpoint: "http://localhost:7777/invalid", - }; - this.validator = create(config); - - const req = { - headers: { - authorization: "Bearer active", - }, - url: "http://test", - }; - - try { - await this.validator.validate(req as http.IncomingMessage, [], /.*/g); - assert(false, "method did not throw"); - } catch (error) { - assert(true); - } - } - - @test async "should throw invalid introspection token response"() { - const config: IntrospectionEndpoint = { - name: "introspection_endpoint", - endpoint: "http://localhost:7777/invalidResponse", - }; - this.validator = create(config); - - const req = { - headers: { - authorization: "Bearer active", - }, - url: "http://test", - }; - - try { - await this.validator.validate(req as http.IncomingMessage, [], /.*/g); - assert(false, "method did not throw"); - } catch (error) { - assert(true); - } - } - - @test async "should throw invalid introspection content type response"() { - const config: IntrospectionEndpoint = { - name: "introspection_endpoint", - endpoint: "http://localhost:7777/invalidContent", - }; - this.validator = create(config); - - const req = { - headers: { - authorization: "Bearer active", - }, - url: "http://test", - }; - - try { - await this.validator.validate(req as http.IncomingMessage, [], /.*/g); - assert(false, "method did not throw"); - } catch (error) { - assert(true); - } - } - - @test async "should connect using https"() { - // Initialize test - - const introspectEndpoint: express.Express = express(); - introspectEndpoint.use(express.urlencoded({ extended: true })); - - introspectEndpoint.use((req, res) => { - // No validation just testing https connection - return res - .status(200) - .json({ - active: true, - scope: "1 2", - client_id: "coolClient", - }) - .end(); - }); - - const server = https.createServer( - { - key: fs.readFileSync("./test/server.key"), - cert: fs.readFileSync("./test/server.cert"), - }, - introspectEndpoint - ); - const serverStarted = new Promise((resolve, reject) => { - server.listen(7778, resolve); // might need to check if there was an error - }); - await serverStarted; - - const config: IntrospectionEndpoint = { - name: "introspection_endpoint", - endpoint: "https://localhost:7778", - allowSelfSigned: true, - }; - this.validator = create(config); - - const req = { - headers: { - authorization: "Bearer active", - }, - url: "http://test", - }; - - // test - const valid = await this.validator.validate(req as http.IncomingMessage, ["1"], /.*/g); - valid.should.eql(true); - await promisify(server.close.bind(server))(); - } - } -}); diff --git a/packages/binding-http/test/server.cert b/packages/binding-http/test/server.cert deleted file mode 100644 index 7cdde760f..000000000 --- a/packages/binding-http/test/server.cert +++ /dev/null @@ -1,21 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDXTCCAkWgAwIBAgIJAMsp9gVwegP9MA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV -BAYTAklUMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX -aWRnaXRzIFB0eSBMdGQwHhcNMjAwNDE0MDkxMTQzWhcNMjAwNTE0MDkxMTQzWjBF -MQswCQYDVQQGEwJJVDETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50 -ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB -CgKCAQEA3ToVq2J/9We8lYG0EWRGn0s488mp+H/8Z6GJUOabhRpdrapPS8NyFqzK -wh5t/kAq3R5GH5BTTc75pF9ksIK4FfUbKtOPcubvJEpt+eCZzeD0v5oH73dwPaoY -oZp8WfUkILhdMSyFBUyNLzVMrbjyCyH/J6z0QUy6Llg9nCmGh5xF0DHNApVnRpDK -efVO9SVJQAi5U2DTswHYzV6H/Pi199yevepnc3qNnlGLDxr1uPzx7iZrR5GrSsjQ -TtjGAMRnlF2tEinx6BEgIf1m+6PriYtCZ9FKvBCKjVwp/tvvXWQcyNDuJum8u2TE -rD3T/LABP53XXnfEPdfsl7YFfWNcTQIDAQABo1AwTjAdBgNVHQ4EFgQUqQ3t0q3h -J/KjeYdzwGHIAa8U0UAwHwYDVR0jBBgwFoAUqQ3t0q3hJ/KjeYdzwGHIAa8U0UAw -DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAny9MlmTVQdhDW3xV1Iwi -3fwUPEJ1Y5ACPenhbQBwet3DiwGbgrEYNp/pOgDWfQbJd7szKf4xOmC68TLnztrJ -0n7x5l3DeRJjM4d3mHZG/MwdEQe0EZr5eI3tEyHqBX9ll9ntIScbiTkPJbl/YytX -PzkDar4krEKAgw9QQerYtcv85TIg0XfXcOjud6RILBYkF1B5QAFFKybj+z527FlG -+mDoYKyPk0fG2q945Tzc8AU/Z3pfpRCUQAahH7uUXS4Ytk2WGCrmqW/OV93zOCfK -4KuVPJqvnTPMOZgROmNwqIgIakN6y1tUxco0fKcWz7MGYlnfB+U7bvn3cP8A9bRz -Fg== ------END CERTIFICATE----- diff --git a/packages/binding-http/test/server.key b/packages/binding-http/test/server.key deleted file mode 100644 index 9723416aa..000000000 --- a/packages/binding-http/test/server.key +++ /dev/null @@ -1,28 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDdOhWrYn/1Z7yV -gbQRZEafSzjzyan4f/xnoYlQ5puFGl2tqk9Lw3IWrMrCHm3+QCrdHkYfkFNNzvmk -X2SwgrgV9Rsq049y5u8kSm354JnN4PS/mgfvd3A9qhihmnxZ9SQguF0xLIUFTI0v -NUytuPILIf8nrPRBTLouWD2cKYaHnEXQMc0ClWdGkMp59U71JUlACLlTYNOzAdjN -Xof8+LX33J696mdzeo2eUYsPGvW4/PHuJmtHkatKyNBO2MYAxGeUXa0SKfHoESAh -/Wb7o+uJi0Jn0Uq8EIqNXCn+2+9dZBzI0O4m6by7ZMSsPdP8sAE/ndded8Q91+yX -tgV9Y1xNAgMBAAECggEAZd2WFsmPRA/VTT5hDNUQk87OVasZQdyk+47tLLxTBjIC -yrGuO6OuR/SelE3wte8UDacgAO6y/RP97YsjUQeP/kRTkx/BYNYnbZ18EGA0UhFw -gT1KxCBKJYrKNrds5Ps0/IAIIVlvkuYR6JfxS9jqUuf/lmRUQDE1VZu6GssgMBaQ -uNN1eSl1abNkv+FzSpeRf64sbOjAKMYMqeVRT/OXoQ+Fed/sMR5uCRKnzf+HEOTo -rRcStdqjTZfrhGsLU9PmUr5NSVojk057zg8EpMeE+AOZJg705enN17g0Lnk16wXC -PTjZqlNfku4XHpAPW6Chgjy6Yq0O2plgd/VUy6BZYQKBgQD6Z1+JhoTeY3ew8pne -rMrqEq0HhYx15BCvgbBi/LlLQVCk1SlEFB3Kyow9ePyJyS0gMo0s0PPg+UgTRiP6 -F4KKeyq+ebarQQ6cazOBkppmS+46201UjVUtWPKGxYEzXTL4eW4bNvZvPYVbZYH1 -sZyIqHP7IN23t5GyzKq2VdIKNQKBgQDiK8hYF/8SPRLIyoArdRhUI2j6fPLNyq6B -I/Yw9/Xad17ZXSz1yp6kEjF9zMG/7RjsYPlNSMUO3qvZeb/rlr0MUcTGvnXUf2Ac -D4ilL2In64hnvmVymXZsKjCdIsKXhUn2MfTut/Oxm9r/390zJ3jJJhrUxZxGiT3T -pKIx55GMuQKBgQCyHEWAdQadiPLiEi4Qjafnx1D8QKogURDtRbpJLqo+NT8/f7/S -hNyMGnaWYzB+cXJ/rHVuo1JVe/sgeousoHxiNCm7QqgEOEpESJxiVf0tNmVFyCfB -NKaZkuMvqdy0xF41D2qaQQYZKhFpaVJ/iM5nBis5Pa93tkMUgzAKchDs4QKBgQDe -6conUVdcUYd/EV8r9dgJrXDdWWMyA27k513Ru8K8FOUOFXCnvJGJIklXNQVNNdFX -wOhWDL/0PPFhgkbvWv1u8/Se8b9OtlAyt/i03jO/Jryli5mPTsPSLe8GIOlrz7or -BefjlLiCOBusD6eDsIaysnTFexLgzol6XIxj5VmmOQKBgEjgJIVpdpuquIdM3a0q -nUgppPhWyCxACIfWEyLWZZNi68PdM1lr3C1pm4tBJrP17poLOWKd2iQTqu5+zZaq -TeC7S1quBgaBgJWPmrcG6qTzMF7gt8HMafikF2gmsOAcrhsBiPvcbl6jd9qe5toW -4uu1Qsft3nPMgpjM15neMof+ ------END PRIVATE KEY----- diff --git a/packages/binding-http/test/tsconfig.json b/packages/binding-http/test/tsconfig.json deleted file mode 100644 index 786b1b616..000000000 --- a/packages/binding-http/test/tsconfig.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "extends": "../tsconfig.json", - "compilerOptions": { - "rootDir": ".." - }, - "include": ["*.ts", "**/*.ts", "../src/**/*.ts"] -} diff --git a/packages/binding-http/tsconfig.json b/packages/binding-http/tsconfig.json deleted file mode 100644 index e9fa7c062..000000000 --- a/packages/binding-http/tsconfig.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "extends": "../../tsconfig.json", - "compilerOptions": { - "outDir": "dist", - "rootDir": "src" - }, - "include": ["src/**/*"], - "references": [{ "path": "../td-tools" }, { "path": "../core" }] -} diff --git a/packages/binding-modbus/.eslintrc.json b/packages/binding-modbus/.eslintrc.json deleted file mode 100644 index c4237119e..000000000 --- a/packages/binding-modbus/.eslintrc.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "extends": "../../.eslintrc.js" -} diff --git a/packages/binding-modbus/README.md b/packages/binding-modbus/README.md deleted file mode 100644 index b19208fba..000000000 --- a/packages/binding-modbus/README.md +++ /dev/null @@ -1,235 +0,0 @@ -# Modbus Client Protocol Binding of node-wot - -## Overview - -W3C Web of Things (WoT) Protocol Binding for [Modbus](https://en.wikipedia.org/wiki/Modbus) TCP [RFC](https://tools.ietf.org/html/draft-dube-modbus-applproto-00). -This package uses [modbus-serial](https://www.npmjs.com/package/modbus-serial) as a low-level client for Modbus TCP. -W3C WoT Binding Template for Modbus can be found [here](https://w3c.github.io/wot-binding-templates/bindings/protocols/modbus/index.html). - -Current Maintainer(s): [@relu91](https://github.com/relu91) [@fillobotto](https://github.com/fillobotto) - -## Client Example - -You can use a code like the following to use the binding. This specific code is interacting with one of the Eclipse Thingweb Test Things at . - -```js -// Protocols and Servient -Servient = require("@node-wot/core").Servient; -ModbusClientFactory = require("@node-wot/binding-modbus").ModbusClientFactory; - -// create Servient and add Modbus binding -let servient = new Servient(); -servient.addClientFactory(new ModbusClientFactory()); - -async function main() { - let td = {}; // see https://github.com/eclipse-thingweb/test-things/blob/main/things/elevator/modbus/js/modbus-elevator.td.json - - const WoT = await servient.start(); - const thing = await WoT.consume(td); - - const readData1 = await thing.readProperty("lightSwitch"); // coil - const readData2 = await thing.readProperty("floorNumber"); // register - - const readValue1 = await readData1.value(); - console.log(readValue1); - - const readValue2 = await readData2.value(); - console.log(readValue2); -} - -main(); -``` - -## Binding Information - -### Protocol specifier - -The protocol prefix handled by this binding is `modbus+tcp://`. - -### New Form Fields for the Modbus Binding - -**Note**: for further details please refer to the [documentation](https://github.com/eclipse-thingweb/node-wot/blob/master/packages/binding-modbus/src/modbus.ts). - -#### modv:function - -Specifies which Modbus function should be issued in the requested command. The list of the available functions is the following: - -| Function ID | Label | -| ----------- | :---------------------------: | -| 1 | readCoil | -| 2 | readDiscreteInput | -| 3 | readHoldingRegisters | -| 4 | readInputRegisters | -| 5 | writeSingleCoil | -| 6 | writeSingleHoldingRegister | -| 15 | writeMultipleCoils | -| 16 | writeMultipleHoldingRegisters | - -This list is from [the Modbus Binding Template](https://w3c.github.io/wot-binding-templates/bindings/protocols/modbus/#function). -Function IDs are used on the wire but the labels should be used in a TD as the values of `modv:function` property. - -#### modv:entity - -The Modbus resource the `modv:function` acts on. It can be filled with the following keywords: `Coil`, `DiscreteInput`, `InputRegister`, `HoldingRegister`. The client will then determine the right function code to be applied in the Modbus request. Furthermore, it can be used in multi-operation forms where `modv:function` cannot be used but the correct function is filled based on [the default assumptions](https://w3c.github.io/wot-binding-templates/bindings/protocols/modbus/#default-mappings). See the [example]. - -**Note** that when used in conjunction with `modv:function`, the value of `modv:entity` property is ignored. - -#### modv:unitID - -The physical bus address of the unit targeted by the Modbus request. - -#### modv:address - -This property defines the starting address of registers or coils that are meant to be written. - -#### modv:quantity - -This property defines the total amount of registers or coils that should be written, beginning with the register specified with the property `modv:address`. - -#### modv:pollingTime - -The polling time used for subscriptions. The client will issue a reading command every `modv:pollingTime` milliseconds. Note that the reading request timeout can be still controlled using `modv:timeout` property. - -#### modv:timeout - -Timeout in milliseconds of the Modbus request. Default to 1000 milliseconds - -### URL format - -The URL is used to transport all addressing information necessary to describe the MODBUS connection and register addresses. It has the following structure: - -``` -modbus+tcp:// [ : ] [/ [ /
] [&quantity= ] ] -``` - -with the following meaning: - -- `` is the hostname or IP address of the MODBUS slave -- `` is the optional TCP port number used to access the MODBUS slave. Default is 502 -- `` is the MODBUS unit id of the MODBUS slave; same as [modv:unitID](#modv:unitID) -- `
` is the starting address register number; see [modv:address](#modv:address) -- `` is the optional number of registers to access. Default is 1; see [modv:quantity](#modv:quantity) - -When specified URL values override the corresponding `form` parameter. - -### DataSchema - -The MODBUS binding uses and provides plain binary data for reading and writing. Therefore in most cases, it will be associated with the content type `application/octet-stream`. Please refer to the description of this codec on how to decode and encode plain register values to/from JavaScript objects (See `OctetstreamCodec`). **Note** `array` and `object` schema are not supported. - -Along with content type `application/octet-stream`, this protocol binding accepts also an optional `byteSeq` parameter. `byteSeq` specifies the endian-ness of the raw byte data being read/written by the MODBUS binding. It follows the notation `application/octet-stream;byteSeq=value`, where its value can be one of the following: - -- `BIG_ENDIAN`, which is the default value -- `LITTLE_ENDIAN` -- `BIG_ENDIAN_BYTE_SWAP` -- `LITTLE_ENDIAN_BYTE_SWAP` - -**Note**: the list above may be extended in the future. - -In particular, the decimal numbers `9545` and `22880` will be encoded as follows: - -- `BIG_ENDIAN`: `25 49 59 60` -- `LITTLE_ENDIAN`: `60 59 49 25` -- `BIG_ENDIAN_BYTE_SWAP`: `49 25 60 59` -- `LITTLE_ENDIAN_BYTE_SWAP`: `59 60 25 49` - -For register resources, the payload is just the plain sequence of bytes read from or written to the registers. For coils and discrete inputs, the payload is a sequence of bytes, each corresponding to a single coil or discrete input. Each byte contains the value `0` or `1`. So the encoder and decoder should work on this series of bytes and does not have to take care about handling the individual bits. Mapping each coil or discrete input to a single property of type `boolean` works just fine. - -Another parameter that can be used in conjunction with `application/octet-stream` is `length`. This parameter specifies the length of the payload in bytes. This is useful to indicate the actual length of the payload when reading or writing a sequence of registers. For example, when reading a property using `readHoldingRegisters`, the payload length can be used to specify the number of registers to be read. Notice that the payload length must always -be equal to the number of registers multiplied by the size of the register in bytes. - -### Security - -The protocol does not support security. - -## Implementation notes - -This implementation handles multiple requests to the same slave by serializing and combining them if possible. In the following, the terms **request** and **transaction** are used as follows to describe this: - -- A **request** is a read or write request to a resource as issued by a user of the node-wot API. -- A **transaction** is a Modbus operation and may cover the data of multiple **requests**. - -### Combination - -When two **requests** of the same type are issued and these requests cover neighbored registers, then they are combined into a single **transaction** reading or writing the combined register range. Note that this algorithm is currently rather simple: New **requests** are just checked if they can be prepended or appended to an existing **transaction**. If not, a new **transaction** is created. When a **transaction** completes, all **requests** contained in this **transaction** are completed. - -### Serialization - -Multiple **transactions** to the same slave are serialized. This means that a new MODBUS **transaction** is only started when the previous **transaction** was finished. **Transactions** are held in a queue and executed in a first-come-first-serve manner. - -### Combination using the node-wot API - -To help the MODBUS binding to perform combination a user of the API should create several requests for neighbor registers and resolve them all together in a single call to `Promise.all()`, e.g. as follows: - -```javascript -console.info("Creating promise vl1n"); -let vl1n = pac3200Thing.readProperty("Voltage/L1N"); -console.info("Creating promise vl2n"); -let vl2n = pac3200Thing.readProperty("Voltage/L2N"); -console.info("Creating promise vl3n"); -let vl3n = pac3200Thing.readProperty("Voltage/L3N"); - -console.info("Resolving all promises"); -Promise.all([vl1n, vl2n, vl3n]) - .then((values) => values.forEach((v) => console.info("Voltage = ", await v.value()))) - .catch((reason) => console.warn("Failed ", reason)); -``` - -This procedure guarantees that the **requests** are all combined into a single **transaction** _before_ this **transaction** is executed. A similar approach can also be used to write multiple properties in a single **transaction**. - -## Valid Form Examples - -### Base read function form - -Reads the 8th input register of the unit 1 - -```json -{ - "href": "modbus+tcp://127.0.0.1:60000/1/8", - "contentType": "application/octet-stream;length=2", - "op": ["readproperty"], - "modv:function": "readInputRegister", - "modv:timeout": 2000 -} -``` - -### Write/read function form - -Read and write the 8th holding register of the unit 1 - -```json -{ - "href": "modbus+tcp://127.0.0.1:60000/1/8", - "contentType": "application/octet-stream;length=2", - "op": ["readproperty", "writeproperty"], - "modv:entity": "HoldingRegister" -} -``` - -### Subscribe form - -Polls the 8th holding register of the unit 1 every second. - -```json -{ - "href": "modbus+tcp://127.0.0.1:60000/1/8", - "contentType": "application/octet-stream;length=2", - "op": ["observeproperty"], - "modv:entity": "HoldingRegister", - "modv:pollingTime": 1000 -} -``` - -## TODOs - -- [x] TEST -- [ ] (Modbus Server Protocol Binding) -- [x] Connection pooling -- [ ] More sophisticated algorithm for combining requests. Some ideas - - Not only append or prepend requests to transactions, but also combine transactions which become neighboured later on - - Impose some limit to the overall number of registers. The MODBUS protocol has such a limit and devices may define even lower values -- [ ] When a connection times out, re-connection does not work (see `connectionTimeout` in modbus-client.ts) -- [x] When a Modbus device is not reachable, scripts using binding-modbus stop working - corresponding error handling is necessary - -## More Details - -See . diff --git a/packages/binding-modbus/package.json b/packages/binding-modbus/package.json deleted file mode 100644 index cfa4ff53c..000000000 --- a/packages/binding-modbus/package.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "name": "@node-wot/binding-modbus", - "version": "0.8.15", - "description": "Modbus TCP client protocol binding for node-wot", - "author": "Eclipse Thingweb (https://thingweb.io/)", - "contributors": [ - "Siemens AG" - ], - "license": "EPL-2.0 OR W3C-20150513", - "repository": "https://github.com/thingweb/node-wot/tree/master/packages/binding-modbus", - "publishConfig": { - "access": "public" - }, - "files": [ - "dist/" - ], - "main": "dist/modbus.js", - "types": "dist/modbus.d.ts", - "dependencies": { - "@node-wot/core": "0.8.15", - "@node-wot/td-tools": "0.8.15", - "modbus-serial": "^8.0.17", - "rxjs": "5.5.11", - "wot-typescript-definitions": "0.8.0-SNAPSHOT.29" - }, - "scripts": { - "build": "tsc -b", - "test": "mocha --require ts-node/register --extension ts", - "lint": "eslint .", - "lint:fix": "eslint . --fix", - "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\" \"**/*.json\"" - }, - "bugs": { - "url": "https://github.com/eclipse-thingweb/node-wot/issues" - }, - "homepage": "https://github.com/eclipse-thingweb/node-wot/tree/master/packages/binding-modbus#readme", - "directories": { - "test": "test" - } -} diff --git a/packages/binding-modbus/src/modbus-client-factory.ts b/packages/binding-modbus/src/modbus-client-factory.ts deleted file mode 100644 index 118e86941..000000000 --- a/packages/binding-modbus/src/modbus-client-factory.ts +++ /dev/null @@ -1,50 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2020 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 { ProtocolClientFactory, ProtocolClient, createDebugLogger, createWarnLogger } from "@node-wot/core"; -import ModbusClient from "./modbus-client"; - -const debug = createDebugLogger("binding-modbus", "modbus-client-factory"); -const warn = createWarnLogger("binding-modbus", "modbus-client-factory"); - -export default class ModbusClientFactory implements ProtocolClientFactory { - public readonly scheme: string = "modbus+tcp"; - private singleton?: ModbusClient; - - public getClient(): ProtocolClient { - debug(`Get client for '${this.scheme}'`); - this.init(); - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- singleton is initialized in init() - return this.singleton!; - } - - public init(): boolean { - if (!this.singleton) { - debug(`Initializing client for '${this.scheme}'`); - this.singleton = new ModbusClient(); - } - return true; - } - - public destroy(): boolean { - debug(`Destroying client for '${this.scheme}'`); - if (!this.singleton) { - warn(`Destroying a not initialized client factory for '${this.scheme}'`); - return true; // do not cause an error - } - this.singleton.stop(); - this.singleton = undefined; - return true; - } -} diff --git a/packages/binding-modbus/src/modbus-client.ts b/packages/binding-modbus/src/modbus-client.ts deleted file mode 100644 index b4f5830ad..000000000 --- a/packages/binding-modbus/src/modbus-client.ts +++ /dev/null @@ -1,325 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2020 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 - ********************************************************************************/ -/** - * Modbus master based on modbus-serial - */ -import { ModbusForm, ModbusFunction } from "./modbus"; - -import { ProtocolClient, Content, DefaultContent, createDebugLogger, Endianness } from "@node-wot/core"; -import { SecurityScheme } from "@node-wot/td-tools"; -import { modbusFunctionToEntity } from "./utils"; -import { ModbusConnection, ModbusFormWithDefaults, PropertyOperation } from "./modbus-connection"; -import { Readable } from "stream"; -import { Subscription } from "rxjs/Subscription"; - -const debug = createDebugLogger("binding-modbus", "modbus-client"); - -const DEFAULT_PORT = 805; -const DEFAULT_TIMEOUT = 1000; -const DEFAULT_POLLING = 2000; - -class ModbusSubscription { - interval: NodeJS.Timeout; - complete: () => void; - constructor( - form: ModbusForm, - client: ModbusClient, - next: (value: Content) => void, - error?: (error: Error) => void, - complete?: () => void - ) { - if (!complete) { - complete = () => { - // do nothing. - }; - } - this.interval = global.setInterval(async () => { - try { - const result = await client.readResource(form); - next(result); - } catch (e) { - if (error) { - error(e instanceof Error ? e : new Error(JSON.stringify(e))); - } - clearInterval(this.interval); - } - }, form["modv:pollingTime"]); // TODO: Until https://github.com/eclipse-thingweb/node-wot/issues/1236 is clarified, we will use this as the polling rate. - - this.complete = complete; - } - - unsubscribe() { - clearInterval(this.interval); - this.complete(); - } -} - -export default class ModbusClient implements ProtocolClient { - private _connections: Map; - - private _subscriptions: Map = new Map(); - - constructor() { - this._connections = new Map(); - } - - readResource(form: ModbusForm): Promise { - return this.performOperation(form) as Promise; - } - - async writeResource(form: ModbusForm, content: Content): Promise { - await this.performOperation(form, content); - } - - async invokeResource(form: ModbusForm, content: Content): Promise { - await this.performOperation(form, content); - - // Same with MQTT, there is no response - return new DefaultContent(Readable.from("")); - } - - unlinkResource(form: ModbusForm): Promise { - form = this.validateAndFillDefaultForm(form, 0); - const id = `${form.href}/${form["modv:unitID"]}#${form["modv:function"]}?${form["modv:address"]}&${form["modv:quantity"]}`; - - const subscription = this._subscriptions.get(id); - if (!subscription) { - throw new Error("No subscription for " + id + " found"); - } - subscription.unsubscribe(); - - this._subscriptions.delete(id); - - return Promise.resolve(); - } - - public subscribeResource( - form: ModbusForm, - next: (value: Content) => void, - error?: (error: Error) => void, - complete?: () => void - ): Promise { - return new Promise((resolve, reject) => { - form = this.validateAndFillDefaultForm(form, 0); - - const id = `${form.href}/${form["modv:unitID"]}#${form["modv:function"]}?${form["modv:address"]}&${form["modv:quantity"]}`; - - if (this._subscriptions.has(id)) { - reject(new Error("Already subscribed for " + id + ". Multiple subscriptions are not supported")); - } - - const subscription = new ModbusSubscription(form, this, next, error, complete); - - this._subscriptions.set(id, subscription); - resolve( - new Subscription(() => { - subscription.unsubscribe(); - }) - ); - }); - } - - /** - * @inheritdoc - */ - public async requestThingDescription(uri: string): Promise { - throw new Error("Method not implemented"); - } - - async start(): Promise { - // do nothing - } - - async stop(): Promise { - this._connections.forEach((connection) => { - connection.close(); - }); - } - - setSecurity(metadata: SecurityScheme[], credentials?: unknown): boolean { - return false; - } - - private async performOperation(form: ModbusForm, content?: Content): Promise { - // get host and port - const parsed = new URL(form.href); - const port = parsed.port ? parseInt(parsed.port, 10) : DEFAULT_PORT; - let body; - if (content != null) { - body = await content.toBuffer(); - } - const formValidated = this.validateAndFillDefaultForm(form, body?.byteLength); - - const endianness = this.validateEndianness(formValidated); - - const host = parsed.hostname; - const hostAndPort = host + ":" + port; - - if (body != null) { - this.validateBufferLength(formValidated, body); - } - - // find or create connection - let connection = this._connections.get(hostAndPort); - - if (!connection) { - debug(`Creating new ModbusConnection for ${hostAndPort}`); - - connection = new ModbusConnection(host, port, { - connectionTimeout: form["modv:timeout"] ?? DEFAULT_TIMEOUT, - }); - this._connections.set(hostAndPort, connection); - } else { - debug(`Reusing ModbusConnection for ${hostAndPort}`); - } - // create operation - const operation = new PropertyOperation(formValidated, endianness, body); - - // enqueue the operation at the connection - connection.enqueue(operation); - - // return a promise to execute the operation - return operation.execute(); - } - - private validateEndianness(form: ModbusForm): Endianness { - let endianness = Endianness.BIG_ENDIAN; - if (form.contentType != null) { - const contentValues: string[] = form.contentType.split(";") ?? []; - // Check endian-ness - const byteSeq = contentValues.find((value) => /^byteSeq=/.test(value)); - if (byteSeq != null) { - const guessEndianness = Endianness[byteSeq.split("=")[1] as keyof typeof Endianness]; - if (guessEndianness != null) { - endianness = guessEndianness; - } else { - throw new Error("Malformed form: Content Type endianness is not valid"); - } - } - } - return endianness; - } - - // This generates a form used internally with url content based on the uri scheme - // Ideally, more code should be refactored to use uri only - private addFormElementsFromURLPath(input: ModbusForm): ModbusForm { - const returnForm: ModbusForm = { ...input }; - const { pathname, searchParams: query } = new URL(input.href); - const pathComp = pathname.split("/"); - if (pathComp.length < 3 || pathComp[1] === "" || pathComp[2] === "") { - throw new Error("Malformed href: unitID and address must be defined"); - } - returnForm["modv:unitID"] = parseInt(pathComp[1], 10); - returnForm["modv:address"] = parseInt(pathComp[2], 10); - - const queryQuantity = query.get("quantity"); - if (queryQuantity != null) { - returnForm["modv:quantity"] = parseInt(queryQuantity, 10); - } - return returnForm; - } - - private validateBufferLength(form: ModbusFormWithDefaults, buffer: Buffer) { - const mpy = form["modv:entity"] === "InputRegister" || form["modv:entity"] === "HoldingRegister" ? 2 : 1; - const quantity = form["modv:quantity"]; - if (buffer.length !== mpy * quantity) { - throw new Error( - "Content length does not match register / coil count, got " + - buffer.length + - " bytes for " + - quantity + - ` ${mpy === 2 ? "registers" : "coils"}` - ); - } - } - - private validateAndFillDefaultForm(inputForm: ModbusForm, contentLength = 0): ModbusFormWithDefaults { - const mode = contentLength > 0 ? "w" : "r"; - - // Use URI values to generate form keys - const filledForm: ModbusForm = this.addFormElementsFromURLPath(inputForm); - - // take over latest content of form into a new result set - const result: ModbusForm = { ...filledForm }; - - if (filledForm["modv:function"] == null && filledForm["modv:entity"] == null) { - throw new Error("Malformed form: modv:function or modv:entity must be defined"); - } - - if (filledForm["modv:function"] != null) { - // Convert string function to enums if defined - if (typeof filledForm["modv:function"] === "string") { - result["modv:function"] = ModbusFunction[filledForm["modv:function"]]; - } - - // Check if the function is a valid modbus function code - if (!Object.keys(ModbusFunction).includes(filledForm["modv:function"].toString())) { - throw new Error("Undefined function number or name: " + filledForm["modv:function"]); - } - } - - if (filledForm["modv:entity"]) { - switch (filledForm["modv:entity"]) { - case "Coil": - result["modv:function"] = - mode === "r" - ? ModbusFunction.readCoil - : contentLength > 1 - ? ModbusFunction.writeMultipleCoils - : ModbusFunction.writeSingleCoil; - break; - case "HoldingRegister": - // the content length must be divided by 2 (holding registers are 16bit) - result["modv:function"] = - mode === "r" - ? ModbusFunction.readHoldingRegisters - : contentLength / 2 > 1 - ? ModbusFunction.writeMultipleHoldingRegisters - : ModbusFunction.writeSingleHoldingRegister; - break; - case "InputRegister": - result["modv:function"] = ModbusFunction.readInputRegister; - break; - case "DiscreteInput": - result["modv:function"] = ModbusFunction.readDiscreteInput; - break; - default: - throw new Error("Unknown modbus entity: " + filledForm["modv:entity"]); - } - } else { - // 'modv:entity' undefined but modv:function defined - result["modv:entity"] = modbusFunctionToEntity(result["modv:function"] as ModbusFunction); - } - - if (filledForm["modv:address"] === undefined || filledForm["modv:address"] === null) { - throw new Error("Malformed form: address must be defined"); - } - - const hasQuantity = filledForm["modv:quantity"] != null; - - if (!hasQuantity && contentLength === 0) { - result["modv:quantity"] = 1; - } else if (!hasQuantity && contentLength > 0) { - const regSize = - result["modv:entity"] === "InputRegister" || result["modv:entity"] === "HoldingRegister" ? 2 : 1; - result["modv:quantity"] = contentLength / regSize; - } - - result["modv:pollingTime"] ??= DEFAULT_POLLING; - result["modv:timeout"] ??= DEFAULT_TIMEOUT; - - return result as ModbusFormWithDefaults; - } -} diff --git a/packages/binding-modbus/src/modbus-connection.ts b/packages/binding-modbus/src/modbus-connection.ts deleted file mode 100644 index 71cc813f7..000000000 --- a/packages/binding-modbus/src/modbus-connection.ts +++ /dev/null @@ -1,532 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2020 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 ModbusRTU from "modbus-serial"; -import { ReadCoilResult, ReadRegisterResult } from "modbus-serial/ModbusRTU"; -import { ModbusEntity, ModbusFunction, ModbusForm } from "./modbus"; -import { Content, ContentSerdes, createLoggers, Endianness } from "@node-wot/core"; -import { Readable } from "stream"; -import { inspect } from "util"; - -const { debug, warn, error } = createLoggers("binding-modbus", "modbus-connection"); - -const configDefaults = { - connectionTimeout: 1000, - operationTimeout: 2000, - connectionRetryTime: 10000, - maxRetries: 5, -}; - -/** - * ModbusTransaction represents a raw MODBUS operation performed on a ModbusConnection - */ -class ModbusTransaction { - connection: ModbusConnection; - unitId: number; - registerType: ModbusEntity; - function: ModbusFunction; - base: number; - quantity: number; - content?: Buffer; - operations: Array; // operations to be completed when this transaction completes - endianness: Endianness; - constructor( - connection: ModbusConnection, - unitId: number, - registerType: ModbusEntity, - func: ModbusFunction, - base: number, - quantity: number, - endianness: Endianness, - content?: Buffer - ) { - this.connection = connection; - this.unitId = unitId; - this.registerType = registerType; - this.function = func; - this.base = base; - this.quantity = quantity; - this.content = content; - this.operations = new Array(); - this.endianness = endianness; - } - - /** - * Link PropertyOperation with this transaction, so that operations can be - * notified about the result of a transaction. - * - * @param op the PropertyOperation to link with this transaction - */ - inform(op: PropertyOperation) { - op.transaction = this; - this.operations.push(op); - } - - /** - * Trigger work on the associated connection. - * - * @see ModbusConnection.trigger() - */ - trigger() { - debug("ModbusTransaction:trigger"); - this.connection.trigger(); - } - - /** - * Execute this ModbusTransaction and resolve/reject the invoking Promise as well - * as the Promises of all associated PropertyOperations. - * - * @param resolve - * @param reject - */ - async execute(): Promise { - if (!this.content) { - // Read transaction - debug(`Trigger read operation on ${this.base}, len: ${this.quantity}`); - try { - const result = await this.connection.readModbus(this); - debug(`Got result from read operation on ${this.base}, len: ${this.quantity}`); - this.operations.forEach((op) => op.done(this.base, result.buffer)); - } catch (err) { - warn(`Read operation failed on ${this.base}, len: ${this.quantity}, ${err}`); - // inform all operations and the invoker - this.operations.forEach((op) => op.failed(err instanceof Error ? err : new Error(JSON.stringify(err)))); - throw err; - } - } else { - debug(`Trigger write operation on ${this.base}, len: ${this.quantity}`); - try { - await this.connection.writeModbus(this); - this.operations.forEach((op) => op.done()); - } catch (err) { - warn(`Write operation failed on ${this.base}, len: ${this.quantity}, ${err}`); - // inform all operations and the invoker - this.operations.forEach((op) => op.failed(err instanceof Error ? err : new Error(JSON.stringify(err)))); - throw err; - } - } - } -} - -export type ModbusFormWithDefaults = ModbusForm & - Required< - Pick< - ModbusForm, - | "modv:function" - | "modv:entity" - | "modv:unitID" - | "modv:address" - | "modv:quantity" - | "modv:timeout" - | "modv:pollingTime" - > - >; - -/** - * ModbusConnection represents a client connected to a specific host and port - */ -export class ModbusConnection { - host: string; - port: number; - client: ModbusRTU; - connecting: boolean; - connected: boolean; - timer: NodeJS.Timer | null; // connection idle timer - currentTransaction: ModbusTransaction | null; // transaction currently in progress or null - queue: Array; // queue of further transactions - config: { - connectionTimeout: number; - operationTimeout: number; - connectionRetryTime: number; - maxRetries: number; - }; - - constructor( - host: string, - port: number, - config: { - connectionTimeout?: number; - operationTimeout?: number; - connectionRetryTime?: number; - maxRetries?: number; - } = configDefaults - ) { - this.host = host; - this.port = port; - this.client = new ModbusRTU(); // new ModbusClient(); - this.connected = false; - this.connecting = false; - this.timer = null; - this.currentTransaction = null; - this.queue = new Array(); - - this.config = Object.assign(configDefaults, config); - } - - /** - * Enqueue a PropertyOperation by either creating a new ModbusTransaction - * or joining it with an existing compatible transaction. - * - * Note: The algorithm for joining operations implemented here is very simple - * and can be further elaborated. - * - * Note: Devices may also have a limit on the size of a MODBUS transaction. - * This is not accounted for in this implementation. - * - * @param op PropertyOperation to be enqueued - */ - enqueue(op: PropertyOperation): void { - // try to merge with any pending transaction - for (const t of this.queue) { - if ( - op.unitId === t.unitId && - op.registerType === t.registerType && - (op.content != null) === (t.content != null) - ) { - // same type, are registers adjacent? - if (op.base === t.base + t.quantity) { - // append - t.quantity += op.quantity; - // write operation - if (t.content && op.content) { - t.content = Buffer.concat([t.content, op.content]); - } - - t.inform(op); - return; - } - - if (op.base + op.quantity === t.base) { - // prepend - t.base -= op.quantity; - t.quantity += op.quantity; - - // write operation - if (t.content && op.content) { - t.content = Buffer.concat([op.content, t.content]); - } - - t.inform(op); - return; - } - } - } - - // create and append a new transaction - const transaction = new ModbusTransaction( - this, - op.unitId, - op.registerType, - op.function, - op.base, - op.quantity, - op.endianness, - op.content - ); - transaction.inform(op); - this.queue.push(transaction); - } - - async connect(): Promise { - if (!this.connecting && !this.client.isOpen) { - debug(`Trying to connect to ${this.host}`); - this.connecting = true; - - for (let retry = 0; retry < this.config.maxRetries; retry++) { - try { - this.client.setTimeout(this.config.connectionTimeout); - await this.client.connectTCP(this.host, { port: this.port }); - this.connecting = false; - debug(`Modbus connected to ${this.host}`); - return; - } catch (err) { - warn( - `Cannot connect to ${this.host}. Reason: ${err}. Retry in ${this.config.connectionRetryTime}ms.` - ); - this.connecting = false; - if (retry >= this.config.maxRetries - 1) { - throw new Error("Max connection retries"); - } - await new Promise((resolve) => setTimeout(resolve, this.config.connectionRetryTime)); - } - } - } - } - - /** - * Trigger work on this connection. - * - * If the ModbusConnection is unconnected, connect it and retrigger. - * If no transaction is currently being processed but transactions are waiting, - * start the next transaction. - * Retrigger after success or failure. - */ - async trigger(): Promise { - warn("trigger"); - if (!this.connecting && !this.client.isOpen) { - // connection may be closed due to operation timeout - // try to reconnect again - try { - await this.connect(); - this.trigger(); - } catch (error) { - warn("Cannot reconnect to modbus server"); - // inform and clean up all the operations that the connection cannot be recovered - while (this.queue.length > 0) { - const transaction = this.queue.shift(); - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- queue.length > 0 - transaction!.operations.forEach((operation) => { - operation.failed(error instanceof Error ? error : new Error(JSON.stringify(error))); - }); - } - } - } else if (this.client.isOpen && this.currentTransaction == null && this.queue.length > 0) { - // take next transaction from queue and execute - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- queue.length > 0 - this.currentTransaction = this.queue.shift()!; - try { - await this.currentTransaction.execute(); - this.currentTransaction = null; - this.trigger(); - } catch (err) { - warn(`Transaction failed. ${err}`); - this.currentTransaction = null; - this.trigger(); - } - } - } - - public close(): void { - this.modbusstop(); - } - - async readModbus(transaction: ModbusTransaction): Promise { - debug("Invoking read transaction"); - // reset connection idle timer - if (this.timer) { - clearTimeout(this.timer); - } - - this.timer = global.setTimeout(() => this.modbusstop(), this.config.operationTimeout); - - const regType: ModbusEntity = transaction.registerType; - this.client.setID(transaction.unitId); - - switch (regType) { - case "InputRegister": - return this.client.readInputRegisters(transaction.base, transaction.quantity); - case "Coil": - return this.client.readCoils(transaction.base, transaction.quantity); - case "HoldingRegister": - return this.client.readHoldingRegisters(transaction.base, transaction.quantity); - case "DiscreteInput": - return this.client.readDiscreteInputs(transaction.base, transaction.quantity); - default: - throw new Error("cannot read unknown register type " + regType); - } - } - - async writeModbus(transaction: ModbusTransaction): Promise { - debug("Invoking write transaction"); - // reset connection idle timer - if (this.timer) { - clearTimeout(this.timer); - } - - if (!transaction.content) { - throw new Error("Invoked write transaction without content"); - } - - this.timer = global.setTimeout(() => this.modbusstop(), this.config.operationTimeout); - - const modFunc: ModbusFunction = transaction.function; - this.client.setID(transaction.unitId); - switch (modFunc) { - case 5: { - // write single coil - const coil = transaction.content.readUInt8(0) !== 0; - const result = await this.client.writeCoil(transaction.base, coil); - - if (result.address !== transaction.base && result.state !== coil) { - throw new Error(`writing ${coil} to ${transaction.base} failed, state is ${result.state}`); - } - - break; - } - case 15: { - // write multiple coils - const coils = new Array(); - transaction.content.forEach((v) => coils.push(v !== 0)); - const coilsResult = await this.client.writeCoils(transaction.base, coils); - if (coilsResult.address !== transaction.base && coilsResult.length !== transaction.quantity) { - throw new Error(`writing ${coils} to ${transaction.base} failed`); - } - break; - } - case 6: { - // writing a single value to a single register - // Since we are reading from the buffer, we need to convert to a notable endianness - if (transaction.endianness !== Endianness.BIG_ENDIAN) { - transaction.content.swap16(); - } - - const value = transaction.content.readUInt16BE(0); - const resultRegister = await this.client.writeRegister(transaction.base, value); - - if (resultRegister.address !== transaction.base && resultRegister.value !== value) { - throw new Error(`writing ${value} to ${transaction.base} failed, state is ${resultRegister.value}`); - } - break; - } - case 16: { - // writing values to multiple registers - const registers = await this.client.writeRegisters(transaction.base, transaction.content); - - if (registers.address === transaction.base && transaction.quantity / 2 > registers.length) { - warn( - `short write to registers ${transaction.base} + ${transaction.quantity}, wrote ${inspect( - transaction.content - )} to ${registers.address} + ${registers.length} ` - ); - } else if (registers.address !== transaction.base) { - throw new Error( - `writing ${inspect(transaction.content)} to registers ${transaction.base} + ${ - transaction.quantity - } failed, wrote to ${registers.address}` - ); - } - break; - } - default: - throw new Error("cannot read unknown function type " + modFunc); - } - } - - private modbusstop() { - debug("Closing unused connection"); - this.client.close((err: string) => { - if (!err) { - debug("Session closed"); - this.connecting = false; - } else { - error(`Cannot close session. ${err}`); - } - }); - this.timer && clearInterval(this.timer); - this.timer = null; - } -} - -/** - * PropertyOperation represents a read or write operation on a property - */ -export class PropertyOperation { - unitId: number; - registerType: ModbusEntity; - base: number; - quantity: number; - function: ModbusFunction; - content?: Buffer; - endianness: Endianness; - transaction: ModbusTransaction | null; // transaction used to execute this operation - contentType: string; - resolve?: (value?: Content | PromiseLike) => void; - reject?: (reason?: Error) => void; - - constructor(form: ModbusFormWithDefaults, endianness: Endianness, content?: Buffer) { - this.unitId = form["modv:unitID"]; - this.registerType = form["modv:entity"]; - this.base = form["modv:address"]; - this.quantity = form["modv:quantity"]; - this.function = form["modv:function"] as ModbusFunction; - this.endianness = endianness; - this.contentType = form.contentType ?? ContentSerdes.DEFAULT; - this.content = content; - this.transaction = null; - } - - /** - * Trigger execution of this operation. - * - */ - async execute(): Promise<(Content | PromiseLike) | undefined> { - return new Promise( - (resolve: (value?: Content | PromiseLike) => void, reject: (reason?: Error) => void) => { - this.resolve = resolve; - this.reject = reject; - - if (this.transaction == null) { - reject(Error("No transaction for this operation")); - } else { - this.transaction.trigger(); - } - } - ); - } - - /** - * Invoked by the ModbusTransaction when it has completed successfully - * - * @param base Base register address of the transaction (on read) - * @param buffer Result data of the transaction as Buffer (on read) - * @param data Result data of the transaction as array (on read) - */ - done(base?: number, buffer?: Buffer): void { - debug("Operation done"); - - if (!this.resolve || !this.reject) { - throw new Error("Function 'done' was invoked before executing the Modbus operation"); - } - - if (base === null || base === undefined) { - // resolve write operation - this.resolve(); - return; - } - - if (buffer === null || buffer === undefined) { - this.reject(new Error("Write operation finished without buffer")); - return; - } - - // extract the proper part from the result and resolve promise - const address = this.base - base; - let resp: Content; - if (this.registerType === "InputRegister" || this.registerType === "HoldingRegister") { - const bufstart = 2 * address; - const bufend = 2 * (address + this.quantity); - - resp = new Content(this.contentType, Readable.from(buffer.slice(bufstart, bufend))); - } else { - resp = new Content(this.contentType, Readable.from(buffer.slice(address, this.quantity))); - } - - // resolve the Promise given to the invoking script - this.resolve(resp); - } - - /** - * Invoked by the ModbusTransaction when it has failed. - * - * @param reason Reason of failure - */ - failed(reason: Error): void { - warn(`Operation failed: ${reason}`); - if (!this.reject) { - throw new Error("Function 'failed' was invoked before executing the Modbus operation"); - } - // reject the Promise given to the invoking script - this.reject(reason); - } -} diff --git a/packages/binding-modbus/src/modbus.ts b/packages/binding-modbus/src/modbus.ts deleted file mode 100644 index e3986191f..000000000 --- a/packages/binding-modbus/src/modbus.ts +++ /dev/null @@ -1,123 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2020 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 { Form } from "@node-wot/td-tools"; -export { default as ModbusClientFactory } from "./modbus-client-factory"; -export { default as ModbusClient } from "./modbus-client"; -export * from "./modbus-client"; -export * from "./modbus-client-factory"; - -export type ModbusEntity = "Coil" | "InputRegister" | "HoldingRegister" | "DiscreteInput"; - -export enum ModbusFunction { - "readCoil" = 1, - "readDiscreteInput" = 2, - "readHoldingRegisters" = 3, - "readInputRegister" = 4, - "writeSingleCoil" = 5, - "writeSingleHoldingRegister" = 6, - "writeMultipleCoils" = 15, - "writeMultipleHoldingRegisters" = 16, - "readDeviceIdentification" = 43, -} - -/** - * Different modbus function names as defined in - * https://w3c.github.io/wot-binding-templates/bindings/protocols/modbus/#function. - */ -export type ModbusFunctionName = - | "readCoil" - | "readDiscreteInput" - | "readHoldingRegisters" - | "writeSingleCoil" - | "writeSingleHoldingRegister" - | "writeMultipleCoils" - | "writeMultipleHoldingRegisters" - | "readDeviceIdentification"; - -export class ModbusForm extends Form { - /** - * The modbus function issued in the request. - */ - public "modv:function"?: ModbusFunction | ModbusFunctionName; - /** - * Describe the entity type of the request. This property can be - * used instead of 'modv:function' when the form has multiple op. For - * example if op = ['readProperty','writeProperty'] and 'modv:function - * is 'Coil', the low level modbus function will be mapped to 1 when - * reading and to 5 when writing. - */ - public "modv:entity"?: ModbusEntity; - /** - * Physical address of the unit connected to the bus. - */ - public "modv:unitID"?: number; - /** - * Defines the starting address of registers or coils that are - * meant to be written. - */ - public "modv:address"?: number; - /** - * Defines the total amount of registers or coils that - * should be written, beginning with the register specified - * with the property 'modbus:address'. - */ - public "modv:quantity"?: number; - /** - * Maximum polling rate that this implementation uses for subscriptions. - * The client will issue a reading - * command every modv:pollingTime milliseconds. Note that - * the reading request timeout can be still controlled using - * modv:timeout property. - */ - public "modv:pollingTime"?: number; - /** - * When true, it describes that the byte order of the data in the Modbus message is the most significant byte first (i.e., Big-Endian). When false, it describes the least significant byte first (i.e., Little-Endian). - */ - public "modv:mostSignificantByte"?: boolean; - /** - * When true, it describes that the word order of the data in the Modbus message is the most significant word first (i.e., no word swapping). When false, it describes the least significant word first (i.e. word swapping) - */ - public "modv:mostSignificantWord"?: boolean; - /** - * Modbus implementations can differ in the way addressing works, as the first coil/register can be either referred to as true or false. - */ - public "modv:zeroBasedAddressing"?: boolean; - - /** - * Timeout in milliseconds of the modbus request. Default to 1000 milliseconds - */ - public "modv:timeout"?: number; - /** - * Specifies the data type contained in the request or response payload. Users can define the specific data type using a sub type of xsd:decimal. - */ - public "modv:type"?: ModbusDataType; -} - -export type ModbusDataType = - | "xsd:integer" - | "xsd:boolean" - | "xsd:string" - | "xsd:float" - | "xsd:decimal" - | "xsd:byte" - | "xsd:short" - | "xsd:int" - | "xsd:long" - | "xsd:unsignedByte" - | "xsd:unsignedShort" - | "xsd:unsignedInt" - | "xsd:unsignedLong" - | "xsd:double" - | "xsd:hexBinary"; diff --git a/packages/binding-modbus/src/utils.ts b/packages/binding-modbus/src/utils.ts deleted file mode 100644 index 4ee12d6fb..000000000 --- a/packages/binding-modbus/src/utils.ts +++ /dev/null @@ -1,38 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2020 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 { ModbusFunction, ModbusEntity } from "./modbus"; - -export function modbusFunctionToEntity(modbusFun: ModbusFunction): ModbusEntity { - switch (modbusFun) { - case ModbusFunction.readCoil: - return "Coil"; - case ModbusFunction.readDiscreteInput: - return "DiscreteInput"; - case ModbusFunction.readInputRegister: - return "InputRegister"; - case ModbusFunction.readHoldingRegisters: - return "HoldingRegister"; - case ModbusFunction.writeMultipleCoils: - return "Coil"; - case ModbusFunction.writeMultipleHoldingRegisters: - return "HoldingRegister"; - case ModbusFunction.writeSingleCoil: - return "Coil"; - case ModbusFunction.writeSingleHoldingRegister: - return "HoldingRegister"; - default: - throw new Error("Cannot convert " + modbusFun + " to ModbusEntity"); - } -} diff --git a/packages/binding-modbus/test/modbus-client-test.ts b/packages/binding-modbus/test/modbus-client-test.ts deleted file mode 100644 index ea3197e2a..000000000 --- a/packages/binding-modbus/test/modbus-client-test.ts +++ /dev/null @@ -1,419 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2020 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 { should } from "chai"; -import * as chai from "chai"; -import ModbusClient from "../src/modbus-client"; -import { ModbusForm } from "../src/modbus"; -import ModbusServer from "./test-modbus-server"; -import chaiAsPromised from "chai-as-promised"; -import { Readable } from "stream"; -import { Content } from "@node-wot/core"; - -// should must be called to augment all variables -should(); -chai.use(chaiAsPromised); - -describe("Modbus client test", () => { - let client: ModbusClient; - let testServer: ModbusServer; - - before(async () => { - // Turn off logging to have a clean test log - console.debug = () => { - // Do nothing. - }; - console.warn = () => { - // Do nothing. - }; - - testServer = new ModbusServer(1); - await testServer.start(); - }); - - beforeEach(() => { - client = new ModbusClient(); - testServer.clear(); - }); - afterEach(() => { - client.stop(); - }); - after(() => { - testServer.stop(); - }); - it("use entity alias for coil", () => { - const form: ModbusForm = { - href: "modbus+tcp://127.0.0.1:8502/1/0?quantity=1", - "modv:entity": "Coil", - }; - - /* eslint-disable dot-notation */ - client["validateAndFillDefaultForm"](form, 0)["modv:function"].should.be.equal(1, "Wrong default read Coil"); - client["validateAndFillDefaultForm"](form, 1)["modv:function"].should.be.equal(5, "Wrong write Coil"); - client["validateAndFillDefaultForm"](form, 2)["modv:function"].should.be.equal(15, "Wrong write multiple Coil"); - /* eslint-enable dot-notation */ - }); - - it("use entity alias for holding registries", () => { - const form: ModbusForm = { - href: "modbus+tcp://127.0.0.1:8502/1/0/?quantity=1", - "modv:entity": "HoldingRegister", - }; - /* eslint-disable dot-notation */ - client["validateAndFillDefaultForm"](form)["modv:function"].should.be.equal(3, "Wrong read Holding register"); - client["validateAndFillDefaultForm"](form, 2)["modv:function"].should.be.equal( - 6, - "Wrong write Holding register" - ); - client["validateAndFillDefaultForm"](form, 4)["modv:function"].should.be.equal( - 16, - "Wrong write multiple Holding register" - ); - /* eslint-enable dot-notation */ - }); - - it("use entity alias for other entities", () => { - const form: ModbusForm = { - href: "modbus+tcp://127.0.0.1:8502/1/0?quantity=1", - "modv:entity": "DiscreteInput", - }; - /* eslint-disable dot-notation */ - client["validateAndFillDefaultForm"](form)["modv:function"].should.be.equal(2, "Wrong read Discrete input"); - form["modv:entity"] = "InputRegister"; - client["validateAndFillDefaultForm"](form)["modv:function"].should.be.equal(4, "Wrong read Input register"); - /* eslint-enable dot-notation */ - }); - - it("should convert function names", () => { - const form: ModbusForm = { - href: "modbus+tcp://127.0.0.1:8502/1/0?quantity=1", - "modv:function": "readCoil", - }; - - // eslint-disable-next-line dot-notation - client["validateAndFillDefaultForm"](form)["modv:function"].should.be.equal(1, "Wrong substitution"); - }); - - it("should accept just URL parameters", () => { - const form: ModbusForm = { - href: "modbus+tcp://127.0.0.1:8502/2/2?quantity=5", - "modv:function": "readCoil", - }; - - // eslint-disable-next-line dot-notation - const result = client["validateAndFillDefaultForm"](form); - result["modv:unitID"].should.be.equal(2, "unitID value not set"); - result["modv:address"].should.be.equal(2, "address value not set"); - result["modv:quantity"].should.be.equal(5, "quantity value not set"); - }); - - it("should accept just URL parameters without quantity", () => { - const form: ModbusForm = { - href: "modbus+tcp://127.0.0.1:8502/2/2", - "modv:function": "readCoil", - }; - - // eslint-disable-next-line dot-notation - const result = client["validateAndFillDefaultForm"](form); - result["modv:unitID"].should.be.equal(2, "unitID value not set"); - result["modv:address"].should.be.equal(2, "address value not set"); - }); - - describe("misc", () => { - it("multiple operations", async () => { - testServer.setRegisters([1]); - - const form: ModbusForm = { - href: "modbus+tcp://127.0.0.1:8502/1/0?quantity=1", - "modv:function": 1, - }; - - let result = await client.readResource(form); - let body = await result.toBuffer(); - body.should.deep.equal(Buffer.from([1]), "Wrong data"); - result = await client.readResource(form); - body = await result.toBuffer(); - body.should.deep.equal(Buffer.from([1]), "Wrong data"); - }); - it("should fail for timeout", async () => { - const form: ModbusForm = { - href: "modbus+tcp://127.0.0.1:8502/1/4444?quantity=1", - "modv:function": 1, - "modv:timeout": 100, - }; - - await client.readResource(form).should.eventually.be.rejectedWith("Timed out"); - }).timeout(5000); - }); - describe("read resource", () => { - it("should read a resource using read coil function", async () => { - testServer.setRegisters([1]); - - const form: ModbusForm = { - href: "modbus+tcp://127.0.0.1:8502/1/0?quantity=1", - "modv:function": 1, - }; - - const result = await client.readResource(form); - const body = await result.toBuffer(); - body.should.deep.equal(Buffer.from([1]), "Wrong data"); - }); - - it("should read a resource using multiple read coil function", async () => { - testServer.setRegisters([0, 1, 1]); - - const form: ModbusForm = { - href: "modbus+tcp://127.0.0.1:8502/1/0?quantity=3", - "modv:function": 1, - }; - - const result = await client.readResource(form); - const body = await result.toBuffer(); - body.should.deep.equal(Buffer.from([6]), "Wrong data"); // 0x110 is 6 - }); - - it("should read a resource using read discrete input function", async () => { - testServer.setRegisters([1]); - - const form: ModbusForm = { - href: "modbus+tcp://127.0.0.1:8502/1/0?quantity=1", - "modv:function": 2, - }; - - const result = await client.readResource(form); - const body = await result.toBuffer(); - body.should.deep.equal(Buffer.from([1]), "Wrong data"); - }); - - it("should read a resource using read discrete input function", async () => { - testServer.setRegisters([0, 1, 1]); - - const form: ModbusForm = { - href: "modbus+tcp://127.0.0.1:8502/1/0?quantity=3", - "modv:function": 2, - }; - - const result = await client.readResource(form); - const body = await result.toBuffer(); - body.should.deep.equal(Buffer.from([6]), "Wrong data"); // 0x110 is 6 - }); - - it("should read a resource using read holding registers function", async () => { - testServer.setRegisters([3]); - - const form: ModbusForm = { - href: "modbus+tcp://127.0.0.1:8502/1/0?quantity=1", - contentType: "application/octet-stream", - "modv:function": 3, - }; - - const result = await client.readResource(form); - const body = await result.toBuffer(); - body.should.deep.equal(Buffer.from([0, 3]), "Wrong data"); - result.type.should.be.equal("application/octet-stream", "Wrong content type"); - }); - - it("should read a resource using read multiple holding registers function", async () => { - testServer.setRegisters([3, 2, 1]); - - const form: ModbusForm = { - href: "modbus+tcp://127.0.0.1:8502/1/0?quantity=3", - contentType: "application/octet-stream; length=6", - "modv:function": 3, - }; - - const result = await client.readResource(form); - const body = await result.toBuffer(); - body.should.deep.equal(Buffer.from([0, 3, 0, 2, 0, 1]), "Wrong data"); - result.type.should.be.equal("application/octet-stream; length=6", "Wrong content type"); - }); - - it("should read a resource using read input registers function", async () => { - testServer.setRegisters([3]); - - const form: ModbusForm = { - href: "modbus+tcp://127.0.0.1:8502/1/0?quantity=1", - "modv:function": 4, - }; - - const result = await client.readResource(form); - const body = await result.toBuffer(); - body.should.deep.equal(Buffer.from([0, 3]), "Wrong data"); - }); - - it("should read a resource using read multiple input registers function", async () => { - testServer.setRegisters([3, 2, 1]); - - const form: ModbusForm = { - href: "modbus+tcp://127.0.0.1:8502/1/0?quantity=3", - "modv:function": 4, - }; - - const result = await client.readResource(form); - const body = await result.toBuffer(); - body.should.deep.equal(Buffer.from([0, 3, 0, 2, 0, 1]), "Wrong data"); - }); - - it("should throw exception for unknown function", () => { - const form = { - href: "modbus+tcp://127.0.0.1:8502/1/0?quantity=3", - "modv:function": 255, - }; - - const promise = client.readResource(form as ModbusForm); - - return promise.should.eventually.rejectedWith("Undefined function number or name: 255"); - }); - - it("should throw exception for missing address", () => { - const form: ModbusForm = { - href: "modbus+tcp://127.0.0.1:8502/1", - "modv:function": 1, - }; - - const promise = client.readResource(form); - - return promise.should.eventually.rejectedWith("Malformed href: unitID and address must be defined"); - }); - - it("should throw exception for missing unitID", () => { - const form: ModbusForm = { - href: "modbus+tcp://127.0.0.1:8502", - "modv:function": 1, - }; - - const promise = client.readResource(form); - - return promise.should.eventually.rejectedWith("Malformed href: unitID and address must be defined"); - }); - }); - - describe("write resource", () => { - it("should write a resource using write coil function", async () => { - const form: ModbusForm = { - href: "modbus+tcp://127.0.0.1:8502/1/0", - "modv:function": 5, - }; - - await client.writeResource(form, new Content("", Readable.from([1]))); - testServer.registers[0].should.be.equal(true, "wrong coil value"); - }); - it("should write a resource using multiple write coil function", async () => { - const form: ModbusForm = { - href: "modbus+tcp://127.0.0.1:8502/1/0", - "modv:function": 15, - }; - - await client.writeResource(form, new Content("", Readable.from([1, 0, 1]))); - testServer.registers.should.be.deep.equal([true, false, true], "wrong coil value"); - }); - - it("should write a resource using write register function", async () => { - const form: ModbusForm = { - href: "modbus+tcp://127.0.0.1:8502/1/0", - "modv:function": 6, - }; - - await client.writeResource(form, new Content("", Readable.from([1, 1]))); - testServer.registers[0].should.be.equal(257, "wrong register value"); - }); - - it("should write a resource using write multiple register function", async () => { - const form: ModbusForm = { - href: "modbus+tcp://127.0.0.1:8502/1/0", - "modv:function": 16, - }; - - await client.writeResource(form, new Content("", Readable.from([1, 2, 1, 1]))); - testServer.registers.should.be.deep.equal([258, 257], "wrong register value"); - }); - - it("should write a resource with big endian ordering", async () => { - const form: ModbusForm = { - href: "modbus+tcp://127.0.0.1:8502/1/0", - contentType: "application/octet-stream;length=2;byteSeq=BIG_ENDIAN", // FIXME: Use most significant etc. - "modv:function": 16, - }; - - await client.writeResource(form, new Content("", Readable.from([0x25, 0x49, 0x59, 0x60]))); - testServer.registers.should.be.deep.equal([9545, 22880], "wrong coil value"); - }); - - it("should write a resource with little endian ordering", async () => { - const form: ModbusForm = { - href: "modbus+tcp://127.0.0.1:8502/1/0", - contentType: "application/octet-stream;byteSeq=LITTLE_ENDIAN", - "modv:function": 6, - }; - - await client.writeResource(form, new Content("", Readable.from([0x01, 0x01]))); // 257 little-endian - testServer.registers.should.be.deep.equal([257], "wrong coil value"); - }); - }); - - describe("subscribe resource", () => { - it("should wait for subscription", (done) => { - testServer.setRegisters([1]); - const form: ModbusForm = { - href: "modbus+tcp://127.0.0.1:8502/1/0", - "modv:function": 1, - }; - - client - .subscribeResource(form, (value) => { - // Do nothing. - }) - .then((subscription) => { - client.unlinkResource(form); - done(); - }); - }); - - it("should poll data", (done) => { - testServer.setRegisters([1]); - const form: ModbusForm = { - href: "modbus+tcp://127.0.0.1:8502/1/0", - "modv:function": 1, - "modv:pollingTime": 250, - }; - - client.subscribeResource(form, async (value) => { - const body = await value.toBuffer(); - body.should.deep.equal(Buffer.from([1]), "Wrong data"); - client.unlinkResource(form); - done(); - }); - }); - - it("should poll multiple data", (done) => { - testServer.setRegisters([1]); - const form: ModbusForm = { - href: "modbus+tcp://127.0.0.1:8502/1/0", - "modv:function": 1, - "modv:pollingTime": 125, - }; - let count = 0; - client.subscribeResource(form, async (value) => { - count++; - if (count > 1) { - done(); - client.unlinkResource(form); - } - const body = await value.toBuffer(); - body.should.deep.equal(Buffer.from([1]), "Wrong data"); - }); - }); - }); -}); diff --git a/packages/binding-modbus/test/modbus-connection-test.ts b/packages/binding-modbus/test/modbus-connection-test.ts deleted file mode 100644 index 25db80dfb..000000000 --- a/packages/binding-modbus/test/modbus-connection-test.ts +++ /dev/null @@ -1,115 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2020 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 { should } from "chai"; -import * as chai from "chai"; -import { ModbusFunction } from "../src/modbus"; -import ModbusServer from "./test-modbus-server"; -import chaiAsPromised from "chai-as-promised"; -import { ModbusConnection, ModbusFormWithDefaults, PropertyOperation } from "../src/modbus-connection"; -import { Endianness } from "@node-wot/core"; - -// should must be called to augment all variables -should(); -chai.use(chaiAsPromised); - -describe("Modbus connection", () => { - let testServer: ModbusServer; - - before(async () => { - testServer = new ModbusServer(1); - await testServer.start(); - }); - - after(() => { - testServer.stop(); - }); - - it("should connect", async () => { - const connection = new ModbusConnection("127.0.0.1", 8502); - await connection.connect(); - // eslint-disable-next-line no-unused-expressions - connection.client.isOpen.should.be.true; - }); - - it("should throw for unknown host", () => { - const connection = new ModbusConnection("127.0.0.2", 8502, { - connectionTimeout: 200, - connectionRetryTime: 10, - maxRetries: 1, - }); - return connection.connect().should.eventually.be.rejectedWith("Max connection retries"); - }).timeout(5000); - - it("should throw for timeout", () => { - const connection = new ModbusConnection("127.0.0.1", 8503, { - connectionTimeout: 200, - connectionRetryTime: 10, - maxRetries: 1, - }); - return connection.connect().should.eventually.be.rejectedWith("Max connection retries"); - }).timeout(5000); - - describe("Operation", () => { - it("should fail for unknown host", async () => { - const form: ModbusFormWithDefaults = { - href: "modbus+tcp://127.0.0.2:8502/1/0?quantity=1", - "modv:function": "writeMultipleCoils", - "modv:address": 0, - "modv:quantity": 1, - "modv:unitID": 1, - "modv:entity": "HoldingRegister", - "modv:timeout": 1000, - "modv:pollingTime": 1000, - }; - const connection = new ModbusConnection("127.0.0.2", 8503, { - connectionTimeout: 200, - connectionRetryTime: 10, - maxRetries: 1, - }); - const op = new PropertyOperation(form, Endianness.BIG_ENDIAN); - connection.enqueue(op); - - await op.execute().should.eventually.be.rejected; - connection.queue.length.should.equal(0); - - connection.close(); - }).timeout(5000); - - it("should throw with timeout", async () => { - const form: ModbusFormWithDefaults = { - href: "modbus+tcp://127.0.0.1:8502/1/4444?quantity=1", - "modv:function": ModbusFunction.readCoil, - "modv:entity": "Coil", - "modv:address": 4444, - "modv:quantity": 1, - "modv:unitID": 1, - "modv:timeout": 1000, - "modv:pollingTime": 1000, - }; - const connection = new ModbusConnection("127.0.0.1", 8502, { - connectionTimeout: 100, - connectionRetryTime: 10, - maxRetries: 1, - }); - const op = new PropertyOperation(form, Endianness.BIG_ENDIAN); - connection.enqueue(op); - - await op.execute().should.eventually.be.rejected; - connection.queue.length.should.equal(0); - - connection.close(); - }).timeout(5000); - }); -}); diff --git a/packages/binding-modbus/test/test-modbus-server.ts b/packages/binding-modbus/test/test-modbus-server.ts deleted file mode 100644 index 5e14508b3..000000000 --- a/packages/binding-modbus/test/test-modbus-server.ts +++ /dev/null @@ -1,101 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2020 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 { createLoggers } from "@node-wot/core"; -import { ServerTCP } from "modbus-serial"; - -const { error, debug } = createLoggers("binding-modbus", "test-modbus-server"); - -export default class ModbusServer { - serverTCP: ServerTCP; - registers: Array = []; - killers: Array<() => void> = []; - constructor(unitID: number) { - const vector = { - getInputRegister: (addr: number, unitID: number) => { - // Synchronous handling - return this.registers[addr]; - }, - getDiscreteInput: (addr: number, unitID: number) => { - // Synchronous handling - return this.registers[addr]; - }, - getHoldingRegister: (addr: number, unitID: number) => { - return this.registers[addr]; - }, - getCoil: (addr: number, unitID: number) => { - if (addr === 4444) { - // promise sleeps for 100 second. Useful for testing long running requests. - return new Promise((resolve) => { - const timeout = setTimeout(() => { - resolve(this.registers[addr]); - }, 100000); - // when stop forcing every connection to close - // it seems that the modbus client will not properly - // close the connection otherwise - this.killers.push(() => { - clearTimeout(timeout); - resolve(0); - }); - }); - } - return this.registers[addr]; - }, - - setRegister: (addr: number, value: number, unitID: number) => { - this.registers[addr] = value; - }, - - setCoil: (addr: number, value: boolean, unitID: number) => { - this.registers[addr] = value; - }, - }; - this.serverTCP = new ServerTCP(vector, { host: "127.0.0.1", port: 8502, debug: true, unitID }); - } - - setRegisters(data: Array, start = 0): void { - for (let index = 0; index < data.length; index++) { - const element = data[index]; - this.registers[index + start] = element; - } - } - - clear(): void { - this.registers = []; - } - - public start(): Promise { - return new Promise((resolve) => { - this.serverTCP.on("socketError", (err: Error | null) => { - // Handle socket error if needed, can be ignored - error("SocketError:", err?.toString()); - }); - this.serverTCP.on("error", (err) => { - debug(err?.toString()); - }); - - this.serverTCP.on("initialized", resolve); - }); - } - - public stop(): Promise { - return new Promise((resolve) => { - this.killers.forEach((killer) => { - killer(); - }); - this.serverTCP.close(resolve); - }); - } -} diff --git a/packages/binding-modbus/test/test-servient.ts b/packages/binding-modbus/test/test-servient.ts deleted file mode 100644 index 5770f1958..000000000 --- a/packages/binding-modbus/test/test-servient.ts +++ /dev/null @@ -1,35 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2020 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 - ********************************************************************************/ - -// global W3C WoT Scripting API definitions -import * as WoT from "wot-typescript-definitions"; -// node-wot implementation of W3C WoT Servient -import { Servient } from "@node-wot/core"; - -import { ModbusClientFactory } from "../src/modbus"; - -export default class DefaultServient extends Servient { - public constructor() { - super(); - this.addClientFactory(new ModbusClientFactory()); - } - - /** - * start - */ - public start(): Promise { - return super.start(); - } -} diff --git a/packages/binding-modbus/test/tsconfig.json b/packages/binding-modbus/test/tsconfig.json deleted file mode 100644 index 786b1b616..000000000 --- a/packages/binding-modbus/test/tsconfig.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "extends": "../tsconfig.json", - "compilerOptions": { - "rootDir": ".." - }, - "include": ["*.ts", "**/*.ts", "../src/**/*.ts"] -} diff --git a/packages/binding-modbus/tsconfig.json b/packages/binding-modbus/tsconfig.json deleted file mode 100644 index e9fa7c062..000000000 --- a/packages/binding-modbus/tsconfig.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "extends": "../../tsconfig.json", - "compilerOptions": { - "outDir": "dist", - "rootDir": "src" - }, - "include": ["src/**/*"], - "references": [{ "path": "../td-tools" }, { "path": "../core" }] -} diff --git a/packages/binding-mqtt/.eslintrc.json b/packages/binding-mqtt/.eslintrc.json deleted file mode 100644 index c4237119e..000000000 --- a/packages/binding-mqtt/.eslintrc.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "extends": "../../.eslintrc.js" -} diff --git a/packages/binding-mqtt/README.md b/packages/binding-mqtt/README.md deleted file mode 100644 index 5308eb4e5..000000000 --- a/packages/binding-mqtt/README.md +++ /dev/null @@ -1,211 +0,0 @@ -# MQTT Protocol Binding of node-wot - -W3C Web of Things (WoT) Protocol Binding for [MQTT](https://en.wikipedia.org/wiki/MQTT). -This package uses [mqtt](https://www.npmjs.com/package/mqtt) as a low level library for MQTT. -W3C WoT Binding Template for MQTT can be found [here](https://w3c.github.io/wot-binding-templates/bindings/protocols/mqtt/index.html). - -Current Maintainer(s): [@egekorkan](https://github.com/egekorkan) [@sebastiankb](https://github.com/sebastiankb) [@hasanheroglu](https://github.com/hasanheroglu) - -## Protocol specifier - -The protocol prefix handled by this binding is `mqtt://` or `mqtts://`. - -## Getting Started - -In the following examples it is shown how to use the MQTT binding of node-wot. - -### Prerequisites - -- `npm install @node-wot/core` -- `npm install @node-wot/binding-mqtt` - -### MQTT Thing Example I - -An MQTT Thing frequently publishes counter values (as an Event Affordance) to the topic `/MQTT-Test/events/counterEvent` of the MQTT broker running behind the address `test.mosquitto.org:1883`. -In addition, the Thing subscribes to the `resetCounter` topic (via its action handler) as a WoT Action Affordance so that -it can handle requests to reset the counter value. - -```js -const { Servient } = require("@node-wot/core"); -const { MqttBrokerServer } = require("@node-wot/binding-mqtt"); - -// create Servient add MQTT binding -const servient = new Servient(); -servient.addServer(new MqttBrokerServer({ uri: "mqtt://test.mosquitto.org" })); - -servient.start().then((WoT) => { - var counter = 0; - - WoT.produce({ - title: "MQTT-Test", - id: "urn:dev:wot:mqtt:counter", - actions: { - resetCounter: { - description: "Reset counter", - }, - }, - events: { - counterEvent: { - description: "Counter Value", - data: { - type: "integer", - }, - }, - }, - }) - .then((thing) => { - thing.setActionHandler("resetCounter", async () => { - console.log("Resetting counter"); - counter = 0; - }); - - thing.expose().then(() => { - console.info(thing.title + " ready"); - - setInterval(() => { - ++counter; - thing.emitEvent("counterEvent", counter); - console.info("New count ", counter); - }, 1000); - }); - }) - .catch((e) => { - console.log(e); - }); -}); -``` - -#### Sample Thing Description for MQTT Clients - -The Thing Description corresponding to the previous example is shown below: - -```js -{ - "@context": "https://www.w3.org/2019/wot/td/v1", - "title": "MQTT-Test", - "id": "urn:dev:wot:mqtt:counter", - "actions" : { - "resetCounter": { - "description": "Reset counter" - "forms": [ - {"href": "mqtt://test.mosquitto.org:1883/MQTT-Test/actions/resetCounter"} - ] - } - }, - "events": { - "counterEvent": { - "description": "Counter Value", - "data": { - "type": "integer" - }, - "forms": [ - {"href": "mqtt://test.mosquitto.org:1883/MQTT-Test/events/counterEvent"} - ] - } - } -} -``` - -### MQTT Client Example II - -This example takes the Thing Description of the previous example and subscribes to the `counterEvent` and resets the counter every 20s via the `resetCounter` action. - -```js -const { Servient } = require("@node-wot/core"); -const { MqttClientFactory } = require("@node-wot/binding-mqtt"); - -// create Servient and add MQTT binding -const servient = new Servient(); -servient.addClientFactory(new MqttClientFactory(null)); - -// Thing Description can be also fetched -const td = `{ - "@context": "https://www.w3.org/2019/td/v1", - "title": "MQTT-Test", - "id": "urn:dev:wot:mqtt:counter", - "actions" : { - "resetCounter": { - "forms": [ - {"href": "mqtt://test.mosquitto.org:1883/MQTT-Test/actions/resetCounter"} - ] - } - }, - "events": { - "counterEvent": { - "description": "Counter Value", - "data": { - "type": "integer" - }, - "forms": [ - {"href": "mqtt://test.mosquitto.org:1883/MQTT-Test/events/counterEvent"} - ] - } - } -}`; - -try { - servient.start().then((WoT) => { - WoT.consume(JSON.parse(td)).then((source) => { - console.info("=== TD ==="); - console.info(td); - console.info("=========="); - - source.subscribeEvent( - "counterEvent", - (x) => console.info("value:", x), - (e) => console.error("Error: %s", e), - () => console.info("Completed") - ); - console.info("Subscribed"); - - source.invokeAction("resetCounter"); - - setInterval(async () => { - source - .invokeAction("resetCounter") - .then((res) => {}) - .catch((err) => { - console.error("ResetCounter error:", err.message); - }); - console.info("Reset counter!"); - }, 20000); - }); - }); -} catch (err) { - console.error("Script error:", err); -} -``` - -### More Examples - -There are example implementations provided in the [example/scripting folder](https://github.com/eclipse-thingweb/node-wot/tree/master/examples/scripts). - -Please setup node-wot as described at the [node-wot main page](https://github.com/eclipse-thingweb/node-wot#as-a-standalone-application). - -- example-mqtt-publish.js: Shows when node-wot acts as a MQTT Client that publishes data (latest counter value) to a broker. - At the same time, another client can invoke an action, such as `resetCounter`, by sending a publication message to the topic of this action. - This other client does not have to be node-wot, any MQTT client can interact with this Thing. - For node-wot clients, make sure to provide MQTT broker details (`host`, `port`, `username`, `password`, `clientId`) in the wot-servient.conf.json: - -```js -{ - "mqtt" : { - "host" : "mqtt://test.mosquitto.org", - "username" : "username", - "password" : "password", - "clientId" : "uniqueId", - "port": 1883 - } -} - -``` - -Start the script by the command `wot-servient mqtt-publish.js` or `node ../../packages/cli/dist/cli.js mqtt-publish.js`. - -- example-mqtt-subscription.js: Shows how node-wot consumes a Thing Description to do MQTT subscription on the provided event (=latest counter value) as well as initiate the action (reset counter). - -Start the script by the command `wot-servient -c mqtt-subscribe.js` or `node ../../packages/cli/dist/cli.js -c mqtt-subscribe.js`. - -### More Details - -See diff --git a/packages/binding-mqtt/package.json b/packages/binding-mqtt/package.json deleted file mode 100644 index f924fa5b0..000000000 --- a/packages/binding-mqtt/package.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "name": "@node-wot/binding-mqtt", - "version": "0.8.15", - "description": "MQTT binding for node-wot", - "author": "Eclipse Thingweb (https://thingweb.io/)", - "license": "EPL-2.0 OR W3C-20150513", - "repository": "https://github.com/eclipse-thingweb/node-wot/tree/master/packages/binding-mqtt", - "publishConfig": { - "access": "public" - }, - "files": [ - "dist/" - ], - "main": "dist/mqtt.js", - "types": "dist/mqtt.d.ts", - "dependencies": { - "@node-wot/core": "0.8.15", - "@node-wot/td-tools": "0.8.15", - "aedes": "^0.46.2", - "mqtt": "^5.3.2", - "rxjs": "5.5.11" - }, - "scripts": { - "build": "tsc -b", - "test": "mocha --require ts-node/register --extension ts", - "lint": "eslint .", - "lint:fix": "eslint . --fix", - "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\" \"**/*.json\"" - }, - "bugs": { - "url": "https://github.com/eclipse-thingweb/node-wot/issues" - }, - "homepage": "https://github.com/eclipse-thingweb/node-wot/tree/master/packages/binding-mqtt#readme", - "directories": { - "test": "test" - } -} diff --git a/packages/binding-mqtt/src/mqtt-broker-server.ts b/packages/binding-mqtt/src/mqtt-broker-server.ts deleted file mode 100644 index af092ae13..000000000 --- a/packages/binding-mqtt/src/mqtt-broker-server.ts +++ /dev/null @@ -1,479 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2018 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 - ********************************************************************************/ - -/** - * MQTT Broker Server - */ - -import { IPublishPacket } from "mqtt"; -import * as mqtt from "mqtt"; -import * as url from "url"; -import { AuthenticateError, Client, Server, Aedes } from "aedes"; -import * as net from "net"; -import * as tls from "tls"; -import * as TD from "@node-wot/td-tools"; -import { MqttBrokerServerConfig, MqttForm } from "./mqtt"; -import { - ProtocolServer, - Servient, - ExposedThing, - ContentSerdes, - ProtocolHelpers, - Content, - createLoggers, -} from "@node-wot/core"; -import { InteractionOptions } from "wot-typescript-definitions"; -import { ActionElement, PropertyElement } from "wot-thing-description-types"; -import { Readable } from "stream"; -import { mapQoS } from "./util"; - -const { info, debug, error, warn } = createLoggers("binding-mqtt", "mqtt-broker-server"); - -export default class MqttBrokerServer implements ProtocolServer { - private static brokerIsInitialized(broker?: mqtt.MqttClient): asserts broker is mqtt.MqttClient { - if (broker === undefined) { - throw new Error( - `Broker not initialized. You need to start the ${MqttBrokerServer.name} before you can expose things.` - ); - } - } - - readonly scheme: string = "mqtt"; - - private readonly ACTION_SEGMENT_LENGTH = 3; - private readonly PROPERTY_SEGMENT_LENGTH = 4; - - private readonly THING_NAME_SEGMENT_INDEX = 0; - private readonly INTERACTION_TYPE_SEGMENT_INDEX = 1; - private readonly INTERACTION_NAME_SEGMENT_INDEX = 2; - private readonly INTERACTION_EXT_SEGMENT_INDEX = 3; - - private readonly defaults: MqttBrokerServerConfig = { uri: "mqtt://localhost:1883" }; - - private port = -1; - private address?: string = undefined; - - private brokerURI: string; - - private readonly things: Map = new Map(); - - private readonly config: MqttBrokerServerConfig; - - private broker?: mqtt.MqttClient; - - private hostedServer?: Aedes; - private hostedBroker?: net.Server; - - constructor(config: MqttBrokerServerConfig) { - this.config = config ?? this.defaults; - this.config.uri = this.config.uri ?? this.defaults.uri; - - // if there is a MQTT protocol indicator missing, add this - if (config.uri.indexOf("://") === -1) { - config.uri = this.scheme + "://" + config.uri; - } - - this.brokerURI = config.uri; - } - - public async expose(thing: ExposedThing): Promise { - if (this.broker === undefined) { - return; - } - - let name = thing.title; - - if (this.things.has(name)) { - const suffix = name.match(/.+_([0-9]+)$/); - if (suffix !== null) { - name = name.slice(0, -suffix[1].length) + (1 + parseInt(suffix[1])); - } else { - name = name + "_2"; - } - } - - debug(`MqttBrokerServer at ${this.brokerURI} exposes '${thing.title}' as unique '${name}/*'`); - - this.things.set(name, thing); - - for (const propertyName in thing.properties) { - this.exposeProperty(name, propertyName, thing); - } - - for (const actionName in thing.actions) { - this.exposeAction(name, actionName, thing); - } - - for (const eventName in thing.events) { - this.exposeEvent(name, eventName, thing); - } - - // connect incoming messages to Thing - this.broker.on("message", this.handleMessage.bind(this)); - - this.broker.publish(name, JSON.stringify(thing.getThingDescription()), { retain: true }); - } - - private exposeProperty(name: string, propertyName: string, thing: ExposedThing) { - MqttBrokerServer.brokerIsInitialized(this.broker); - const topic = encodeURIComponent(name) + "/properties/" + encodeURIComponent(propertyName); - const property = thing.properties[propertyName]; - - const writeOnly: boolean = property.writeOnly ?? false; - if (!writeOnly) { - const href = this.brokerURI + "/" + topic; - const form = new TD.Form(href, ContentSerdes.DEFAULT); - form.op = ["readproperty", "observeproperty", "unobserveproperty"]; - property.forms.push(form); - debug(`MqttBrokerServer at ${this.brokerURI} assigns '${href}' to property '${propertyName}'`); - - const observeListener = async (content: Content) => { - debug(`MqttBrokerServer at ${this.brokerURI} publishing to Property topic '${propertyName}' `); - const buffer = await content.toBuffer(); - - if (this.broker === undefined) { - warn(`MqttBrokerServer at ${this.brokerURI} has no client to publish to. Probably it was closed.`); - return; - } - - this.broker.publish(topic, buffer); - }; - thing.handleObserveProperty(propertyName, observeListener, { formIndex: property.forms.length - 1 }); - } - const readOnly: boolean = property.readOnly ?? false; - if (!readOnly) { - const href = this.brokerURI + "/" + topic + "/writeproperty"; - this.broker.subscribe(topic + "/writeproperty"); - const form = new TD.Form(href, ContentSerdes.DEFAULT); - form.op = ["writeproperty"]; - thing.properties[propertyName].forms.push(form); - debug(`MqttBrokerServer at ${this.brokerURI} assigns '${href}' to property '${propertyName}'`); - } - } - - private exposeAction(name: string, actionName: string, thing: ExposedThing) { - MqttBrokerServer.brokerIsInitialized(this.broker); - - const topic = encodeURIComponent(name) + "/actions/" + encodeURIComponent(actionName); - this.broker.subscribe(topic); - - const href = this.brokerURI + "/" + topic; - const form = new TD.Form(href, ContentSerdes.DEFAULT); - form.op = ["invokeaction"]; - thing.actions[actionName].forms.push(form); - debug(`MqttBrokerServer at ${this.brokerURI} assigns '${href}' to Action '${actionName}'`); - } - - private exposeEvent(name: string, eventName: string, thing: ExposedThing) { - const topic = encodeURIComponent(name) + "/events/" + encodeURIComponent(eventName); - const event = thing.events[eventName]; - - const href = this.brokerURI + "/" + topic; - const form = new MqttForm(href, ContentSerdes.DEFAULT); - form["mqv:qos"] = "2"; - form.op = ["subscribeevent", "unsubscribeevent"]; - event.forms.push(form); - debug(`MqttBrokerServer at ${this.brokerURI} assigns '${href}' to Event '${eventName}'`); - - const eventListener = async (content: Content) => { - if (this.broker === undefined) { - warn(`MqttBrokerServer at ${this.brokerURI} has no client to publish to. Probably it was closed.`); - return; - } - - if (content == null) { - warn(`MqttBrokerServer on port ${this.getPort()} cannot process data for Event ${eventName}`); - thing.handleUnsubscribeEvent(eventName, eventListener, { formIndex: event.forms.length - 1 }); - return; - } - debug(`MqttBrokerServer at ${this.brokerURI} publishing to Event topic '${eventName}' `); - const buffer = await content.toBuffer(); - this.broker.publish(topic, buffer, { retain: form["mqv:retain"], qos: mapQoS(form["mqv:qos"]) }); - }; - thing.handleSubscribeEvent(eventName, eventListener, { formIndex: event.forms.length - 1 }); - } - - private handleMessage(receivedTopic: string, rawPayload: Buffer | string, packet: IPublishPacket): void { - // route request - const segments = receivedTopic.split("/"); - let payload: Buffer; - if (rawPayload instanceof Buffer) { - payload = rawPayload; - } else if (typeof rawPayload === "string") { - payload = Buffer.from(rawPayload); - } else { - warn(`MqttBrokerServer on port ${this.getPort()} received unexpected payload type`); - return; - } - - if (segments.length === this.ACTION_SEGMENT_LENGTH) { - // connecting to the actions - debug(`MqttBrokerServer at ${this.brokerURI} received message for '${receivedTopic}'`); - const thing = this.things.get(segments[this.THING_NAME_SEGMENT_INDEX]); - if (thing != null) { - if (segments[this.INTERACTION_TYPE_SEGMENT_INDEX] === "actions") { - const action = thing.actions[segments[this.INTERACTION_NAME_SEGMENT_INDEX]]; - if (action != null) { - this.handleAction(action, packet, payload, segments, thing); - return; - } - } // Action exists? - } // Thing exists? - } else if ( - segments.length === this.PROPERTY_SEGMENT_LENGTH && - segments[this.INTERACTION_EXT_SEGMENT_INDEX] === "writeproperty" - ) { - // connecting to the writeable properties - const thing = this.things.get(segments[this.THING_NAME_SEGMENT_INDEX]); - if (thing != null) { - if (segments[this.INTERACTION_TYPE_SEGMENT_INDEX] === "properties") { - const property = thing.properties[segments[this.INTERACTION_NAME_SEGMENT_INDEX]]; - if (property != null) { - this.handlePropertyWrite(property, packet, payload, segments, thing); - } // Property exists? - } - } - return; - } - // topic not found - warn(`MqttBrokerServer at ${this.brokerURI} received message for invalid topic '${receivedTopic}'`); - } - - private handleAction( - action: ActionElement, - packet: IPublishPacket, - payload: Buffer, - segments: string[], - thing: ExposedThing - ) { - /* - * Currently, this branch will never be taken. The main reason for that is in the mqtt library we use: - * https://github.com/mqttjs/MQTT.js/pull/1103 - * For further discussion see https://github.com/eclipse-thingweb/node-wot/pull/253 - */ - const contentType = packet?.properties?.contentType ?? ContentSerdes.DEFAULT; - - const options: InteractionOptions & { formIndex: number } = { - formIndex: ProtocolHelpers.findRequestMatchingFormIndex( - action.forms, - this.scheme, - this.brokerURI, - contentType - ), - }; - - const formContentType = action.forms[options.formIndex].contentType ?? ContentSerdes.DEFAULT; - const inputContent = new Content(formContentType, Readable.from(payload)); - - thing - .handleInvokeAction(segments[this.INTERACTION_NAME_SEGMENT_INDEX], inputContent, options) - .then((output: unknown) => { - if (output != null) { - warn( - `MqttBrokerServer at ${this.brokerURI} cannot return output '${ - segments[this.INTERACTION_NAME_SEGMENT_INDEX] - }'` - ); - } - }) - .catch((err: Error) => { - error( - `MqttBrokerServer at ${this.brokerURI} got error on invoking '${ - segments[this.INTERACTION_NAME_SEGMENT_INDEX] - }': ${err.message}` - ); - }); - } - - private handlePropertyWrite( - property: PropertyElement, - packet: IPublishPacket, - payload: Buffer, - segments: string[], - thing: ExposedThing - ) { - const readOnly = property.readOnly ?? false; - if (!readOnly) { - const contentType = packet?.properties?.contentType ?? ContentSerdes.DEFAULT; - - const options: InteractionOptions & { formIndex: number } = { - formIndex: ProtocolHelpers.findRequestMatchingFormIndex( - property.forms, - this.scheme, - this.brokerURI, - contentType - ), - }; - - const formContentType = property.forms[options.formIndex].contentType ?? ContentSerdes.DEFAULT; - const inputContent = new Content(formContentType, Readable.from(payload)); - - try { - thing.handleWriteProperty(segments[this.INTERACTION_NAME_SEGMENT_INDEX], inputContent, options); - } catch (err) { - error( - `MqttBrokerServer at ${this.brokerURI} got error on writing to property '${ - segments[this.INTERACTION_NAME_SEGMENT_INDEX] - }': ${err}` - ); - } - } else { - warn( - `MqttBrokerServer at ${this.brokerURI} received message for readOnly property at '${segments.join( - "/" - )}'` - ); - } // property is writeable? Not necessary since it didn't actually subscribe to this topic - } - - public async destroy(thingId: string): Promise { - debug(`MqttBrokerServer on port ${this.getPort()} destroying thingId '${thingId}'`); - let removedThing: ExposedThing | undefined; - - for (const name of Array.from(this.things.keys())) { - const expThing = this.things.get(name); - if (expThing != null && expThing.id != null && expThing.id === thingId) { - this.things.delete(name); - removedThing = expThing; - } - } - - if (removedThing != null) { - info(`MqttBrokerServer successfully destroyed '${removedThing.title}'`); - } else { - info(`MqttBrokerServer failed to destroy thing with thingId '${thingId}'`); - } - return removedThing !== undefined; - } - - public async start(servient: Servient): Promise { - if (this.brokerURI === undefined) { - warn(`No broker defined for MQTT server binding - skipping`); - } else { - const selfHost = this.config.selfHost ?? false; - if (selfHost) { - await this.startBroker(); - } - // try to connect to the broker without or with credentials - if (this.config.psw === undefined) { - debug(`MqttBrokerServer trying to connect to broker at ${this.brokerURI}`); - } else if (this.config.clientId === undefined) { - debug(`MqttBrokerServer trying to connect to secured broker at ${this.brokerURI}`); - } else if (this.config.protocolVersion === undefined) { - debug( - `MqttBrokerServer trying to connect to secured broker at ${this.brokerURI} with client ID ${this.config.clientId}` - ); - } else { - debug( - `MqttBrokerServer trying to connect to secured broker at ${this.brokerURI} with client ID ${this.config.clientId}` - ); - } - - try { - this.broker = await mqtt.connectAsync(this.brokerURI, this.config); - info(`MqttBrokerServer connected to broker at ${this.brokerURI}`); - - const parsed = new url.URL(this.brokerURI); - this.address = parsed.hostname; - const port = parseInt(parsed.port); - this.port = port > 0 ? port : 1883; - } catch (err) { - error(`MqttBrokerServer could not connect to broker at ${this.brokerURI}`); - throw err; - } - } - } - - public async stop(): Promise { - if (this.broker !== undefined) { - this.broker.unsubscribe("*"); - this.broker.end(true); - } - - if (this.hostedBroker !== undefined) { - // When the broker is hosted, we need to close it. - // Both this.hostedBroker and this.hostedServer are defined at the same time. - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - await new Promise((resolve) => this.hostedServer!.close(() => resolve())); - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - await new Promise((resolve) => this.hostedBroker!.close(() => resolve())); - } - } - - public getPort(): number { - return this.port; - } - - /** - * - * @returns the address of the broker or undefined if the Server is not started. - */ - public getAddress(): string | undefined { - return this.address; - } - - private selfHostAuthentication( - _client: Client, - username: Readonly, - password: Readonly, - done: (error: AuthenticateError | null, success: boolean | null) => void - ) { - if (this.config.selfHostAuthentication && username !== undefined) { - for (let i = 0; i < this.config.selfHostAuthentication.length; i++) { - if ( - username === this.config.selfHostAuthentication[i].username && - password.equals(Buffer.from(this.config.selfHostAuthentication[i].password ?? "")) - ) { - done(null, true); - return; - } - } - done(null, false); - return; - } - done(null, true); - } - - private async startBroker() { - return new Promise((resolve, reject) => { - this.hostedServer = Server({}); - let server: tls.Server | net.Server; - if (this.config.key) { - server = tls.createServer({ key: this.config.key, cert: this.config.cert }, this.hostedServer.handle); - } else { - server = net.createServer(this.hostedServer.handle); - } - const parsed = new url.URL(this.brokerURI); - const port = parseInt(parsed.port); - - this.port = port > 0 ? port : 1883; - this.hostedServer.authenticate = this.selfHostAuthentication.bind(this); - - const errorListener = (err: Error) => { - error(`error listening for ${this.brokerURI}, ${err}`); - reject(err); - }; - server.once("error", errorListener); - - debug(`MqttBrokerServer creating server for ${this.brokerURI}`); - this.hostedBroker = server.listen(port, parsed.hostname, () => { - debug(`MqttBrokerServer listening ${this.brokerURI}`); - // clean up listener if not called - server.removeListener("error", errorListener); - resolve(); - }); - }); - } -} diff --git a/packages/binding-mqtt/src/mqtt-client-factory.ts b/packages/binding-mqtt/src/mqtt-client-factory.ts deleted file mode 100644 index 78d64f77b..000000000 --- a/packages/binding-mqtt/src/mqtt-client-factory.ts +++ /dev/null @@ -1,44 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2018 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 - ********************************************************************************/ - -/** - * Protocol test suite to test protocol implementations - */ - -import { ProtocolClientFactory, ProtocolClient, createDebugLogger } from "@node-wot/core"; -import MqttClient from "./mqtt-client"; - -const debug = createDebugLogger("binding-mqtt", "mqtt-client-factory"); - -export default class MqttClientFactory implements ProtocolClientFactory { - public readonly scheme: string = "mqtt"; - private readonly clients: Array = []; - - getClient(): ProtocolClient { - const client = new MqttClient(); - this.clients.push(client); - return client; - } - - init(): boolean { - return true; - } - - destroy(): boolean { - debug(`MqttClientFactory stopping all clients for '${this.scheme}'`); - this.clients.forEach((client) => client.stop()); - return true; - } -} diff --git a/packages/binding-mqtt/src/mqtt-client.ts b/packages/binding-mqtt/src/mqtt-client.ts deleted file mode 100644 index 7cdc12d2a..000000000 --- a/packages/binding-mqtt/src/mqtt-client.ts +++ /dev/null @@ -1,209 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2018 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 - ********************************************************************************/ - -/** - * Protocol test suite to test protocol implementations - */ - -import { ProtocolClient, Content, DefaultContent, createLoggers, ContentSerdes } from "@node-wot/core"; -import * as TD from "@node-wot/td-tools"; -import * as mqtt from "mqtt"; -import { MqttClientConfig, MqttForm } from "./mqtt"; -import * as url from "url"; -import { Subscription } from "rxjs/Subscription"; -import { Readable } from "stream"; -import MQTTMessagePool from "./mqtt-message-pool"; -import { mapQoS } from "./util"; - -const { debug, warn } = createLoggers("binding-mqtt", "mqtt-client"); - -declare interface MqttClientSecurityParameters { - username: string; - password: string; -} - -export default class MqttClient implements ProtocolClient { - private scheme: string; - private pools: Map = new Map(); - - constructor(private config: MqttClientConfig = {}, secure = false) { - this.scheme = "mqtt" + (secure ? "s" : ""); - } - - private client?: mqtt.MqttClient; - - public async subscribeResource( - form: MqttForm, - next: (value: Content) => void, - error?: (error: Error) => void, - complete?: () => void - ): Promise { - const contentType = form.contentType ?? ContentSerdes.DEFAULT; - const requestUri = new url.URL(form.href); - const brokerUri: string = `${this.scheme}://` + requestUri.host; - // Keeping the path as the topic for compatibility reasons. - // Current specification allows only form["mqv:filter"] - const filter = requestUri.pathname.slice(1) ?? form["mqv:filter"]; - - let pool = this.pools.get(brokerUri); - - if (pool == null) { - pool = new MQTTMessagePool(); - this.pools.set(brokerUri, pool); - } - - await pool.connect(brokerUri, this.config); - - await pool.subscribe( - filter, - (topic: string, message: Buffer) => { - next(new Content(contentType, Readable.from(message))); - }, - (e: Error) => { - if (error) error(e); - } - ); - - return new Subscription(() => {}); - } - - public async readResource(form: MqttForm): Promise { - const contentType = form.contentType ?? ContentSerdes.DEFAULT; - const requestUri = new url.URL(form.href); - const brokerUri: string = `${this.scheme}://` + requestUri.host; - // Keeping the path as the topic for compatibility reasons. - // Current specification allows only form["mqv:filter"] - const filter = requestUri.pathname.slice(1) ?? form["mqv:filter"]; - - let pool = this.pools.get(brokerUri); - - if (pool == null) { - pool = new MQTTMessagePool(); - this.pools.set(brokerUri, pool); - } - - await pool.connect(brokerUri, this.config); - - const result = await new Promise((resolve, reject) => { - pool!.subscribe( - filter, - (topic: string, message: Buffer) => { - resolve(new Content(contentType, Readable.from(message))); - }, - (e: Error) => { - reject(e); - } - ); - }); - - await pool.unsubscribe(filter); - return result; - } - - public async writeResource(form: MqttForm, content: Content): Promise { - const requestUri = new url.URL(form.href); - const brokerUri = `${this.scheme}://${requestUri.host}`; - const topic = requestUri.pathname.slice(1) ?? form["mqv:topic"]; - - let pool = this.pools.get(brokerUri); - - if (pool == null) { - pool = new MQTTMessagePool(); - this.pools.set(brokerUri, pool); - } - - await pool.connect(brokerUri, this.config); - - // if not input was provided, set up an own body otherwise take input as body - const buffer = content === undefined ? Buffer.from("") : await content.toBuffer(); - await pool.publish(topic, buffer, { - retain: form["mqv:retain"], - qos: mapQoS(form["mqv:qos"]), - }); - } - - public async invokeResource(form: MqttForm, content: Content): Promise { - const requestUri = new url.URL(form.href); - const topic = requestUri.pathname.slice(1); - const brokerUri = `${this.scheme}://${requestUri.host}`; - - let pool = this.pools.get(brokerUri); - - if (pool == null) { - pool = new MQTTMessagePool(); - this.pools.set(brokerUri, pool); - } - - await pool.connect(brokerUri, this.config); - - // if not input was provided, set up an own body otherwise take input as body - const buffer = content === undefined ? Buffer.from("") : await content.toBuffer(); - await pool.publish(topic, buffer, { - retain: form["mqv:retain"], - qos: mapQoS(form["mqv:qos"]), - }); - // there will be no response - return new DefaultContent(Readable.from([])); - } - - public async unlinkResource(form: TD.Form): Promise { - const requestUri = new url.URL(form.href); - const brokerUri: string = `${this.scheme}://` + requestUri.host; - const topic = requestUri.pathname.slice(1); - - const pool = this.pools.get(brokerUri); - if (pool != null) { - await pool.unsubscribe(topic); - debug(`MqttClient unsubscribed from topic '${topic}'`); - } - } - - /** - * @inheritdoc - */ - public async requestThingDescription(uri: string): Promise { - throw new Error("Method not implemented"); - } - - public async start(): Promise { - // do nothing - } - - public async stop(): Promise { - for (const pool of this.pools.values()) { - await pool.end(); - } - if (this.client) return this.client.endAsync(); - } - - public setSecurity(metadata: Array, credentials?: MqttClientSecurityParameters): boolean { - if (metadata === undefined || !Array.isArray(metadata) || metadata.length === 0) { - warn(`MqttClient received empty security metadata`); - return false; - } - const security: TD.SecurityScheme = metadata[0]; - - if (security.scheme === "basic") { - if (credentials === undefined) { - // FIXME: This error message should be reworded and adapt to logging convention - throw new Error("binding-mqtt: security wants to be basic but you have provided no credentials"); - } else { - this.config.username = credentials.username; - this.config.password = credentials.password; - } - } - return true; - } -} diff --git a/packages/binding-mqtt/src/mqtt-message-pool.ts b/packages/binding-mqtt/src/mqtt-message-pool.ts deleted file mode 100644 index a8591303a..000000000 --- a/packages/binding-mqtt/src/mqtt-message-pool.ts +++ /dev/null @@ -1,100 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 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 { createLoggers } from "@node-wot/core"; -import { MqttClientConfig } from "./mqtt"; -import * as mqtt from "mqtt"; - -const { debug, warn } = createLoggers("binding-mqtt", "mqtt-message-pool"); - -export default class MQTTMessagePool { - client?: mqtt.MqttClient; - subscribers: Map void> = new Map(); - errors: Map void> = new Map(); - - public async connect(brokerURI: string, config: MqttClientConfig): Promise { - if (this.client === undefined) { - this.client = await mqtt.connectAsync(brokerURI, config); - this.client.on("message", (receivedTopic: string, payload: Buffer) => { - debug( - `Received MQTT message from ${brokerURI} (topic: ${receivedTopic}, data length: ${payload.length})` - ); - this.subscribers.get(receivedTopic)?.(receivedTopic, payload); - }); - // Connection errors should be deal by the connectAsync - // here we handle "runtime" parsing errors, but we can't do much - // therefore we broadcast the error to all subscribers - this.client.on("error", (error: Error) => { - warn(`MQTT client error: ${error.message}`); - this.errors.forEach((errorCallback) => { - errorCallback(error); - }); - }); - } - } - - public async subscribe( - filter: string | string[], - callback: (topic: string, message: Buffer) => void, - error: (error: Error) => void - ): Promise { - if (this.client == null) { - throw new Error("MQTT client is not connected"); - } - - const filters = Array.isArray(filter) ? filter : [filter]; - filters.forEach((f) => { - if (this.subscribers.has(f)) { - warn(`Already subscribed to ${f}; we are not supporting multiple subscribers to the same topic`); - warn(`The subscription will be ignored`); - return; - } - - this.subscribers.set(f, callback); - this.errors.set(f, error); - }); - - await this.client.subscribeAsync(filters); - } - - public async unsubscribe(filter: string | string[]): Promise { - if (this.client == null) { - throw new Error("MQTT client is not connected"); - } - - const filters = Array.isArray(filter) ? filter : [filter]; - filters.forEach((f) => { - this.subscribers.delete(f); - this.errors.delete(f); - }); - - await this.client.unsubscribeAsync(filters); - } - - public async publish(topic: string, message: Buffer, options?: mqtt.IClientPublishOptions): Promise { - if (this.client == null) { - throw new Error("MQTT client is not connected"); - } - - debug(`Publishing MQTT message to ${topic} (data length: ${message.length})`); - await this.client.publishAsync(topic, message, options); - } - - public async end(): Promise { - for (const filter of this.subscribers.keys()) { - this.unsubscribe(filter); - } - return this.client?.endAsync(); - } -} diff --git a/packages/binding-mqtt/src/mqtt.ts b/packages/binding-mqtt/src/mqtt.ts deleted file mode 100644 index 355c4f29a..000000000 --- a/packages/binding-mqtt/src/mqtt.ts +++ /dev/null @@ -1,64 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2018 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 - ********************************************************************************/ - -/** - * Protocol test suite to test protocol implementations - */ - -import { Form } from "@node-wot/td-tools"; - -export { default as MqttClient } from "./mqtt-client"; -export { default as MqttClientFactory } from "./mqtt-client-factory"; -export { default as MqttsClientFactory } from "./mqtts-client-factory"; -export { default as MqttBrokerServer } from "./mqtt-broker-server"; - -export * from "./mqtt-client"; -export * from "./mqtt-client-factory"; -export * from "./mqtts-client-factory"; -export * from "./mqtt-broker-server"; - -export type MqttQoS = "0" | "1" | "2"; -export class MqttForm extends Form { - public "mqv:qos"?: MqttQoS = "0"; - public "mqv:retain"?: boolean; - - public "mqv:topic"?: string; - - public "mqv:filter"?: string | string[]; - - public "mqv:controlPacket"?: "publish" | "subscribe" | "unsubscribe"; -} - -export interface MqttClientConfig { - // username & password are redundant here (also find them in MqttClientSecurityParameters) - // because MqttClient.setSecurity() method can inject authentication credentials into this interface - // which will be then passed to mqtt.connect() once for all - username?: string; - password?: string; - rejectUnauthorized?: boolean; -} - -export interface MqttBrokerServerConfig { - uri: string; - user?: string; - psw?: string; - clientId?: string; - protocolVersion?: 3 | 4 | 5; - rejectUnauthorized?: boolean; - selfHost?: boolean; - key?: Buffer; - cert?: Buffer | undefined; - selfHostAuthentication?: MqttClientConfig[]; -} diff --git a/packages/binding-mqtt/src/mqtts-client-factory.ts b/packages/binding-mqtt/src/mqtts-client-factory.ts deleted file mode 100644 index 3c0519527..000000000 --- a/packages/binding-mqtt/src/mqtts-client-factory.ts +++ /dev/null @@ -1,47 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2021 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 - ********************************************************************************/ - -/** - * Protocol test suite to test protocol implementations - */ - -import { ProtocolClientFactory, ProtocolClient, createDebugLogger } from "@node-wot/core"; -import { MqttClientConfig } from "./mqtt"; -import MqttClient from "./mqtt-client"; - -const debug = createDebugLogger("binding-mqtt", "mqtts-client-factory"); - -export default class MqttsClientFactory implements ProtocolClientFactory { - public readonly scheme: string = "mqtts"; - private readonly clients: Array = []; - - // eslint-disable-next-line no-useless-constructor - constructor(private readonly config: MqttClientConfig) {} - getClient(): ProtocolClient { - const client = new MqttClient(this.config, true); - this.clients.push(client); - return client; - } - - init(): boolean { - return true; - } - - destroy(): boolean { - debug(`MqttClientFactory stopping all clients for '${this.scheme}'`); - this.clients.forEach((client) => client.stop()); - return true; - } -} diff --git a/packages/binding-mqtt/src/util.ts b/packages/binding-mqtt/src/util.ts deleted file mode 100644 index e0ad7dceb..000000000 --- a/packages/binding-mqtt/src/util.ts +++ /dev/null @@ -1,36 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 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 { createLoggers } from "@node-wot/core"; -import { MqttQoS } from "./mqtt"; -import { IClientPublishOptions } from "mqtt"; - -const { warn } = createLoggers("binding-mqtt", "mqtt-util"); - -export function mapQoS(qos: MqttQoS | undefined): Required["qos"] { - switch (qos) { - case "0": - return 0; - case "1": - return 1; - case "2": - return 2; - case undefined: - return 0; - default: - warn(`MqttClient received unsupported QoS level '${qos}'`); - warn(`MqttClient falling back to QoS level '0'`); - return 0; - } -} diff --git a/packages/binding-mqtt/test/mqtt-broker-server-interaction-test.integration.ts b/packages/binding-mqtt/test/mqtt-broker-server-interaction-test.integration.ts deleted file mode 100644 index 4f8c9fb69..000000000 --- a/packages/binding-mqtt/test/mqtt-broker-server-interaction-test.integration.ts +++ /dev/null @@ -1,290 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 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, { createInfoLogger } from "@node-wot/core"; -import { expect, should } from "chai"; -import MqttBrokerServer from "../src/mqtt-broker-server"; -import MqttClientFactory from "../src/mqtt-client-factory"; -import { ConsumedThing, ExposedThing } from "wot-typescript-definitions"; - -const info = createInfoLogger("binding-mqtt", "mqtt-broker-server-interaction-test.integration"); - -// should must be called to augment all variables -should(); - -describe("MQTT broker server interaction implementation", () => { - let servient: Servient; - let brokerServer: MqttBrokerServer; - - const brokerAddress = "localhost"; - const brokerPort = 1889; - const brokerUri = `mqtt://${brokerAddress}:${brokerPort}`; - - const mqttThingModel: WoT.ExposedThingInit = { - title: "mqtt-thing-model", - description: "MQTT Thing Model for testing", - properties: { - stringProperty: { - type: "string", - observable: true, - readOnly: false, - writeOnly: true, - }, - numberProperty: { - type: "number", - observable: true, - readOnly: false, - writeOnly: true, - }, - arrayProperty: { - type: "array", - observable: true, - readOnly: false, - writeOnly: true, - }, - objectProperty: { - type: "object", - observable: true, - readOnly: false, - writeOnly: true, - }, - }, - actions: { - stringAction: { - input: { - type: "string", - }, - }, - numberAction: { - input: { - type: "number", - }, - }, - arrayAction: { - input: { - type: "array", - }, - }, - objectAction: { - input: { - type: "object", - }, - }, - }, - }; - - let mqttClient: ConsumedThing; - - let stringProperty: string; - let numberProperty: number; - let arrayProperty: []; - let objectProperty: Record; - - let stringAction: string; - let numberAction: number; - let arrayAction: []; - let objectAction: Record; - - let testThing: ExposedThing; - - before((done: Mocha.Done) => { - servient = new Servient(); - brokerServer = new MqttBrokerServer({ uri: brokerUri, selfHost: true }); - servient.addServer(brokerServer); - servient.addClientFactory(new MqttClientFactory()); - servient.start().then((WoT) => { - WoT.produce(mqttThingModel) - .then((thing) => { - testThing = thing; - - thing.expose().then(() => { - info(`Exposed ${thing.getThingDescription().title}`); - - WoT.consume(thing.getThingDescription()).then((client) => { - mqttClient = client; - }); - }); - }) - .then(() => { - done(); - }); - }); - }); - - after(async () => { - await servient.shutdown(); - await brokerServer.stop(); - }); - - it("should write property (string)", (done: Mocha.Done) => { - const input = "writeProperty"; - - testThing.setPropertyWriteHandler("stringProperty", async (inputData) => { - stringProperty = (await inputData.value()) as string; - try { - expect(stringProperty).to.equal(input); - done(); - } catch (e) { - done(e); - } - }); - - mqttClient.writeProperty("stringProperty", input).catch((e) => { - done(e); - }); - }).timeout(20000); - - it("should write property (number)", (done: Mocha.Done) => { - const input = 1337; - - testThing.setPropertyWriteHandler("numberProperty", async (inputData) => { - numberProperty = (await inputData.value()) as number; - try { - expect(numberProperty).to.equal(input); - done(); - } catch (e) { - done(e); - } - }); - - mqttClient.writeProperty("numberProperty", input).catch((e) => { - done(e); - }); - }).timeout(20000); - - it("should write property (array)", (done: Mocha.Done) => { - const input = [1, 3, 3, 7]; - - testThing.setPropertyWriteHandler("arrayProperty", async (inputData) => { - arrayProperty = (await inputData.value()) as []; - try { - expect(arrayProperty).to.eql(input); - done(); - } catch (e) { - done(e); - } - }); - - mqttClient.writeProperty("arrayProperty", input).catch((e) => { - done(e); - }); - }).timeout(20000); - - it("should write property (object)", (done: Mocha.Done) => { - const input = { - test_number: 23, - test_string: "test", - test_array: ["t", "e", "s", "t"], - }; - - testThing.setPropertyWriteHandler("objectProperty", async (inputData) => { - objectProperty = (await inputData.value()) as Record; - try { - expect(objectProperty).to.eql(input); - done(); - } catch (e) { - done(e); - } - }); - - mqttClient.writeProperty("objectProperty", input).catch((e) => { - done(e); - }); - }).timeout(20000); - - it("should invoke action (string)", (done: Mocha.Done) => { - const input = "invokeAction"; - - testThing.setActionHandler("stringAction", async (inputData) => { - stringAction = (await inputData.value()) as string; - try { - expect(stringAction).to.equal(input); - done(); - } catch (e) { - done(e); - } - - return stringAction; - }); - - mqttClient.invokeAction("stringAction", input).catch((e) => { - done(e); - }); - }).timeout(20000); - - it("should invoke action (number)", (done: Mocha.Done) => { - const input = 1337; - - testThing.setActionHandler("numberAction", async (inputData) => { - numberAction = (await inputData.value()) as number; - try { - expect(numberAction).to.equal(input); - done(); - } catch (e) { - done(e); - } - - return numberAction; - }); - - mqttClient.invokeAction("numberAction", input).catch((e) => { - done(e); - }); - }).timeout(20000); - - it("should invoke action (array)", (done: Mocha.Done) => { - const input = [1, 3, 3, 7]; - - testThing.setActionHandler("arrayAction", async (inputData) => { - arrayAction = (await inputData.value()) as []; - try { - expect(arrayAction).to.eql(input); - done(); - } catch (e) { - done(e); - } - - return arrayAction; - }); - - mqttClient.invokeAction("arrayAction", input).catch((e) => { - done(e); - }); - }).timeout(20000); - - it("should invoke action (object)", (done: Mocha.Done) => { - const input = { - test_number: 23, - test_string: "test", - test_array: ["t", "e", "s", "t"], - }; - - testThing.setActionHandler("objectAction", async (inputData) => { - objectAction = (await inputData.value()) as Record; - try { - expect(objectAction).to.eql(input); - done(); - } catch (e) { - done(e); - } - - return objectAction; - }); - - mqttClient.invokeAction("objectAction", input).catch((e) => { - done(e); - }); - }).timeout(20000); -}); diff --git a/packages/binding-mqtt/test/mqtt-client-subscribe-test.integration.ts b/packages/binding-mqtt/test/mqtt-client-subscribe-test.integration.ts deleted file mode 100644 index 35ccc126f..000000000 --- a/packages/binding-mqtt/test/mqtt-client-subscribe-test.integration.ts +++ /dev/null @@ -1,166 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2021 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 - ********************************************************************************/ - -/** - * Protocol test suite to test protocol implementations - */ - -import { createInfoLogger, ProtocolHelpers, Servient } from "@node-wot/core"; -import { expect, should } from "chai"; -import MqttBrokerServer from "../src/mqtt-broker-server"; -import MqttClientFactory from "../src/mqtt-client-factory"; -import MqttsClientFactory from "../src/mqtts-client-factory"; - -const info = createInfoLogger("binding-mqtt", "mqtt-client-subscribe-test.integration"); - -// should must be called to augment all variables -should(); - -describe("MQTT client implementation - integration", () => { - let servient: Servient; - let brokerServer: MqttBrokerServer; - - const brokerAddress = "localhost"; - const brokerPort = 1889; - const brokerUri = `mqtt://${brokerAddress}:${brokerPort}`; - - beforeEach(() => { - servient = new Servient(); - }); - - afterEach(async () => { - await servient.shutdown(); - await brokerServer.stop(); - }); - - it("should expose via broker", (done: Mocha.Done) => { - brokerServer = new MqttBrokerServer({ uri: brokerUri, selfHost: true }); - servient.addServer(brokerServer); - - servient.addClientFactory(new MqttClientFactory()); - - servient.start().then((WoT) => { - expect(brokerServer.getPort()).to.equal(brokerPort); - expect(brokerServer.getAddress()).to.equal(brokerAddress); - - const eventNumber = Math.floor(Math.random() * 1000000); - const eventName: string = "event" + eventNumber; - const events: { [key: string]: Record } = {}; - events[eventName] = { data: { type: "number" } }; - - WoT.produce({ - title: "TestWoTMQTT", - events, - }).then((thing) => { - thing.expose().then(() => { - info(`Exposed ${thing.getThingDescription().title}`); - - WoT.consume(thing.getThingDescription()).then((client) => { - let check = 0; - let eventReceived = false; - - client - .subscribeEvent(eventName, (x) => { - if (!eventReceived) { - eventReceived = true; - } else { - if (x.data == null) { - done(new Error("No data received")); - return; - } - ProtocolHelpers.readStreamFully(ProtocolHelpers.toNodeStream(x.data)).then( - (received) => { - expect(JSON.parse(received.toString())).to.equal(++check); - if (check === 3) thing.destroy().then(() => done()); - } - ); - } - }) - .then(() => { - for (let i = 0; i < 5; i++) { - thing.emitEvent(eventName, i); - } - }) - .catch((e) => { - done(e); - }); - }); - }); - }); - }); - }).timeout(20000); - - it("should expose via broker using mqtts", (done: Mocha.Done) => { - brokerServer = new MqttBrokerServer({ - uri: brokerUri, - selfHost: true, - key: undefined /** fs.readFileSync("your_key.pem") */, - }); - servient.addServer(brokerServer); - - servient.addClientFactory(new MqttClientFactory()); - servient.addClientFactory(new MqttsClientFactory({ rejectUnauthorized: false })); - - servient.start().then((WoT) => { - expect(brokerServer.getPort()).to.equal(brokerPort); - expect(brokerServer.getAddress()).to.equal(brokerAddress); - - const eventNumber = Math.floor(Math.random() * 1000000); - const eventName: string = "event" + eventNumber; - const events: { [key: string]: Record } = {}; - events[eventName] = { data: { type: "number" } }; - - WoT.produce({ - title: "TestWoTMQTT", - events, - }).then((thing) => { - thing.expose().then(() => { - info(`Exposed ${thing.getThingDescription().title}`); - - WoT.consume(thing.getThingDescription()).then((client) => { - let check = 0; - let eventReceived = false; - - client - .subscribeEvent(eventName, (x) => { - if (!eventReceived) { - eventReceived = true; - } else { - if (x.data == null) { - done(new Error("No data received")); - return; - } - ProtocolHelpers.readStreamFully(ProtocolHelpers.toNodeStream(x.data)).then( - (received) => { - expect(JSON.parse(received.toString())).to.equal(++check); - if (check === 3) done(); - } - ); - } - }) - .then(() => { - for (let i = 0; i < 5; i++) { - thing.emitEvent(eventName, i); - } - }) - .catch((e) => { - done(e); - }); - }); - }); - }); - }); - }).timeout(20000); -}); diff --git a/packages/binding-mqtt/test/mqtt-client-subscribe-test.unit.ts b/packages/binding-mqtt/test/mqtt-client-subscribe-test.unit.ts deleted file mode 100644 index 39a60bdc6..000000000 --- a/packages/binding-mqtt/test/mqtt-client-subscribe-test.unit.ts +++ /dev/null @@ -1,167 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2021 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 - ********************************************************************************/ - -/** - * Protocol test suite to test protocol implementations - */ - -import * as chai from "chai"; -import chaiAsPromised from "chai-as-promised"; -import { MqttClient, MqttForm } from "../src/mqtt"; -import { expect, should } from "chai"; -import { Aedes, Server } from "aedes"; -import * as net from "net"; -import { Content } from "@node-wot/core"; -import { Readable } from "stream"; - -chai.use(chaiAsPromised); - -// should must be called to augment all variables -should(); - -describe("MQTT client implementation - unit", () => { - let aedes: Aedes; - let hostedBroker: net.Server; - const property = "test1"; - const brokerAddress = "localhost"; - const brokerPort = 1889; - const brokerUri = `mqtt://${brokerAddress}:${brokerPort}`; - - before(() => { - aedes = Server({}); - }); - - after(() => { - aedes.close(); - }); - - describe("tests without authorization", () => { - beforeEach(() => { - hostedBroker = net.createServer(aedes.handle); - hostedBroker.listen(brokerPort); - }); - - afterEach(() => { - hostedBroker.close(); - }); - - it("should publish and subscribe", (done: Mocha.Done) => { - const mqttClient = new MqttClient(); - const form: MqttForm = { - href: brokerUri + "/" + property, - "mqv:qos": "2", - "mqv:retain": false, - }; - - mqttClient - .subscribeResource(form, async (value: Content) => { - try { - const data = await value.toBuffer(); - expect(data.toString()).to.be.equal("test"); - done(); - } catch (err) { - done(err); - } - }) - .then(async (sub) => { - await mqttClient.invokeResource(form, new Content("", Readable.from(Buffer.from("test")))); - await mqttClient.stop(); - }) - .catch((err) => done(err)); - }).timeout(10000); - - it("should subscribe unsubscribe and subscribe again", (done: Mocha.Done) => { - const mqttClient = new MqttClient(); - const form: MqttForm = { - href: brokerUri + "/" + property, - "mqv:qos": "2", - "mqv:retain": false, - }; - - mqttClient - .subscribeResource(form, () => { - /** No-op */ - }) - .then(async (sub) => { - await mqttClient.unlinkResource(form); - await mqttClient.subscribeResource(form, async (value: Content) => { - try { - const data = await value.toBuffer(); - expect(data.toString()).to.be.equal("test"); - done(); - } catch (err) { - done(err); - } finally { - // Note: stopping the client clears also all subscriptions - await mqttClient.stop(); - } - }); - await mqttClient.invokeResource(form, new Content("", Readable.from(Buffer.from("test")))); - }) - .catch((err) => done(err)); - }).timeout(10000); - }); - - describe("tests with authorization", () => { - beforeEach(() => { - aedes.authenticate = function (_client, username: Readonly, password: Readonly, done) { - if (username !== undefined) { - done(null, username === "user" && password.equals(Buffer.from("pass"))); - return; - } - done(null, true); - }; - const server = net.createServer(aedes.handle); - hostedBroker = server.listen(brokerPort); - }); - - afterEach(() => { - hostedBroker.close(); - }); - - it("should not authenticate with basic auth", (done: Mocha.Done) => { - const mqttClient = new MqttClient(); - mqttClient.setSecurity([{ scheme: "basic" }], { username: "user", password: "wrongpass" }); - - const form: MqttForm = { - href: brokerUri + "/" + property, - "mqv:qos": "1", - "mqv:retain": false, - }; - - mqttClient - .subscribeResource(form, () => { - /** */ - }) - .then(() => done(new Error("Should not authenticate"))) - .should.eventually.be.rejectedWith(Error, "Connection refused: Not authorized") - .then(() => done()); - }).timeout(10000); - - it("should authenticate with basic auth", async () => { - const mqttClient = new MqttClient(); - mqttClient.setSecurity([{ scheme: "basic" }], { username: "user", password: "pass" }); - - const form: MqttForm = { - href: brokerUri + "/" + property, - "mqv:qos": "1", - "mqv:retain": false, - }; - - await mqttClient.subscribeResource(form, () => {}); - await mqttClient.stop(); - }).timeout(10000); - }); -}); diff --git a/packages/binding-mqtt/test/tsconfig.json b/packages/binding-mqtt/test/tsconfig.json deleted file mode 100644 index 786b1b616..000000000 --- a/packages/binding-mqtt/test/tsconfig.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "extends": "../tsconfig.json", - "compilerOptions": { - "rootDir": ".." - }, - "include": ["*.ts", "**/*.ts", "../src/**/*.ts"] -} diff --git a/packages/binding-mqtt/tsconfig.json b/packages/binding-mqtt/tsconfig.json deleted file mode 100644 index e9fa7c062..000000000 --- a/packages/binding-mqtt/tsconfig.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "extends": "../../tsconfig.json", - "compilerOptions": { - "outDir": "dist", - "rootDir": "src" - }, - "include": ["src/**/*"], - "references": [{ "path": "../td-tools" }, { "path": "../core" }] -} diff --git a/packages/binding-opcua/.eslintrc.json b/packages/binding-opcua/.eslintrc.json deleted file mode 100644 index c4237119e..000000000 --- a/packages/binding-opcua/.eslintrc.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "extends": "../../.eslintrc.js" -} diff --git a/packages/binding-opcua/.mocharc.yml b/packages/binding-opcua/.mocharc.yml deleted file mode 100644 index fed922102..000000000 --- a/packages/binding-opcua/.mocharc.yml +++ /dev/null @@ -1,8 +0,0 @@ -recursive: false -require: - - ts-node/register -timeout: 100000 -enable-source-maps: true -spec: "test/*-test.ts" -bail: true -parallel: false diff --git a/packages/binding-opcua/README.md b/packages/binding-opcua/README.md deleted file mode 100644 index e87152a91..000000000 --- a/packages/binding-opcua/README.md +++ /dev/null @@ -1,170 +0,0 @@ -# OPC UA Client Protocol Binding - -W3C Web of Things (WoT) Protocol Binding for OPC UA. -This package uses [nodep-opcua](https://www.npmjs.com/package/node-opcua) as a low-level client for OPCUA over TCP. - -Current Maintainer(s): [@erossignon](https://github.com/erossignon) - -## Protocol specifier - -The protocol prefix handled by this binding is `opc.tcp`. -This is the standard prefix used by OPC-UA connection endpoint. - -## Getting Started - -You can define an OPCUA property in a thing description, by using an "opc.tcp://" href. - -```js -const thingDescription = { - "@context": "https://www.w3.org/2019/wot/td/v1", - "@type": ["Thing"], - securityDefinitions: { nosec_sc: { scheme: "nosec" } }, - security: "nosec_sc", - title: "servient", - description: "node-wot CLI Servient", - properties: { - pumpSpeed: { - description: "the pump speed", - type: "number", - forms: [ - { - href: "opc.tcp://opcuademo.sterfive.com:26543", // endpoint, - op: ["readproperty", "observeproperty"], - "opcua:nodeId": "ns=1;s=PumpSpeed", - }, - ], - }, - }, -}; -``` - -```javascript -// examples/src/opcua/demo-opcua1.ts -import { Servient } from "@node-wot/core"; -import { OPCUAClientFactory } from "@node-wot/binding-opcua"; - -import { thingDescription } from "./demo-opcua-thing-description"; -(async () => { - const servient = new Servient(); - servient.addClientFactory(new OPCUAClientFactory()); - - const wot = await servient.start(); - const thing = await wot.consume(thingDescription); - - const content = await thing.readProperty("pumpSpeed"); - const pumpSpeed = await content.value(); - - console.log("------------------------------"); - console.log("Pump Speed is : ", pumpSpeed, "m/s"); - console.log("------------------------------"); - - await servient.shutdown(); -})(); -``` - -### Run the Example App - -The `examples/src/opcua` folder contains a set of typescript demo that shows you -how to define a thing description containing OPCUA Variables and methods. - -- `demo-opcua1.ts` shows how to define and read an OPC-UA variable in WoT. -- `demo-opcua2.ts` shows how to subscribe to an OPC-UA variable in WoT. -- `opcua-coffee-machine-demo.ts` demonstrates how to define and invoke OPCUA methods as WoT actions. - -### Form extensions - -#### href - -the `href` property must contains a OPCUA endpoint url in the form `opc.tcp://MACHINE:PORT/Application` -such as for instance: -`opc.tcp://opcuademo.sterfive.com:26543` or `opc.tcp://localhost:48010` - -#### opcua:nodeId - -The form must contain an `opcua:nodeId` property that describes the nodeId of the OPCUA Variable to read/write/subscribe or the nodeId of the OPCUA Object related to the action. - -The `opcua:nodeId` can have 2 forms: - -- a **NodeId** as a string, such as `"ns=1;i=1234"` , for instance: - -```javascript -"opcua:nodeId": "ns=1;s=\"Machine\".\"Component\"" -``` - -- or **browsePath**: The browse path will be converted into the corresponding nodeId at runtime when first encountered. - -``` -"opcua:nodeId": { root: "i=84", path: "/Objects/2:DeviceSet/1:CoffeeMachine" }, -``` - -### opcua:method - -for example: - -```typescript -const thingDescription = { - // ... - actions: { - brewCoffee: { - forms: [ - { - href: "opc.tcp://opcuademo.sterfive.com:26543", - op: ["invokeaction"], - "opcua:nodeId": { root: "i=84", path: "/Objects/2:DeviceSet/1:CoffeeMachine" }, - "opcua:method": { root: "i=84", path: "/Objects/2:DeviceSet/1:CoffeeMachine/2:MethodSet/9:Start" }, - }, - ], - }, - }, -}; -``` - -### defining a property - -```javascript -const thingDescription = { - // ... - properties: { - temperature: { - description: "the temperature", - observable: true, - readOnly: true, - unit: "m/s", - type: "number", - forms: [ - { - href: "opc.tcp://opcuademo.sterfive.com:26543", - op: ["readproperty", "observeproperty"], - "opcua:nodeId": "ns=1;s=Temperature", - }, - ], - }, - }, -}; -``` - -## Advanced - -The OPC-UA binding for node-wot offers additional features to allow you to interact with -OPCUA Variant and DataValue in OPCUA JSON encoded form. -For an example of use, you can dive into the unit test of the binding-opcua library. - -### Exploring the unit tests - -A set of examples can be found in this unit test: packages\binding-opcua\test\full-opcua-thing-test.ts - -## Additional tools - -### basic OPC-UA demo server - -A basic demo OPC-UA server can be started using the following command. - -``` -thingweb.node-wot> ts-node packages/binding-opcua/test/fixture/basic-opcua-server.ts -Server started opc.tcp://:7890 -``` - -### awesome WoT - OPCUA tools - -the [node-wot-opcua-tools](https://github.com/node-opcua/node-wot-opcua-tools) project provides -some useful applications built on top of node-wot and the OPCUA binding. diff --git a/packages/binding-opcua/package.json b/packages/binding-opcua/package.json deleted file mode 100644 index adb1b1d86..000000000 --- a/packages/binding-opcua/package.json +++ /dev/null @@ -1,65 +0,0 @@ -{ - "name": "@node-wot/binding-opcua", - "version": "0.8.15", - "description": "opcua client protocol binding for node-wot", - "author": "Eclipse Thingweb (https://thingweb.io/)", - "license": "EPL-2.0 OR W3C-20150513", - "repository": "https://github.com/thingweb/node-wot/tree/master/packages/binding-opcua", - "publishConfig": { - "access": "public" - }, - "files": [ - "dist", - "src" - ], - "main": "dist/index.js", - "types": "dist/index.d.ts", - "devDependencies": { - "should": "^13.2.3" - }, - "dependencies": { - "ajv": "^8.16.0", - "@node-wot/core": "0.8.15", - "@node-wot/td-tools": "0.8.15", - "node-opcua": "2.113.0", - "node-opcua-address-space": "2.113.0", - "node-opcua-basic-types": "2.113.0", - "node-opcua-binary-stream": "2.110.0", - "node-opcua-buffer-utils": "2.110.0", - "node-opcua-client": "2.113.0", - "node-opcua-constants": "2.98.1", - "node-opcua-data-model": "2.113.0", - "node-opcua-data-value": "2.113.0", - "node-opcua-date-time": "2.113.0", - "node-opcua-debug": "2.113.0", - "node-opcua-extension-object": "2.113.0", - "node-opcua-factory": "2.113.0", - "node-opcua-json": "0.50.0", - "node-opcua-nodeid": "2.113.0", - "node-opcua-numeric-range": "2.113.0", - "node-opcua-pseudo-session": "2.113.0", - "node-opcua-pubsub-client": "0.19.1", - "node-opcua-service-browse": "2.113.0", - "node-opcua-service-translate-browse-path": "2.113.0", - "node-opcua-status-code": "2.110.0", - "node-opcua-types": "2.113.0", - "node-opcua-variant": "2.113.0", - "rxjs": "5.5.11" - }, - "scripts": { - "build": "tsc -b", - "lint": "eslint .", - "lint:fix": "eslint . --fix", - "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\" \"**/*.json\"", - "ncu:opcua": "npx -y npm-check-updates -u -f \"node-opcua*\" -t newest", - "test": "mocha" - }, - "bugs": { - "url": "https://github.com/eclipse-thingweb/node-wot/issues" - }, - "homepage": "https://github.com/eclipse-thingweb/node-wot/tree/master/packages/binding-opcua#readme", - "directories": { - "test": "test" - }, - "keywords": [] -} diff --git a/packages/binding-opcua/src/codec.ts b/packages/binding-opcua/src/codec.ts deleted file mode 100644 index f4cbdf3e0..000000000 --- a/packages/binding-opcua/src/codec.ts +++ /dev/null @@ -1,291 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2022 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 { ContentCodec, createLoggers } from "@node-wot/core"; -import { DataSchema } from "@node-wot/td-tools"; -import { DataValue } from "node-opcua-data-value"; -import { DataType, Variant } from "node-opcua-variant"; -import Ajv from "ajv"; -import addFormats from "ajv-formats"; - -// see https://www.w3.org/Protocols/rfc1341/4_Content-Type.html -import { - opcuaJsonEncodeDataValue, - opcuaJsonDecodeDataValue, - opcuaJsonDecodeVariant, - DataValueJSON, - opcuaJsonEncodeVariant, -} from "node-opcua-json"; -import { BinaryStream } from "node-opcua-binary-stream"; -import { DataSchemaValue } from "wot-typescript-definitions"; - -const { debug } = createLoggers("binding-opcua", "codec"); - -// Strict mode has a lot of other checks and it prevents runtime unexpected problems -// TODO: in the future we should use the strict mode -const ajv = new Ajv({ strict: false }); -addFormats(ajv); -/** - * this schema, describe the node-opcua JSON format for a DataValue object - * - * const pojo = (new DataValue({})).toString(); - * - */ -export const schemaDataValue = { - type: ["object"], // "number", "integer", "string", "boolean", "array", "null"], - properties: { - serverPicoseconds: { type: "integer" }, - sourcePicoseconds: { type: "integer" }, - serverTimestamp: { type: "string", /* format: "date", */ nullable: true }, - sourceTimestamp: { type: "string", /* format: "date", */ nullable: true }, - statusCode: { - type: ["object"], - properties: { - value: { - type: "number", - }, - }, - }, - value: { - type: ["object"], - properties: { - dataType: { - type: ["string", "integer"], - }, - arrayType: { - type: ["string"], - }, - value: { - type: ["number", "integer", "string", "boolean", "array", "null", "object"], - }, - dimension: { - type: ["array"], - items: { type: "integer" }, - }, - additionalProperties: false, - }, - }, - }, - additionalProperties: true, -}; - -export const schemaVariantJSONNull = { - type: "null", - nullable: true, -}; - -export const schemaVariantJSON = { - type: "object", - properties: { - Type: { - type: ["number"], - }, - Body: { - type: ["number", "integer", "string", "boolean", "array", "null", "object"], - nullable: true, - }, - Dimensions: { - type: ["array"], - items: { type: "integer" }, - }, - }, - additionalProperties: false, - required: ["Type", "Body"], -}; - -export const schemaDataValueJSON1 = { - type: ["object"], // "number", "integer", "string", "boolean", "array", "null"], - properties: { - ServerPicoseconds: { type: "integer" }, - SourcePicoseconds: { type: "integer" }, - ServerTimestamp: { - type: "string" /*, format: "date" */, - }, - SourceTimestamp: { - type: "string" /*, format: "date" */, - }, - StatusCode: { - type: "integer", - minimum: 0, - }, - - Value: schemaVariantJSON, - Value1: { type: "number", nullable: true }, - - Value2: { - oneOf: [schemaVariantJSON, schemaVariantJSONNull], - }, - }, - - additionalProperties: false, - required: ["Value"], -}; -export const schemaDataValueJSON2 = { - properties: { - Value: { type: "null" }, - }, -}; -export const schemaDataValueJSON = { - oneOf: [schemaDataValueJSON2, schemaDataValueJSON1], -}; -export const schemaDataValueJSONValidate = ajv.compile(schemaDataValueJSON); -export const schemaDataValueValidate = ajv.compile(schemaDataValue); - -export function formatForNodeWoT(dataValue: DataValueJSON): DataValueJSON { - // remove unwanted/unneeded properties - delete dataValue.SourcePicoseconds; - delete dataValue.ServerPicoseconds; - delete dataValue.ServerTimestamp; - return dataValue; -} - -// application/json => is equivalent to application/opcua+json;type=Value - -export class OpcuaJSONCodec implements ContentCodec { - getMediaType(): string { - return "application/opcua+json"; - } - - bytesToValue(bytes: Buffer, schema: DataSchema, parameters?: { [key: string]: string }): DataSchemaValue { - const type = parameters?.type ?? "DataValue"; - let parsed = JSON.parse(bytes.toString()); - - const wantDataValue = parameters?.to === "DataValue" || false; - - switch (type) { - case "DataValue": { - const isValid = schemaDataValueJSONValidate(parsed); - if (!isValid) { - debug(`bytesToValue: parsed = ${parsed}`); - debug(`bytesToValue: ${schemaDataValueJSONValidate.errors}`); - throw new Error("Invalid JSON dataValue : " + JSON.stringify(parsed, null, " ")); - } - if (wantDataValue) { - return opcuaJsonDecodeDataValue(parsed); - } - return formatForNodeWoT(opcuaJsonEncodeDataValue(opcuaJsonDecodeDataValue(parsed), true)); - // return parsed; - } - case "Variant": { - if (wantDataValue) { - const dataValue = new DataValue({ value: opcuaJsonDecodeVariant(parsed) }); - return dataValue; - } - const v = opcuaJsonEncodeVariant(opcuaJsonDecodeVariant(parsed), true); - debug(`${v}`); - return v; - } - case "Value": { - if (wantDataValue) { - if (!parameters || !parameters.dataType) { - throw new Error("[OpcuaJSONCodec|bytesToValue]: unknown dataType for Value encoding" + type); - } - if (parameters.dataType === DataType[DataType.DateTime]) { - parsed = new Date(parsed); - } - const value = { - dataType: DataType[parameters.dataType as keyof typeof DataType], - value: parsed, - }; - return new DataValue({ value }); - } else { - if (parameters?.dataType === DataType[DataType.DateTime]) { - parsed = new Date(parsed); - } - return parsed; - } - } - default: - throw new Error("[OpcuaJSONCodec|bytesToValue]: Invalid type " + type); - } - } - - valueToBytes(value: unknown, _schema: DataSchema, parameters?: { [key: string]: string }): Buffer { - const type = parameters?.type ?? "DataValue"; - switch (type) { - case "DataValue": { - let dataValueJSON: DataValueJSON; - if (value instanceof DataValue) { - dataValueJSON = opcuaJsonEncodeDataValue(value, true); - } else if (value instanceof Variant) { - dataValueJSON = opcuaJsonEncodeDataValue(new DataValue({ value }), true); - } else if (typeof value === "string") { - dataValueJSON = JSON.parse(value) as DataValueJSON; - } else { - dataValueJSON = opcuaJsonEncodeDataValue(opcuaJsonDecodeDataValue(value as DataValueJSON), true); - } - dataValueJSON = formatForNodeWoT(dataValueJSON); - return Buffer.from(JSON.stringify(dataValueJSON), "ascii"); - } - case "Variant": { - if (value instanceof DataValue) { - value = opcuaJsonEncodeVariant(value.value, true); - } else if (value instanceof Variant) { - value = opcuaJsonEncodeVariant(value, true); - } else if (typeof value === "string") { - value = JSON.parse(value); - } - return Buffer.from(JSON.stringify(value), "ascii"); - } - case "Value": { - if (value === undefined) { - return Buffer.alloc(0); - } - if (value instanceof DataValue) { - value = opcuaJsonEncodeVariant(value.value, false); - } else if (value instanceof Variant) { - value = opcuaJsonEncodeVariant(value, false); - } - return Buffer.from(JSON.stringify(value), "ascii"); - } - default: - throw new Error("[OpcuaJSONCodec|valueToBytes]: Invalid type : " + type); - } - } -} -export const theOpcuaJSONCodec = new OpcuaJSONCodec(); - -export class OpcuaBinaryCodec implements ContentCodec { - getMediaType(): string { - return "application/opcua+octet-stream"; // see Ege - } - - bytesToValue(bytes: Buffer, schema: DataSchema, parameters?: { [key: string]: string }): DataValueJSON { - const binaryStream = new BinaryStream(bytes); - const dataValue = new DataValue(); - dataValue.decode(binaryStream); - return opcuaJsonEncodeDataValue(dataValue, true); - } - - valueToBytes( - dataValue: DataValueJSON | DataValue, - schema: DataSchema, - parameters?: { [key: string]: string } - ): Buffer { - dataValue = dataValue instanceof DataValue ? dataValue : opcuaJsonDecodeDataValue(dataValue); - - // remove unwanted properties - dataValue.serverPicoseconds = 0; - dataValue.sourcePicoseconds = 0; - dataValue.serverTimestamp = null; - - const size = dataValue.binaryStoreSize(); - const stream = new BinaryStream(size); - dataValue.encode(stream); - const body = stream.buffer; - return body; - } -} -export const theOpcuaBinaryCodec = new OpcuaBinaryCodec(); diff --git a/packages/binding-opcua/src/factory.ts b/packages/binding-opcua/src/factory.ts deleted file mode 100644 index efd6d1f25..000000000 --- a/packages/binding-opcua/src/factory.ts +++ /dev/null @@ -1,60 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2022 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 { ProtocolClientFactory, ProtocolClient, ContentSerdes, createLoggers } from "@node-wot/core"; -import { OpcuaJSONCodec, OpcuaBinaryCodec } from "./codec"; -import { OPCUAProtocolClient } from "./opcua-protocol-client"; - -const { debug } = createLoggers("binding-opcua", "factory"); - -export class OPCUAClientFactory implements ProtocolClientFactory { - readonly scheme: string = "opc.tcp"; - - private _clients: OPCUAProtocolClient[] = []; - - public contentSerdes: ContentSerdes = ContentSerdes.get(); - - constructor() { - this.contentSerdes.addCodec(new OpcuaJSONCodec()); - this.contentSerdes.addCodec(new OpcuaBinaryCodec()); - } - - getClient(): ProtocolClient { - debug(`OpcuaClientFactory creating client for '${this.scheme}'`); - if (this._clients[0] != null) { - return this._clients[0]; - } - this._clients[0] = new OPCUAProtocolClient(); - return this._clients[0]; - } - - init(): boolean { - debug("init"); - return true; - } - - destroy(): boolean { - debug("destroy"); - - const clients = this._clients; - this._clients = []; - (async () => { - for (const client of clients) { - await client.stop(); - } - })(); - return true; - } -} diff --git a/packages/binding-opcua/src/index.ts b/packages/binding-opcua/src/index.ts deleted file mode 100644 index b91cabeaa..000000000 --- a/packages/binding-opcua/src/index.ts +++ /dev/null @@ -1,19 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2022 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 * from "./factory"; -export * from "./codec"; -export * from "./opcua-protocol-client"; -// no protocol_client here => get access from factor diff --git a/packages/binding-opcua/src/opcua-protocol-client.ts b/packages/binding-opcua/src/opcua-protocol-client.ts deleted file mode 100644 index 85ac6813d..000000000 --- a/packages/binding-opcua/src/opcua-protocol-client.ts +++ /dev/null @@ -1,630 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2022 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 { Subscription } from "rxjs/Subscription"; -import { promisify } from "util"; -import { Readable } from "stream"; - -import { ProtocolClient, Content, ContentSerdes, createLoggers } from "@node-wot/core"; -import { Form, SecurityScheme } from "@node-wot/td-tools"; - -import { - ClientSession, - ClientSubscription, - OPCUAClient, - ReadValueIdOptions, - MonitoringParametersOptions, - ClientMonitoredItem, - DataValue, - TimestampsToReturn, - MonitoringMode, - VariantLike, - DataType, - IBasicSession, - VariantArrayType, - Variant, - VariantOptions, -} from "node-opcua-client"; -import { ArgumentDefinition, getBuiltInDataType } from "node-opcua-pseudo-session"; - -import { makeNodeId, NodeId, NodeIdLike, NodeIdType, resolveNodeId } from "node-opcua-nodeid"; -import { AttributeIds, BrowseDirection, makeResultMask } from "node-opcua-data-model"; -import { makeBrowsePath } from "node-opcua-service-translate-browse-path"; -import { StatusCodes } from "node-opcua-status-code"; - -import { schemaDataValue } from "./codec"; -import { FormElementProperty } from "wot-thing-description-types"; -import { opcuaJsonEncodeVariant } from "node-opcua-json"; -import { Argument, BrowseDescription, BrowseResult } from "node-opcua-types"; -import { isGoodish2, ReferenceTypeIds } from "node-opcua"; - -const { debug } = createLoggers("binding-opcua", "opcua-protocol-client"); - -export type Command = "Read" | "Write" | "Subscribe"; - -export interface NodeByBrowsePath { - root: NodeIdLike; - path: string; -} -export type NodeIdLike2 = NodeIdLike & { - root: undefined; - path: undefined; -}; - -export interface FormPartialNodeDescription { - "opcua:nodeId": NodeIdLike | NodeByBrowsePath; -} - -export interface OPCUAForm extends Form, FormPartialNodeDescription {} - -export interface OPCUAFormElement extends FormElementProperty, FormPartialNodeDescription {} - -export interface OPCUAFormInvoke extends OPCUAForm { - "opcua:method": NodeIdLike | NodeByBrowsePath; -} -export interface OPCUAFormSubscribe extends OPCUAForm { - "opcua:samplingInterval"?: number; -} - -interface OPCUAConnection { - session: ClientSession; - client: OPCUAClient; - subscription: ClientSubscription; -} - -type Resolver = (...arg: [...unknown[]]) => void; - -interface OPCUAConnectionEx extends OPCUAConnection { - pending?: Resolver[]; -} - -export function findBasicDataTypeC( - session: IBasicSession, - dataTypeId: NodeId, - callback: (err: Error | null, dataType?: DataType) => void -): void { - const resultMask = makeResultMask("ReferenceType"); - - if (dataTypeId.identifierType === NodeIdType.NUMERIC && Number(dataTypeId.value) <= 25) { - // we have a well-known DataType - callback(null, dataTypeId.value as DataType); - } else { - // let's browse for the SuperType of this object - const nodeToBrowse = new BrowseDescription({ - browseDirection: BrowseDirection.Inverse, - includeSubtypes: false, - nodeId: dataTypeId, - referenceTypeId: makeNodeId(ReferenceTypeIds.HasSubtype), - resultMask, - }); - - session.browse(nodeToBrowse, (err: Error | null, browseResult?: BrowseResult) => { - /* istanbul ignore next */ - if (err) { - return callback(err); - } - - /* istanbul ignore next */ - if (!browseResult) { - return callback(new Error("Internal Error")); - } - - browseResult.references = browseResult.references ?? /* istanbul ignore next */ []; - const baseDataType = browseResult.references[0].nodeId; - return findBasicDataTypeC(session, baseDataType, callback); - }); - } -} -const findBasicDataType: (session: IBasicSession, dataTypeId: NodeId) => Promise = - promisify(findBasicDataTypeC); - -function _variantToJSON(variant: Variant, contentType: string) { - contentType = contentType.split(";")[0]; - - switch (contentType) { - case "application/opcua+json": { - return opcuaJsonEncodeVariant(variant, true); - } - case "application/json": { - return opcuaJsonEncodeVariant(variant, false); - } - default: { - throw new Error("Unsupported content type here : " + contentType); - } - } -} - -export class OPCUAProtocolClient implements ProtocolClient { - private _connections: Map = new Map(); - - private async _withConnection(form: OPCUAForm, next: (connection: OPCUAConnection) => Promise): Promise { - const endpoint = form.href; - const matchesScheme: boolean = endpoint?.match(/^opc.tcp:\/\//) != null; - if (!matchesScheme) { - debug(`invalid opcua:endpoint ${endpoint} specified`); - throw new Error("Invalid OPCUA endpoint " + endpoint); - } - let c: OPCUAConnectionEx | undefined = this._connections.get(endpoint); - if (!c) { - const client = OPCUAClient.create({ - endpointMustExist: false, - connectionStrategy: { - maxRetry: 1, - }, - }); - client.on("backoff", () => { - debug(`connection:backoff: cannot connection to ${endpoint}`); - }); - - c = { - client, - pending: [] as Resolver[], - } as OPCUAConnectionEx; // but incomplete still - - this._connections.set(endpoint, c); - try { - await client.connect(endpoint); - const session = await client.createSession(); - c.session = session; - - const subscription = await session.createSubscription2({ - maxNotificationsPerPublish: 100, - publishingEnabled: true, - requestedLifetimeCount: 100, - requestedPublishingInterval: 250, - requestedMaxKeepAliveCount: 10, - priority: 1, - }); - c.subscription = subscription; - - const p = c.pending; - c.pending = undefined; - p && p.forEach((t) => t()); - - this._connections.set(endpoint, c); - } catch (err) { - throw new Error("Cannot connected to endpoint " + endpoint + "\nmsg = " + (err).message); - } - } - if (c.pending) { - await new Promise((resolve) => { - c?.pending?.push(resolve); - }); - } - return next(c); - } - - private async _withSession(form: OPCUAForm, next: (session: ClientSession) => Promise): Promise { - return this._withConnection(form, async (c: OPCUAConnection) => { - return next(c.session); - }); - } - - private async _withSubscription( - form: OPCUAForm, - next: (session: ClientSession, subscription: ClientSubscription) => Promise - ): Promise { - return this._withConnection(form, async (c: OPCUAConnection) => { - return next(c.session, c.subscription); - }); - } - - private async _resolveNodeId2(form: OPCUAForm, fNodeId: NodeIdLike | NodeByBrowsePath): Promise { - if (fNodeId instanceof NodeId) { - return fNodeId; - } else if ((fNodeId).root != null) { - const f = fNodeId; - const r: NodeIdLike = f.root; - const rootNodeId = resolveNodeId(r); - const nodeId = this._withSession(form, async (session) => { - const path = makeBrowsePath(rootNodeId, f.path); - const result = await session.translateBrowsePath(path); - if (result.statusCode !== StatusCodes.Good || !result.targets) { - debug(`resolveNodeId: failed to extract ${f.path}`); - throw new Error(`cannot resolve nodeId from path - root =${f.root} - path =${f.path} - statusCode =${result.statusCode.toString()}`); - } - return result.targets[0].targetId; - }); - return nodeId; - } else { - return resolveNodeId(fNodeId as NodeIdLike); - } - } - - private async _resolveNodeId(form: OPCUAForm): Promise { - const fNodeId = form["opcua:nodeId"]; - if (fNodeId == null) { - debug(`resolveNodeId: form = ${form}`); - throw new Error("form must expose a 'opcua:nodeId'"); - } - return this._resolveNodeId2(form, fNodeId); - } - - /** extract the dataType of a variable */ - private async _predictDataType(form: OPCUAForm): Promise { - const fNodeId = form["opcua:nodeId"]; - if (fNodeId == null) { - debug(`resolveNodeId: form = ${form}`); - throw new Error("form must expose a 'opcua:nodeId'"); - } - const nodeId = await this._resolveNodeId2(form, fNodeId); - return await this._withSession(form, async (session: IBasicSession) => { - const dataTypeOrNull = await promisify(getBuiltInDataType)(session, nodeId); - if (dataTypeOrNull !== null) { - return dataTypeOrNull as DataType; - } - throw new Error("cannot predict dataType for nodeId " + nodeId.toString()); - }); - } - - private async _resolveMethodNodeId(form: OPCUAFormInvoke): Promise { - // const objectNode = this._resolveNodeId(form); - const fNodeId = form["opcua:method"]; - if (fNodeId == null) { - debug(`resolveNodeId: form = ${form}`); - throw new Error("form must expose a 'opcua:nodeId'"); - } - return this._resolveNodeId2(form, fNodeId); - } - - public async readResource(form: OPCUAForm): Promise { - debug(`readResource: reading ${form}`); - - const content = await this._withSession(form, async (session) => { - const nodeId = await this._resolveNodeId(form); - const dataValue = await session.read({ - nodeId, - attributeId: AttributeIds.Value, - }); - return this._dataValueToContent(form, dataValue); - }); - debug(`readResource: contentType ${content.type}`); - return content; - } - - public async writeResource(form: OPCUAForm, content: Content): Promise { - const statusCode = await this._withSession(form, async (session) => { - const nodeId = await this._resolveNodeId(form); - const dataValue = await this._contentToDataValue(form, content); - const statusCode = await session.write({ - nodeId, - attributeId: AttributeIds.Value, - value: dataValue, - }); - return statusCode; - }); - debug(`writeResource: statusCode ${statusCode}`); - if (statusCode !== StatusCodes.Good && !isGoodish2(statusCode, { treatUncertainAsBad: false })) { - throw new Error("Error in OPCUA Write : " + statusCode.toString()); - } - } - - public async invokeResource(form: OPCUAFormInvoke, content: Content): Promise { - return await this._withSession(form, async (session) => { - const objectId = await this._resolveNodeId(form); - const methodId = await this._resolveMethodNodeId(form); - - const argumentDefinition: ArgumentDefinition = await session.getArgumentDefinition(methodId); - - const inputArguments = await this._resolveInputArguments(session, form, content, argumentDefinition); - - const callResult = await session.call({ - objectId, - methodId, - inputArguments, - }); - // Shall we throw an exception if call failed ? - if (callResult.statusCode !== StatusCodes.Good) { - throw new Error("Error in Calling OPCUA Method : " + callResult.statusCode.toString()); - } - const output = await this._resolveOutputArguments( - session, - form, - argumentDefinition, - callResult.outputArguments ?? [] - ); - return output; - }); - } - - public subscribeResource( - form: OPCUAForm, - next: (content: Content) => void, - error?: (error: Error) => void, - complete?: () => void - ): Promise { - debug(`subscribeResource: form ${form["opcua:nodeId"]}`); - - return this._withSubscription(form, async (session, subscription) => { - const nodeId = await this._resolveNodeId(form); - const key = nodeId.toString(); - - if (this._monitoredItems.has(key)) { - // what to do if we are already subscribed ? - const m = this._monitoredItems.get(key); - m?.handlers.push(next); - if (complete) { - complete(); - complete = undefined; - } - return new Subscription(async () => { - await this._unmonitor(nodeId); - }); - } - - const itemToMonitor: ReadValueIdOptions = { - nodeId, - attributeId: AttributeIds.Value, - }; - const parameters: MonitoringParametersOptions = { - samplingInterval: 250, - discardOldest: true, - queueSize: 1, - }; - - const monitoredItem = await subscription.monitor( - itemToMonitor, - parameters, - TimestampsToReturn.Both, - MonitoringMode.Reporting - ); - - const m = { - monitoredItem, - handlers: [next], - }; - this._monitoredItems.set(key, m); - monitoredItem.on("changed", async (dataValue: DataValue) => { - try { - const content = await this._dataValueToContent(form, dataValue); - m.handlers.forEach((n) => n(content)); - } catch (err) { - debug(`${nodeId}: ${dataValue}`); - if (error) { - error(new Error(JSON.stringify(err))); - } - } - if (complete) { - complete(); - complete = undefined; - } - }); - monitoredItem.once("err", (err) => { - error && error(err as Error); - }); - return new Subscription(async () => { - await this._unmonitor(nodeId); - }); - }); - } - - private async _unmonitor(nodeId: NodeId) { - const key = nodeId.toString(); - if (this._monitoredItems.has(key)) { - const m = this._monitoredItems.get(key); - this._monitoredItems.delete(key); - await m?.monitoredItem.terminate(); - } - } - - async unlinkResource(form: OPCUAForm): Promise { - debug(`unlinkResource: form ${form["opcua:nodeId"]}`); - this._withSubscription(form, async (session, subscription) => { - const nodeId = await this._resolveNodeId(form); - await this._unmonitor(nodeId); - }); - } - - /** - * @inheritdoc - */ - public async requestThingDescription(uri: string): Promise { - throw new Error("Method not implemented"); - } - - start(): Promise { - debug("start: Sorry not implemented"); - throw new Error("Method not implemented."); - } - - async stop(): Promise { - debug("stop"); - for (const c of this._connections.values()) { - await c.subscription.terminate(); - await c.session.close(); - await c.client.disconnect(); - } - } - - setSecurity(metadata: SecurityScheme[], credentials?: unknown): boolean { - return true; - // throw new Error("Method not implemented."); - } - - private _monitoredItems: Map< - string, - { - monitoredItem: ClientMonitoredItem; - handlers: ((content: Content) => void | Promise)[]; - } - > = new Map(); - - /// - private async _dataValueToContent(form: OPCUAForm, dataValue: DataValue): Promise { - const contentType = form.contentType ?? "application/json"; - - // QUESTION: how can we extend the default contentSerDes.valueToContent for application/json, - const contentSerDes = ContentSerdes.get(); - if (contentType === "application/json") { - const variantInJson = opcuaJsonEncodeVariant(dataValue.value, false); - const content = contentSerDes.valueToContent(variantInJson, schemaDataValue, contentType); - return content; - } - const content = contentSerDes.valueToContent(dataValue, schemaDataValue, contentType); - return content; - } - - private async _contentToDataValue(form: OPCUAForm, content: Content): Promise { - const content2: { type: string; body: Buffer } = { - ...content, - body: await content.toBuffer(), - }; - - const contentSerDes = ContentSerdes.get(); - - const contentType = content2.type ? content2.type.split(";")[0] : "application/json"; - - switch (contentType) { - case "application/json": { - const dataType = await this._predictDataType(form); - const value = contentSerDes.contentToValue(content2, schemaDataValue); - return new DataValue({ value: { dataType, value } }); - } - case "application/opcua+json": { - const fullContentType = content2.type + ";to=DataValue"; - const content3 = { - type: fullContentType, - body: content2.body, - }; - const dataValue = contentSerDes.contentToValue(content3, schemaDataValue) as DataValue; - if (!(dataValue instanceof DataValue)) { - contentSerDes.contentToValue(content2, schemaDataValue) as DataValue; - throw new Error("Internal Error, expecting a DataValue here "); - } - debug(`_contentToDataValue: write ${form}`); - debug( - `_contentToDataValue: content ${{ - ...content2, - body: content2.body.toString("ascii"), - }}` - ); - - return dataValue; - } - default: { - throw new Error("Unsupported content type here : " + contentType); - } - } - } - - private async _contentToVariant( - contentType: undefined | string, - body: Buffer, - dataType: DataType - ): Promise { - const contentSerDes = ContentSerdes.get(); - - contentType = contentType?.split(";")[0] ?? "application/json"; - - switch (contentType) { - case "application/json": { - const value = contentSerDes.contentToValue({ type: contentType, body }, schemaDataValue); - return new Variant({ dataType, value }); - } - case "application/opcua+json": { - contentType += ";type=Variant;to=DataValue"; - const content2 = { type: contentType, body }; - const dataValue = contentSerDes.contentToValue(content2, schemaDataValue) as DataValue; - if (!(dataValue instanceof DataValue)) { - throw new Error("Internal Error, expecting a DataValue here "); - } - const variant = dataValue.value; - if (variant.dataType !== dataType) { - debug(`Unexpected dataType ${variant.dataType}`); - } - return variant; - } - default: { - throw new Error("Unsupported content type here : " + contentType); - } - } - } - - private async _findBasicDataType(session: IBasicSession, dataType: NodeId): Promise { - return await findBasicDataType(session, dataType); - } - - private async _resolveInputArguments( - session: IBasicSession, - form: OPCUAFormInvoke, - content: Content | undefined | null, - argumentDefinition: ArgumentDefinition - ): Promise { - if (content?.body == null) { - return []; - } - const content2 = { ...content, body: await content.toBuffer() }; - const bodyInput = JSON.parse(content2.body.toString()); - - const inputArguments = (argumentDefinition.inputArguments ?? []) as unknown as Argument[]; - - const variants: VariantLike[] = []; - for (let index = 0; index < inputArguments.length; index++) { - const argument = inputArguments[index]; - - const { name, dataType, /* description, */ arrayDimensions, valueRank } = argument; - - if (bodyInput[name ?? "null"] === undefined) { - throw new Error("missing value in bodyInput for argument " + name); - } - const basicDataType = await this._findBasicDataType(session, dataType); - if (basicDataType === undefined) { - throw new Error("basicDataType is undefined for dataType " + dataType); - } - - const arrayType: VariantArrayType = - valueRank === -1 - ? VariantArrayType.Scalar - : valueRank === 1 - ? VariantArrayType.Array - : VariantArrayType.Matrix; - - const n = (a: unknown) => Buffer.from(JSON.stringify(a)); - const v = await this._contentToVariant(content2.type, n(bodyInput[name ?? "null"]), basicDataType); - - variants.push({ - dataType: basicDataType, - arrayType, - dimensions: arrayType === VariantArrayType.Matrix ? arrayDimensions : undefined, - value: v.value, - }); - } - return variants; - } - - private async _resolveOutputArguments( - session: IBasicSession, - form: OPCUAFormInvoke, - argumentDefinition: ArgumentDefinition, - outputVariants: Variant[] - ): Promise { - const outputArguments = (argumentDefinition.outputArguments ?? []) as unknown as Argument[]; - - const contentType = form.contentType ?? "application/json"; - - const body: Record = {}; - for (let index = 0; index < outputArguments.length; index++) { - const argument = outputArguments[index]; - const { name } = argument; - const element = _variantToJSON(outputVariants[index], contentType); - body[name ?? "null"] = element; - } - - return new Content("application/json", Readable.from(JSON.stringify(body))); - } -} diff --git a/packages/binding-opcua/test/client-test.ts b/packages/binding-opcua/test/client-test.ts deleted file mode 100644 index 34f3e81ec..000000000 --- a/packages/binding-opcua/test/client-test.ts +++ /dev/null @@ -1,193 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2022 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 Ajv from "ajv/dist/core"; -import { expect } from "chai"; - -import { ContentSerdes, createLoggers } from "@node-wot/core"; - -import { VariableIds, OPCUAServer } from "node-opcua"; - -import { OPCUAProtocolClient, OPCUAForm, OPCUAFormInvoke } from "../src/opcua-protocol-client"; -import { OpcuaJSONCodec, schemaDataValue } from "../src/codec"; -import { startServer } from "./fixture/basic-opcua-server"; - -const { debug } = createLoggers("binding-opcua", "opcua-protocol-client"); - -describe("OPCUA Client", function () { - this.timeout(60000); - - let opcuaServer: OPCUAServer; - let endpoint: string; - before(async () => { - opcuaServer = await startServer(); - endpoint = opcuaServer.getEndpointUrl(); - debug(`endpoint = ${endpoint}`); - }); - before(() => { - // ensure codec is loaded - const codecSerDes = ContentSerdes.get(); - codecSerDes.addCodec(new OpcuaJSONCodec()); - }); - - let client: OPCUAProtocolClient; - before(async function () { - client = new OPCUAProtocolClient(); - }); - after(async () => { - await client.stop(); - }); - // server shall stop after client - after(async () => { - await opcuaServer.shutdown(); - }); - - [ - // 0 - { contentType: "application/json", expected: "2022-01-31T10:45:00.000Z" }, - // 1 - { - contentType: "application/opcua+json", - expected: { - SourceTimestamp: "*", - Value: { - Type: 13, - Body: new Date("2022-01-31T10:45:00.000Z"), - }, - }, - }, - // 2 - { - contentType: "application/opcua+json;type=DataValue", - expected: { - SourceTimestamp: "*", - Value: { - Type: 13, - Body: new Date("2022-01-31T10:45:00.000Z"), - }, - }, - }, - // 3 - { - contentType: "application/opcua+json;type=Variant", - expected: { - Type: 13, - Body: new Date("2022-01-31T10:45:00.000Z"), - }, - }, - // 4 - { - contentType: "application/opcua+json;type=Value;dataType=DateTime", - expected: new Date("2022-01-31T10:45:00.000Z"), - }, - ].forEach(({ contentType, expected }, index) => { - it(`Y1-${index} should read a topic with contentType= ${contentType}`, async () => { - const readForm: OPCUAForm = { - href: endpoint, - "opcua:nodeId": "ns=1;s=ManufacturingDate", - contentType, - }; - - const content = await client.readResource(readForm); - const content2 = { ...content, body: await content.toBuffer() }; - - debug(`readResource returned: ${content2.body.toString("ascii")}`); - - const codecSerDes = ContentSerdes.get(); - const dataValue = codecSerDes.contentToValue(content2, schemaDataValue) as Record; - - // (deal with always changing date ) - if (dataValue.SourceTimestamp != null) { - expect(dataValue.SourceTimestamp).to.be.instanceOf(Date); - dataValue.SourceTimestamp = "*"; - } - debug(`${dataValue}`); - expect(dataValue).to.eql(expected); - }); - }); - - it("Y2 - should subscribe to a topic", async () => { - const form: OPCUAForm = { - href: endpoint, - "opcua:nodeId": VariableIds.Server_ServerStatus_CurrentTime, - }; - - let counter = 0; - const sub = await client.subscribeResource(form, async () => { - counter++; - if (counter > 3) { - // await client.unlinkResource(form); - sub.unsubscribe(); - } - }); - }); - - it("Y3 - should subscribe to many topics but establish the opcua connection once", async () => { - const form: OPCUAForm = { - href: endpoint, - "opcua:nodeId": VariableIds.Server_ServerStatus_CurrentTime, - }; - - await new Promise((resolve) => { - let counter = 0; - const onSubscribedValueChanged = async () => { - counter++; - if (counter > 3) { - await client.unlinkResource(form); - resolve(); - } - }; - client.subscribeResource(form, onSubscribedValueChanged); - client.subscribeResource(form, onSubscribedValueChanged); - client.subscribeResource(form, onSubscribedValueChanged); - client.subscribeResource(form, onSubscribedValueChanged); - }); - }); - - it("Y4 - invokeResource", async () => { - const inputSchema = { - type: "object", - properties: { - TargetTemperature: { type: "number" }, - }, - required: ["TargetTemperature"], - }; - - const form: OPCUAFormInvoke = { - href: endpoint, - "opcua:nodeId": { root: "i=84", path: "/Objects/1:MySensor" }, - "opcua:method": { root: "i=84", path: "/Objects/1:MySensor/2:MethodSet/1:SetTemperatureSetPoint" }, - }; - const contentType = "application/json"; - const contentSerDes = ContentSerdes.get(); - - const value = { TargetTemperature: 25 }; // inputSchema - const ajv = new Ajv({ strict: false }); - expect(ajv.compile(inputSchema)(value)).to.equal(true); - - const content = contentSerDes.valueToContent(value, schemaDataValue, contentType); - - const contentResult = await client.invokeResource(form, content); - - const contentResult2 = { ...contentResult, body: await contentResult.toBuffer() }; - const codecSerDes = ContentSerdes.get(); - const outputArguments = codecSerDes.contentToValue(contentResult2, schemaDataValue); - debug(`Y4: outputArguments: ${outputArguments}`); - - if (outputArguments == null) { - expect.fail("outputArguments is null"); - } - outputArguments.should.eql({ PreviousSetPoint: 27 }); - }); -}); diff --git a/packages/binding-opcua/test/fixture/basic-opcua-server.ts b/packages/binding-opcua/test/fixture/basic-opcua-server.ts deleted file mode 100644 index 4f3918991..000000000 --- a/packages/binding-opcua/test/fixture/basic-opcua-server.ts +++ /dev/null @@ -1,210 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2022 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 { - ISessionContext, - nodesets, - OPCUAServer, - StatusCodes, - UAObject, - DataType, - Variant, - UAVariable, - VariantLike, - StatusCode, - NumericRange, - QualifiedNameLike, - DataValue, - coerceNodeId, - DataTypeIds, - coerceQualifiedName, - VariantArrayType, - CallbackT, - CallMethodResultOptions, -} from "node-opcua"; -import { KeyValuePair } from "node-opcua-types"; -import { createLoggers } from "@node-wot/core"; - -const { info } = createLoggers("binding-opcua", "basic-opcua-server"); - -interface UAVariable2 extends UAVariable { - setValueFromSource(value: VariantLike, statusCode?: StatusCode, sourceTimestamp?: Date): void; - readValue( - context?: ISessionContext | null, - indexRange?: NumericRange, - dataEncoding?: QualifiedNameLike | null - ): DataValue; -} - -export async function startServer(): Promise { - const server = new OPCUAServer({ - port: 7890, - nodeset_filename: [nodesets.standard, nodesets.di], - }); - - await server.initialize(); - - const addressSpace = server.engine.addressSpace; - if (!addressSpace) { - throw new Error("addressSpace not initialized"); - } - - const nsDI = addressSpace.getNamespaceIndex("http://opcfoundation.org/UA/DI/"); - if (!nsDI) { - throw new Error("cannot find DI namespace"); - } - - const deviceType = addressSpace.findObjectType("DeviceType", nsDI); - if (!deviceType) { - throw new Error("cannot find DeviceType"); - } - - const namespace = addressSpace.getOwnNamespace(); - - const sensorType = namespace.addObjectType({ - subtypeOf: deviceType, - browseName: "SensorType", - }); - - const mySensor = sensorType.instantiate({ - browseName: "MySensor", - organizedBy: addressSpace.rootFolder.objects, - optionals: ["ParameterSet", "MethodSet"], - }); - - const manufacturingDate = namespace.addVariable({ - browseName: "ManufacturingDate", - nodeId: "s=ManufacturingDate", - dataType: "DateTime", - componentOf: mySensor, - }) as UAVariable2; - manufacturingDate.setValueFromSource({ dataType: DataType.DateTime, value: new Date("2022-01-31T10:45:00.000Z") }); - - const parameterSet = mySensor.getComponentByName("ParameterSet", nsDI); - if (!parameterSet) { - throw new Error("cannot find ParameterSet"); - } - const temperature = namespace.addVariable({ - browseName: "Temperature", - dataType: "Double", - componentOf: parameterSet, - }) as UAVariable2; - - temperature.setValueFromSource({ dataType: DataType.Double, value: 25.0 }); - - const temperatureSetPoint = namespace.addVariable({ - browseName: "TemperatureSetPoint", - dataType: "Double", - componentOf: parameterSet, - }) as UAVariable2; - temperatureSetPoint.setValueFromSource({ dataType: DataType.Double, value: 27.0 }); - - const methodSet = mySensor.getComponentByName("MethodSet", nsDI) as UAObject; - if (methodSet == null) { - throw new Error("cannot find MethodSet"); - } - const method = namespace.addMethod(methodSet, { - browseName: "SetTemperatureSetPoint", - inputArguments: [ - { - name: "TargetTemperature", - dataType: DataType.Double, - description: "the temperature set point to set", - }, - ], - outputArguments: [ - { - dataType: DataType.Double, - name: "PreviousSetPoint", - }, - ], - }); - - method.bindMethod( - (inputArguments: Variant[], context: ISessionContext, callback: CallbackT) => { - const newTemperatureSetPointVar = inputArguments[0]; - - const oldValue = temperatureSetPoint.readValue(); - temperatureSetPoint.setValueFromSource(newTemperatureSetPointVar); - - const callMethodResult = { - statusCode: StatusCodes.Good, - outputArguments: [oldValue.value], - }; - callback(null, callMethodResult); - } - ); - - const methodWithComplexArguments = namespace.addMethod(methodSet, { - browseName: "GetSongLyrics", - inputArguments: [ - { - name: "SongList", - dataType: DataType.String, - valueRank: 1, - description: "the songs to sing", - }, - { - name: "Volume", - dataType: DataType.Byte, - description: "the volume to sing at 0-255", - }, - ], - outputArguments: [ - { - dataType: coerceNodeId(DataTypeIds.KeyValuePair), - name: "SoundAndLyrics", - valueRank: 1, - description: "an array of key value pair containing song as key and lyrics as value", - }, - ], - }); - methodWithComplexArguments.bindMethod( - (inputArguments: Variant[], context: ISessionContext, callback: CallbackT) => { - const songs = inputArguments[0].value as string[]; - const volume = inputArguments[1].value as number; - - const value = songs.map( - (song) => - new KeyValuePair({ - key: coerceQualifiedName(song), - value: { - dataType: DataType.String, - value: "Lyrics for '" + song + "' (Volume = " + volume + ")", - }, - }) - ); - - const callMethodResult = { - statusCode: StatusCodes.Good, - outputArguments: [{ dataType: DataType.ExtensionObject, arrayType: VariantArrayType.Array, value }], - }; - callback(null, callMethodResult); - } - ); - - await server.start(); - info(`Server started: ${server.getEndpointUrl()}`); - return server; -} - -if (require.main === module) { - (async () => { - const server = await startServer(); - process.once("SIGINT", () => { - server.shutdown(); - }); - })(); -} diff --git a/packages/binding-opcua/test/full-opcua-thing-test.ts b/packages/binding-opcua/test/full-opcua-thing-test.ts deleted file mode 100644 index a22ee1b58..000000000 --- a/packages/binding-opcua/test/full-opcua-thing-test.ts +++ /dev/null @@ -1,620 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2022 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 - ********************************************************************************/ - -// node-wot implementation of W3C WoT Servient - -import { expect } from "chai"; -import { Servient, createLoggers } from "@node-wot/core"; -import { InteractionOptions } from "wot-typescript-definitions"; - -import { OPCUAServer } from "node-opcua"; - -import { OPCUAClientFactory } from "../src"; -import { startServer } from "./fixture/basic-opcua-server"; -const endpoint = "opc.tcp://localhost:7890"; - -const { debug } = createLoggers("binding-opcua", "full-opcua-thing-test"); - -const thingDescription: WoT.ThingDescription = { - "@context": "https://www.w3.org/2019/wot/td/v1", - "@type": ["Thing"], - - securityDefinitions: { nosec_sc: { scheme: "nosec" } }, - security: "nosec_sc", - - title: "servient", - description: "node-wot CLI Servient", - - opcua: { - namespace: ["http://opcfoundation.org/UA", "own", "http://opcfoundation.org/UA/DI/"], - endpoint, - }, - base: endpoint, - properties: { - // bare value like needed by WoT - temperature: { - description: "the temperature in the room", - observable: true, - readOnly: true, - unit: "°C", - type: "number", - "opcua:nodeId": { root: "i=84", path: "/Objects/1:MySensor/2:ParameterSet/1:Temperature" }, - // Don't specify type here as it could be multi form: type: [ "object", "number" ], - forms: [ - // 0 -> standard Node WoT form => Raw value - { - href: "/", // endpoint, - op: ["readproperty", "observeproperty"], - "opcua:nodeId": { root: "i=84", path: "/Objects/1:MySensor/2:ParameterSet/1:Temperature" }, - contentType: "application/json", - }, - { - href: "/", // endpoint, - op: ["readproperty", "observeproperty"], - "opcua:nodeId": { root: "i=84", path: "/Objects/1:MySensor/2:ParameterSet/1:Temperature" }, - contentType: "application/opcua+json;type=Value;dataType=Double", - }, - { - href: "/", // endpoint, - op: ["readproperty", "observeproperty"], - "opcua:nodeId": { root: "i=84", path: "/Objects/1:MySensor/2:ParameterSet/1:Temperature" }, - contentType: "application/opcua+json;type=Variant", - }, - { - href: "/", // endpoint, - op: ["readproperty", "observeproperty"], - "opcua:nodeId": { root: "i=84", path: "/Objects/1:MySensor/2:ParameterSet/1:Temperature" }, - contentType: "application/opcua+json;type=DataValue", - }, - ], - }, - // Enriched value like provided by OPCUA - $Variant$temperature: { - description: "the temperature in the room", - observable: true, - readOnly: true, - unit: "°C", - "opcua:nodeId": { root: "i=84", path: "/Objects/1:MySensor/2:ParameterSet/1:Temperature" }, - contentType: "application/json", - type: "object", - properties: { - Type: { - type: "number", - }, - Body: { - // could be any ! type: [ "object" , "number", "boolean" ], - }, - }, - forms: [ - { - type: "object", - href: "/", // endpoint, - op: ["readproperty", "observeproperty"], - "opcua:nodeId": { root: "i=84", path: "/Objects/1:MySensor/2:ParameterSet/1:Temperature" }, - contentType: "application/opcua+json;type=Variant", - }, - ], - }, - // -------------------------------------------------- - temperatureSetPoint: { - description: "the temperature set point", - observable: true, - unit: "°C", - type: "number", - // don't - forms: [ - { - href: "/", - op: ["readproperty", "observeproperty", "writeproperty"], - "opcua:nodeId": { root: "i=84", path: "/Objects/1:MySensor/2:ParameterSet/1:TemperatureSetPoint" }, - contentType: "application/json", - }, - { - href: "/", - op: ["readproperty", "observeproperty", "writeproperty"], - "opcua:nodeId": { root: "i=84", path: "/Objects/1:MySensor/2:ParameterSet/1:TemperatureSetPoint" }, - contentType: "application/opcua+json;type=Value;dataType=Double", - }, - { - href: "/", - op: ["readproperty", "observeproperty", "writeproperty"], - "opcua:nodeId": { root: "i=84", path: "/Objects/1:MySensor/2:ParameterSet/1:TemperatureSetPoint" }, - contentType: "application/opcua+json;type=Variant", - }, - { - href: "/", - op: ["readproperty", "observeproperty", "writeproperty"], - "opcua:nodeId": { root: "i=84", path: "/Objects/1:MySensor/2:ParameterSet/1:TemperatureSetPoint" }, - contentType: "application/opcua+json;type=DataValue", - }, - ], - }, - }, - actions: { - setTemperatureSetPoint: { - forms: [ - { - type: "object", - href: "/", - op: ["invokeaction"], - "opcua:nodeId": { root: "i=84", path: "/Objects/1:MySensor" }, - "opcua:method": { root: "i=84", path: "/Objects/1:MySensor/2:MethodSet/1:SetTemperatureSetPoint" }, - }, - ], - description: "set the temperature set point", - // see https://www.w3.org/TR/wot-thing-description11/#action-serialization-sample - input: { - type: "object", - properties: { - TargetTemperature: { - title: "the new temperature set point", - type: "number", - // minimum: 0, - // maximum: 100, - }, - }, - required: ["TargetTemperature"], - }, - output: { - type: "object", - properties: { - PreviousSetPoint: { - type: "number", - title: "the previous temperature set point", - // minimum: 0, - // maximum: 100, - }, - }, - required: ["PreviousSetPoint"], - }, - }, - $OPCUA$setTemperatureSetPoint: { - forms: [ - { - type: "object", - href: "/", - op: ["invokeaction"], - "opcua:nodeId": { root: "i=84", path: "/Objects/1:MySensor" }, - "opcua:method": { root: "i=84", path: "/Objects/1:MySensor/2:MethodSet/1:SetTemperatureSetPoint" }, - contentType: "application/opcua+json;type=Variant", - }, - ], - description: "set the temperature set point", - // see https://www.w3.org/TR/wot-thing-description11/#action-serialization-sample - input: { - type: "object", - properties: { - TargetTemperature: { - title: "the new temperature set point", - type: "object", // a variant of type double - // minimum: 0, - // maximum: 100, - }, - }, - required: ["TargetTemperature"], - }, - output: { - type: "object", - properties: { - PreviousSetPoint: { - type: "object", // << Note here this is an object representing a JSON OPCUA Variant - title: "the previous temperature set point", - // minimum: 0, - // maximum: 100, - }, - }, - required: ["PreviousSetPoint"], - }, - }, - - GetSongLyrics: { - forms: [ - { - type: "object", - href: "/", - op: ["invokeaction"], - "opcua:nodeId": { root: "i=84", path: "/Objects/1:MySensor" }, - "opcua:method": { root: "i=84", path: "/Objects/1:MySensor/2:MethodSet/1:GetSongLyrics" }, - }, - ], - input: { - type: "object", - properties: { - SongList: { - title: "the songs to sing", - type: "array", - // minimum: 0, - // maximum: 100, - }, - Volume: { - type: "number", - minimum: 0, - maximum: 255, - }, - }, - required: ["SongList", "Volume"], - }, - output: { - type: "object", - properties: { - SoundAndLyrics: { - type: "array", - title: "an array of key value pair containing song as key and lyrics as value", - // minimum: 0, - // maximum: 100, - }, - }, - required: ["SoundAndLyrics"], - }, - }, - $OPCUA$GetSongLyrics: { - forms: [ - { - type: "object", - href: "/", - op: ["invokeaction"], - "opcua:nodeId": { root: "i=84", path: "/Objects/1:MySensor" }, - "opcua:method": { root: "i=84", path: "/Objects/1:MySensor/2:MethodSet/1:GetSongLyrics" }, - contentType: "application/opcua+json;type=Variant", - }, - ], - input: { - type: "object", - properties: { - SongList: { - title: "the songs to sing", - type: "array", - // minimum: 0, - // maximum: 100, - }, - Volume: { - type: "number", - minimum: 0, - maximum: 255, - }, - }, - required: ["SongList", "Volume"], - }, - output: { - type: "object", - properties: { - SoundAndLyrics: { - type: "array", - title: "an array of key value pair containing song as key and lyrics as value", - // minimum: 0, - // maximum: 100, - }, - }, - required: ["SoundAndLyrics"], - }, - }, - }, -}; - -describe("Full OPCUA Thing Test", () => { - let opcuaServer: OPCUAServer; - let endpoint: string; - before(async () => { - opcuaServer = await startServer(); - endpoint = opcuaServer.getEndpointUrl(); - debug(`endpoint = ${endpoint}`); - - // adjust TD to endpoint - thingDescription.base = endpoint; - (thingDescription.opcua as unknown as { endpoint: string }).endpoint = endpoint; - }); - after(async () => { - await opcuaServer.shutdown(); - }); - - it("Z1- should create a servient (produce) with OPCUA client factory", async () => { - const servient = new Servient(); - - const opcuaClientFactory = new OPCUAClientFactory(); - servient.addClientFactory(opcuaClientFactory); - - const wot = await servient.start(); - const thing = await wot.produce(thingDescription); - - thing.expose(); - - let temperature = 10; - const readHandler = async () => temperature; - thing.setPropertyReadHandler("temperature", readHandler); - - const temperatureCheck1 = await readHandler(); - expect(temperatureCheck1).to.equal(10); - - temperature = 100; - - const temperatureCheck2 = await readHandler(); - expect(temperatureCheck2).to.equal(100); - - await servient.shutdown(); - }); - - async function makeThing() { - const servient = new Servient(); - - const opcuaClientFactory = new OPCUAClientFactory(); - - servient.addClientFactory(opcuaClientFactory); - - const wot = await servient.start(); - - const thing: WoT.ConsumedThing = await wot.consume(thingDescription); - - debug(`${thing.getThingDescription().properties}`); - - return { thing, servient }; - } - async function doTest( - thing: WoT.ConsumedThing, - propertyName: string, - localOptions: InteractionOptions, - forceParsing = false - ) { - debug("------------------------------------------------------"); - try { - const content = await thing.readProperty(propertyName, localOptions); - if (forceParsing) { - // In opcua binding it is possible to return a special response that contains - // richer details than the bare value. However this makes the returned value - // not complaint with its data schema. Therefore we have to fallback to - // custom parsing. - const raw = await content.arrayBuffer(); - const json = JSON.parse(Buffer.from(raw).toString("utf-8")); - debug(json?.toString()); - return json; - } - const json = await content.value(); - debug(json?.toString()); - return json; - } catch (e) { - debug(`${e}`); - return { err: `${e}` }; - } - } - it("Z2 - test $Variant$temperature", async () => { - const { thing, servient } = await makeThing(); - try { - const propertyName = "$Variant$temperature"; - const json0 = await doTest(thing, propertyName, {}); - expect(json0).to.eql({ - Type: 11, - Body: 25, - }); - } finally { - await servient.shutdown(); - } - }); - - it("Z3 - test temperature with various formIndex", async () => { - const { thing, servient } = await makeThing(); - const propertyName = "temperature"; - - try { - const json0 = await doTest(thing, propertyName, { formIndex: 0 }); - expect(json0).to.eql(25); - - const json1 = await doTest(thing, propertyName, { formIndex: 1 }); - expect(json1).to.eql(25); - - const json2 = await doTest(thing, propertyName, { formIndex: 2 }, true); - expect(json2).to.eql({ Type: 11, Body: 25 }); - - expect(thingDescription.properties?.temperature.forms[3].contentType).eql( - "application/opcua+json;type=DataValue" - ); - const json3 = await doTest(thing, propertyName, { formIndex: 3 }, true); - debug(json3?.toString()); - expect((json3 as Record).Value).to.eql({ Type: 11, Body: 25 }); - } finally { - await servient.shutdown(); - } - }); - - const readTemperature = async (thing: WoT.ConsumedThing): Promise => { - const content = await thing.readProperty("temperatureSetPoint"); - const value = await content.value(); - debug(`TemperatureSetPoint = ${value}`); - return value as number; - }; - - it("Z4- should create a servient (consume) with OPCUA client factory - writeProperty - application/json ", async () => { - const { thing, servient } = await makeThing(); - - try { - // read temperature before - const temperatureBefore = await readTemperature(thing); - expect(temperatureBefore).to.eql(27); - - // ---------------------------------------------- application/json - expect(thingDescription.properties?.temperatureSetPoint.forms[0].contentType).eql("application/json"); - await thing.writeProperty("temperatureSetPoint", 110); - const temperatureAfter = await readTemperature(thing); - expect(temperatureAfter).to.eql(110); - } finally { - await servient.shutdown(); - } - }); - - it("Z5- should create a servient (consume) with OPCUA client factory - writeProperty - application/opcua+json;type=DataValue", async () => { - const { thing, servient } = await makeThing(); - try { - // ---------------------------------------------- application/opcua+json;type=DataValue - expect(thingDescription.properties?.temperatureSetPoint.forms[3].contentType).eql( - "application/opcua+json;type=DataValue" - ); - await thing.writeProperty( - "temperatureSetPoint", - { Value: { Type: 11, Body: 100 }, StatusCode: 1, SourceTimestamp: new Date() }, - { formIndex: 3 } - ); - const temperatureAfter2 = await readTemperature(thing); - expect(temperatureAfter2).to.eql(100); - } finally { - await servient.shutdown(); - } - }); - - it("Z6- should create a servient (consume) with OPCUA client factory - writeProperty - application/opcua+json;type=Variant", async () => { - const { thing, servient } = await makeThing(); - try { - // ---------------------------------------------- application/opcua+json;type=Variant - expect(thingDescription.properties?.temperatureSetPoint.forms[2].contentType).eql( - "application/opcua+json;type=Variant" - ); - await thing.writeProperty("temperatureSetPoint", { Type: 11, Body: 90 }, { formIndex: 2 }); - - const temperatureAfter3 = await readTemperature(thing); - expect(temperatureAfter3).to.eql(90); - } finally { - await servient.shutdown(); - } - }); - - it("Z7 - should create a servient (consume) with OPCUA client factory - InvokeAction - simplest form", async () => { - const { thing, servient } = await makeThing(); - - await thing.writeProperty("temperatureSetPoint", 27); - - try { - // read temperature before - const contentA = await thing.invokeAction("setTemperatureSetPoint", { TargetTemperature: 26 }); - if (!contentA) { - expect.fail("contentA null"); - } - const returnedValue = await contentA.value(); - - debug(`Temperature setpoint before ${returnedValue}`); - expect(returnedValue).to.eql({ PreviousSetPoint: 27 }); - - const contentVerif = await (await thing.readProperty("temperatureSetPoint")).value(); - debug(`Temperature setpoint before -verified ${contentVerif}`); - expect(contentVerif).to.eql(26); - } finally { - await servient.shutdown(); - } - }); - - it("Z8 - should create a servient (consume) with OPCUA client factory - InvokeAction - application/opcua+json;type=Variant", async () => { - const { thing, servient } = await makeThing(); - - await thing.writeProperty("temperatureSetPoint", 27); - - try { - // read temperature before - const contentA = await thing.invokeAction("$OPCUA$setTemperatureSetPoint", { - TargetTemperature: { Type: 11, Body: 26 }, - }); - if (!contentA) { - expect.fail("contentA null"); - } - const returnedValue = await contentA.value(); - - debug(`Temperature setpoint before ${returnedValue}`); - expect(returnedValue).to.eql({ PreviousSetPoint: { Type: 11, Body: 27 } }); - - const contentVerif = await (await thing.readProperty("temperatureSetPoint")).value(); - debug(`Temperature setpoint before -verified ${contentVerif}`); - expect(contentVerif).to.eql(26); - } finally { - await servient.shutdown(); - } - }); - - it("Z9 - should create a servient (consume) with OPCUA client factory - InvokeAction (GetSongLyrics) - simplest form", async () => { - const { thing, servient } = await makeThing(); - - try { - const content = await ( - await thing.invokeAction("GetSongLyrics", { - SongList: ["Jingle Bell", "Mary has a little lamb"], - Volume: 100, - }) - )?.value(); - const returnedValue = content; - debug(`Return value ${JSON.stringify(returnedValue, null, " ")}`); - expect(returnedValue).to.eql({ - SoundAndLyrics: [ - { - Key: { - Name: "Jingle Bell", - }, - Value: "Lyrics for 'Jingle Bell' (Volume = 100)", - }, - { - Key: { - Name: "Mary has a little lamb", - }, - Value: "Lyrics for 'Mary has a little lamb' (Volume = 100)", - }, - ], - }); - } finally { - await servient.shutdown(); - } - }); - - xit("Z10 - should create a servient (consume) with OPCUA client factory - InvokeAction (GetSongLyrics) - application/opcua+json;type=Variant", async () => { - const { thing, servient } = await makeThing(); - - try { - const content = await ( - await thing.invokeAction("$OPCUA$GetSongLyrics", { - SongList: ["Jingle Bell", "Mary has a little lamb"], - Volume: 100, - }) - )?.value(); - const returnedValue = content; - debug(`Return value ${JSON.stringify(returnedValue, null, " ")}`); - expect(returnedValue).to.eql({ - SoundAndLyrics: [ - { - TypeId: { - Id: 14533, - }, - Body: { - Key: { - Name: "Jingle Bell", - }, - Value: { - Type: 12, - Body: "Lyrics for 'Jingle Bell' (Volume = 100)", - }, - }, - }, - { - TypeId: { - Id: 14533, - }, - Body: { - Key: { - Name: "Mary has a little lamb", - }, - Value: { - Type: 12, - Body: "Lyrics for 'Mary has a little lamb' (Volume = 100)", - }, - }, - }, - ], - }); - } finally { - await servient.shutdown(); - } - }); -}); diff --git a/packages/binding-opcua/test/opcua-codec-test.ts b/packages/binding-opcua/test/opcua-codec-test.ts deleted file mode 100644 index e751606db..000000000 --- a/packages/binding-opcua/test/opcua-codec-test.ts +++ /dev/null @@ -1,122 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2022 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 { exist } from "should"; -import { expect } from "chai"; - -import { ContentSerdes, Helpers, createLoggers } from "@node-wot/core"; -import { ObjectSchema } from "@node-wot/td-tools"; - -import { DataValue } from "node-opcua-data-value"; -import { DataType, VariantArrayType } from "node-opcua-variant"; -import { coerceLocalizedText } from "node-opcua-data-model"; -import { opcuaJsonEncodeDataValue, DataValueJSON } from "node-opcua-json"; -import { StatusCodes } from "node-opcua-status-code"; - -import { OpcuaBinaryCodec, OpcuaJSONCodec, theOpcuaBinaryCodec, theOpcuaJSONCodec } from "../src/codec"; - -const { debug } = createLoggers("binding-opcua", "opcua-codec-test"); - -const dataValue1 = new DataValue({}); -const dataValue2 = new DataValue({ - // serverTimestamp: new Date(), - - statusCode: StatusCodes.Good, - sourceTimestamp: new Date(Date.UTC(2021, 10, 11)), - // sourcePicoseconds: 100, - value: { - dataType: DataType.String, - arrayType: VariantArrayType.Array, - value: ["hello", "world"], - }, -}); -const dataValue3 = new DataValue({ - value: { - dataType: DataType.LocalizedText, - value: [coerceLocalizedText("a"), coerceLocalizedText("b")], - }, -}); - -const dataValue1Json = Helpers.structuredClone(opcuaJsonEncodeDataValue(dataValue1, true)); -const dataValue2Json = Helpers.structuredClone(opcuaJsonEncodeDataValue(dataValue2, true)); -const dataValue3Json = Helpers.structuredClone(opcuaJsonEncodeDataValue(dataValue3, true)); - -describe("OPCUA Binary Serdes ", () => { - [dataValue1Json, dataValue2Json, dataValue3Json].forEach((dataValue, index) => { - // - const schema: ObjectSchema = { type: "object", properties: {} }; - - it("should encode and decode a dataValue with application/opcua+binary codec " + index, () => { - const payload = theOpcuaBinaryCodec.valueToBytes(dataValue as DataValue | DataValueJSON, schema); - const dataValueReloaded = theOpcuaBinaryCodec.bytesToValue(payload, schema); - expect(dataValue).to.eql(Helpers.structuredClone(dataValueReloaded)); - }); - }); -}); - -describe("OPCUA JSON Serdes ", () => { - [dataValue1Json, dataValue2Json, dataValue3Json].forEach((dataValue, index) => { - const schema: ObjectSchema = { type: "object", properties: {} }; - - it("should encode and decode a dataValue with application/opcua+json codec " + index, () => { - const payload = theOpcuaJSONCodec.valueToBytes(dataValue, schema); - const dataValueReloaded = theOpcuaJSONCodec.bytesToValue(payload, schema); - expect(dataValue).to.eql(Helpers.structuredClone(dataValueReloaded)); - }); - }); - const expected1 = [ - { Value: null }, - { Value: { Type: 12, Body: ["hello", "world"] }, SourceTimestamp: "2021-11-11T00:00:00.000Z" }, - { Value: { Type: 21, Body: [{ Text: "a" }, { Text: "b" }] } }, - ]; - [dataValue1, dataValue2, dataValue3].forEach((dataValue, index) => { - it( - "should simplify serialize deserialize with application/opcua+json;type=DataValue (index = " + - index + - ")", - async () => { - const serdes = ContentSerdes.get(); - serdes.addCodec(new OpcuaJSONCodec()); - serdes.addCodec(new OpcuaBinaryCodec()); - - const schema: WoT.DataSchema = {}; - const contentType = "application/opcua+json;type=DataValue"; - exist(ContentSerdes.getMediaType(contentType)); - const payload = serdes.valueToContent(dataValue, schema, contentType); - const body = await payload.toBuffer(); - debug(body.toString("ascii")); - JSON.parse(body.toString()).should.eql(expected1[index]); - } - ); - const expected2 = [ - "null", - '{"Type":12,"Body":["hello","world"]}', - '{"Type":21,"Body":[{"Text":"a"},{"Text":"b"}]}', - ]; - it("should serialize deserialize with application/opcua+json;type=Variant" + index, async () => { - const serdes = ContentSerdes.get(); - serdes.addCodec(new OpcuaJSONCodec()); - serdes.addCodec(new OpcuaBinaryCodec()); - - const schema: WoT.DataSchema = {}; - const contentType = "application/opcua+json;type=Variant"; - exist(ContentSerdes.getMediaType(contentType)); - const payload = serdes.valueToContent(dataValue, schema, contentType); - const body = await payload.toBuffer(); - debug(body.toString("ascii")); - body.toString().should.eql(expected2[index]); - }); - }); -}); diff --git a/packages/binding-opcua/test/schema-validation-test.ts b/packages/binding-opcua/test/schema-validation-test.ts deleted file mode 100644 index 1d5e1496c..000000000 --- a/packages/binding-opcua/test/schema-validation-test.ts +++ /dev/null @@ -1,103 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2022 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 { Helpers, createLoggers } from "@node-wot/core"; -import { expect } from "chai"; - -import { DataType, DataValue, StatusCodes, VariantArrayType } from "node-opcua-client"; -import { opcuaJsonEncodeDataValue } from "node-opcua-json"; - -import { schemaDataValueValidate, schemaDataValueJSONValidate } from "../src/codec"; - -const { debug } = createLoggers("binding-opcua", "schema-validation-test"); - -const data = { - uint32: new DataValue({ - statusCode: StatusCodes.Good, - value: { - dataType: DataType.UInt32, - value: 42, - }, - }), - - "string array": new DataValue({ - sourcePicoseconds: 10, - sourceTimestamp: new Date(1909, 12, 31), - statusCode: StatusCodes.BadAggregateInvalidInputs, - value: { - dataType: DataType.String, - value: ["Hello", "World"], - }, - }), - "current time": new DataValue({ - sourcePicoseconds: 10, - sourceTimestamp: new Date(1909, 12, 31), - statusCode: StatusCodes.BadAggregateInvalidInputs, - value: { - dataType: DataType.DateTime, - value: [new Date(2019, 11, 23)], - }, - }), - - matrix: new DataValue({ - serverPicoseconds: 10, - serverTimestamp: new Date(1909, 12, 31), - statusCode: StatusCodes.BadAggregateInvalidInputs, - value: { - dataType: DataType.UInt64, - arrayType: VariantArrayType.Matrix, - dimensions: [2, 3], - value: [1, 2, 3, 4, 5, 6], - }, - }), - null: new DataValue({}), -}; - -describe("schemas", () => { - describe("schemaDataValue", () => { - Object.entries(data).forEach(([name, obj1]) => { - it("(experimental) " + name, () => { - const validate = schemaDataValueValidate; - - const obj1 = new DataValue({}).toJSON(); - const isValid = validate(obj1); - if (!isValid) { - debug(`Valid: ${isValid}`); - debug(`Errors: ${validate.errors}`); - } - debug(`${obj1}`); - expect(isValid).equal(true); - }); - }); - }); - describe("schemaDataValueJSON", () => { - const validate = schemaDataValueJSONValidate; - - Object.entries(data).forEach(([name, obj1]) => { - it("DataValue " + name, () => { - const dataValueJSON = Helpers.structuredClone(opcuaJsonEncodeDataValue(obj1, true)); - - const isValid = validate(dataValueJSON); - if (!isValid) { - debug(`Valid: ${isValid}`); - debug(`Errors: ${validate.errors}`); - debug(`dataValueJSON: ${dataValueJSON}`); - } - - expect(isValid).eql(true); - }); - }); - }); -}); diff --git a/packages/binding-opcua/tsconfig.json b/packages/binding-opcua/tsconfig.json deleted file mode 100644 index 65cfae503..000000000 --- a/packages/binding-opcua/tsconfig.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "extends": "../../tsconfig.json", - "compilerOptions": { - "outDir": "dist", - "rootDir": "src" - }, - "include": ["./src/**/*"], - "references": [{ "path": "../td-tools" }, { "path": "../core" }] -} diff --git a/packages/binding-websockets/.eslintrc.json b/packages/binding-websockets/.eslintrc.json deleted file mode 100644 index c4237119e..000000000 --- a/packages/binding-websockets/.eslintrc.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "extends": "../../.eslintrc.js" -} diff --git a/packages/binding-websockets/README.md b/packages/binding-websockets/README.md deleted file mode 100644 index 0b390609b..000000000 --- a/packages/binding-websockets/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Websocket Protocol Binding of node-wot - -Current Maintainer(s): [@miguelrk](https://github.com/miguelrk) diff --git a/packages/binding-websockets/package.json b/packages/binding-websockets/package.json deleted file mode 100644 index 8f5843796..000000000 --- a/packages/binding-websockets/package.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "name": "@node-wot/binding-websockets", - "version": "0.8.15", - "description": "WebSockets client & server protocol binding for node-wot", - "author": "Eclipse Thingweb (https://thingweb.io/)", - "license": "EPL-2.0 OR W3C-20150513", - "repository": "https://github.com/eclipse-thingweb/node-wot/tree/master/packages/binding-websockets", - "publishConfig": { - "access": "public" - }, - "files": [ - "dist/" - ], - "main": "dist/ws.js", - "types": "dist/ws.d.ts", - "browser": "dist/ws-browser.js", - "dependencies": { - "@node-wot/binding-http": "0.8.15", - "@node-wot/core": "0.8.15", - "@node-wot/td-tools": "0.8.15", - "slugify": "^1.4.5", - "ws": "^7.5.10" - }, - "scripts": { - "build": "tsc -b", - "test": "mocha --require ts-node/register --extension ts", - "lint": "eslint .", - "lint:fix": "eslint . --fix", - "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\" \"**/*.json\"" - }, - "bugs": { - "url": "https://github.com/eclipse-thingweb/node-wot/issues" - }, - "homepage": "https://github.com/eclipse-thingweb/node-wot/tree/master/packages/binding-websockets#readme", - "directories": { - "test": "test" - }, - "keywords": [] -} diff --git a/packages/binding-websockets/src/ws-browser.ts b/packages/binding-websockets/src/ws-browser.ts deleted file mode 100644 index 699bef3fd..000000000 --- a/packages/binding-websockets/src/ws-browser.ts +++ /dev/null @@ -1,21 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2019 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 { default as WebSocketClient } from "./ws-client"; -export { default as WebSocketClientFactory } from "./ws-client-factory"; -export { default as WebSocketSecureClientFactory } from "./wss-client-factory"; -export * from "./ws-client"; -export * from "./ws-client-factory"; -export * from "./wss-client-factory"; diff --git a/packages/binding-websockets/src/ws-client-factory.ts b/packages/binding-websockets/src/ws-client-factory.ts deleted file mode 100644 index 09425cfc0..000000000 --- a/packages/binding-websockets/src/ws-client-factory.ts +++ /dev/null @@ -1,49 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2018 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 - ********************************************************************************/ - -/** - * HTTP client Factory - */ - -import { ProtocolClientFactory, ProtocolClient, createLoggers } from "@node-wot/core"; -import WebSocketClient from "./ws-client"; - -const { debug } = createLoggers("binding-websockets", "ws-client-factory"); - -export default class WebSocketClientFactory implements ProtocolClientFactory { - public readonly scheme: string = "ws"; - private clientSideProxy: unknown = null; - - constructor(proxy: unknown = null) { - this.clientSideProxy = proxy; - } - - public getClient(): ProtocolClient { - debug(`HttpClientFactory creating client for '${this.scheme}'`); - return new WebSocketClient(); - } - - public init(): boolean { - // info(`HttpClientFactory for '${HttpClientFactory.scheme}' initializing`); - // TODO uncomment info if something is executed here - return true; - } - - public destroy(): boolean { - // info(`HttpClientFactory for '${HttpClientFactory.scheme}' destroyed`); - // TODO uncomment info if something is executed here - return true; - } -} diff --git a/packages/binding-websockets/src/ws-client.ts b/packages/binding-websockets/src/ws-client.ts deleted file mode 100644 index 3671ace3c..000000000 --- a/packages/binding-websockets/src/ws-client.ts +++ /dev/null @@ -1,95 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2018 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 - ********************************************************************************/ - -/** - * WebSockets client - */ - -import { ProtocolClient, Content, createLoggers } from "@node-wot/core"; -import { Form, SecurityScheme } from "@node-wot/td-tools"; -import { Subscription } from "rxjs/Subscription"; - -const { debug, warn } = createLoggers("binding-websockets", "ws-client"); - -export default class WebSocketClient implements ProtocolClient { - // eslint-disable-next-line no-useless-constructor - constructor() { - // TODO: implement and remove eslint-ignore-useless-constructor - } - - public toString(): string { - return `[WebSocketClient]`; - } - - public readResource(form: Form): Promise { - return new Promise((resolve, reject) => { - // TODO: implement - }); - } - - public writeResource(form: Form, content: Content): Promise { - return new Promise((resolve, reject) => { - // TODO: implement - }); - } - - public invokeResource(form: Form, content?: Content): Promise { - return new Promise((resolve, reject) => { - // TODO: implement - }); - } - - public unlinkResource(form: Form): Promise { - return new Promise((resolve, reject) => { - // TODO: implement - }); - } - - public subscribeResource( - form: Form, - next: (content: Content) => void, - error?: (error: Error) => void, - complete?: () => void - ): Promise { - throw new Error("Websocket client does not implement subscribeResource"); - } - - /** - * @inheritdoc - */ - public async requestThingDescription(uri: string): Promise { - throw new Error("Method not implemented"); - } - - public async start(): Promise { - // do nothing - } - - public async stop(): Promise { - // do nothing - } - - public setSecurity(metadata: Array, credentials?: unknown): boolean { - if (metadata === undefined || !Array.isArray(metadata) || metadata.length === 0) { - warn("WebSocketClient received empty security metadata"); - return false; - } - // TODO support for multiple security schemes (see http-client.ts) - const security: SecurityScheme = metadata[0]; - - debug(`WebSocketClient using security scheme '${security.scheme}'`); - return true; - } -} diff --git a/packages/binding-websockets/src/ws-server.ts b/packages/binding-websockets/src/ws-server.ts deleted file mode 100644 index 049428fe8..000000000 --- a/packages/binding-websockets/src/ws-server.ts +++ /dev/null @@ -1,319 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2018 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 - ********************************************************************************/ - -/** - * HTTP Server based on http - */ - -import * as http from "http"; -import * as https from "https"; -import * as url from "url"; -import * as fs from "fs"; -import * as net from "net"; - -import * as WebSocket from "ws"; -import { AddressInfo } from "net"; - -import * as TD from "@node-wot/td-tools"; -import { ProtocolServer, Servient, ExposedThing, ContentSerdes, Helpers, Content, createLoggers } from "@node-wot/core"; -import { HttpServer, HttpConfig } from "@node-wot/binding-http"; -import slugify from "slugify"; - -const { debug, info, error } = createLoggers("binding-websockets", "ws-server"); - -export default class WebSocketServer implements ProtocolServer { - public readonly scheme: string; - public readonly PROPERTY_DIR: string = "properties"; - public readonly ACTION_DIR: string = "actions"; - public readonly EVENT_DIR: string = "events"; - private readonly port: number = 8081; - private readonly address?: string = undefined; - private readonly ownServer: boolean = true; - private readonly httpServer: http.Server | https.Server; - - private readonly thingNames: Set = new Set(); - private readonly thingPaths: Map = new Map(); - private readonly socketServers: { [key: string]: WebSocket.Server } = {}; - - constructor(serverOrConfig: HttpServer | HttpConfig = {}) { - // FIXME instanceof did not work reliably - if (serverOrConfig instanceof HttpServer && typeof serverOrConfig.getServer === "function") { - this.ownServer = false; - this.httpServer = serverOrConfig.getServer(); - this.port = serverOrConfig.getPort(); - this.scheme = serverOrConfig.scheme === "https" ? "wss" : "ws"; - } else if (typeof serverOrConfig === "object") { - const config: HttpConfig = serverOrConfig; - // HttpConfig - if (config.port !== undefined) { - this.port = config.port; - } - if (config.address !== undefined) { - this.address = config.address; - } - - // TLS - if (config.serverKey != null && config.serverCert != null) { - const options = { - key: fs.readFileSync(config.serverKey), - cert: fs.readFileSync(config.serverCert), - }; - this.scheme = "wss"; - this.httpServer = https.createServer(options); - } else { - this.scheme = "ws"; - this.httpServer = http.createServer(); - } - } else { - throw new Error(`WebSocketServer constructor argument must be HttpServer, HttpConfig, or undefined`); - } - } - - public start(servient: Servient): Promise { - debug(`WebSocketServer starting on ${this.address !== undefined ? this.address + " " : ""}port ${this.port}`); - return new Promise((resolve, reject) => { - // handle incoming WebSocket connections - this.httpServer.on("upgrade", (request, socket, head) => { - const pathname = new url.URL(request.url ?? "", `${this.scheme}://${request.headers.host}`).pathname; - - const socketServer = this.socketServers[pathname]; - - if (socketServer != null) { - socketServer.handleUpgrade(request, socket as net.Socket /* fix me */, head, (ws) => { - socketServer.emit("connection", ws, request); - }); - } else { - socket.destroy(); - } - }); - - if (this.ownServer) { - this.httpServer.once("error", (err: Error) => { - reject(err); - }); - this.httpServer.once("listening", () => { - // once started, console "handles" errors - this.httpServer.on("error", (err: Error) => { - error(`WebSocketServer on port ${this.port} failed: ${err.message}`); - }); - resolve(); - }); - this.httpServer.listen(this.port, this.address); - } else { - resolve(); - } - }); - } - - public stop(): Promise { - debug(`WebSocketServer stopping on port ${this.port}`); - return new Promise((resolve, reject) => { - for (const pathSocket in this.socketServers) { - this.socketServers[pathSocket].close(); - } - - // stop promise handles all errors from now on - if (this.ownServer) { - debug("WebSocketServer stopping own HTTP server"); - this.httpServer.once("error", (err: Error) => { - reject(err); - }); - this.httpServer.once("close", () => { - resolve(); - }); - this.httpServer.close(); - } - }); - } - - public getPort(): number { - if (this.httpServer.address() != null && typeof this.httpServer.address() === "object") { - return (this.httpServer.address()).port; - } else { - // includes typeof "string" case, which is only for unix sockets - return -1; - } - } - - public expose(thing: ExposedThing): Promise { - let urlPath = slugify(thing.title, { lower: true }); - - if (this.thingNames.has(urlPath)) { - urlPath = Helpers.generateUniqueName(urlPath); - } - - if (this.getPort() !== -1) { - debug(`WebSocketServer on port ${this.getPort()} exposes '${thing.title}' as unique '/${urlPath}/*'`); - - this.thingNames.add(urlPath); - this.thingPaths.set(thing.id, urlPath); - - // TODO more efficient routing to ExposedThing without ResourceListeners in each server - - for (const propertyName in thing.properties) { - const path = - "/" + - encodeURIComponent(urlPath) + - "/" + - this.PROPERTY_DIR + - "/" + - encodeURIComponent(propertyName); - const property = thing.properties[propertyName]; - - // Populate forms related to the property - for (const address of Helpers.getAddresses()) { - const href = `${this.scheme}://${address}:${this.getPort()}${path}`; - const form = new TD.Form(href, ContentSerdes.DEFAULT); - const ops = []; - - const writeOnly: boolean = property.writeOnly ?? false; - const readOnly: boolean = property.readOnly ?? false; - - if (!writeOnly) { - ops.push("readproperty", "observeproperty", "unobserveproperty"); - } - if (!readOnly) { - ops.push("writeproperty"); - } - form.op = ops; - thing.properties[propertyName].forms.push(form); - debug(`WebSocketServer on port ${this.getPort()} assigns '${href}' to Property '${propertyName}'`); - } - - debug(`WebSocketServer on port ${this.getPort()} adding socketServer for '${path}'`); - this.socketServers[path] = new WebSocket.Server({ noServer: true }); - this.socketServers[path].on("connection", (ws, req) => { - debug( - `WebSocketServer on port ${this.getPort()} received connection for '${path}' from ${Helpers.toUriLiteral( - req.socket.remoteAddress - )}:${req.socket.remotePort}` - ); - - const observeListener = async (content: Content) => { - debug(`WebSocketServer on port ${this.getPort()} publishing to property '${propertyName}' `); - - for await (const chunk of content.body) { - ws.send(chunk); - } - }; - - const writeOnly: boolean = property.writeOnly ?? false; - if (writeOnly) { - for (let formIndex = 0; formIndex < thing.properties[propertyName].forms.length; formIndex++) { - thing - .handleObserveProperty(propertyName, observeListener, { formIndex }) - .catch((err: Error) => ws.close(-1, err.message)); - } - } - - ws.on("close", () => { - for (let formIndex = 0; formIndex < thing.properties[propertyName].forms.length; formIndex++) { - thing.handleUnobserveProperty(propertyName, observeListener, { formIndex }); - } - debug( - `WebSocketServer on port ${this.getPort()} closed connection for '${path}' from ${Helpers.toUriLiteral( - req.socket.remoteAddress - )}:${req.socket.remotePort}` - ); - }); - }); - } - - for (const actionName in thing.actions) { - const path = - "/" + encodeURIComponent(urlPath) + "/" + this.ACTION_DIR + "/" + encodeURIComponent(actionName); - // eslint-disable-next-line unused-imports/no-unused-vars - const action = thing.actions[actionName]; - - for (const address of Helpers.getAddresses()) { - const href = `${this.scheme}://${address}:${this.getPort()}${path}`; - const form = new TD.Form(href, ContentSerdes.DEFAULT); - form.op = ["invokeaction"]; - thing.actions[actionName].forms.push(form); - debug(`WebSocketServer on port ${this.getPort()} assigns '${href}' to Action '${actionName}'`); - } - } - - for (const eventName in thing.events) { - const path = - "/" + encodeURIComponent(urlPath) + "/" + this.EVENT_DIR + "/" + encodeURIComponent(eventName); - // eslint-disable-next-line unused-imports/no-unused-vars - const event = thing.events[eventName]; - - // Populate forms related to the event - for (const address of Helpers.getAddresses()) { - const href = `${this.scheme}://${address}:${this.getPort()}${path}`; - const form = new TD.Form(href, ContentSerdes.DEFAULT); - form.op = "subscribeevent"; - event.forms.push(form); - debug(`WebSocketServer on port ${this.getPort()} assigns '${href}' to Event '${eventName}'`); - } - - debug(`WebSocketServer on port ${this.getPort()} adding socketServer for '${path}'`); - this.socketServers[path] = new WebSocket.Server({ noServer: true }); - this.socketServers[path].on("connection", (ws, req) => { - debug( - `WebSocketServer on port ${this.getPort()} received connection for '${path}' from ${Helpers.toUriLiteral( - req.socket.remoteAddress - )}:${req.socket.remotePort}` - ); - - const eventListener = async (content: Content) => { - for await (const chunk of content.body) { - ws.send(chunk); - } - }; - - for (let formIndex = 0; formIndex < event.forms.length; formIndex++) { - thing - .handleSubscribeEvent(eventName, eventListener, { formIndex }) - .catch((err: Error) => ws.close(-1, err.message)); - } - - ws.on("close", () => { - for (let formIndex = 0; formIndex < event.forms.length; formIndex++) { - thing.handleUnsubscribeEvent(eventName, eventListener, { formIndex }); - } - debug( - `WebSocketServer on port ${this.getPort()} closed connection for '${path}' from ${Helpers.toUriLiteral( - req.socket.remoteAddress - )}:${req.socket.remotePort}` - ); - }); - }); - } - } - return new Promise((resolve, reject) => { - resolve(); - }); - } - - public destroy(thingId: string): Promise { - debug(`WebSocketServer on port ${this.getPort()} destroying thingId '${thingId}'`); - return new Promise((resolve, reject) => { - let removedThing = false; - for (const name of Array.from(this.thingPaths.keys())) { - const thingPath = this.thingPaths.get(name) as string; - removedThing = this.thingNames.delete(thingPath); - } - if (removedThing) { - info(`WebSocketServer successfully destroyed '${thingId}'`); - } else { - info(`WebSocketServer failed to destroy thing with thingId '${thingId}'`); - } - resolve(removedThing !== undefined); - }); - } -} diff --git a/packages/binding-websockets/src/ws.ts b/packages/binding-websockets/src/ws.ts deleted file mode 100644 index c277a8f60..000000000 --- a/packages/binding-websockets/src/ws.ts +++ /dev/null @@ -1,23 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2018 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 { default as WebSocketServer } from "./ws-server"; -export { default as WebSocketClient } from "./ws-client"; -export { default as WebSocketClientFactory } from "./ws-client-factory"; -export { default as WebSocketSecureClientFactory } from "./wss-client-factory"; -export * from "./ws-server"; -export * from "./ws-client"; -export * from "./ws-client-factory"; -export * from "./wss-client-factory"; diff --git a/packages/binding-websockets/src/wss-client-factory.ts b/packages/binding-websockets/src/wss-client-factory.ts deleted file mode 100644 index a87747e2b..000000000 --- a/packages/binding-websockets/src/wss-client-factory.ts +++ /dev/null @@ -1,45 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2018 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 - ********************************************************************************/ - -/** - * HTTPS client Factory - */ - -import { ProtocolClientFactory, ProtocolClient } from "@node-wot/core"; - -export default class WssClientFactory implements ProtocolClientFactory { - public readonly scheme: string = "wss"; - - // eslint-disable-next-line no-useless-constructor - constructor() { - // TODO: implement and remove eslint-ignore-useless-constructor - } - - public getClient(): ProtocolClient { - throw new Error("WssClientFactory for 'wss' is not implemented"); - } - - public init(): boolean { - // info(`HttpsClientFactory for '${HttpsClientFactory.scheme}' initializing`); - // TODO uncomment info if something is executed here - return true; - } - - public destroy(): boolean { - // info(`HttpsClientFactory for '${HttpsClientFactory.scheme}' destroyed`); - // TODO uncomment info if something is executed here - return true; - } -} diff --git a/packages/binding-websockets/test/tsconfig.json b/packages/binding-websockets/test/tsconfig.json deleted file mode 100644 index 786b1b616..000000000 --- a/packages/binding-websockets/test/tsconfig.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "extends": "../tsconfig.json", - "compilerOptions": { - "rootDir": ".." - }, - "include": ["*.ts", "**/*.ts", "../src/**/*.ts"] -} diff --git a/packages/binding-websockets/test/ws-tests.ts b/packages/binding-websockets/test/ws-tests.ts deleted file mode 100644 index 5a132cbbd..000000000 --- a/packages/binding-websockets/test/ws-tests.ts +++ /dev/null @@ -1,44 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2018 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 - ********************************************************************************/ - -/** - * Protocol test suite to test protocol implementations - */ - -import Servient, { createLoggers } from "@node-wot/core"; -import { suite, test } from "@testdeck/mocha"; -import { expect, should } from "chai"; -import WebSocketServer from "../src/ws-server"; - -const { info } = createLoggers("binding-websockets", "ws-tests"); - -// should must be called to augment all variables -should(); - -const port = 31080; -@suite("WebSockets binding") -class WebSocketsTest { - @test async "should start and stop own server"() { - const wsServer = new WebSocketServer({ port }); - - await wsServer.start(new Servient()); - expect(wsServer.getPort()).to.eq(port); // from test - - info("Test stopping WebSocket server"); - - await wsServer.stop(); - expect(wsServer.getPort()).to.eq(-1); // from getPort() when not listening - } -} diff --git a/packages/binding-websockets/tsconfig.json b/packages/binding-websockets/tsconfig.json deleted file mode 100644 index e9fa7c062..000000000 --- a/packages/binding-websockets/tsconfig.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "extends": "../../tsconfig.json", - "compilerOptions": { - "outDir": "dist", - "rootDir": "src" - }, - "include": ["src/**/*"], - "references": [{ "path": "../td-tools" }, { "path": "../core" }] -} diff --git a/packages/browser-bundle/README.md b/packages/browser-bundle/README.md deleted file mode 100644 index 17afe7e2b..000000000 --- a/packages/browser-bundle/README.md +++ /dev/null @@ -1,40 +0,0 @@ -# Browser Bundle of node-wot - -Bundle to run node-wot as browser-side library. Note, this will only include the node-wot in client mode with limited binding support. - -Current Maintainer(s): [@relu91](https://github.com/relu91) [@danielpeintner](https://github.com/danielpeintner) - -## Supported bindings - -HTTP / HTTPS / WebSockets - -## Embedding node-wot library in HTML - -Include the following script tag in your html - -```js - -``` - -You can access all node-wot functionality through the "Wot" global object: - -```js -var servient = new Wot.Core.Servient(); -var client = new Wot.Http.HttpClient(); -``` - -## Using node-wot browser bundle library in web frameworks (e.g., Angular) - -Install browser-bundle in your project by running - -- `npm install @node-wot/browser-bundle` - -## Example and live demo - -An example of how to use node-wot as a browser-side library can be found under `https://github.com/eclipse-thingweb/node-wot/blob/master/examples/browser/index.html`. -To run it live, open [`examples/browser/index.html`](http://plugfest.thingweb.io/webui/) in a modern browser, -and consume the test Thing available under `http://plugfest.thingweb.io:8083/testthing` to interact with it. - -## More Details - -See diff --git a/packages/browser-bundle/build.js b/packages/browser-bundle/build.js deleted file mode 100644 index 430ecc2bb..000000000 --- a/packages/browser-bundle/build.js +++ /dev/null @@ -1,34 +0,0 @@ -const browserify = require("browserify"); -const builtIns = require("browserify/lib/builtins"); - -const stream = require.resolve("readable-stream4/lib/stream"); -const _stream_duplex = require.resolve("readable-stream4/lib/_stream_duplex"); -const _stream_passthrough = require.resolve("readable-stream4/lib/_stream_passthrough"); -const _stream_readable = require.resolve("readable-stream4/lib/_stream_readable"); -const _stream_transform = require.resolve("readable-stream4/lib/_stream_transform"); -const _stream_writable = require.resolve("readable-stream4/lib/_stream_writable"); - -const fs = require("fs"); - -const builder = browserify({ - builtins: { - ...builtIns, - stream, - _stream_duplex, - _stream_passthrough, - _stream_readable, - _stream_transform, - _stream_writable, - }, -}); - -builder.add("./index.js"); - -const bundleStream = builder.bundle(); - -if (!fs.existsSync("./dist")) { - fs.mkdirSync("./dist"); -} - -const output = fs.createWriteStream("./dist/wot-bundle.min.js"); -bundleStream.pipe(output, { end: true }); diff --git a/packages/browser-bundle/index.js b/packages/browser-bundle/index.js deleted file mode 100644 index 6867756ca..000000000 --- a/packages/browser-bundle/index.js +++ /dev/null @@ -1,13 +0,0 @@ -"use strict"; - -var Wot = {}; -Wot.Tools = require("@node-wot/td-tools"); -Wot.Core = require("@node-wot/core"); -Wot.Http = require("@node-wot/binding-http"); -Wot.WebSocket = require("@node-wot/binding-websockets"); - -if (typeof window !== "undefined") { - window.Wot = Wot; -} else if (typeof module !== "undefined" && module.exports) { - module.exports = Wot; -} diff --git a/packages/browser-bundle/package.json b/packages/browser-bundle/package.json deleted file mode 100644 index 9b018ca07..000000000 --- a/packages/browser-bundle/package.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "name": "@node-wot/browser-bundle", - "version": "0.8.15", - "description": "A node-wot bundle that can run in a web browser", - "author": "Eclipse Thingweb (https://thingweb.io/)", - "license": "EPL-2.0 OR W3C-20150513", - "repository": "https://github.com/eclipse-thingweb/node-wot/tree/master/packages/browser-bundle", - "publishConfig": { - "access": "public" - }, - "files": [ - "dist/" - ], - "main": "dist/wot-bundle.min.js", - "devDependencies": { - "@node-wot/binding-http": "0.8.15", - "@node-wot/binding-websockets": "0.8.15", - "@node-wot/core": "0.8.15", - "@node-wot/td-tools": "0.8.15", - "browserify": "^17.0.0", - "readable-stream4": "npm:readable-stream@^4.0.0" - }, - "scripts": { - "build": "node build.js", - "format": "prettier --write index.js \"**/*.json\"" - }, - "bugs": { - "url": "https://github.com/eclipse-thingweb/node-wot/issues" - }, - "homepage": "https://github.com/eclipse-thingweb/node-wot/tree/master/packages/browser-bundle#readme", - "keywords": [], - "dependencies": { - "events": "^2.1.0", - "inherits": "^2.0.3", - "resolve": "^1.1.7" - } -} diff --git a/packages/cli/.eslintrc.json b/packages/cli/.eslintrc.json deleted file mode 100644 index c4237119e..000000000 --- a/packages/cli/.eslintrc.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "extends": "../../.eslintrc.js" -} diff --git a/packages/cli/README.md b/packages/cli/README.md deleted file mode 100644 index 9dcfac40f..000000000 --- a/packages/cli/README.md +++ /dev/null @@ -1,159 +0,0 @@ -# Command-line interface (CLI) of node-wot - -Current Maintainer(s): [@relu91](https://github.com/relu91) [@danielpeintner](https://github.com/danielpeintner) [@mkovatsc](https://github.com/mkovatsc) - -## Getting Started - -### Installation - -- `npm install @node-wot/cli` -- You can alternatively install the node-wot CLI, either globally (`npm i @node-wot/cli -g`) or as - a (dev) dependency (`npm i @node-wot/cli --save` or `npm i @node-wot/cli --save-dev`). - -### Usage - -With the CLI, you don't need to specify any further node-wot dependencies and can implement your application -(e.g., `main.js`) without explicitly requiring node-wot dependencies: - -```JavaScript -// No need to require node-wot components -// WoT runtime is provided as global object - -WoT.produce({/*.....*/}) -``` - -If the CLI is globally installed, you don't need to set up a Node.js project. -If you do so, anyway, you can specify the entry point as follows: - -```JavaScript -"scripts":{ - "start": "wot-servient main.js" -} -``` - -There are several ways to start the application: - -a. Execute `npm start`. -b. Execute `./node_modules/.bin/wot-servient main.js`. -c. Execute `node ./node_modules/@node-wot/cli/dist/cli.js main.js`. -d. If you have installed `@node-wot/cli` globally you can even start the application right -away using this command `wot-servient main.js`. However, in the current implementation, the -import of local dependencies is not supported in this case. - -wot-servient can execute multiple files at once, for example as follows: - -``` -wot-servient script1.js ./src/script2.js -``` - -### Configuration - -The `-h` option explains the functionality and also how node-wot can be configured based on `wot-servient.conf.json`. - -- `wot-servient -h` _or_ -- `node packages\cli\dist\cli.js` - -The `-h` help option shows the following output: - -``` -Usage: wot-servient [options] [files...] - - -Run a WoT Servient in the current directory. - - -Arguments: - files script files to execute. If no script is given, all .js files in the current directory are - loaded. If one or more script is given, these files are loaded instead of the directory. - -Options: - -v, --version display node-wot version - -i, --inspect [host]:[port] activate inspector on host:port (default: 127.0.0.1:9229) - -ib, --inspect-brk [host]:[port] activate inspector on host:port (default: 127.0.0.1:9229) - -c, --client-only do not start any servers (enables multiple instances without port conflicts) - -cp, --compiler load module as a compiler - -f, --config-file load configuration from specified file (default: "wot-servient.conf.json") - -p, --config-params override configuration parameters [key1:=value1 key2:=value2 ...] (e.g. http.port:=8080) - -h, --help show this help - -wot-servient.conf.json syntax: -{ - "servient": { - "clientOnly": CLIENTONLY, - "staticAddress": STATIC, - "scriptAction": RUNSCRIPT - }, - "http": { - "port": HPORT, - "proxy": PROXY, - "allowSelfSigned": ALLOW - }, - "mqtt" : { - "broker": BROKER-URL, - "username": BROKER-USERNAME, - "password": BROKER-PASSWORD, - "clientId": BROKER-UNIQUEID, - "protocolVersion": MQTT_VERSION - }, - "credentials": { - THING_ID1: { - "token": TOKEN - }, - THING_ID2: { - "username": USERNAME, - "password": PASSWORD - } - } -} - -wot-servient.conf.json fields: - CLIENTONLY : boolean setting if no servers shall be started (default=false) - STATIC : string with hostname or IP literal for static address config - RUNSCRIPT : boolean to activate the 'runScript' Action (default=false) - HPORT : integer defining the HTTP listening port - PROXY : object with "href" field for the proxy URI, - "scheme" field for either "basic" or "bearer", and - corresponding credential fields as defined below - ALLOW : boolean whether self-signed certificates should be allowed - BROKER-URL : URL to an MQTT broker that publisher and subscribers will use - BROKER-UNIQUEID : unique id set by MQTT client while connecting to the broker - MQTT_VERSION : number indicating the MQTT protocol version to be used (3, 4, or 5) - THING_IDx : string with TD "id" for which credentials should be configured - TOKEN : string for providing a Bearer token - USERNAME : string for providing a Basic Auth username - PASSWORD : string for providing a Basic Auth password - --------------------------------------------------------------------------- - -Environment variables must be provided in a .env file in the current working directory. - -Example: -VAR1=Value1 -VAR2=Value2 -``` - -Additionally, you can look at [the JSON Schema](https://github.com/eclipse-thingweb/node-wot/blob/master/packages/cli/src/wot-servient-schema.conf.json) to understand possible values for each field. - -> In the current implementation, the **middleware** option (that you can use to handle raw HTTP requests _before_ they hit the Servient) is only available when using the `@node-wot/binding-http` package as a library. See [Adding a middleware](../binding-http/README.md#adding-a-middleware) for more information. - -### Environment variables - -If your scripts needs to access environment variables those must be supplied in a particular file. Node-wot cli uses [dotenv](https://github.com/motdotla/dotenv) library to load `.env` files located at the current working directory. For example, providing the following `.env` file will fill variables `PORT` and `ADDRESS` in scripts `process.env` field: - -``` -PORT=4242 -ADDRESS=http://hello.com -``` - -### Debugging - -To debug, use the option `--inspect` or `--inspect-brk` if you want to hang until your debug client is connected. Then start [Chrome Dev Tools](chrome://inspect) or [vscode debugger](https://code.visualstudio.com/docs/nodejs/nodejs-debugging#_attaching-to-nodejs) or your preferred v8 inspector to debug your code. - -For further details check: `wot-servient --help` - -### Examples - -See [node-wot examples using Node.js](https://github.com/eclipse-thingweb/node-wot/#no-time-for-explanations---show-me-a-running-example). - -## More Details - -See diff --git a/packages/cli/bin/index.js b/packages/cli/bin/index.js deleted file mode 100755 index 0f6de6471..000000000 --- a/packages/cli/bin/index.js +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env node -// WARNING: this is a workaround for issue https://github.com/npm/cli/issues/2632 -require("../dist/cli.js"); diff --git a/packages/cli/package.json b/packages/cli/package.json deleted file mode 100644 index e945860f8..000000000 --- a/packages/cli/package.json +++ /dev/null @@ -1,51 +0,0 @@ -{ - "name": "@node-wot/cli", - "version": "0.8.15", - "description": "servient command line interface", - "author": "Eclipse Thingweb (https://thingweb.io/)", - "license": "EPL-2.0 OR W3C-20150513", - "repository": "https://github.com/eclipse-thingweb/node-wot/tree/master/packages/cli", - "publishConfig": { - "access": "public" - }, - "files": [ - "dist/" - ], - "main": "bin/index.js", - "types": "dist/cli.d.ts", - "bin": { - "wot-servient": "bin/index.js" - }, - "optionalDependencies": { - "ts-node": "10.9.1" - }, - "dependencies": { - "@node-wot/binding-coap": "0.8.15", - "@node-wot/binding-file": "0.8.15", - "@node-wot/binding-http": "0.8.15", - "@node-wot/binding-mqtt": "0.8.15", - "@node-wot/binding-websockets": "0.8.15", - "@node-wot/core": "0.8.15", - "@node-wot/td-tools": "0.8.15", - "@types/lodash": "^4.14.199", - "ajv": "^8.11.0", - "commander": "^9.1.0", - "dotenv": "^8.6.0", - "lodash": "^4.17.21", - "vm2": "3.9.18" - }, - "scripts": { - "build": "tsc -b", - "start": "ts-node src/cli.ts", - "debug": "node -r ts-node/register --inspect-brk=9229 src/cli.ts", - "lint": "eslint .", - "lint:fix": "eslint . --fix", - "format": "prettier --write \"src/**/*.ts\" \"**/*.json\"", - "test": "mocha --require ts-node/register --extension ts" - }, - "bugs": { - "url": "https://github.com/eclipse-thingweb/node-wot/issues" - }, - "homepage": "https://github.com/eclipse-thingweb/node-wot/tree/master/packages/cli#readme", - "keywords": [] -} diff --git a/packages/cli/src/cli-default-servient.ts b/packages/cli/src/cli-default-servient.ts deleted file mode 100644 index 5f10eaa30..000000000 --- a/packages/cli/src/cli-default-servient.ts +++ /dev/null @@ -1,410 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -/******************************************************************************** - * Copyright (c) 2018 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 - ********************************************************************************/ - -// global W3C WoT Scripting API definitions -import * as WoT from "wot-typescript-definitions"; -// node-wot implementation of W3C WoT Servient -import { Servient, Helpers, createLoggers } from "@node-wot/core"; -// protocols used -import { HttpServer, HttpClientFactory, HttpsClientFactory } from "@node-wot/binding-http"; -// import { WebSocketServer } from "@node-wot/binding-websockets"; -import { CoapServer, CoapClientFactory, CoapsClientFactory } from "@node-wot/binding-coap"; -import { MqttBrokerServer, MqttClientFactory } from "@node-wot/binding-mqtt"; -import { FileClientFactory } from "@node-wot/binding-file"; -import { CompilerFunction, NodeVM } from "vm2"; -import { ThingModelHelpers } from "@node-wot/td-tools"; - -const { debug, error, info } = createLoggers("cli", "cli-default-servient"); - -// Helper function needed for `mergeConfigs` function -function isObject(item: unknown) { - return item != null && typeof item === "object" && !Array.isArray(item); -} - -/** - * Helper function merging default parameters into a custom config file. - * - * @param {object} target - an object containing default config parameters - * @param {object} source - an object containing custom config parameters - * - * @return {object} The new config file containing both custom and default parameters - */ -function mergeConfigs(target: any, source: any): any { - const output = Object.assign({}, target); - Object.keys(source).forEach((key) => { - if (!(key in target)) { - Object.assign(output, { [key]: source[key] }); - } else { - if (isObject(target[key]) && isObject(source[key])) { - output[key] = mergeConfigs(target[key], source[key]); - } else { - Object.assign(output, { [key]: source[key] }); - } - } - }); - return output; -} -export interface ScriptOptions { - argv?: Array; - compiler?: CompilerFunction; - env?: Record; -} -export default class DefaultServient extends Servient { - private static readonly defaultConfig = { - servient: { - clientOnly: false, - scriptAction: false, - }, - http: { - port: 8080, - allowSelfSigned: false, - }, - coap: { - port: 5683, - }, - log: { - level: "info", - }, - }; - - private uncaughtListeners: Array = []; - private runtime: typeof WoT | undefined; - public readonly config: any; - // current log level - public logLevel = "info"; - - // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types - public constructor(clientOnly: boolean, config?: any) { - super(); - - // init config - this.config = - typeof config === "object" - ? mergeConfigs(DefaultServient.defaultConfig, config) - : DefaultServient.defaultConfig; - - // apply flags - if (clientOnly) { - this.config.servient ??= {}; - this.config.servient.clientOnly = true; - } - - // set log level before any output - this.setLogLevel(this.config.log.level); - - // load credentials from config - this.addCredentials(this.config.credentials); - - // remove secrets from original for displaying config (already added) - if (this.config.credentials != null) { - delete this.config.credentials; - } - - // display - debug("DefaultServient configured with"); - debug(`${this.config}`); - - // apply config - if (typeof this.config.servient.staticAddress === "string") { - Helpers.setStaticAddress(this.config.servient.staticAddress); - } - - let coapServer: CoapServer | undefined; - if (this.config.servient.clientOnly === false) { - if (this.config.http != null) { - const httpServer = new HttpServer(this.config.http); - this.addServer(httpServer); - - // re-use httpServer (same port) - // this.addServer(new WebSocketServer(httpServer)); - } - const coapConfig = this.config.coap; - if (coapConfig != null) { - coapServer = new CoapServer(coapConfig); - this.addServer(coapServer); - } - if (this.config.mqtt != null) { - const mqttBrokerServer = new MqttBrokerServer({ - uri: this.config.mqtt.broker, - user: typeof this.config.mqtt.username === "string" ? this.config.mqtt.username : undefined, - psw: typeof this.config.mqtt.password === "string" ? this.config.mqtt.password : undefined, - clientId: typeof this.config.mqtt.clientId === "string" ? this.config.mqtt.clientId : undefined, - protocolVersion: - typeof this.config.mqtt.protocolVersion === "number" - ? this.config.mqtt.protocolVersion - : undefined, - }); - this.addServer(mqttBrokerServer); - } - } - - this.addClientFactory(new FileClientFactory()); - this.addClientFactory(new HttpClientFactory(this.config.http)); - this.addClientFactory(new HttpsClientFactory(this.config.http)); - this.addClientFactory(new CoapClientFactory(coapServer)); - this.addClientFactory(new CoapsClientFactory()); - this.addClientFactory(new MqttClientFactory()); - } - - /** - * Runs the script in a new sandbox - * @param {string} code - the script to run - * @param {string} filename - the filename of the script - */ - public runScript(code: string, filename = "script"): unknown { - if (!this.runtime) { - throw new Error("WoT runtime not loaded; have you called start()?"); - } - const helpers = new Helpers(this); - const context = { - WoT: this.runtime, - WoTHelpers: helpers, - ModelHelpers: new ThingModelHelpers(helpers), - }; - - const vm = new NodeVM({ - sandbox: context, - }); - - const listener = (err: Error) => { - this.logScriptError(`Asynchronous script error '${filename}'`, err); - // TODO: clean up script resources - process.exit(1); - }; - process.prependListener("uncaughtException", listener); - this.uncaughtListeners.push(listener); - - try { - return vm.run(code, filename); - } catch (err) { - if (err instanceof Error) { - this.logScriptError(`Servient found error in script '${filename}'`, err); - } else { - error(`Servient found error in script '${filename}' ${err}`); - } - return undefined; - } - } - - /** - * Runs the script in privileged context (dangerous). In practice, this means that the script can - * require system modules. - * @param {string} code - the script to run - * @param {string} filename - the filename of the script - * @param {object} options - pass cli variables or envs to the script - */ - public runPrivilegedScript(code: string, filename = "script", options: ScriptOptions = {}): unknown { - if (!this.runtime) { - throw new Error("WoT runtime not loaded; have you called start()?"); - } - const helpers = new Helpers(this); - const context = { - WoT: this.runtime, - WoTHelpers: helpers, - ModelHelpers: new ThingModelHelpers(helpers), - }; - - const vm = new NodeVM({ - sandbox: context, - require: { - external: true, - builtin: ["*"], - }, - argv: options.argv, - compiler: options.compiler, - env: options.env, - }); - - const listener = (err: Error) => { - this.logScriptError(`Asynchronous script error '${filename}'`, err); - // TODO: clean up script resources - process.exit(1); - }; - process.prependListener("uncaughtException", listener); - this.uncaughtListeners.push(listener); - - try { - return vm.run(code, filename); - } catch (err) { - if (err instanceof Error) { - this.logScriptError(`Servient found error in privileged script '${filename}'`, err); - } else { - error(`Servient found error in privileged script '${filename}' ${err}`); - } - return undefined; - } - } - - private logScriptError(description: string, err: Error): void { - let message: string; - if (typeof err === "object" && err.stack != null) { - const match = err.stack.match(/evalmachine\.:([0-9]+:[0-9]+)/); - if (Array.isArray(match)) { - message = `and halted at line ${match[1]}\n ${err}`; - } else { - message = `and halted with ${err.stack}`; - } - } else { - message = `that threw ${typeof err} instead of Error\n ${err}`; - } - error(`Servient caught ${description} ${message}`); - } - - /** - * start - */ - public start(): Promise { - return new Promise((resolve, reject) => { - super - .start() - .then((myWoT) => { - info("DefaultServient started"); - this.runtime = myWoT; - // TODO think about builder pattern that starts with produce() ends with expose(), which exposes/publishes the Thing - myWoT - .produce({ - title: "servient", - description: "node-wot CLI Servient", - properties: { - things: { - type: "object", - description: "Get things", - observable: false, - readOnly: true, - }, - }, - actions: { - setLogLevel: { - description: "Set log level", - input: { oneOf: [{ type: "string" }, { type: "number" }] }, - output: { type: "string" }, - }, - shutdown: { - description: "Stop servient", - output: { type: "string" }, - }, - runScript: { - description: "Run script", - input: { type: "string" }, - output: { type: "string" }, - }, - }, - }) - .then((thing) => { - thing.setActionHandler("setLogLevel", async (level) => { - const ll = await Helpers.parseInteractionOutput(level); - if (typeof ll === "number") { - this.setLogLevel(ll as number); - } else if (typeof ll === "string") { - this.setLogLevel(ll as string); - } else { - // try to convert it to strings - this.setLogLevel(ll + ""); - } - return `Log level set to '${this.logLevel}'`; - }); - thing.setActionHandler("shutdown", async () => { - debug("shutting down by remote"); - await this.shutdown(); - return undefined; - }); - thing.setActionHandler("runScript", async (script) => { - const scriptv = await Helpers.parseInteractionOutput(script); - debug("running script", scriptv); - this.runScript(scriptv as string); - return undefined; - }); - thing.setPropertyReadHandler("things", async () => { - debug("returning things"); - return this.getThings(); - }); - thing - .expose() - .then(() => { - // pass on WoTFactory - resolve(myWoT); - }) - .catch((err) => reject(err)); - }); - }) - .catch((err) => reject(err)); - }); - } - - public async shutdown(): Promise { - await super.shutdown(); - - this.uncaughtListeners.forEach((listener) => { - process.removeListener("uncaughtException", listener); - }); - } - - // Save default loggers (needed when changing log levels) - private readonly loggers: any = { - warn: console.warn, - info: console.info, - debug: console.debug, - }; - - private setLogLevel(logLevel: string | number): void { - if (logLevel === "error" || logLevel === 0) { - console.warn = () => { - /* nothing */ - }; - console.info = () => { - /* nothing */ - }; - console.debug = () => { - /* nothing */ - }; - - this.logLevel = "error"; - } else if (logLevel === "warn" || logLevel === "warning" || logLevel === 1) { - console.warn = this.loggers.warn; - console.info = () => { - /* nothing */ - }; - console.debug = () => { - /* nothing */ - }; - - this.logLevel = "warn"; - } else if (logLevel === "info" || logLevel === 2) { - console.warn = this.loggers.warn; - console.info = this.loggers.info; - console.debug = () => { - /* nothing */ - }; - - this.logLevel = "info"; - } else if (logLevel === "debug" || logLevel === 3) { - console.warn = this.loggers.warn; - console.info = this.loggers.info; - console.debug = this.loggers.debug; - - this.logLevel = "debug"; - } else { - // Fallback to default ("info") - console.warn = this.loggers.warn; - console.info = this.loggers.info; - console.debug = () => { - /* nothing */ - }; - - this.logLevel = "info"; - } - } -} diff --git a/packages/cli/src/cli.ts b/packages/cli/src/cli.ts deleted file mode 100644 index d1604b901..000000000 --- a/packages/cli/src/cli.ts +++ /dev/null @@ -1,397 +0,0 @@ -#!/usr/bin/env node -/******************************************************************************** - * Copyright (c) 2018 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 - ********************************************************************************/ - -// default implementation of W3C WoT Servient (http(s) and file bindings) -import DefaultServient from "./cli-default-servient"; -import ErrnoException = NodeJS.ErrnoException; - -// tools -import fs = require("fs"); -import * as dotenv from "dotenv"; -import * as path from "path"; -import { Command, InvalidArgumentError, Argument } from "commander"; -import Ajv, { ValidateFunction, ErrorObject } from "ajv"; -import ConfigSchema from "./wot-servient-schema.conf.json"; -import _ from "lodash"; -import { version } from "@node-wot/core/package.json"; -import { createLoggers } from "@node-wot/core"; -import inspector from "inspector"; - -const { error, info, warn } = createLoggers("cli", "cli"); - -const program = new Command(); -const ajv = new Ajv({ strict: true }); -const schemaValidator = ajv.compile(ConfigSchema) as ValidateFunction; -const defaultFile = "wot-servient.conf.json"; -const baseDir = "."; - -const dotEnvConfigParameters: DotEnvConfigParameter = {}; - -// General commands -program - .name("wot-servient") - .description( - ` -Run a WoT Servient in the current directory. - ` - ) - .helpOption("-h, --help", "show this help") - .version(version, "-v, --version", "display node-wot version"); - -// Help infos -program.addHelpText( - "after", - ` -wot-servient.conf.json syntax: -{ - "servient": { - "clientOnly": CLIENTONLY, - "staticAddress": STATIC, - "scriptAction": RUNSCRIPT - }, - "http": { - "port": HPORT, - "address": HADDRESS, - "baseUri": HBASEURI, - "urlRewrite": HURLREWRITE, - "proxy": PROXY, - "allowSelfSigned": ALLOW - }, - "mqtt" : { - "broker": BROKER-URL, - "username": BROKER-USERNAME, - "password": BROKER-PASSWORD, - "clientId": BROKER-UNIQUEID, - "protocolVersion": MQTT_VERSION - }, - "credentials": { - THING_ID1: { - "token": TOKEN - }, - THING_ID2: { - "username": USERNAME, - "password": PASSWORD - } - } -} - -wot-servient.conf.json fields: - CLIENTONLY : boolean setting if no servers shall be started (default=false) - STATIC : string with hostname or IP literal for static address config - RUNSCRIPT : boolean to activate the 'runScript' Action (default=false) - HPORT : integer defining the HTTP listening port - HADDRESS : string defining HTTP address - HBASEURI : string defining HTTP base URI - HURLREWRITE : map (from URL -> to URL) defining HTTP URL rewrites - PROXY : object with "href" field for the proxy URI, - "scheme" field for either "basic" or "bearer", and - corresponding credential fields as defined below - ALLOW : boolean whether self-signed certificates should be allowed - BROKER-URL : URL to an MQTT broker that publisher and subscribers will use - BROKER-UNIQUEID : unique id set by MQTT client while connecting to the broker - MQTT_VERSION : number indicating the MQTT protocol version to be used (3, 4, or 5) - THING_IDx : string with TD "id" for which credentials should be configured - TOKEN : string for providing a Bearer token - USERNAME : string for providing a Basic Auth username - PASSWORD : string for providing a Basic Auth password - --------------------------------------------------------------------------- - -Environment variables must be provided in a .env file in the current working directory. - -Example: -VAR1=Value1 -VAR2=Value2` -); - -// Typings -type DotEnvConfigParameter = { - [key: string]: unknown; -}; -interface DebugParams { - shouldBreak: boolean; - host: string; - port: number; -} - -// Parsers & validators -function parseIp(value: string, previous: string) { - if (!/^([a-z]*|[\d.]*)(:[0-9]{2,5})?$/.test(value)) { - throw new InvalidArgumentError("Invalid host:port combo"); - } - - return value; -} -function parseConfigFile(filename: string, previous: string) { - try { - const open = filename || path.join(baseDir, defaultFile); - const data = fs.readFileSync(open, "utf-8"); - if (!schemaValidator(JSON.parse(data))) { - throw new InvalidArgumentError( - `Config file contains invalid an JSON: ${(schemaValidator.errors ?? []) - .map((o: ErrorObject) => o.message) - .join("\n")}` - ); - } - return filename; - } catch (err) { - throw new InvalidArgumentError(`Error reading config file: ${err}`); - } -} -function parseConfigParams(param: string, previous: unknown) { - // Validate key-value pair - if (!/^([a-zA-Z0-9_.]+):=([a-zA-Z0-9_]+)$/.test(param)) { - throw new InvalidArgumentError("Invalid key-value pair"); - } - const fieldNamePath = param.split(":=")[0]; - const fieldNameValue = param.split(":=")[1]; - let fieldNameValueCast; - if (Number(fieldNameValue)) { - fieldNameValueCast = +fieldNameValue; - } else if (fieldNameValue === "true" || fieldNameValue === "false") { - fieldNameValueCast = Boolean(fieldNameValue); - } else { - fieldNameValueCast = fieldNamePath; - } - - // Build object using dot-notation JSON path - const obj = _.set({}, fieldNamePath, fieldNameValueCast); - if (!schemaValidator(obj)) { - throw new InvalidArgumentError( - `Config parameter '${param}' is not valid: ${(schemaValidator.errors ?? []) - .map((o: ErrorObject) => o.message) - .join("\n")}` - ); - } - // Concatenate validated parameters - let result = previous ?? {}; - result = _.merge(result, obj); - return result; -} - -// CLI options declaration -program - .option("-i, --inspect [host]:[port]", "activate inspector on host:port (default: 127.0.0.1:9229)", parseIp) - .option("-ib, --inspect-brk [host]:[port]", "activate inspector on host:port (default: 127.0.0.1:9229)", parseIp) - .option("-c, --client-only", "do not start any servers (enables multiple instances without port conflicts)") - .option("-cp, --compiler ", "load module as a compiler") - .option( - "-f, --config-file ", - "load configuration from specified file", - parseConfigFile, - "wot-servient.conf.json" - ) - .option( - "-p, --config-params ", - "override configuration parameters [key1:=value1 key2:=value2 ...] (e.g. http.port:=8080)", - parseConfigParams - ); - -// CLI arguments -program.addArgument( - new Argument( - "[files...]", - "script files to execute. If no script is given, all .js files in the current directory are loaded. If one or more script is given, these files are loaded instead of the directory." - ) -); - -program.parse(process.argv); -const options = program.opts(); -const args = program.args; - -// .env parsing -const env: dotenv.DotenvConfigOutput = dotenv.config(); -const errorNoException: ErrnoException | undefined = env.error; -if (errorNoException?.code !== "ENOENT") { - throw env.error; -} else if (env.parsed) { - for (const [key, value] of Object.entries(env.parsed)) { - // Parse and validate on configfile-related entries - if (key.startsWith("config.")) { - dotEnvConfigParameters[key.replace("config.", "")] = value; - } - } -} - -// Functions -async function buildConfig(): Promise { - const fileToOpen = options?.configFile ?? path.join(baseDir, defaultFile); - let configFileData = {}; - - // JSON config file - try { - configFileData = JSON.parse(await fs.promises.readFile(fileToOpen, "utf-8")); - } catch (err) { - error(`WoT-Servient config file error: ${err}`); - } - - // .env file - for (const [key, value] of Object.entries(dotEnvConfigParameters)) { - const obj = _.set({}, key, value); - configFileData = _.merge(configFileData, obj); - } - - // CLI arguments - if (options?.configParams != null) { - configFileData = _.merge(configFileData, options.configParams); - } - - return configFileData; -} -const loadCompilerFunction = function (compilerModule: string | undefined) { - if (compilerModule != null) { - // eslint-disable-next-line @typescript-eslint/no-var-requires - const compilerMod = require(compilerModule); - - if (compilerMod.create == null) { - throw new Error("No create function defined for " + compilerModule); - } - - const compilerObject = compilerMod.create(); - - if (compilerObject.compile == null) { - throw new Error("No compile function defined for create return object"); - } - return compilerObject.compile; - } - return undefined; -}; -const loadEnvVariables = function () { - const env: dotenv.DotenvConfigOutput = dotenv.config(); - - const errorNoException: ErrnoException | undefined = env.error; - // ignore file not found but throw otherwise - if (errorNoException?.code !== "ENOENT") { - throw env.error; - } - return env; -}; - -const runScripts = async function (servient: DefaultServient, scripts: Array, debug?: DebugParams) { - const env = loadEnvVariables(); - - const launchScripts = (scripts: Array) => { - const compile = loadCompilerFunction(options.compiler); - scripts.forEach((fname: string) => { - info(`WoT-Servient reading script ${fname}`); - fs.readFile(fname, "utf8", (err, data) => { - if (err) { - error(`WoT-Servient experienced error while reading script. ${err}`); - } else { - // limit printout to first line - info( - `WoT-Servient running script '${data.substr(0, data.indexOf("\n")).replace("\r", "")}'... (${ - data.split(/\r\n|\r|\n/).length - } lines)` - ); - - fname = path.resolve(fname); - servient.runPrivilegedScript(data, fname, { - argv: args, - env: env.parsed, - compiler: compile, - }); - } - }); - }); - }; - - // eslint-disable-next-line @typescript-eslint/no-var-requires - if (debug && debug.shouldBreak) { - // Activate inspector only if is not already opened and wait for the debugger to attach - inspector.url() == null && inspector.open(debug.port, debug.host, true); - - // Set a breakpoint at the first line of of first script - // the breakpoint gives time to inspector clients to set their breakpoints - const session = new inspector.Session(); - session.connect(); - session.post("Debugger.enable", (error: Error) => { - if (error != null) { - warn("Cannot set breakpoint; reason: cannot enable debugger"); - warn(error.toString()); - } - - session.post( - "Debugger.setBreakpointByUrl", - { - lineNumber: 0, - url: "file:///" + path.resolve(scripts[0]).replace(/\\/g, "/"), - }, - (err: Error | null) => { - if (err != null) { - warn("Cannot set breakpoint"); - warn(error.toString()); - } - launchScripts(scripts); - } - ); - }); - } else { - // Activate inspector only if is not already opened and don't wait - debug != null && inspector.url() == null && inspector.open(debug.port, debug.host, false); - launchScripts(scripts); - } -}; - -const runAllScripts = function (servient: DefaultServient, debug?: DebugParams) { - fs.readdir(baseDir, (err, files) => { - if (err) { - warn(`WoT-Servient experienced error while loading directory. ${err}`); - return; - } - - // unhidden .js files - const scripts = files.filter((file) => { - return file.substr(0, 1) !== "." && file.slice(-3) === ".js"; - }); - info(`WoT-Servient using current directory with ${scripts.length} script${scripts.length > 1 ? "s" : ""}`); - - runScripts( - servient, - scripts.map((filename) => path.resolve(path.join(baseDir, filename))), - debug - ); - }); -}; - -buildConfig() - .then((conf) => { - return new DefaultServient(options.clientOnly, conf); - }) - .catch((err) => { - if (err.code === "ENOENT" && options.configFile == null) { - warn(`WoT-Servient using defaults as '${defaultFile}' does not exist`); - return new DefaultServient(options.clientOnly); - } else { - error(`"WoT-Servient config file error. ${err}`); - process.exit(err.errno); - } - }) - .then((servient) => { - servient - .start() - .then(() => { - if (args.length > 0) { - info(`WoT-Servient loading ${args.length} command line script${args.length > 1 ? "s" : ""}`); - return runScripts(servient, args, options.inspect ?? options.inspectBrk); - } else { - return runAllScripts(servient, options.inspect ?? options.inspectBrk); - } - }) - .catch((err) => { - error(`WoT-Servient cannot start. ${err}`); - }); - }) - .catch((err) => error(`WoT-Servient main error. ${err}`)); diff --git a/packages/cli/src/wot-servient-schema.conf.json b/packages/cli/src/wot-servient-schema.conf.json deleted file mode 100644 index 95123eb04..000000000 --- a/packages/cli/src/wot-servient-schema.conf.json +++ /dev/null @@ -1,142 +0,0 @@ -{ - "definitions": {}, - "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", - "properties": { - "servient": { - "type": "object", - "properties": { - "clientOnly": { - "type": "boolean", - "default": false - }, - "staticAddress": { - "type": "string" - }, - "scriptAction": { - "type": "boolean", - "default": false - } - } - }, - "http": { - "type": "object", - "properties": { - "port": { - "type": "integer" - }, - "address": { - "type": "string" - }, - "baseUri": { - "type": "string" - }, - "urlRewrite": { - "type": "object", - "additionalProperties": { "type": "string" } - }, - "proxy": { - "type": "object", - "required": ["href"], - "properties": { - "href": { - "type": "string" - }, - "scheme": { - "enum": ["basic", "bearer"] - }, - "token": { - "type": "string" - }, - "username": { - "type": "string" - }, - "password": { - "type": "string" - } - } - }, - "allowSelfSigned": { - "type": "boolean" - }, - "serverKey": { - "type": "string" - }, - "serverCert": { - "type": "string" - } - } - }, - "mqtt": { - "type": "object", - "properties": { - "broker": { - "type": "string" - }, - "username": { - "type": "string" - }, - "password": { - "type": "string" - }, - "clientId": { - "type": "string" - }, - "protocolVersion": { - "type": "integer", - "examples": [3, 4, 5], - "default": 5 - } - } - }, - "coap": { - "type": "object", - "properties": { - "port": { - "type": "integer" - } - } - }, - "credentials": { - "type": "object", - "patternProperties": { - "^THING_ID([a-zA-Z0-9_]+)$": { - "type": "object", - "properties": { - "token": { - "type": "string" - }, - "username": { - "type": "string" - }, - "password": { - "type": "string" - } - } - } - } - }, - "log": { - "type": "object", - "oneOf": [ - { - "properties": { - "level": { - "type": "integer", - "enum": [0, 1, 2, 3] - } - } - }, - { - "properties": { - "level": { - "type": "string", - "enum": ["debug", "info", "warn", "error"] - } - } - } - ] - } - }, - "additionalProperties": false -} diff --git a/packages/cli/test/RuntimeTest.ts b/packages/cli/test/RuntimeTest.ts deleted file mode 100644 index 46e5d21d5..000000000 --- a/packages/cli/test/RuntimeTest.ts +++ /dev/null @@ -1,186 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2022 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 - ********************************************************************************/ - -/** - * Basic test suite to demonstrate test setup - * uncomment the @skip to see failing tests - * - * h0ru5: there is currently some problem with VSC failing to recognize experimentalDecorators option, it is present in both tsconfigs - */ - -import { suite, test } from "@testdeck/mocha"; -import { should, assert } from "chai"; -import DefaultServient from "../src/cli-default-servient"; - -import fs from "fs"; -// should must be called to augment all variables -should(); - -@suite("Test suite for script runtime") -class WoTRuntimeTest { - static servient: DefaultServient; - - static WoT: typeof WoT; - - exit: (code?: number) => never = () => { - throw new Error(""); - }; - - static async before() { - // We need to disable this check for killing servient logs - /* eslint-disable @typescript-eslint/no-empty-function */ - console.error = () => {}; - console.debug = () => {}; - console.warn = () => {}; - console.info = () => {}; - /* eslint-enable @typescript-eslint/no-empty-function */ - this.servient = new DefaultServient(true); - await this.servient.start(); - } - - beforeEach() { - this.exit = process.exit; - } - - afterEach() { - process.exit = this.exit; - } - - static async after(): Promise { - await this.servient.shutdown(); - } - - @test "should provide cli args"() { - const envScript = `module.exports = process.argv[0]`; - - const test = WoTRuntimeTest.servient.runPrivilegedScript(envScript, undefined, { argv: ["myArg"] }); - assert.equal(test, "myArg"); - } - - @test "should use the compiler function"() { - const envScript = `this is not js`; - - const test = WoTRuntimeTest.servient.runPrivilegedScript(envScript, undefined, { - compiler: () => { - return "module.exports = 'ok'"; - }, - }); - assert.equal(test, "ok"); - } - - @test "should provide env variables"() { - const envScript = `module.exports = process.env.MY_VAR`; - const test = WoTRuntimeTest.servient.runPrivilegedScript(envScript, undefined, { env: { MY_VAR: "test" } }); - assert.equal(test, "test"); - } - - @test "should hide system env variables"() { - const envScript = `module.exports = process.env.OS`; - - const test = WoTRuntimeTest.servient.runPrivilegedScript(envScript); - assert.equal(test, undefined); - } - - @test "should require node builtin module"() { - const envScript = `module.exports = require("fs")`; - - const test = WoTRuntimeTest.servient.runPrivilegedScript(envScript); - assert.equal(test, fs); - } - - @test "should catch synchronous errors"() { - const failNowScript = `throw new Error("Synchronous error in Servient sandbox");`; - - assert.doesNotThrow(() => { - WoTRuntimeTest.servient.runScript(failNowScript); - }); - assert.doesNotThrow(() => { - WoTRuntimeTest.servient.runPrivilegedScript(failNowScript); - }); - } - - @test "should catch bad errors"() { - const failNowScript = `throw "Bad synchronous error in Servient sandbox";`; - - assert.doesNotThrow(() => { - WoTRuntimeTest.servient.runScript(failNowScript); - }); - assert.doesNotThrow(() => { - WoTRuntimeTest.servient.runPrivilegedScript(failNowScript); - }); - } - - @test "should catch bad asynchronous errors for runScript"(done: Mocha.Done) { - // Mocha does not like string errors: https://github.com/trufflesuite/ganache-cli/issues/658 - // so here I am removing its listeners for uncaughtException. - // WARNING: Remove this line as soon the issue is resolved. - const listeners = this.clearUncaughtListeners(); - let called = false; - - this.mockupProcessExitWithFunction(() => { - if (!called) { - done(); - this.restoreUncaughtListeners(listeners); - called = true; - } - }); - - const failThenScript = `setTimeout( () => { throw "Bad asynchronous error in Servient sandbox"; }, 1);`; - - assert.doesNotThrow(() => { - WoTRuntimeTest.servient.runScript(failThenScript); - }); - } - - @test "should catch bad asynchronous errors for runPrivilegedScript"(done: Mocha.Done) { - // Mocha does not like string errors: https://github.com/trufflesuite/ganache-cli/issues/658 - // so here I am removing its listeners for uncaughtException. - // WARNING: Remove this line as soon the issue is resolved. - const listeners = this.clearUncaughtListeners(); - let called = false; - - this.mockupProcessExitWithFunction(() => { - if (!called) { - done(); - this.restoreUncaughtListeners(listeners); - called = true; - } - }); - - const failThenScript = `setTimeout( () => { throw "Bad asynchronous error in Servient sandbox"; }, 1);`; - assert.doesNotThrow(() => { - WoTRuntimeTest.servient.runPrivilegedScript(failThenScript); - }); - } - - private mockupProcessExitWithFunction(func: () => void) { - // Mockup is needed cause servient will call process.exit() - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - process.exit = func; - } - - private clearUncaughtListeners() { - const listeners = process.listeners("uncaughtException"); - process.removeAllListeners("uncaughtException"); - return listeners; - } - - private restoreUncaughtListeners(listeners: Array) { - listeners.forEach((element) => { - process.on("uncaughtException", element); - }); - } -} diff --git a/packages/cli/test/tsconfig.json b/packages/cli/test/tsconfig.json deleted file mode 100644 index 786b1b616..000000000 --- a/packages/cli/test/tsconfig.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "extends": "../tsconfig.json", - "compilerOptions": { - "rootDir": ".." - }, - "include": ["*.ts", "**/*.ts", "../src/**/*.ts"] -} diff --git a/packages/cli/tsconfig.json b/packages/cli/tsconfig.json deleted file mode 100644 index 2a18482f3..000000000 --- a/packages/cli/tsconfig.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "extends": "../../tsconfig.json", - "compilerOptions": { - "outDir": "dist", - "rootDir": "src", - "resolveJsonModule": true, - "esModuleInterop": true - }, - "include": ["src/**/*", "src/*.json"], - "references": [ - { "path": "../td-tools" }, - { "path": "../core" }, - { "path": "../binding-http" }, - { "path": "../binding-file" }, - { "path": "../binding-websockets" }, - { "path": "../binding-coap" }, - { "path": "../binding-mqtt" } - ] -} diff --git a/packages/cli/wot-servient.conf.json.md b/packages/cli/wot-servient.conf.json.md deleted file mode 100644 index fd95d2280..000000000 --- a/packages/cli/wot-servient.conf.json.md +++ /dev/null @@ -1,69 +0,0 @@ -# Configuration of WoT Servient - -File "wot-servient.conf.json" - -``` -{ - "servient": { - "clientOnly": CLIENTONLY, - "staticAddress": STATIC, - "scriptAction": RUNSCRIPT - }, - "http": { - "port": HPORT, - "proxy": PROXY, - "allowSelfSigned": ALLOW - }, - "mqtt" : { - "broker": BROKER-URL, - "username": BROKER-USERNAME, - "password": BROKER-PASSWORD, - "clientId": BROKER-UNIQUEID, - "protocolVersion": MQTT_VERSION - }, - "log": { - "level": LEVEL - }, - "credentials": { - THING_ID1: { - "token": TOKEN - }, - THING_ID2: { - "username": USERNAME, - "password": PASSWORD - } - } -} -``` - -CLIENTONLY is a boolean telling whether to start server part or not - -STATIC is a string indicating a static hostname / IP address - -RUNSCRIPT is a boolean indicating whether to provide the 'runScript' Action - -HPORT is a number defining the HTTP listening port - -PROXY is an object with `"href"` for the proxy URI, `"authorization"` for `"Basic"` or `"Bearer"`, and then corresponding credential fields `"username"`/`"password"` or `"token"` as defined below - -ALLOW is a boolean indicating whether self-signed certificates should be allowed - -BROKER-URL is a string indicating the MQTT broker URL with optional port number (default :1883) - -BROKER-USERNAME is a string indicating the MQTT broker username - -BROKER-PASSWORD is a string indicating the MQTT broker password - -BROKER-UNIQUEID is a string indicating an optional MQTT broker unique ID - -MQTT_VERSION is a number indicating the MQTT protocol version to be used (3, 4, or 5) - -LEVEL is a string or number to set the logging level: `{ error: 0, warn: 1, info: 2, debug: 3 }` (default `info`) - -THING_IDx is a TD @id for which credentials should be configured - -TOKEN is an OAuth (Bearer) token - -USERNAME is an HTTP Basic Auth username - -PASSWORD is an HTTP Basic Auth password diff --git a/packages/examples/.eslintrc.json b/packages/examples/.eslintrc.json deleted file mode 100644 index c4237119e..000000000 --- a/packages/examples/.eslintrc.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "extends": "../../.eslintrc.js" -} diff --git a/packages/examples/README.md b/packages/examples/README.md deleted file mode 100644 index 6f0bc04b0..000000000 --- a/packages/examples/README.md +++ /dev/null @@ -1,33 +0,0 @@ -# node-wot Examples - -## Script Examples - -Script examples are located in - -- `src\scripts` -- `src\testthing` - -The idea of these folders is to use TypeScript to work on the examples which offers support in being up-to-date with the current API. - -see https://github.com/eclipse-thingweb/node-wot/issues/171. - -### Workflow - -1. Run `npm run build` -2. Remove the following 3/4 lines in JS files of folder `dist/` - -``` -Object.defineProperty(exports, "__esModule", { value: true }); -require("wot-typescript-definitions"); -let WoT; -let WoTHelpers; -``` - -3. Copy the according JS file(s) to - -- `/examples/scripts` -- `/examples/testthing` - -## Test Thing - -Test thing and client is located in `src\testthing`. diff --git a/packages/examples/package.json b/packages/examples/package.json deleted file mode 100644 index 8df22de40..000000000 --- a/packages/examples/package.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "name": "@node-wot/examples", - "version": "0.8.15", - "private": true, - "description": "Examples for node-wot (not published)", - "author": "Eclipse Thingweb (https://thingweb.io/)", - "license": "EPL-2.0 OR W3C-20150513", - "repository": "https://github.com/eclipse-thingweb/node-wot/tree/master/packages/examples", - "dependencies": { - "@node-wot/binding-coap": "0.8.15", - "@node-wot/binding-file": "0.8.15", - "@node-wot/binding-http": "0.8.15", - "@node-wot/binding-mqtt": "0.8.15", - "@node-wot/binding-opcua": "0.8.15", - "@node-wot/core": "0.8.15", - "@node-wot/td-tools": "0.8.15", - "rxjs": "5.5.11" - }, - "scripts": { - "build": "tsc -b", - "lint": "eslint .", - "lint:fix": "eslint . --fix", - "format": "prettier --write \"src/**/*.ts\" \"**/*.json\"" - }, - "bugs": { - "url": "https://github.com/eclipse-thingweb/node-wot/issues" - }, - "homepage": "https://github.com/eclipse-thingweb/node-wot/tree/master/packages/examples#readme", - "main": "index.js", - "keywords": [] -} diff --git a/packages/examples/src/bindings/coap/example-client.ts b/packages/examples/src/bindings/coap/example-client.ts deleted file mode 100644 index 6e03307fd..000000000 --- a/packages/examples/src/bindings/coap/example-client.ts +++ /dev/null @@ -1,39 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2022 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 } from "@node-wot/core"; -import { CoapClientFactory } from "@node-wot/binding-coap"; - -// create Servient and add CoAP binding -const servient = new Servient(); -servient.addClientFactory(new CoapClientFactory()); - -servient - .start() - .then(async (WoT) => { - try { - const td = await WoT.requestThingDescription("coap://plugfest.thingweb.io:5683/testthing"); - const thing = await WoT.consume(td); - - // read property - const read1 = await thing.readProperty("string"); - console.log("string value is: ", await read1.value()); - } catch (err) { - console.error("Script error:", err); - } - }) - .catch((err) => { - console.error("Start error:", err); - }); diff --git a/packages/examples/src/bindings/coap/example-server.ts b/packages/examples/src/bindings/coap/example-server.ts deleted file mode 100644 index 4e7f528f9..000000000 --- a/packages/examples/src/bindings/coap/example-server.ts +++ /dev/null @@ -1,50 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2022 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 } from "@node-wot/core"; -import { CoapServer } from "@node-wot/binding-coap"; - -// create Servient add HTTP binding -const servient = new Servient(); -servient.addServer(new CoapServer()); - -servient.start().then((WoT) => { - WoT.produce({ - title: "MyCounter", - properties: { - count: { - type: "integer", - }, - }, - }).then((thing) => { - let count = 0; - - console.log("Produced " + thing.getThingDescription().title); - - // set property handlers (using async-await) - thing.setPropertyReadHandler("count", async () => count); - thing.setPropertyWriteHandler("count", async (intOutput) => { - const value = await intOutput.value(); - if (typeof value === "number") { - count = value; - } - }); - - thing.expose().then(() => { - console.info(thing.getThingDescription().title + " ready"); - console.info("TD : " + JSON.stringify(thing.getThingDescription())); - }); - }); -}); diff --git a/packages/examples/src/bindings/http/example-client.ts b/packages/examples/src/bindings/http/example-client.ts deleted file mode 100644 index 417f83de3..000000000 --- a/packages/examples/src/bindings/http/example-client.ts +++ /dev/null @@ -1,39 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2022 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 } from "@node-wot/core"; -import { HttpClientFactory } from "@node-wot/binding-http"; - -// create Servient and add HTTP binding -const servient = new Servient(); -servient.addClientFactory(new HttpClientFactory()); - -servient - .start() - .then(async (WoT) => { - try { - const td = await WoT.requestThingDescription("http://plugfest.thingweb.io:8083/testthing"); - const thing = await WoT.consume(td); - - // read property - const read1 = await thing.readProperty("string"); - console.log("string value is: ", await read1.value()); - } catch (err) { - console.error("Script error:", err); - } - }) - .catch((err) => { - console.error("Start error:", err); - }); diff --git a/packages/examples/src/bindings/http/example-server-secure.ts b/packages/examples/src/bindings/http/example-server-secure.ts deleted file mode 100644 index ee69cd675..000000000 --- a/packages/examples/src/bindings/http/example-server-secure.ts +++ /dev/null @@ -1,69 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2022 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 } from "@node-wot/core"; -import { HttpServer } from "@node-wot/binding-http"; - -// create secure Servient with username & password credentials -const servient = new Servient(); -servient.addCredentials({ - "urn:dev:wot:org:eclipse:thingweb:my-example-secure": { - username: "node-wot", - password: "hello", - // token: "1/mZ1edKKACtPAb7zGlwSzvs72PvhAbGmB8K1ZrGxpcNM" - }, -}); -const httpConfig = { - allowSelfSigned: true, // client configuration - serverKey: "privatekey.pem", - serverCert: "certificate.pem", - security: [ - { - scheme: "basic", // (username & password) - }, - ], -}; -// add HTTPS binding with configuration -servient.addServer(new HttpServer(httpConfig)); - -servient.start().then((WoT) => { - WoT.produce({ - title: "MyCounter", - properties: { - count: { - type: "integer", - }, - }, - }).then((thing) => { - // init property value - let count = 0; - - console.log("Produced " + thing.getThingDescription().title); - - // set property handlers (using async-await) - thing.setPropertyReadHandler("count", async () => count); - thing.setPropertyWriteHandler("count", async (intOutput) => { - const value = await intOutput.value(); - if (typeof value === "number") { - count = value; - } - }); - - thing.expose().then(() => { - console.info(thing.getThingDescription().title + " ready"); - console.info("TD : " + JSON.stringify(thing.getThingDescription())); - }); - }); -}); diff --git a/packages/examples/src/bindings/http/example-server.ts b/packages/examples/src/bindings/http/example-server.ts deleted file mode 100644 index a1441017e..000000000 --- a/packages/examples/src/bindings/http/example-server.ts +++ /dev/null @@ -1,59 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2022 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 } from "@node-wot/core"; -import { HttpServer } from "@node-wot/binding-http"; - -// create Servient add HTTP binding with port configuration -const servient = new Servient(); -servient.addServer( - new HttpServer({ - port: 8081, // (default 8080) - urlRewrite: { - "/myroot/foo/cnt": "/mycounter/properties/count", - "/mysecondURL/for/the/same/resource": "/mycounter/properties/count", - }, - }) -); - -servient.start().then((WoT) => { - WoT.produce({ - title: "MyCounter", - properties: { - count: { - type: "integer", - }, - }, - }).then((thing) => { - // init property value - let count = 0; - - console.log("Produced " + thing.getThingDescription().title); - - // set property handlers (using async-await) - thing.setPropertyReadHandler("count", async () => count); - thing.setPropertyWriteHandler("count", async (intOutput) => { - const value = await intOutput.value(); - if (typeof value === "number") { - count = value; - } - }); - - thing.expose().then(() => { - console.info(thing.getThingDescription().title + " ready"); - console.info("TD : " + JSON.stringify(thing.getThingDescription())); - }); - }); -}); diff --git a/packages/examples/src/bindings/opcua/demo-opcua-thing-description.ts b/packages/examples/src/bindings/opcua/demo-opcua-thing-description.ts deleted file mode 100644 index d015cef1d..000000000 --- a/packages/examples/src/bindings/opcua/demo-opcua-thing-description.ts +++ /dev/null @@ -1,58 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2022 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 endpointUrl = "opc.tcp://opcuademo.sterfive.com:26543"; -export const thingDescription: WoT.ThingDescription = { - "@context": "https://www.w3.org/2019/wot/td/v1", - "@type": ["Thing"], - securityDefinitions: { - nosec_sc: { - scheme: "nosec", - }, - }, - security: "nosec_sc", - title: "servient", - description: "node-wot CLI Servient", - properties: { - pumpSpeed: { - description: "the pump speed", - observable: true, - readOnly: true, - unit: "m/s", - type: "number", - forms: [ - { - href: endpointUrl, - op: ["readproperty", "observeproperty"], - "opcua:nodeId": "ns=1;s=PumpSpeed", - }, - ], - }, - temperature: { - description: "the temperature", - observable: true, - readOnly: true, - unit: "m/s", - type: "number", - forms: [ - { - href: endpointUrl, - op: ["readproperty", "observeproperty"], - "opcua:nodeId": "ns=1;s=Temperature", - }, - ], - }, - }, -}; diff --git a/packages/examples/src/bindings/opcua/demo-opcua1.ts b/packages/examples/src/bindings/opcua/demo-opcua1.ts deleted file mode 100644 index 3f58c1e34..000000000 --- a/packages/examples/src/bindings/opcua/demo-opcua1.ts +++ /dev/null @@ -1,35 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2022 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 } from "@node-wot/core"; -import { OPCUAClientFactory } from "@node-wot/binding-opcua"; - -import { thingDescription } from "./demo-opcua-thing-description"; -(async () => { - const servient = new Servient(); - servient.addClientFactory(new OPCUAClientFactory()); - - const wot = await servient.start(); - const thing = await wot.consume(thingDescription); - - const content = await thing.readProperty("pumpSpeed"); - const pumpSpeed = await content.value(); - - console.log("------------------------------"); - console.log("Pump Speed is : ", pumpSpeed, "m/s"); - console.log("------------------------------"); - - await servient.shutdown(); -})(); diff --git a/packages/examples/src/bindings/opcua/demo-opcua2.ts b/packages/examples/src/bindings/opcua/demo-opcua2.ts deleted file mode 100644 index c1a95981c..000000000 --- a/packages/examples/src/bindings/opcua/demo-opcua2.ts +++ /dev/null @@ -1,36 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2022 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 } from "@node-wot/core"; -import { OPCUAClientFactory } from "@node-wot/binding-opcua"; -import { thingDescription } from "./demo-opcua-thing-description"; - -(async () => { - const servient = new Servient(); - servient.addClientFactory(new OPCUAClientFactory()); - - const wot = await servient.start(); - const thing = await wot.consume(thingDescription); - - thing.observeProperty("temperature", async (data) => { - const temperature = await data.value(); - console.log("------------------------------"); - console.log("temperature : ", temperature, "m/s"); - console.log("------------------------------"); - }); - - await new Promise((resolve) => setTimeout(resolve, 10000)); - - await servient.shutdown(); -})(); diff --git a/packages/examples/src/bindings/opcua/opcua-coffee-machine-demo.ts b/packages/examples/src/bindings/opcua/opcua-coffee-machine-demo.ts deleted file mode 100644 index 1596eac1a..000000000 --- a/packages/examples/src/bindings/opcua/opcua-coffee-machine-demo.ts +++ /dev/null @@ -1,50 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2022 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 } from "@node-wot/core"; -import { OPCUAClientFactory } from "@node-wot/binding-opcua"; -import { thingDescription } from "./opcua-coffee-machine-thing-description"; - -const pause = async (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); -(async () => { - const servient = new Servient(); - servient.addClientFactory(new OPCUAClientFactory()); - - const wot = await servient.start(); - const thing = await wot.consume(thingDescription); - - try { - thing.observeProperty("waterTankLevel", async (data) => { - const waterTankLevel = await data.value(); - console.log("------------------------------"); - console.log("tankLevel : ", waterTankLevel, "ml"); - console.log("------------------------------"); - }); - thing.observeProperty("coffeeBeanLevel", async (data) => { - const coffeBeanLevel = await data.value(); - console.log("------------------------------"); - console.log("bean level : ", coffeBeanLevel, "g"); - console.log("------------------------------"); - }); - await thing.invokeAction("brewCoffee", { CoffeeType: 1 }); - await pause(5000); - await thing.invokeAction("brewCoffee", { CoffeeType: 0 }); - await pause(5000); - - await thing.invokeAction("fillTank"); - await pause(5000); - } finally { - await servient.shutdown(); - } -})(); diff --git a/packages/examples/src/bindings/opcua/opcua-coffee-machine-thing-description.ts b/packages/examples/src/bindings/opcua/opcua-coffee-machine-thing-description.ts deleted file mode 100644 index 099a92566..000000000 --- a/packages/examples/src/bindings/opcua/opcua-coffee-machine-thing-description.ts +++ /dev/null @@ -1,112 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2022 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 endpointUrl = "opc.tcp://opcuademo.sterfive.com:26543"; -export const thingDescription: WoT.ThingDescription = { - "@context": "https://www.w3.org/2019/wot/td/v1", - "@type": ["Thing"], - securityDefinitions: { - nosec_sc: { - scheme: "nosec", - }, - }, - security: "nosec_sc", - title: "servient", - description: "node-wot CLI Servient", - properties: { - deviceHealth: { - // type: "number", - observable: true, - readOnly: true, - forms: [ - { - href: endpointUrl, - op: ["readproperty", "observeproperty"], - "opcua:nodeId": { - root: "i=84", - path: "/Objects/2:DeviceSet/2:DeviceHealth", - }, - }, - ], - }, - waterTankLevel: { - // type: "number", - observable: true, - readOnly: true, - forms: [ - { - href: endpointUrl, - op: ["readproperty", "observeproperty"], - "opcua:nodeId": { - root: "i=84", - path: "/Objects/2:DeviceSet/1:CoffeeMachine/2:ParameterSet/9:WaterTankLevel", - }, - }, - ], - }, - coffeeBeanLevel: { - // type: "number", - observable: true, - readOnly: true, - forms: [ - { - href: endpointUrl, - op: ["readproperty", "observeproperty"], - "opcua:nodeId": { - root: "i=84", - path: "/Objects/2:DeviceSet/1:CoffeeMachine/2:ParameterSet/9:CoffeeBeanLevel", - }, - }, - ], - }, - }, - actions: { - brewCoffee: { - forms: [ - { - type: "object", - href: endpointUrl, - op: ["invokeaction"], - "opcua:nodeId": { root: "i=84", path: "/Objects/2:DeviceSet/1:CoffeeMachine" }, - "opcua:method": { root: "i=84", path: "/Objects/2:DeviceSet/1:CoffeeMachine/2:MethodSet/9:Start" }, - }, - ], - input: { - type: "object", - properties: { - CoffeeType: { - title: "1 for Americano, 2 for Expressp", - type: "number", - }, - }, - required: ["CoffeeType"], - }, - }, - fillTank: { - forms: [ - { - type: "object", - href: endpointUrl, - op: ["invokeaction"], - "opcua:nodeId": { root: "i=84", path: "/Objects/2:DeviceSet/1:CoffeeMachine" }, - "opcua:method": { - root: "i=84", - path: "/Objects/2:DeviceSet/1:CoffeeMachine/2:MethodSet/9:FillTank", - }, - }, - ], - }, - }, -}; diff --git a/packages/examples/src/quickstart/README.md b/packages/examples/src/quickstart/README.md deleted file mode 100644 index 0183dad5f..000000000 --- a/packages/examples/src/quickstart/README.md +++ /dev/null @@ -1,10 +0,0 @@ -# Quick Start Things - -This set of Things can be used to build mashup applications for a smart home scenario. -These Things are: - -- Simple coffee machine that can take an order to brew a coffee. -- Presence sensor that emits an event when a person is detected. -- Smart clock that runs 60 times faster than real time, where 1 hour happens in 1 minute. - -These Things are hosted on plugfest.thingweb.io but can be also self-hosted. diff --git a/packages/examples/src/quickstart/presence-sensor.ts b/packages/examples/src/quickstart/presence-sensor.ts deleted file mode 100644 index cb6614e01..000000000 --- a/packages/examples/src/quickstart/presence-sensor.ts +++ /dev/null @@ -1,64 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 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 - ********************************************************************************/ - -// This is an example Thing script which is a simple presence detector -// It fires an event when it detects a person (mocked as every 5 second) - -import { Servient } from "@node-wot/core"; -import { MqttBrokerServer } from "@node-wot/binding-mqtt"; - -// create Servient add MQTT binding with port configuration -const servient = new Servient(); -servient.addServer(new MqttBrokerServer({ uri: "mqtt://test.mosquitto.org" })); - -servient.start().then((WoT) => { - WoT.produce({ - title: "PresenceSensor", - 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); - - // 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); - }); -}); diff --git a/packages/examples/src/quickstart/simple-coffee-machine.ts b/packages/examples/src/quickstart/simple-coffee-machine.ts deleted file mode 100644 index 69fdc7c2f..000000000 --- a/packages/examples/src/quickstart/simple-coffee-machine.ts +++ /dev/null @@ -1,213 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 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 - ********************************************************************************/ - -// This is an example Thing script which is a simple coffee machine. -// You can order coffee and see the status of the resources - -import { Servient, Helpers } from "@node-wot/core"; -import { HttpServer } from "@node-wot/binding-http"; - -// create Servient add HTTP binding with port configuration -const servient = new Servient(); - -// const staticAddress = "plugfest.thingweb.io"; -const staticAddress = "localhost"; // use this for testing locally -const httpPort = 8081; -servient.addServer( - new HttpServer({ - port: httpPort, - }) -); -Helpers.setStaticAddress(staticAddress); - -let waterAmount = 1000; -let beansAmount = 1000; -let milkAmount = 1000; - -// promisify timeout since it does not return a promise -function timeout(ms: number) { - return new Promise((resolve) => setTimeout(resolve, ms)); -} - -servient.start().then((WoT) => { - WoT.produce({ - title: "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: { - 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"], - }, - }, - }, - }, - 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.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; - }); - - // expose the thing - thing.expose().then(() => { - console.info(thing.getThingDescription().title + " ready"); - console.info("TD available at http://" + staticAddress + ":" + httpPort); - }); - }) - .catch((e) => { - console.log(e); - }); -}); diff --git a/packages/examples/src/quickstart/smart-clock.ts b/packages/examples/src/quickstart/smart-clock.ts deleted file mode 100644 index 2043dbcb1..000000000 --- a/packages/examples/src/quickstart/smart-clock.ts +++ /dev/null @@ -1,97 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 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 - ********************************************************************************/ - -// This is an example Thing which is a smart clock that runs 60 times faster than real time, where 1 hour happens in 1 minute. - -import { Servient, Helpers } from "@node-wot/core"; -import { CoapServer } from "@node-wot/binding-coap"; - -// create Servient add CoAP binding with port configuration -const servient = new Servient(); -servient.addServer(new CoapServer({ port: 5686 })); - -Helpers.setStaticAddress("plugfest.thingweb.io"); // comment this out if you are testing locally - -let minuteCounter = 0; -let hourCounter = 0; - -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, - }); - - hourCounter++; - if (hourCounter === 24) { - hourCounter = 0; - } -} - -servient.start().then((WoT) => { - WoT.produce({ - title: "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: { - minute: { - type: "integer", - minimum: 0, - maximum: 59, - }, - hour: { - type: "integer", - minimum: 0, - maximum: 23, - }, - }, - }, - }, - }) - .then(async (thing) => { - console.log("Produced " + thing.getThingDescription().title); - - 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 - - // expose the thing - thing.expose().then(() => { - console.info(thing.getThingDescription().title + " ready"); - }); - }) - .catch((e) => { - console.log(e); - }); -}); diff --git a/packages/examples/src/scripts/countdown.ts b/packages/examples/src/scripts/countdown.ts deleted file mode 100644 index b1e4e70b1..000000000 --- a/packages/examples/src/scripts/countdown.ts +++ /dev/null @@ -1,194 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2021 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 - ********************************************************************************/ - -function uuidv4(): string { - return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function (c) { - const r = (Math.random() * 16) | 0; - const v = c === "x" ? r : (r & 0x3) | 0x8; - return v.toString(16); - }); -} - -enum Status { - "pending", - "running", - "completed", - "failed", -} - -interface ActionStatus { - status: Status; - output?: number; // any - error?: Error; - href?: string; -} - -let countdowns: Map; - -WoT.produce({ - title: "countdown", - description: "Countdown example Thing", - support: "https://github.com/eclipse-thingweb/node-wot/", - properties: { - countdowns: { - type: "array", - items: { - type: "string", - }, - observable: true, - readOnly: true, - }, - }, - actions: { - startCountdown: { - description: "Start countdown in secs (default 100 secs)", - input: { - // optional init value - /* type: "integer" */ - oneOf: [{ type: "integer" }, {}], - }, - }, - stopCountdown: { - // SHOULD BE DELETE for WoT-Profile - description: "Stops countdown", - input: { - type: "string", - }, - }, - monitorCountdown: { - // SHOULD BE GET for WoT-Profile - description: "Reports current countdown status/value", - input: { - type: "string", - }, - output: { - type: "object", - }, - }, - }, -}) - .then((thing) => { - console.log("Produced " + thing.getThingDescription().title); - - // init property values and start update loop - countdowns = new Map(); - setInterval(() => { - if (countdowns.size > 0) { - console.log("Update countdowns"); - const listToDelete: string[] = []; - for (const id of countdowns.keys()) { - const as = countdowns.get(id); - if (as?.output !== undefined) { - const prev = as.output; - as.output--; - console.log("\t" + id + ", from " + prev + " to " + as.output); - - if (as.output > 0) { - as.status = Status.running; - } else { - as.status = Status.completed; - } - - if (as.output < -10) { - // remove from list after a while - listToDelete.push(id); - } - } - } - listToDelete.forEach((id) => { - console.log("Remove countdown for href = " + id); - countdowns.delete(id); - }); - } - }, 1000); - - // set property handlers (using async-await) - thing.setPropertyReadHandler("countdowns", async (options): Promise => { - const cts: string[] = []; - for (const id of countdowns.keys()) { - cts.push(id); - } - return cts; - }); - // set action handlers (using async-await) - thing.setActionHandler( - "startCountdown", - async (params: WoT.InteractionOutput, options): Promise => { - let initValue = 100; - if (params != null) { - const value = await params.value(); - if (typeof value === "number") { - initValue = value as number; - } - } - const resp: ActionStatus = { - href: uuidv4(), - output: initValue, - status: initValue > 0 ? Status.pending : Status.completed, - }; - const ii: WoT.InteractionInput = resp; - console.log("init countdown value = " + JSON.stringify(resp)); - countdowns.set(resp.href ?? "", resp); - return ii; - } - ); - thing.setActionHandler( - "stopCountdown", - async (params: WoT.InteractionOutput, options): Promise => { - if (params != null) { - const value = await params.value(); - if (typeof value === "string" && countdowns.has(value)) { - const as = countdowns.get(value); - if (as !== undefined) { - as.output = 0; - as.status = Status.completed; - console.log("Countdown stopped for href: " + value); - return null; - } else { - throw Error("Countdown value is undefined for href, " + value); - } - } else { - throw Error("Input provided for stopCountdown is no string or invalid href, " + value); - } - } else { - throw Error("No input specified for stopCountdown"); - } - } - ); - thing.setActionHandler( - "monitorCountdown", - async (params: WoT.InteractionOutput, options): Promise => { - if (params != null) { - const value = await params.value(); - if (typeof value === "string" && countdowns.has(value)) { - const as = countdowns.get(value); - return JSON.stringify(as); - } else { - throw Error("Input provided for monitorCountdown is no string or invalid href, " + value); - } - } else { - throw Error("No input specified for monitorCountdown"); - } - } - ); - - // expose the thing - thing.expose().then(() => { - console.info(thing.getThingDescription().title + " ready"); - }); - }) - .catch((e) => { - console.log(e); - }); diff --git a/packages/examples/src/scripts/counter-client.ts b/packages/examples/src/scripts/counter-client.ts deleted file mode 100644 index 6e143c617..000000000 --- a/packages/examples/src/scripts/counter-client.ts +++ /dev/null @@ -1,63 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2020 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 - ********************************************************************************/ - -function getFormIndexForDecrementWithCoAP(thing: WoT.ConsumedThing): number { - const forms = thing.getThingDescription().actions?.decrement.forms; - if (forms !== undefined) { - for (let i = 0; i < forms.length; i++) { - if (/^coaps?:\/\/.*/.test(forms[i].href)) { - return i; - } - } - } - // return formIndex: 0 if no CoAP target IRI found - return 0; -} - -WoT.requestThingDescription("coap://localhost:5683/counter") - .then(async (td) => { - try { - const thing = await WoT.consume(td); - console.info("=== TD ==="); - console.info(td); - console.info("=========="); - - // read property #1 - const read1 = await thing.readProperty("count"); - console.log("count value is", await read1.value()); - - // increment property #1 (without step) - await thing.invokeAction("increment"); - const inc1 = await thing.readProperty("count"); - console.info("count value after increment #1 is", await inc1.value()); - - // increment property #2 (with step) - await thing.invokeAction("increment", undefined, { uriVariables: { step: 3 } }); - const inc2 = await thing.readProperty("count"); - console.info("count value after increment #2 (with step 3) is", await inc2.value()); - - // look for the first form for decrement with CoAP binding - await thing.invokeAction("decrement", undefined, { - formIndex: getFormIndexForDecrementWithCoAP(thing), - }); - const dec1 = await thing.readProperty("count"); - console.info("count value after decrement is", await dec1.value()); - } catch (err) { - console.error("Script error:", err); - } - }) - .catch((err) => { - console.error("Fetch error:", err); - }); diff --git a/packages/examples/src/scripts/counter.ts b/packages/examples/src/scripts/counter.ts deleted file mode 100644 index 6b561b657..000000000 --- a/packages/examples/src/scripts/counter.ts +++ /dev/null @@ -1,283 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2020 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 - ********************************************************************************/ - -// This is an example Thing script. -// It has a count property that can be incremented or decremented via actions and its changes are reported via events. -// It also has two properties that return an image. The SVG property is also influenced by the increment and decrement actions. -// Features -// * basic properties, actions, events -// * local/global uriVariables -// * multi-language -// * image contentTypes for properties (Note: the contentType applies to all forms of the property) -// * links with entry containing rel and sizes - -let count: number; -let lastChange: string; - -WoT.produce({ - title: "Counter", - titles: { - en: "Counter", - de: "Zähler", - it: "Contatore", - }, - description: "Counter example Thing", - descriptions: { - en: "Counter example Thing", - de: "Zähler Beispiel Ding", - it: "Contatore di esempio", - }, - support: "https://github.com/eclipse-thingweb/node-wot/", - links: [ - { - href: "https://www.thingweb.io/img/favicon/favicon.png", - sizes: "16x14", - rel: "icon", - }, - ], - "@context": [ - "https://www.w3.org/2019/wot/td/v1", - "https://www.w3.org/2022/wot/td/v1.1", - { - iot: "http://example.org/iot", - }, - ], - uriVariables: { - step: { - type: "integer", - minimum: 1, - maximum: 250, - }, - }, - properties: { - count: { - title: "Count", - titles: { - en: "Count", - de: "Zähler", - it: "Valore", - }, - type: "integer", - description: "Current counter value", - descriptions: { - en: "Current counter value", - de: "Derzeitiger Zählerwert", - it: "Valore attuale del contatore", - }, - "iot:Custom": "example annotation", - observable: true, - readOnly: true, - }, - countAsImage: { - description: "Current counter value as SVG image", - descriptions: { - en: "Current counter value as SVG image", - de: "Aktueller Zählerwert als SVG-Bild", - it: "Valore attuale del contatore come immagine SVG", - }, - observable: false, - readOnly: true, - uriVariables: { - fill: { - type: "string", - }, - }, - forms: [ - { - contentType: "image/svg+xml", - }, - ], - }, - redDotImage: { - description: "Red dot image as PNG", - descriptions: { - en: "Red dot image as PNG", - de: "Rotes Punktbild als PNG", - it: "Immagine punto rosso come PNG", - }, - observable: false, - readOnly: true, - forms: [ - { - contentType: "image/png;base64", - }, - ], - }, - lastChange: { - title: "Last change", - titles: { - en: "Last change", - de: "Letzte Zählerwertänderung", - it: "Ultima modifica", - }, - type: "string", - description: "Last change of counter value", - descriptions: { - en: "Last change of counter value", - de: "Letzte Änderung", - it: "Ultima modifica del valore", - }, - observable: true, - readOnly: true, - }, - }, - actions: { - increment: { - title: "Increment", - titles: { - en: "Increment", - de: "Erhöhen", - it: "Incrementa", - }, - description: "Increment counter value", - descriptions: { - en: "Increment counter value", - de: "Zählerwert erhöhen", - it: "Incrementa il valore del contatore", - }, - }, - decrement: { - title: "Decrement", - titles: { - en: "Decrement", - de: "Verringern", - it: "Decrementa", - }, - description: "Decrementing counter value", - descriptions: { - en: "Decrementing counter value", - de: "Zählerwert verringern", - it: "Decrementare il valore del contatore", - }, - }, - reset: { - title: "Reset", - titles: { - en: "Reset", - de: "Zurücksetzen", - it: "Reset", - }, - description: "Resetting counter value", - descriptions: { - en: "Resetting counter value", - de: "Zählerwert zurücksetzen", - it: "Resettare il valore del contatore", - }, - }, - }, - events: { - change: { - title: "Changed", - titles: { - en: "Changed", - de: "Geändert", - it: "Valore modificato", - }, - description: "Change event", - descriptions: { - en: "Change event", - de: "Änderungsereignis", - it: "Valore modificato", - }, - }, - }, -}) - .then((thing) => { - console.log("Produced " + thing.getThingDescription().title); - - // init property values - count = 0; - lastChange = new Date().toISOString(); - - // set property handlers (using async-await) - thing.setPropertyReadHandler("count", async () => count); - thing.setPropertyReadHandler("lastChange", async () => lastChange); - thing.setPropertyReadHandler("countAsImage", async (options) => { - let fill = "black"; - if (options && typeof options === "object" && "uriVariables" in options) { - console.log("options = " + JSON.stringify(options)); - if (options.uriVariables && "fill" in options.uriVariables) { - const uriVariables = options.uriVariables as Record; - fill = uriVariables.fill; - } - } - return ( - "" + - "" + - count + - "" + - "" - ); - }); - thing.setPropertyReadHandler( - "redDotImage", - async () => - "iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==" - ); - - // set action handlers (using async-await) - thing.setActionHandler("increment", async (params, options) => { - let step = 1; - if (options && typeof options === "object" && "uriVariables" in options) { - console.log("options = " + JSON.stringify(options)); - if (options.uriVariables && "step" in options.uriVariables) { - const uriVariables = options.uriVariables as Record; - step = uriVariables.step as number; - } - } - const newValue = count + step; - console.log("Incrementing count from " + count + " to " + newValue + " (with step " + step + ")"); - count = newValue; - lastChange = new Date().toISOString(); - thing.emitEvent("change", count); - thing.emitPropertyChange("count"); - return undefined; - }); - thing.setActionHandler("decrement", async (params, options) => { - let step = 1; - if (options && typeof options === "object" && "uriVariables" in options) { - console.log("options = " + JSON.stringify(options)); - if (options.uriVariables && "step" in options.uriVariables) { - const uriVariables = options.uriVariables as Record; - step = uriVariables.step as number; - } - } - const newValue = count - step; - console.log("Decrementing count from " + count + " to " + newValue + " (with step " + step + ")"); - count = newValue; - lastChange = new Date().toISOString(); - thing.emitEvent("change", count); - thing.emitPropertyChange("count"); - return undefined; - }); - thing.setActionHandler("reset", async (params, options) => { - console.log("Resetting count"); - count = 0; - lastChange = new Date().toISOString(); - thing.emitEvent("change", count); - thing.emitPropertyChange("count"); - return undefined; - }); - - // expose the thing - thing.expose().then(() => { - console.info(thing.getThingDescription().title + " ready"); - }); - }) - .catch((e) => { - console.log(e); - }); diff --git a/packages/examples/src/scripts/smart-coffee-machine-client.ts b/packages/examples/src/scripts/smart-coffee-machine-client.ts deleted file mode 100644 index ab81b579b..000000000 --- a/packages/examples/src/scripts/smart-coffee-machine-client.ts +++ /dev/null @@ -1,97 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2020 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 - ********************************************************************************/ - -// This is an example of Web of Things consumer ("client" 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. - -// Print data and an accompanying message in a distinguishable way -function log(msg: string, data: unknown) { - console.info("======================"); - console.info(msg); - console.dir(data); - console.info("======================"); -} - -WoT.requestThingDescription("http://127.0.0.1:8080/smart-coffee-machine").then(async (td) => { - try { - const thing = await WoT.consume(td); - log("Thing Description:", td); - - // Read property allAvailableResources - let allAvailableResources = await (await thing.readProperty("allAvailableResources")).value(); - log("allAvailableResources value is:", allAvailableResources); - - // Now let's change water level to 80 - await thing.writeProperty("availableResourceLevel", 80, { uriVariables: { id: "water" } }); - - // And see that the water level has changed - const waterLevel = await ( - await thing.readProperty("availableResourceLevel", { uriVariables: { id: "water" } }) - ).value(); - log("waterLevel value after change is:", waterLevel); - - // This can also be seen in allAvailableResources property - allAvailableResources = await (await thing.readProperty("allAvailableResources")).value(); - log("allAvailableResources value after change is:", allAvailableResources); - - // It's also possible to set a client-side handler for observable properties - thing.observeProperty("maintenanceNeeded", async (data) => { - log("maintenanceNeeded property has changed! New value is:", await data.value()); - }); - - // Now let's make 3 cups of latte! - const makeCoffee = await thing.invokeAction("makeDrink", undefined, { - uriVariables: { drinkId: "latte", size: "l", quantity: 3 }, - }); - const makeCoffeep = (await makeCoffee?.value()) as Record; - if (makeCoffeep.result != null) { - log("Enjoy your drink!", makeCoffeep); - } else { - log("Failed making your drink:", makeCoffeep); - } - - // See how allAvailableResources property value has changed - allAvailableResources = await (await thing.readProperty("allAvailableResources")).value(); - log("allAvailableResources value is:", allAvailableResources); - - // Let's add a scheduled task - const scheduledTask = await thing.invokeAction("setSchedule", { - drinkId: "espresso", - size: "m", - quantity: 2, - time: "10:00", - mode: "everyday", - }); - const scheduledTaskp = (await scheduledTask?.value()) as Record; - log(scheduledTaskp.message, scheduledTaskp); - - // See how it has been added to the schedules property - const schedules = await (await thing.readProperty("schedules")).value(); - log("schedules value: ", schedules); - - // Let's set up a handler for outOfResource event - thing.subscribeEvent("outOfResource", async (data) => { - // Here we are simply logging the message when the event is emitted - // But, of course, could have a much more sophisticated handler - log("outOfResource event:", await data.value()); - }); - - // fire property change for maintenanceNeeded - await thing.writeProperty("servedCounter", 1001); - } catch (err) { - console.error("Script error:", err); - } -}); diff --git a/packages/examples/src/scripts/smart-coffee-machine.ts b/packages/examples/src/scripts/smart-coffee-machine.ts deleted file mode 100644 index 977b1693a..000000000 --- a/packages/examples/src/scripts/smart-coffee-machine.ts +++ /dev/null @@ -1,420 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2020 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 - ********************************************************************************/ - -// 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. - -let allAvailableResources: Record; -let possibleDrinks: string[]; -let maintenanceNeeded: boolean; -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; -} - -function notify(subscribers: unknown, msg: string) { - // Actual implementation of notifying subscribers with a message can go here - console.log(msg); -} - -WoT.produce({ - title: "Smart-Coffee-Machine", - description: `A smart coffee machine with a range of capabilities. -A complementary tutorial is available at http://www.thingweb.io/smart-coffee-machine.html.`, - support: "https://github.com/eclipse-thingweb/node-wot/", - properties: { - allAvailableResources: { - type: "object", - description: `Current level of all available resources given as an integer percentage for each particular resource. -The 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, - }, - }, - }, - availableResourceLevel: { - type: "number", - description: `Current level of a particular resource. Requires resource id variable as uriVariables. -The 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: `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. -Brews 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. -Assumes 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", - }, - }, - }, -}) - .then((thing) => { - // 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.log(`Produced ${thing.getThingDescription().title}`); - }) - .catch((e) => { - console.log(e); - }); diff --git a/packages/examples/src/security/certificate.pem b/packages/examples/src/security/certificate.pem deleted file mode 100644 index 153d2789f..000000000 --- a/packages/examples/src/security/certificate.pem +++ /dev/null @@ -1,13 +0,0 @@ ------BEGIN CERTIFICATE----- -MIICDDCCAXUCFEcgrATArZXi0508eBk873JbhNT5MA0GCSqGSIb3DQEBCwUAMEUx -CzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRl -cm5ldCBXaWRnaXRzIFB0eSBMdGQwHhcNMjAwNTA3MTcwMzEwWhcNMjAwNjA2MTcw -MzEwWjBFMQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UE -CgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIGfMA0GCSqGSIb3DQEBAQUAA4GN -ADCBiQKBgQDB4USEKBahhOhznS3oF4C1hGi7K4j2zpJIxIKxJD8zvlV+MazQOsCv -4FtudE9Lf9JV4Cni8Ci/bLgSENgkMOK6JJrvYSZBmcO/soDoz6szzIuLJbQoZvM6 -wRxwKwXYP77qK+zbYhdFTZiESO77RPvnnAxU7e4Ym2bZXjghqB0GDQIDAQABMA0G -CSqGSIb3DQEBCwUAA4GBAJjPDHpmbfd4QMtARLyslQ8LpE+fk/OtF58eOFF/izko -DWRf3dXGGNnhZOESMbRYSgygEZR0dhbaUSdiGMrj+MapOwsmoYnvz44bSeGOHAgq -cutpTJ2oXeeiHsOo18/tNc5RgvRDJivlgrheaC4LSq4eb+hUFV0Zj6cR+GRauSsL ------END CERTIFICATE----- diff --git a/packages/examples/src/security/oauth/README.md b/packages/examples/src/security/oauth/README.md deleted file mode 100644 index de673f355..000000000 --- a/packages/examples/src/security/oauth/README.md +++ /dev/null @@ -1,47 +0,0 @@ -# oAuth example - -The example is composed by two actors: one Thing that expose an action with oAuth2.0 client credential security constraint and a client who want to use that function. In the example, we are using an utility server to generate tokens and validate client credentials. Therefore, the **wot-consumer** force the action `href` to be the same as the utility server with the goal to validate the obtained oAuth2.0 token. In the feature the exposing servient could also play this role. - -## run the example - -Set the current working directory to this folder. Then execute: - -```bash -npm install -npm run build -``` - -After this follow the procedure described in [here](../../README.md) and remove the unwanted line of codes inside the scripts `./dist/consumer.js` and `./dist/exposer.js`. - -Now you are ready to run the example. - -```bash -# start the server -npm run server -``` - -in a different terminal - -```bash -# start the exposer -npm run start:exposer -``` - -Finally, in other terminal - -```bash -npm run start:consumer -``` - -you should see the following line at the end of consumer log: - -```bash -oAuth token was Ok! -``` - -This confirms that the oAuth flow was completed successfully. Now you can have fun revoking the access to the consumer script. Go -to `./exposer.ts` and try to remove the string `"user"` from the scopes. Run again the example and you will see that the action is not executed and an error is returned by the client. - -## Where is a JS version? - -See [here](../../../examples/security/oauth) diff --git a/packages/examples/src/security/oauth/consumer.ts b/packages/examples/src/security/oauth/consumer.ts deleted file mode 100644 index efc4ac090..000000000 --- a/packages/examples/src/security/oauth/consumer.ts +++ /dev/null @@ -1,26 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2018 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 - ********************************************************************************/ - -WoT.requestThingDescription("https://localhost:8080/oauth").then((td) => { - WoT.consume(td).then(async (thing) => { - try { - const resp = await thing.invokeAction("sayOk"); - const result = await resp?.value(); - console.log("oAuth token was", result); - } catch (error) { - console.log("It seems that I couldn't access the resource"); - } - }); -}); diff --git a/packages/examples/src/security/oauth/exposer.ts b/packages/examples/src/security/oauth/exposer.ts deleted file mode 100644 index 827f6323a..000000000 --- a/packages/examples/src/security/oauth/exposer.ts +++ /dev/null @@ -1,45 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2018 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 { ExposedThingInit } from "wot-typescript-definitions"; - -const td: ExposedThingInit = { - "@context": "https://www.w3.org/2019/wot/td/v1", - title: "OAuth", - id: "urn:dev:wot:oauth:test", - securityDefinitions: { - oauth2_sc: { - scheme: "oauth2", - flow: "client", - token: "https://localhost:3000/token", - scopes: ["user", "admin"], - }, - }, - security: ["oauth2_sc"], - actions: { - sayOk: { - description: "A simple action protected with oauth", - idempotent: true, - }, - }, -}; -try { - WoT.produce(td).then((thing) => { - thing.setActionHandler("sayOk", async () => "Ok!"); - thing.expose(); - }); -} catch (err) { - console.error("Script error: " + err); -} diff --git a/packages/examples/src/security/oauth/memory-model.js b/packages/examples/src/security/oauth/memory-model.js deleted file mode 100644 index 079b27913..000000000 --- a/packages/examples/src/security/oauth/memory-model.js +++ /dev/null @@ -1,119 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2018 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 - ********************************************************************************/ - -/** - * oAuth server logic. See https://oauth2-server.readthedocs.io/en/latest/model/overview.html - */ -module.exports = class InMemoryModel { - /** - * - */ - constructor() { - this.clients = [ - { - clientId: "node-wot", - clientSecret: "isgreat!", - redirectUris: [""], - grants: ["client_credentials"], - scopes: ["user"], - }, - ]; - this.tokens = []; - this.users = [{ id: "123", username: "thomseddon", password: "nightworld" }]; - } - - dump() { - console.log("clients", this.clients); - console.log("tokens", this.tokens); - console.log("users", this.users); - } - - /* - * Get access token. - */ - - getAccessToken(bearerToken) { - var tokens = this.tokens.filter(function (token) { - return token.accessToken === bearerToken; - }); - - return tokens.length ? tokens[0] : false; - } - - /** - * Get refresh token. - */ - - getRefreshToken(bearerToken) { - var tokens = this.tokens.filter(function (token) { - return token.refreshToken === bearerToken; - }); - - return tokens.length ? tokens[0] : false; - } - - /** - * Get client. - */ - - getClient(clientId, clientSecret) { - var clients = this.clients.filter(function (client) { - return client.clientId === clientId && (!clientSecret || client.clientSecret === clientSecret); - }); - - return clients.length ? clients[0] : false; - } - - /** - * Save token. - */ - - saveToken(token, client, user) { - let { accessToken, accessTokenExpiresAt, refreshTokenExpiresAt, refreshToken } = token; - this.tokens.push({ - accessToken: accessToken, - accessTokenExpiresAt: accessTokenExpiresAt, - client: client, - refreshToken: refreshToken, - refreshTokenExpiresAt: refreshTokenExpiresAt, - user: user, - }); - return this.tokens[this.tokens.length - 1]; - } - - /* - * Get user. - */ - - getUser(username, password) { - var users = this.users.filter(function (user) { - return user.username === username && user.password === password; - }); - - return users.length ? users[0] : false; - } - - getUserFromClient(client) { - return this.users[0]; - } - - saveAuthorizationCode() {} - - expireAllTokens() { - for (const token of this.tokens) { - token.accessTokenExpiresAt = Date.now(); - } - } -}; diff --git a/packages/examples/src/security/oauth/package.json b/packages/examples/src/security/oauth/package.json deleted file mode 100644 index eb30f2694..000000000 --- a/packages/examples/src/security/oauth/package.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "name": "oauth-test", - "version": "1.0.0", - "description": "Simple project setup to test oauth functionalities", - "main": "index.js", - "scripts": { - "build": "tsc -b", - "server": "node server.js", - "start:exposer": "node ../../../../cli/dist/cli.js -f ./wot-server-servient-conf.json ../../../dist/security/oauth/exposer.js", - "start:consumer": "node ../../../../cli/dist/cli.js -f ./wot-client-servient-conf.json ../../../dist/security/oauth/consumer.js", - "debug:consumer": "node --inspect-brk ../../../../cli/dist/cli.js -f ./wot-client-servient-conf.json ../../../dist/security/oauth/consumer.js" - }, - "author": "Eclipse Thingweb (https://thingweb.io/)", - "license": "EPL-2.0 OR W3C-20150513", - "devDependencies": { - "@node-oauth/express-oauth-server": "^3.0.0", - "@types/node": "16.18.35", - "cors": "^2.8.5", - "ts-node": "10.9.1", - "typescript": "4.7.4", - "typescript-standard": "^0.3.36", - "wot-typescript-definitions": "0.8.0-SNAPSHOT.29" - } -} diff --git a/packages/examples/src/security/oauth/server.js b/packages/examples/src/security/oauth/server.js deleted file mode 100644 index 717a2c1b8..000000000 --- a/packages/examples/src/security/oauth/server.js +++ /dev/null @@ -1,82 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2018 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 - ********************************************************************************/ -/** - * A simple oAuth test server - */ -const OAuthServer = require("@node-oauth/express-oauth-server"); -const bodyParser = require("body-parser"); -const cors = require("cors"); -const https = require("https"); -const fs = require("fs"); -const express = require("express"); -const Memory = require("./memory-model"); - -var app = express(); -app.use(cors()); -app.options("*", cors()); - -const model = new Memory(); - -app.oauth = new OAuthServer({ - model: model, -}); - -app.use(bodyParser.json()); -app.use("/introspect", bodyParser.urlencoded({ extended: false })); -app.use("/introspect", (req, res, next) => { - if (req.method !== "POST" || !req.is("application/x-www-form-urlencoded")) { - return res.status(400).end(); - } - - // rewrite body authenticate method is not compliant to https://tools.ietf.org/html/rfc7662 - const token = req.body.token; - delete req.body.token; - req.body.access_token = token; - console.log("Body changed,"); - next(); -}); - -app.use("/introspect", async (req, res, next) => { - return app.oauth.authenticate()(req, res, next); -}); -app.use("/introspect", (req, res) => { - const token = res.locals.oauth.token; - console.log("Token was", token ? "Ok" : "not Ok"); - res.json({ - active: !!token, - scope: token.client.scopes.join(" "), - client_id: token.client.clientId, - }).end(); -}); - -app.use("/token", bodyParser.urlencoded({ extended: false })); -app.use("/token", app.oauth.token()); - -app.use("/resource", (req, res) => { - console.log("qui?"); - res.send("Ok!"); -}); - -https - .createServer( - { - key: fs.readFileSync("../privatekey.pem"), - cert: fs.readFileSync("../certificate.pem"), - }, - app - ) - .listen(3000, "localhost", () => { - console.log("listening"); - }); diff --git a/packages/examples/src/security/oauth/tsconfig.json b/packages/examples/src/security/oauth/tsconfig.json deleted file mode 100644 index eab64572b..000000000 --- a/packages/examples/src/security/oauth/tsconfig.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "compilerOptions": { - "outDir": "../../../dist" - }, - "extends": "../../../tsconfig.json", - "include": [], - "files": ["consumer.ts", "exposer.ts"] -} diff --git a/packages/examples/src/security/oauth/wot-client-servient-conf.json b/packages/examples/src/security/oauth/wot-client-servient-conf.json deleted file mode 100644 index d6e9a1529..000000000 --- a/packages/examples/src/security/oauth/wot-client-servient-conf.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "servient": { - "clientOnly": true - }, - "http": { - "allowSelfSigned": true - }, - "credentials": { - "urn:dev:wot:oauth:test": { - "clientId": "node-wot", - "clientSecret": "isgreat!" - } - } -} diff --git a/packages/examples/src/security/oauth/wot-server-servient-conf.json b/packages/examples/src/security/oauth/wot-server-servient-conf.json deleted file mode 100644 index b34eed731..000000000 --- a/packages/examples/src/security/oauth/wot-server-servient-conf.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "servient": {}, - "http": { - "allowSelfSigned": true, - "serverKey": "../privatekey.pem", - "serverCert": "../certificate.pem", - "security": [ - { - "scheme": "oauth2", - "method": { - "name": "introspection_endpoint", - "endpoint": "https://localhost:3000/introspect", - "allowSelfSigned": true - } - } - ] - }, - "credentials": { - "urn:dev:wot:oauth:test": { - "clientId": "node-wot", - "clientSecret": "isgreat!" - } - } -} diff --git a/packages/examples/src/security/privatekey.pem b/packages/examples/src/security/privatekey.pem deleted file mode 100644 index 1a65f9c28..000000000 --- a/packages/examples/src/security/privatekey.pem +++ /dev/null @@ -1,15 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIICXQIBAAKBgQDB4USEKBahhOhznS3oF4C1hGi7K4j2zpJIxIKxJD8zvlV+MazQ -OsCv4FtudE9Lf9JV4Cni8Ci/bLgSENgkMOK6JJrvYSZBmcO/soDoz6szzIuLJbQo -ZvM6wRxwKwXYP77qK+zbYhdFTZiESO77RPvnnAxU7e4Ym2bZXjghqB0GDQIDAQAB -AoGBAIZCwXfjawifSXoGtuuqmu8Yqo4zL736f0L1EqwpNbtXa0zgiZQJm7Yv4CE1 -WlQmSDo/6cHGHfGyECYyk/s7/0VDdfS6jf5AMzpkCXP+32FZZegN7JxasgYsddZC -LlpYhxjUd1vnYEIvvAefJ7N3Y0jdp1gR1Iri9+7vJjuFXJoBAkEA6foaD4sFDJhO -/zYPPOJljtwtQz+ik4nXJCIvzLYAIKBcOgFirqLqcHfRnHh17DrumqVB3IhtjyoX -XAQ0+uFZnQJBANQg/Xpijb5NuK43cg1q/MLyR8vdp1AxvmpS0YnsObxJAqp79oT/ -EEU+WXEJKmxBWtVbwryraAnUamyUtG3rqzECQQCqUGSR9dcKEQBH47j5z66va/QC -pJqaHNcfNUdoEv9KwJABpSqJBovyrDVHWaQ0/bzbkvm7Jw/hC3mPgHx0Nn89AkBj -E+2QhguSQRbrODZrEfdwzly+WY7WJAW9/2SjONRWnvtByZiA0ek1jkOUSiWoXnYg -hTVVjfGP9cinYmVLmfFhAkA+L4iyT0uKjXoqugJU2wYDdLDNh3zPg4xu+Pg2Ua62 -SFr28bvOWI7g2DV6KX/gS1wKbSHbctUylJHQRxFaHSlX ------END RSA PRIVATE KEY----- diff --git a/packages/examples/src/testthing/testclient.ts b/packages/examples/src/testthing/testclient.ts deleted file mode 100644 index 672c387f2..000000000 --- a/packages/examples/src/testthing/testclient.ts +++ /dev/null @@ -1,100 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2020 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 - ********************************************************************************/ - -console.log = () => { - /* empty */ -}; -console.debug = () => { - /* empty */ -}; - -async function testPropertyRead(thing: WoT.ConsumedThing, name: string) { - try { - const res = await thing.readProperty(name); - const value = await res.value(); - console.info("PASS " + name + " READ:", value); - } catch (err) { - console.error("FAIL " + name + " READ:", JSON.stringify(err)); - } -} - -async function testPropertyWrite( - thing: WoT.ConsumedThing, - name: string, - value: WoT.InteractionInput, - shouldFail: boolean -) { - const displayValue = JSON.stringify(value); - try { - await thing.writeProperty(name, value); - if (!shouldFail) console.info("PASS " + name + " WRITE (" + displayValue + ")"); - else console.error("FAIL " + name + " WRITE: (" + displayValue + ")"); - } catch (err) { - if (!shouldFail) console.error("FAIL " + name + " WRITE (" + displayValue + "):", JSON.stringify(err)); - else console.info("PASS " + name + " WRITE (" + displayValue + "):", JSON.stringify(err)); - } -} - -WoT.requestThingDescription("http://localhost:8080/testthing") - .then(async (td) => { - try { - const thing = await WoT.consume(td); - console.info("=== TD ==="); - console.info(td); - console.info("=========="); - console.info(); - - console.info("========== bool"); - await testPropertyRead(thing, "bool"); - await testPropertyWrite(thing, "bool", true, false); - await testPropertyWrite(thing, "bool", false, false); - await testPropertyWrite(thing, "bool", "true", true); - - console.info("========== int"); - await testPropertyRead(thing, "int"); - await testPropertyWrite(thing, "int", 4711, false); - await testPropertyWrite(thing, "int", 3.1415, true); - await testPropertyWrite(thing, "int", "Pi", true); - - console.info("========== num"); - await testPropertyRead(thing, "num"); - await testPropertyWrite(thing, "num", 4711, false); - await testPropertyWrite(thing, "num", 3.1415, false); - await testPropertyWrite(thing, "num", "Pi", true); - - console.info("========== string"); - await testPropertyRead(thing, "string"); - await testPropertyWrite(thing, "string", "testclient", false); - await testPropertyWrite(thing, "string", 13, true); - await testPropertyWrite(thing, "string", null, true); - - console.info("========== array"); - await testPropertyRead(thing, "array"); - await testPropertyWrite(thing, "array", [23, "illuminated"], false); - await testPropertyWrite(thing, "array", { id: 24, name: "dark" }, true); - await testPropertyWrite(thing, "array", null, true); - - console.info("========== object"); - await testPropertyRead(thing, "object"); - await testPropertyWrite(thing, "object", { id: 23, name: "illuminated" }, false); - await testPropertyWrite(thing, "object", null, true); - await testPropertyWrite(thing, "object", [24, "dark"], true); - } catch (err) { - console.error("Script error:", err); - } - }) - .catch((err) => { - console.error("Fetch error:", err); - }); diff --git a/packages/examples/src/testthing/testthing.ts b/packages/examples/src/testthing/testthing.ts deleted file mode 100644 index 4590ee58b..000000000 --- a/packages/examples/src/testthing/testthing.ts +++ /dev/null @@ -1,307 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2020 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 - ********************************************************************************/ - -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); - } -} - -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); - } -} - -// init property values -let bool = false; -let int = 42; -let num = 3.14; -let string = "unset"; -let array: unknown[] = [2, "unset"]; -let object: Record = { id: 123, name: "abc" }; - -WoT.produce({ - title: "TestThing", - 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" }, - }, - "on-object": { - title: "Object Property Change", - description: "Event with number data that is emitted when the object property is written to", - data: { type: "number" }, - }, - }, -}) - .then((thing) => { - 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(() => { - console.info(thing.getThingDescription().title + " ready"); - }); - }) - .catch((e) => { - console.log(e); - }); diff --git a/packages/examples/tsconfig.json b/packages/examples/tsconfig.json deleted file mode 100644 index 63049fbba..000000000 --- a/packages/examples/tsconfig.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "extends": "../../tsconfig.json", - "compilerOptions": { - "outDir": "dist", - "rootDir": "src", - "target": "ES2018", - "sourceMap": false, - "removeComments": false - }, - "include": ["src/**/*"], - "references": [{ "path": "../td-tools" }] -} diff --git a/tsconfig.json b/tsconfig.json index f60306d3c..2af9a07a2 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -23,19 +23,9 @@ "strictNullChecks": true }, "references": [ - { "path": "./packages/binding-coap" }, - { "path": "./packages/binding-file" }, - { "path": "./packages/binding-http" }, { "path": "./packages/binding-mbus" }, - { "path": "./packages/binding-modbus" }, - { "path": "./packages/binding-mqtt" }, - { "path": "./packages/binding-netconf" }, - { "path": "./packages/binding-opcua" }, - { "path": "./packages/binding-websockets" }, - { "path": "./packages/cli" }, { "path": "./packages/core" }, - { "path": "./packages/td-tools" }, - { "path": "./packages/examples" } + { "path": "./packages/td-tools" } ], "include": [ /** specifically nothing here */