diff --git a/.github/workflows/npm-publish-next.yml b/.github/workflows/npm-publish-next.yml index a1a7384..cf0de17 100644 --- a/.github/workflows/npm-publish-next.yml +++ b/.github/workflows/npm-publish-next.yml @@ -11,9 +11,17 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: - node-version: '20' + node-version: '22' registry-url: https://registry.npmjs.org/ - - run: npm ci + - run: npm ci --ignore-scripts + - run: npm run build - run: npm publish --tag next env: NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}} + - name: Create Git Tag + run: | + VERSION=$(node -p "require('./package.json').version") + git config user.name "GitHub Actions" + git config user.email "actions@github.com" + git tag v$VERSION + git push origin v$VERSION diff --git a/.github/workflows/npm-publish.yml b/.github/workflows/npm-publish.yml index 17d58ac..14ccea0 100644 --- a/.github/workflows/npm-publish.yml +++ b/.github/workflows/npm-publish.yml @@ -15,7 +15,15 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: - node-version: '20' + node-version: '22' registry-url: https://registry.npmjs.org/ - - run: npm ci + - run: npm ci --ignore-scripts + - run: npm run build - run: npm publish + - name: Create Git Tag + run: | + VERSION=$(node -p "require('./package.json').version") + git config user.name "GitHub Actions" + git config user.email "actions@github.com" + git tag v$VERSION + git push origin v$VERSION diff --git a/.gitignore b/.gitignore index 91d3f84..f3850ba 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ node_modules/ -.npmrc + .DS_Store +dist/ diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..97b895e --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +ignore-scripts=true diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 0000000..850238e --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,18 @@ +{ + "trailingComma": "es5", + "tabWidth": 2, + "useTabs": false, + "semi": true, + "singleQuote": true, + "bracketSameLine": false, + "arrowParens": "always", + "jsxSingleQuote": false, + "printWidth": 128, + "quoteProps": "as-needed", + "bracketSpacing": true, + "svelteSortOrder": "options-scripts-styles-markup", + "svelteStrictMode": false, + "svelteAllowShorthand": true, + "svelteIndentScriptAndStyle": true, + "htmlWhitespaceSensitivity": "ignore" +} diff --git a/README.md b/README.md index 6bcd0ba..248f4a3 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ A helper module to connect third-party modules to RealityHub. -## Support Requests +## Support Requests This repository is for distribution only. For support please visit: [Zero Density Support](https://support.zerodensity.io). @@ -21,7 +21,7 @@ The client can be bundled for browsers with a bundling tool (e.g. rollup). ### Connecting to Hub ```js -const { BrokerClient } = require('@zerodensity/realityhub-api'); +import { BrokerClient } from '@zerodensity/realityhub-api'; BrokerClient.initModule({ menuTitle: '', @@ -40,7 +40,8 @@ BrokerClient.initModule({ ### Listing Reality 5 (API v1.2+) engines ```js -brokerClient.api.hub.reality5_1_2_world.listEngines() +brokerClient.api.hub.reality5_1_2_world + .listEngines() .then((engines) => { console.log(engines); @@ -65,28 +66,29 @@ brokerClient.api.hub.reality5_1_2_world.listEngines() ### Listing the nodes running on an Reality 5 engine (API v1.2+) ```js -brokerClient.api.hub.reality5_1_2_world.getNodes(79 /* id of the engine */) +brokerClient.api.hub.reality5_1_2_world + .getNodes(79 /* id of the engine */) .then((nodes) => { console.log(nodes); /** - * { - * ... - * // The keys are the NodePath - * Cyclorama: { - * ... - * Functions: { - * // The keys are the FunctionPath - * 'Cyclorama/AddProjection': {...}, - * 'Cyclorama/ClearProjection': {...}, - * } - * ... - * } - * ... - * } - */ - - // NOTE: For backward compatiblity, the NodePath and FunctionPath omit the leading '/'. + * { + * ... + * // The keys are the NodePath + * Cyclorama: { + * ... + * Functions: { + * // The keys are the FunctionPath + * 'Cyclorama/AddProjection': {...}, + * 'Cyclorama/ClearProjection': {...}, + * } + * ... + * } + * ... + * } + */ + + // NOTE: For backward compatiblity, the NodePath and FunctionPath omit the leading '/'. }) .catch((ex) => console.trace(ex)); ``` @@ -103,11 +105,16 @@ of engine IDs. If `engineIds` is not supplied then the function will be called o * @param {string} params.NodePath * @param {string} params.PropertyPath * @param {number} [engineIds] - optional, default is all engines - */ -brokerClient.api.hub.reality5_1_2_world.callNodeFunction({ - NodePath: '/Cyclorama', - FunctionPath: '/Cyclorama/AddProjection', -}, [/* engine id = */ 79]).catch((ex) => console.trace(ex)); + */ +brokerClient.api.hub.reality5_1_2_world + .callNodeFunction( + { + NodePath: '/Cyclorama', + FunctionPath: '/Cyclorama/AddProjection', + }, + [/* engine id = */ 79] + ) + .catch((ex) => console.trace(ex)); ``` ### Setting a Node's Property Value @@ -136,17 +143,22 @@ brokerClient.api.hub.reality5_1_2_world.setNodeProperty({ You can use `interpolate(params[, engineIds])` to interpolate a node's property value. `engineIds` is an array of engine IDs. If `engineIds` not supplied then all of the engines will receive the same interpolate command. ```js -brokerClient.api.hub.reality5_1_2_world.interpolate({ - NodePath: '/Add_f32', - PropertyPath: 'X', - StartValue: 5.0, // optional, default is the current value - EndValue: 10.0, - Duration: 2000, // in milliseconds, optional, default is 0 - Delay: 2000, // in milliseconds, optional, default is 0 - - /** @type {'Jump' | 'Linear' | 'EaseIn' | 'EaseOut' | 'EaseInOut'} */ - InterpType: 'EaseIn', // optional, default is 'Jump' -}, [/* engine id = */ 79]).catch((ex) => console.trace(ex)); +brokerClient.api.hub.reality5_1_2_world + .interpolate( + { + NodePath: '/Add_f32', + PropertyPath: 'X', + StartValue: 5.0, // optional, default is the current value + EndValue: 10.0, + Duration: 2000, // in milliseconds, optional, default is 0 + Delay: 2000, // in milliseconds, optional, default is 0 + + /** @type {'Jump' | 'Linear' | 'EaseIn' | 'EaseOut' | 'EaseInOut'} */ + InterpType: 'EaseIn', // optional, default is 'Jump' + }, + [/* engine id = */ 79] + ) + .catch((ex) => console.trace(ex)); ``` ### Getting a Node's Property Value @@ -154,14 +166,12 @@ brokerClient.api.hub.reality5_1_2_world.interpolate({ You can use `getNodeProperty(params[, engineIds])` to get a node's property value. `engineIds` is an array of engine IDs. If `engineIds` not supplied then all of the engines will be queried. That is why this method returns an array of promises. ```js -brokerClient.api.hub.reality5_1_2_world.getNodeProperty( - { NodePath: '/Add_f32', PropertyPath: 'X', }, - [/* engine id = */ 79] -) -.then((results) => { - console.log(results[0].Value); -}) -.catch((ex) => console.trace(ex)); +brokerClient.api.hub.reality5_1_2_world + .getNodeProperty({ NodePath: '/Add_f32', PropertyPath: 'X' }, [/* engine id = */ 79]) + .then((results) => { + console.log(results[0].Value); + }) + .catch((ex) => console.trace(ex)); ``` ### Registering Your Own Methods to RealityHub @@ -194,28 +204,29 @@ function longTask(taskDuration) { } // Let's register these as our API methods -brokerClient.api.exampleCompany.exampleModule.registerAPIHandlers({ - addNumbers, - multiplyNumbers, - performSlowTask: longTask, -}).catch((ex) => console.trace(ex)); +brokerClient.api.exampleCompany.exampleModule + .registerAPIHandlers({ + addNumbers, + multiplyNumbers, + performSlowTask: longTask, + }) + .catch((ex) => console.trace(ex)); ``` ```js // client.js -brokerClient.api.exampleCompany.exampleModule.addNumbers(3, 5) - .then((result) => { - console.log('The result of addNumbers() is', result); - }); +brokerClient.api.exampleCompany.exampleModule.addNumbers(3, 5).then((result) => { + console.log('The result of addNumbers() is', result); +}); -brokerClient.api.exampleCompany.exampleModule.performAsyncMultiplication(3, 5) - .then((result) => { - console.log('The result of performAsyncMultiplication() is', result); - }); +brokerClient.api.exampleCompany.exampleModule.performAsyncMultiplication(3, 5).then((result) => { + console.log('The result of performAsyncMultiplication() is', result); +}); // Default timeout for API requests is 2 seconds. We need to specify a longer timeout for our slow async task. const timeout = 10 * 1000; // 10 seconds -brokerClient.api.exampleCompany.exampleModule.callTimeout(timeout) +brokerClient.api.exampleCompany.exampleModule + .callTimeout(timeout) .slowAsyncTask(5) .then((result) => console.log('Slow async task returned:', result)); ``` @@ -229,7 +240,7 @@ brokerClient.api.exampleCompany.exampleModule.callTimeout(timeout) setInterval(() => { // A random number between 0 and 1000. const randomNumber = Math.round(Math.random() * 1000); - brokerClient.api.exampleCompany.exampleModule.emit('randomnumber', randomNumber); + brokerClient.api.exampleCompany.exampleModule.emit('randomnumber', randomNumber); }, 1000); ``` @@ -238,7 +249,7 @@ setInterval(() => { brokerClient.api.exampleCompany.exampleModule.on('randomnumber', (randomNumber) => { console.log('Received a random number from the server', randomNumber); -}); +}); ``` ### Questions and Feedback diff --git a/package-lock.json b/package-lock.json index b178fee..5a96701 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,34 +1,761 @@ { "name": "@zerodensity/realityhub-api", - "version": "1.15.0", - "lockfileVersion": 2, + "version": "2.0.1", + "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@zerodensity/realityhub-api", - "version": "1.15.0", + "version": "2.0.1", "license": "GPL-2.0", "dependencies": { - "uuid": "^11.0.3", - "ws": "^8.18.0" + "uuid": "^13.0.0", + "ws": "^8.18.3" + }, + "devDependencies": { + "@rollup/plugin-commonjs": "^29.0.0", + "@rollup/plugin-node-resolve": "^16.0.3", + "@rollup/plugin-terser": "^0.4.4", + "rollup": "^4.53.3" + }, + "optionalDependencies": { + "@rollup/rollup-linux-x64-gnu": "^4.53.3" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.11", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@rollup/plugin-commonjs": { + "version": "29.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^5.0.1", + "commondir": "^1.0.1", + "estree-walker": "^2.0.2", + "fdir": "^6.2.0", + "is-reference": "1.2.1", + "magic-string": "^0.30.3", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=16.0.0 || 14 >= 14.17" + }, + "peerDependencies": { + "rollup": "^2.68.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-node-resolve": { + "version": "16.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^5.0.1", + "@types/resolve": "1.20.2", + "deepmerge": "^4.2.2", + "is-module": "^1.0.0", + "resolve": "^1.22.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^2.78.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-terser": { + "version": "0.4.4", + "dev": true, + "license": "MIT", + "dependencies": { + "serialize-javascript": "^6.0.1", + "smob": "^1.0.0", + "terser": "^5.17.4" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/pluginutils": { + "version": "5.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.53.3.tgz", + "integrity": "sha512-mRSi+4cBjrRLoaal2PnqH82Wqyb+d3HsPUN/W+WslCXsZsyHa9ZeQQX/pQsZaVIWDkPcpV6jJ+3KLbTbgnwv8w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.53.3.tgz", + "integrity": "sha512-CbDGaMpdE9sh7sCmTrTUyllhrg65t6SwhjlMJsLr+J8YjFuPmCEjbBSx4Z/e4SmDyH3aB5hGaJUP2ltV/vcs4w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.53.3", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.53.3", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.53.3.tgz", + "integrity": "sha512-yMTrCrK92aGyi7GuDNtGn2sNW+Gdb4vErx4t3Gv/Tr+1zRb8ax4z8GWVRfr3Jw8zJWvpGHNpss3vVlbF58DZ4w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.53.3.tgz", + "integrity": "sha512-lMfF8X7QhdQzseM6XaX0vbno2m3hlyZFhwcndRMw8fbAGUGL3WFMBdK0hbUBIUYcEcMhVLr1SIamDeuLBnXS+Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.53.3.tgz", + "integrity": "sha512-k9oD15soC/Ln6d2Wv/JOFPzZXIAIFLp6B+i14KhxAfnq76ajt0EhYc5YPeX6W1xJkAdItcVT+JhKl1QZh44/qw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.53.3.tgz", + "integrity": "sha512-vTNlKq+N6CK/8UktsrFuc+/7NlEYVxgaEgRXVUVK258Z5ymho29skzW1sutgYjqNnquGwVUObAaxae8rZ6YMhg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.53.3.tgz", + "integrity": "sha512-RGrFLWgMhSxRs/EWJMIFM1O5Mzuz3Xy3/mnxJp/5cVhZ2XoCAxJnmNsEyeMJtpK+wu0FJFWz+QF4mjCA7AUQ3w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.53.3.tgz", + "integrity": "sha512-kASyvfBEWYPEwe0Qv4nfu6pNkITLTb32p4yTgzFCocHnJLAHs+9LjUu9ONIhvfT/5lv4YS5muBHyuV84epBo/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.53.3.tgz", + "integrity": "sha512-JiuKcp2teLJwQ7vkJ95EwESWkNRFJD7TQgYmCnrPtlu50b4XvT5MOmurWNrCj3IFdyjBQ5p9vnrX4JM6I8OE7g==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.53.3.tgz", + "integrity": "sha512-EoGSa8nd6d3T7zLuqdojxC20oBfNT8nexBbB/rkxgKj5T5vhpAQKKnD+h3UkoMuTyXkP5jTjK/ccNRmQrPNDuw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.53.3.tgz", + "integrity": "sha512-4s+Wped2IHXHPnAEbIB0YWBv7SDohqxobiiPA1FIWZpX+w9o2i4LezzH/NkFUl8LRci/8udci6cLq+jJQlh+0g==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.53.3.tgz", + "integrity": "sha512-68k2g7+0vs2u9CxDt5ktXTngsxOQkSEV/xBbwlqYcUrAVh6P9EgMZvFsnHy4SEiUl46Xf0IObWVbMvPrr2gw8A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.53.3.tgz", + "integrity": "sha512-VYsFMpULAz87ZW6BVYw3I6sWesGpsP9OPcyKe8ofdg9LHxSbRMd7zrVrr5xi/3kMZtpWL/wC+UIJWJYVX5uTKg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.53.3.tgz", + "integrity": "sha512-3EhFi1FU6YL8HTUJZ51imGJWEX//ajQPfqWLI3BQq4TlvHy4X0MOr5q3D2Zof/ka0d5FNdPwZXm3Yyib/UEd+w==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.53.3.tgz", + "integrity": "sha512-eoROhjcc6HbZCJr+tvVT8X4fW3/5g/WkGvvmwz/88sDtSJzO7r/blvoBDgISDiCjDRZmHpwud7h+6Q9JxFwq1Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.53.3.tgz", + "integrity": "sha512-OueLAWgrNSPGAdUdIjSWXw+u/02BRTcnfw9PN41D2vq/JSEPnJnVuBgw18VkN8wcd4fjUs+jFHVM4t9+kBSNLw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.53.3", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.53.3.tgz", + "integrity": "sha512-iah+THLcBJdpfZ1TstDFbKNznlzoxa8fmnFYK4V67HvmuNYkVdAywJSoteUszvBQ9/HqN2+9AZghbajMsFT+oA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.53.3", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.53.3", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/resolve": { + "version": "1.20.2", + "dev": true, + "license": "MIT" + }, + "node_modules/acorn": { + "version": "8.15.0", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/commander": { + "version": "2.20.3", + "dev": true, + "license": "MIT" + }, + "node_modules/commondir": { + "version": "1.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "dev": true, + "license": "MIT" + }, + "node_modules/fdir": { + "version": "6.5.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-module": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/is-reference": { + "version": "1.2.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "*" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "dev": true, + "license": "MIT" + }, + "node_modules/picomatch": { + "version": "4.0.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/randombytes": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/resolve": { + "version": "1.22.11", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/rollup": { + "version": "4.53.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.53.3", + "@rollup/rollup-android-arm64": "4.53.3", + "@rollup/rollup-darwin-arm64": "4.53.3", + "@rollup/rollup-darwin-x64": "4.53.3", + "@rollup/rollup-freebsd-arm64": "4.53.3", + "@rollup/rollup-freebsd-x64": "4.53.3", + "@rollup/rollup-linux-arm-gnueabihf": "4.53.3", + "@rollup/rollup-linux-arm-musleabihf": "4.53.3", + "@rollup/rollup-linux-arm64-gnu": "4.53.3", + "@rollup/rollup-linux-arm64-musl": "4.53.3", + "@rollup/rollup-linux-loong64-gnu": "4.53.3", + "@rollup/rollup-linux-ppc64-gnu": "4.53.3", + "@rollup/rollup-linux-riscv64-gnu": "4.53.3", + "@rollup/rollup-linux-riscv64-musl": "4.53.3", + "@rollup/rollup-linux-s390x-gnu": "4.53.3", + "@rollup/rollup-linux-x64-gnu": "4.53.3", + "@rollup/rollup-linux-x64-musl": "4.53.3", + "@rollup/rollup-openharmony-arm64": "4.53.3", + "@rollup/rollup-win32-arm64-msvc": "4.53.3", + "@rollup/rollup-win32-ia32-msvc": "4.53.3", + "@rollup/rollup-win32-x64-gnu": "4.53.3", + "@rollup/rollup-win32-x64-msvc": "4.53.3", + "fsevents": "~2.3.2" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "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/serialize-javascript": { + "version": "6.0.2", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/smob": { + "version": "1.5.0", + "dev": true, + "license": "MIT" + }, + "node_modules/source-map": { + "version": "0.6.1", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/terser": { + "version": "5.44.1", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.15.0", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" } }, "node_modules/uuid": { - "version": "11.0.3", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.0.3.tgz", - "integrity": "sha512-d0z310fCWv5dJwnX1Y/MncBAqGMKEzlBb1AOf7z9K8ALnd0utBX/msg/fA0+sbyN1ihbMsLhrBlnl1ak7Wa0rg==", + "version": "13.0.0", "funding": [ "https://github.com/sponsors/broofa", "https://github.com/sponsors/ctavan" ], + "license": "MIT", "bin": { - "uuid": "dist/esm/bin/uuid" + "uuid": "dist-node/bin/uuid" } }, "node_modules/ws": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", - "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", + "version": "8.20.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.20.1.tgz", + "integrity": "sha512-It4dO0K5v//JtTXuPkfEOaI3uUN87iYPnqo/ZzqCoG3g8uhA66QUMs/SrM0YK7/NAu+r4LMh/9dq2A7k+rHs+w==", + "license": "MIT", "engines": { "node": ">=10.0.0" }, @@ -45,18 +772,5 @@ } } } - }, - "dependencies": { - "uuid": { - "version": "11.0.3", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.0.3.tgz", - "integrity": "sha512-d0z310fCWv5dJwnX1Y/MncBAqGMKEzlBb1AOf7z9K8ALnd0utBX/msg/fA0+sbyN1ihbMsLhrBlnl1ak7Wa0rg==" - }, - "ws": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", - "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", - "requires": {} - } } } diff --git a/package.json b/package.json index 4989eb4..c5a184d 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,22 @@ { "name": "@zerodensity/realityhub-api", - "version": "1.20.0", + "version": "2.0.1", "description": "RealityHub API Javascript Implementation", - "main": "src/index.js", + "type": "module", + "main": "dist/index.cjs", + "module": "src/index.js", + "types": "./src/index.d.ts", + "exports": { + ".": { + "types": "./src/index.d.ts", + "import": "./src/index.js", + "require": "./dist/index.cjs" + }, + "./package.json": "./package.json" + }, + "scripts": { + "build": "rollup -c" + }, "repository": { "type": "git", "url": "git+ssh://git@github.com/zerodensity/realityhub-api.git" @@ -30,7 +44,16 @@ }, "homepage": "https://github.com/zerodensity/realityhub-api#readme", "dependencies": { - "uuid": "^11.0.3", - "ws": "^8.18.0" + "uuid": "^13.0.0", + "ws": "^8.18.3" + }, + "devDependencies": { + "@rollup/plugin-commonjs": "^29.0.0", + "@rollup/plugin-node-resolve": "^16.0.3", + "@rollup/plugin-terser": "^0.4.4", + "rollup": "^4.53.3" + }, + "optionalDependencies": { + "@rollup/rollup-linux-x64-gnu": "^4.53.3" } } diff --git a/rollup.config.js b/rollup.config.js new file mode 100644 index 0000000..fb16ccb --- /dev/null +++ b/rollup.config.js @@ -0,0 +1,15 @@ +import resolve from '@rollup/plugin-node-resolve'; +import commonjs from '@rollup/plugin-commonjs'; +import terser from '@rollup/plugin-terser'; + +export default { + input: 'src/index.js', + output: { + file: 'dist/index.cjs', + format: 'cjs', + exports: 'named', + sourcemap: true, + }, + plugins: [resolve(), commonjs(), terser()], + external: ['ws', 'uuid'], +}; diff --git a/src/index.d.ts b/src/index.d.ts new file mode 100644 index 0000000..8a2ac59 --- /dev/null +++ b/src/index.d.ts @@ -0,0 +1,115 @@ +import { EventEmitter } from 'events'; + +export class BrokerError extends Error {} + +export class RawRequest { + setCallback(callback: Function): void; + setAncillaryData(ancillaryData: any): void; + getAncillaryData(): any; + call(...args: any[]): any; +} + +export interface Logger { + log(...args: any[]): void; + info(...args: any[]): void; + warn(...args: any[]): void; + error(...args: any[]): void; + debug(...args: any[]): void; + trace(...args: any[]): void; +} + +export interface BrokerBaseParams { + moduleName?: string; + maxPacketSize?: number; + logger?: Logger; +} + +export class BrokerBase extends EventEmitter { + constructor(params: BrokerBaseParams); + moduleName: string; + maxPacketSize: number; + logger: Logger; + apiHandlers: Map; + events: Map; + api: { + [vendor: string]: { + [module: string]: { + emit(eventName: string, ...args: any[]): void; + on(eventName: string, handler: Function): void; + off(eventName: string, handler: Function): void; + once(eventName: string, handler: Function): void; + } & { + [method: string]: (...args: any[]) => Promise; + }; + }; + }; + + getMethodProxy(vendorName: string, moduleName: string, options?: any): any; + initProxy(): void; + sendResponse(socket: any, message: any, success: boolean, data?: any[], relayedMessage?: boolean): Promise; + registerAPIHandler(messageType: string, messageHandler: Function): boolean; + subscribeToAPIEvent( + eventName: string, + eventHandler: Function, + options?: { sendMessage?: boolean; once?: boolean } + ): Promise; + unsubscribeFromAPIEvent(eventName: string, eventHandler?: Function, sendMessage?: boolean): Promise; + sendMessage(message: any, socket: any, relayedMessage?: boolean): Promise; + ping(targetModuleName: string): Promise; + emitMessage(args: any[], vendorName: string, moduleName: string, options?: any): void; + destroy(): void | Promise; +} + +export interface BrokerClientParams extends BrokerBaseParams { + webSocketURL?: string; + isDuplicate?: boolean; + parent?: BrokerClient; + ssl?: boolean; +} + +export interface ConnectOptions { + host: string; + port: number; +} + +export interface InitModuleParams { + clientModuleName?: string; + menuTitle?: string; + moduleName: string; + serverURL?: string; + webSocketURL?: string; + hub: ConnectOptions; +} + +export class BrokerClient extends BrokerBase { + constructor(params?: BrokerClientParams); + webSocketURL: string; + connected: boolean; + isDuplicate: boolean; + parent: BrokerClient; + ssl: boolean; + socket: any; + + getConnectPromise(): Promise; + forceReconnect(): void; + connect(options: ConnectOptions): void; + duplicate(params: { moduleName: string }): BrokerClient; + isConnected(): boolean; + ping(): Promise; + getSocket(): any; + handleMessage(rawMessage: string): Promise; + sendMessage(message: any): Promise; + addSocketListeners(): void; + removeSocketListeners(): void; + resubscribeModuleEvents(): void; + onOpen(): Promise; + onError(err: any): void; + onClose(e: any): void; + registerHandlersToRemote(targetModuleName: string): Promise; + deregisterHandlersFromRemotes(): Promise; + unsubscribeFromAllEvents(): Promise; + destroy(): Promise; + registerAPIHandlers(handlers: { [key: string]: Function }, context?: any, remote?: string): Promise; + + static initModule(params: InitModuleParams): Promise; +} diff --git a/src/index.js b/src/index.js index 6023bfa..7eead43 100644 --- a/src/index.js +++ b/src/index.js @@ -3,7 +3,7 @@ // This file is part of realityhub-api. // // realityhub-api is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License version 2, as published by +// it under the terms of the GNU General Public License version 2, as published by // the Free Software Foundation. // // realityhub-api is distributed in the hope that it will be useful, @@ -14,9 +14,9 @@ // You should have received a copy of the GNU General Public License // along with realityhub-api. If not, see . -module.exports = { - BrokerBase: require('./lib/BrokerBase'), - BrokerClient: require('./lib/BrokerClient'), - BrokerError: require('./lib/BrokerError'), - RawRequest: require('./lib/RawRequest'), -}; +import BrokerBase from './lib/BrokerBase.js'; +import BrokerClient from './lib/BrokerClient.js'; +import BrokerError from './lib/BrokerError.js'; +import RawRequest from './lib/RawRequest.js'; + +export { BrokerBase, BrokerClient, BrokerError, RawRequest }; diff --git a/src/lib/BrokerBase.js b/src/lib/BrokerBase.js index 91338c5..f17a267 100644 --- a/src/lib/BrokerBase.js +++ b/src/lib/BrokerBase.js @@ -3,7 +3,7 @@ // This file is part of realityhub-api. // // realityhub-api is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License version 2, as published by +// it under the terms of the GNU General Public License version 2, as published by // the Free Software Foundation. // // realityhub-api is distributed in the hope that it will be useful, @@ -14,21 +14,21 @@ // You should have received a copy of the GNU General Public License // along with realityhub-api. If not, see . -const { v4: uuid } = require('uuid'); -const EventEmitter = require('events'); -const BrokerError = require('./BrokerError.js'); -const onceMultiple = require('./onceMultiple.js'); +import { v4 as uuid } from 'uuid'; +import EventEmitter from 'events'; +import BrokerError from './BrokerError.js'; +import onceMultiple from './onceMultiple.js'; const DEFAULT_MAX_WS_PACKET_SIZE = 50 /*MB*/ * 1024 * 1024; - /** - * BrokerBase constructor - * @param {object} params Parameters - * @param {string} [params.moduleName] Module name - * @param {number} [params.maxPacketSize] Maximum websocket packet size - * @param {Logger} [params.logger] Logger instance - */ -module.exports = class BrokerBase extends EventEmitter { +/** + * BrokerBase constructor + * @param {object} params Parameters + * @param {string} [params.moduleName] Module name + * @param {number} [params.maxPacketSize] Maximum websocket packet size + * @param {Logger} [params.logger] Logger instance + */ +export default class BrokerBase extends EventEmitter { constructor(params) { super(); this.moduleName = params.moduleName; @@ -40,7 +40,7 @@ module.exports = class BrokerBase extends EventEmitter { this.apiHandlers = new Map(); this.messageTimeout = 2000; - + this.overridenTimeout = NaN; // NaN = use the implementation try { @@ -50,14 +50,14 @@ module.exports = class BrokerBase extends EventEmitter { this.overridenTimeout = Number(process.env.BROKER_TIMEOUT) || NaN; } } catch (ex) {} - + if (this.overridenTimeout) { const logger = this.logger || console; logger.warn(`Broker Timeout is overriden to ${this.overridenTimeout} milliseconds!`); } let maxPacketSizeRead = DEFAULT_MAX_WS_PACKET_SIZE; - + try { if (typeof window != 'undefined' && typeof localStorage != 'undefined') { maxPacketSizeRead ||= Number(localStorage.getItem('MAX_WS_PACKET_SIZE')); @@ -66,12 +66,12 @@ module.exports = class BrokerBase extends EventEmitter { } } catch (ex) { const logger = this.logger || console; - logger.error(`Cannot read maxPacketSize from either localStorage or env, defaulting to ${maxPacketSizeRead}`) + logger.error(`Cannot read maxPacketSize from either localStorage or env, defaulting to ${maxPacketSizeRead}`); } this.maxPacketSize = Math.max(this.maxPacketSize, maxPacketSizeRead); - - if(this.maxPacketSize !== DEFAULT_MAX_WS_PACKET_SIZE) { + + if (this.maxPacketSize !== DEFAULT_MAX_WS_PACKET_SIZE) { const logger = this.logger || console; logger.log(`BrokerBase is created with maxPacketSize: ${this.maxPacketSize}`); } @@ -86,36 +86,36 @@ module.exports = class BrokerBase extends EventEmitter { ...options, }; - return new Proxy({}, { - get: (_, methodName) => { - if (methodName === 'emit' && this.moduleName !== `${vendorName}.${moduleName}`) { - throw new Error('A module can only emit its own events.'); - } + return new Proxy( + {}, + { + get: (_, methodName) => { + if (methodName === 'emit' && this.moduleName !== `${vendorName}.${moduleName}`) { + throw new Error('A module can only emit its own events.'); + } - return (...args) => { - switch (methodName) { - case 'emit': { - this.emitMessage(args, vendorName, moduleName, options); - break; - } + return (...args) => { + switch (methodName) { + case 'emit': { + this.emitMessage(args, vendorName, moduleName, options); + break; + } - case 'on': { - const eventName = args.shift(); - const eventHandler = args.shift(); + case 'on': { + const eventName = args.shift(); + const eventHandler = args.shift(); - if (typeof eventName !== 'string') { - throw new Error('eventName must be a string'); - } + if (typeof eventName !== 'string') { + throw new Error('eventName must be a string'); + } - if (typeof eventHandler !== 'function') { - throw new Error('eventHandler must be a function'); - } + if (typeof eventHandler !== 'function') { + throw new Error('eventHandler must be a function'); + } - const fullyQualifiedName = `${vendorName}.${moduleName}.${eventName}`; + const fullyQualifiedName = `${vendorName}.${moduleName}.${eventName}`; - this - .subscribeToAPIEvent(fullyQualifiedName, eventHandler) - .catch((err) => { + this.subscribeToAPIEvent(fullyQualifiedName, eventHandler).catch((err) => { console.error(`Couldn't subscribe to ${fullyQualifiedName}`); if (err.code !== 'TIMEOUT') { @@ -123,62 +123,60 @@ module.exports = class BrokerBase extends EventEmitter { } }); - break; - } + break; + } - case 'once': { - const eventName = args.shift(); - const eventHandler = args.shift(); + case 'once': { + const eventName = args.shift(); + const eventHandler = args.shift(); - /** - * If the subscribed event is not emitted within the given timeout then the - * event handler will be removed automatically to prevent memory leak. - * If a timeout is not provided by caller than a default timeout of 5 minutes is set. - */ - const timeout = args.shift() || 60 * 1000 * 5; + /** + * If the subscribed event is not emitted within the given timeout then the + * event handler will be removed automatically to prevent memory leak. + * If a timeout is not provided by caller than a default timeout of 5 minutes is set. + */ + const timeout = args.shift() || 60 * 1000 * 5; - if (typeof eventName !== 'string') { - throw new Error('eventName must be a string'); - } + if (typeof eventName !== 'string') { + throw new Error('eventName must be a string'); + } - if (typeof eventHandler !== 'function') { - throw new Error('eventHandler must be a function'); - } + if (typeof eventHandler !== 'function') { + throw new Error('eventHandler must be a function'); + } - if (typeof timeout !== 'number' || isNaN(timeout)) { - throw new Error('timeout must be a number'); - } + if (typeof timeout !== 'number' || isNaN(timeout)) { + throw new Error('timeout must be a number'); + } - const fullyQualifiedName = `${vendorName}.${moduleName}.${eventName}`; + const fullyQualifiedName = `${vendorName}.${moduleName}.${eventName}`; - this - .subscribeToAPIEvent(fullyQualifiedName, eventHandler, { once: true }) - .catch((err) => { + this.subscribeToAPIEvent(fullyQualifiedName, eventHandler, { + once: true, + }).catch((err) => { console.error(`Couldn't subscribe to ${fullyQualifiedName}`); if (err.code !== 'TIMEOUT') { console.trace(err); } }); - break; - } + break; + } - case 'off': { - const eventName = args.shift(); - const eventHandler = args.shift(); - const fullyQualifiedName = `${vendorName}.${moduleName}.${eventName}`; + case 'off': { + const eventName = args.shift(); + const eventHandler = args.shift(); + const fullyQualifiedName = `${vendorName}.${moduleName}.${eventName}`; - if (typeof eventName !== 'string') { - throw new Error('eventName must be a string'); - } + if (typeof eventName !== 'string') { + throw new Error('eventName must be a string'); + } - if (eventHandler && typeof eventHandler !== 'function') { - throw new Error('eventHandler must be a function'); - } + if (eventHandler && typeof eventHandler !== 'function') { + throw new Error('eventHandler must be a function'); + } - this - .unsubscribeFromAPIEvent(fullyQualifiedName, eventHandler) - .catch((err) => { + this.unsubscribeFromAPIEvent(fullyQualifiedName, eventHandler).catch((err) => { console.error(`Couldn't unsubscribe from ${fullyQualifiedName}`); if (err.code !== 'TIMEOUT') { @@ -186,61 +184,62 @@ module.exports = class BrokerBase extends EventEmitter { } }); - break; - } + break; + } - case 'callTimeout': { - const timeout = args[0]; + case 'callTimeout': { + const timeout = args[0]; - if (typeof timeout !== 'number') { - throw new Error('callTimeout: timeout is required.'); + if (typeof timeout !== 'number') { + throw new Error('callTimeout: timeout is required.'); + } + + const clonedOptions = JSON.parse(JSON.stringify(options)); + clonedOptions.timeout = timeout; + return this.getMethodProxy(vendorName, moduleName, clonedOptions); } - const clonedOptions = JSON.parse(JSON.stringify(options)); - clonedOptions.timeout = timeout; - return this.getMethodProxy(vendorName, moduleName, clonedOptions); - } + case 'excludeClients': { + const excludedClients = args[0] || []; - case 'excludeClients': { - const excludedClients = args[0] || []; + if (!(excludedClients instanceof Array)) { + throw new Error('excludedClients requires 1 parameter: an array of strings'); + } - if (!(excludedClients instanceof Array)) { - throw new Error('excludedClients requires 1 parameter: an array of strings'); + const clonedOptions = JSON.parse(JSON.stringify(options)); + clonedOptions.excludedClients = clonedOptions.excludedClients.concat(excludedClients); + return this.getMethodProxy(vendorName, moduleName, clonedOptions); } - const clonedOptions = JSON.parse(JSON.stringify(options)); - clonedOptions.excludedClients = clonedOptions.excludedClients.concat(excludedClients); - return this.getMethodProxy(vendorName, moduleName, clonedOptions); - } - - default: { - return this.sendMessage({ - data: args, - timeout: options.timeout, - type: `${vendorName}.${moduleName}.${methodName}`, - targetModuleName: `${vendorName}.${moduleName}`, - excludedClients: options.excludedClients, - }); + default: { + return this.sendMessage({ + data: args, + timeout: options.timeout, + type: `${vendorName}.${moduleName}.${methodName}`, + targetModuleName: `${vendorName}.${moduleName}`, + excludedClients: options.excludedClients, + }); + } } + }; + }, + set: (_, methodName, handler) => { + if (this.moduleName !== `${vendorName}.${moduleName}`) { + throw new Error('Cannot register methods to other modules.'); } - }; - }, - set: (_, methodName, handler) => { - if (this.moduleName !== `${vendorName}.${moduleName}`) { - throw new Error('Cannot register methods to other modules.'); - } - if (typeof handler !== 'function') { - throw new Error('Handler must be a function.'); - } + if (typeof handler !== 'function') { + throw new Error('Handler must be a function.'); + } - if (['emit', 'on', 'off'].includes(methodName)) { - throw new Error(`${methodName} is a reserved method name.`); - } + if (['emit', 'on', 'off'].includes(methodName)) { + throw new Error(`${methodName} is a reserved method name.`); + } - return this.registerAPIHandler(methodName, handler); - }, - }); + return this.registerAPIHandler(methodName, handler); + }, + } + ); } /** @@ -250,42 +249,48 @@ module.exports = class BrokerBase extends EventEmitter { initProxy() { // These nested proxies allow us to get vendorName, moduleName and methodName. // e.g. const pong = await this.api.hub.core.ping(); - this.api = new Proxy({}, { - get: (_, vendorName) => { - return new Proxy({}, { - get: (_, moduleName) => { - return this.getMethodProxy(vendorName, moduleName); - }, - set: (_, moduleName, api) => { - if (this.moduleName !== `${vendorName}.${moduleName}`) { - throw new Error('Cannot register methods to other modules.'); - } - - if (typeof api !== 'object') { - throw new Error('API must be set to an object.'); - } + this.api = new Proxy( + {}, + { + get: (_, vendorName) => { + return new Proxy( + {}, + { + get: (_, moduleName) => { + return this.getMethodProxy(vendorName, moduleName); + }, + set: (_, moduleName, api) => { + if (this.moduleName !== `${vendorName}.${moduleName}`) { + throw new Error('Cannot register methods to other modules.'); + } + + if (typeof api !== 'object') { + throw new Error('API must be set to an object.'); + } + + for (const [methodName, handler] of Object.entries(api)) { + if (typeof handler !== 'function') { + throw new Error('Handler must be a function.'); + } - for (const [methodName, handler] of Object.entries(api)) { - if (typeof handler !== 'function') { - throw new Error('Handler must be a function.'); - } + if (['emit', 'on', 'off'].includes(methodName)) { + throw new Error(`${methodName} is a reserved method name.`); + } - if (['emit', 'on', 'off'].includes(methodName)) { - throw new Error(`${methodName} is a reserved method name.`); - } + this.registerAPIHandler(methodName, handler); + } - this.registerAPIHandler(methodName, handler); + return true; + }, } - - return true; - }, - }); - }, - set: function () { - console.warn('Module name and method name are required.'); - return false; - }, - }); + ); + }, + set: function () { + console.warn('Module name and method name are required.'); + return false; + }, + } + ); } /** @@ -322,7 +327,7 @@ module.exports = class BrokerBase extends EventEmitter { /** * Registers an API request handler. * @param {string} messageType Message type. - * @param {function} messageHandler Handler function. + * @param {function} messageHandler Handler function. * @returns {boolean} `false` if a handler has already been assigned to the `messageType`. */ registerAPIHandler(messageType, messageHandler) { @@ -341,7 +346,7 @@ module.exports = class BrokerBase extends EventEmitter { /** * Subscribe to an API event. * @param {string} eventName Fully qualified event name. - * @param {function} eventHandler A function which will be called when the event is received. + * @param {function} eventHandler A function which will be called when the event is received. * @param {object} [options] Options * @param {boolean} [options.sendMessage=true] If set to `true`, it will send a subscription message over WebSocket. * Otherwise the message will only be registered internally. @@ -363,9 +368,7 @@ module.exports = class BrokerBase extends EventEmitter { // Send a subscription message over WebSocket if (options.sendMessage) { - const targetModuleName = eventName.split('.') - .slice(0, 2) - .join('.'); + const targetModuleName = eventName.split('.').slice(0, 2).join('.'); return this.sendMessage({ type: 'subscribe', @@ -392,9 +395,7 @@ module.exports = class BrokerBase extends EventEmitter { } if (sendMessage) { - const targetModuleName = eventName.split('.') - .slice(0, 2) - .join('.'); + const targetModuleName = eventName.split('.').slice(0, 2).join('.'); return this.sendMessage({ type: 'unsubscribe', @@ -434,7 +435,11 @@ module.exports = class BrokerBase extends EventEmitter { let responseMessage; try { - responseMessage = await onceMultiple(this, [`response::${message.id}`], this.overridenTimeout || message.timeout || this.messageTimeout); + responseMessage = await onceMultiple( + this, + [`response::${message.id}`], + this.overridenTimeout || message.timeout || this.messageTimeout + ); } catch (ex) { const logger = this.logger || console; logger.debug(`${this.moduleName} failed to send message ${message.type} to ${message.targetModuleName || ''}`); @@ -460,7 +465,7 @@ module.exports = class BrokerBase extends EventEmitter { /** * Send a ping request. - * @param {string} targetModuleName + * @param {string} targetModuleName * @private */ ping(targetModuleName) { @@ -468,10 +473,10 @@ module.exports = class BrokerBase extends EventEmitter { } /** - * @private - * @param {array} args - * @param {string} vendorName - * @param {string} moduleName + * @private + * @param {array} args + * @param {string} vendorName + * @param {string} moduleName */ emitMessage(args, vendorName, moduleName, options = {}) { const eventName = args.shift(); @@ -487,14 +492,13 @@ module.exports = class BrokerBase extends EventEmitter { eventName: fullyQualifiedName, data: args, excludedClients: options.excludedClients || [], - }) - .catch((err) => { - console.error(`Couldn't emit ${fullyQualifiedName}`); + }).catch((err) => { + console.error(`Couldn't emit ${fullyQualifiedName}`); - if (err.code !== 'TIMEOUT') { - console.trace(err); - } - }); + if (err.code !== 'TIMEOUT') { + console.trace(err); + } + }); } destroy() { diff --git a/src/lib/BrokerClient.js b/src/lib/BrokerClient.js index c2f86d7..d85ca49 100644 --- a/src/lib/BrokerClient.js +++ b/src/lib/BrokerClient.js @@ -3,7 +3,7 @@ // This file is part of realityhub-api. // // realityhub-api is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License version 2, as published by +// it under the terms of the GNU General Public License version 2, as published by // the Free Software Foundation. // // realityhub-api is distributed in the hope that it will be useful, @@ -14,24 +14,17 @@ // You should have received a copy of the GNU General Public License // along with realityhub-api. If not, see . -const { v4: uuid } = require('uuid'); -const BrokerBase = require('./BrokerBase.js'); -const BrokerError = require('./BrokerError.js'); -const RawRequest = require('./RawRequest.js'); -const consoleLogger = require('./consoleLogger.js'); -const onceMultiple = require('./onceMultiple.js'); +import { v4 as uuid } from 'uuid'; +import BrokerBase from './BrokerBase.js'; +import BrokerError from './BrokerError.js'; +import RawRequest from './RawRequest.js'; +import consoleLogger from './consoleLogger.js'; +import onceMultiple from './onceMultiple.js'; +import WebSocket from 'ws'; -let ws; +const WS = typeof window !== 'undefined' ? window.WebSocket : WebSocket; -const runningInBrowser = typeof window !== 'undefined'; - -if (runningInBrowser) { - ws = WebSocket; -} else { - ws = require('ws'); -} - -module.exports = class BrokerClient extends BrokerBase { +export default class BrokerClient extends BrokerBase { /** * BrokerClient constructor * @param {object} params Parameters @@ -47,13 +40,7 @@ module.exports = class BrokerClient extends BrokerBase { this.setMaxListeners(20); - for (const func of [ - this.onOpen, - this.onClose, - this.onError, - this.connect, - this.onSocketMessage, - ]) { + for (const func of [this.onOpen, this.onClose, this.onError, this.connect, this.onSocketMessage]) { const name = func.name; this[name] = func.bind(this); } @@ -126,7 +113,7 @@ module.exports = class BrokerClient extends BrokerBase { url = { hostname: options.host, port: options.port }; } - const scheme = (url.protocol === 'https:' || this.ssl) ? 'wss' : 'ws'; + const scheme = url.protocol === 'https:' || this.ssl ? 'wss' : 'ws'; const webSocketURL = url.port ? `${scheme}://${url.hostname}:${url.port}${this.webSocketURL}` @@ -134,22 +121,22 @@ module.exports = class BrokerClient extends BrokerBase { this.logger.info(`BrokerClient is connecting to ${webSocketURL}`); - if (typeof process !== "undefined" && process.versions != null && process.versions.node != null) { + if (typeof process !== 'undefined' && process.versions != null && process.versions.node != null) { const NODE_TLS_REJECT_UNAUTHORIZED = process.env.NODE_TLS_REJECT_UNAUTHORIZED; process.env.NODE_TLS_REJECT_UNAUTHORIZED = 0; - this.socket = new ws(`${webSocketURL}?module=true`); + this.socket = new WS(`${webSocketURL}?module=true`); process.env.NODE_TLS_REJECT_UNAUTHORIZED = NODE_TLS_REJECT_UNAUTHORIZED; } else { - this.socket = new ws(webSocketURL); + this.socket = new WS(webSocketURL); } - + this.addSocketListeners(); } /** * Duplicates a BrokerClient in order to share the same WebSocket. * @param {object} params Parameters - * @param {string} params.moduleName Module name of the duplicate BrokerClient (a duplicate + * @param {string} params.moduleName Module name of the duplicate BrokerClient (a duplicate * can have a different name than its parent) * @returns {BrokerClient} */ @@ -181,12 +168,10 @@ module.exports = class BrokerClient extends BrokerBase { duplicate.on('destroy', () => duplicates.delete(duplicate)); duplicates.add(duplicate); - duplicate - .ping() - .catch((ex) => { - console.error(`Failed to send ping`); - console.trace(ex.message); - }); + duplicate.ping().catch((ex) => { + console.error(`Failed to send ping`); + console.trace(ex.message); + }); return duplicate; } @@ -301,21 +286,20 @@ module.exports = class BrokerClient extends BrokerBase { } } - await this.sendResponse( - socket, - message, - false, - [{ error: `${message.eventName} sent to ${this.moduleName}. This is probably a mistake.` }], - ); + await this.sendResponse(socket, message, false, [ + { + error: `${message.eventName} sent to ${this.moduleName}. This is probably a mistake.`, + }, + ]); } break; } case 'unsubscribe': { - const [vendor, moduleName, ...rest] = message.eventName.split("."); - const eventName = rest.join("."); - const targetModuleName = [vendor, moduleName].join("."); + const [vendor, moduleName, ...rest] = message.eventName.split('.'); + const eventName = rest.join('.'); + const targetModuleName = [vendor, moduleName].join('.'); if (this.moduleName === targetModuleName) { this.emit('unsubscribe', { eventName }); @@ -331,12 +315,11 @@ module.exports = class BrokerClient extends BrokerBase { } } - await this.sendResponse( - socket, - message, - false, - [{ error: `${message.eventName} sent to ${this.moduleName}. This is probably a mistake.` }], - ); + await this.sendResponse(socket, message, false, [ + { + error: `${message.eventName} sent to ${this.moduleName}. This is probably a mistake.`, + }, + ]); } break; @@ -371,12 +354,11 @@ module.exports = class BrokerClient extends BrokerBase { default: { if (!this.apiHandlers.has(message.type)) { - await this.sendResponse( - socket, - message, - false, - [{ error: `There is no handler registered for this type of message: ${message.type}` }], - ); + await this.sendResponse(socket, message, false, [ + { + error: `There is no handler registered for this type of message: ${message.type}`, + }, + ]); return; } @@ -425,23 +407,19 @@ module.exports = class BrokerClient extends BrokerBase { /** * Sends a message. * @async - * @param {object} message + * @param {object} message * @returns {Promise.} */ async sendMessage(message) { const id = uuid(); const socket = this.isDuplicate ? this.parent.getSocket() : this.socket; - const webSocketMessage = Object.assign( - {}, - message, - { - id, - time: new Date().valueOf(), - moduleName: this.moduleName, - }, - ); - - if (socket.readyState !== ws.OPEN) { + const webSocketMessage = Object.assign({}, message, { + id, + time: new Date().valueOf(), + moduleName: this.moduleName, + }); + + if (socket.readyState !== WS.OPEN) { try { await onceMultiple(this, ['connect'], webSocketMessage.timeout || this.messageTimeout); } catch (ex) { @@ -488,18 +466,13 @@ module.exports = class BrokerClient extends BrokerBase { */ resubscribeModuleEvents() { for (const eventName of this.events.keys()) { - const moduleName = eventName - .split('.') - .slice(0, 2) - .join('.'); - - this - .sendMessage({ - type: 'subscribe', - eventName, - targetModuleName: moduleName, - }) - .catch(new Function()); + const moduleName = eventName.split('.').slice(0, 2).join('.'); + + this.sendMessage({ + type: 'subscribe', + eventName, + targetModuleName: moduleName, + }).catch(new Function()); } } @@ -532,7 +505,7 @@ module.exports = class BrokerClient extends BrokerBase { /** * @private - * @param {ErrorEvent} err + * @param {ErrorEvent} err */ onError(err) { if (err.error instanceof Error) { @@ -656,13 +629,13 @@ module.exports = class BrokerClient extends BrokerBase { } } - /** + /** * Register a module's API handlers to RealityHub * @async - * @param {Object.} handlers Key will be registered to the API tree. + * @param {Object.} handlers Key will be registered to the API tree. * @param {*} [context=null] Handlers' `this` will be set to this context. - * The value (function) will handle the API calls. - * @param {string} [remote='hub.core'] Remote + * The value (function) will handle the API calls. + * @param {string} [remote='hub.core'] Remote * @example * // server.js * brokerClient.registerAPIHandlers(this, { @@ -670,7 +643,7 @@ module.exports = class BrokerClient extends BrokerBase { * return number1 + number2; * }, * }).catch((ex) => console.trace(ex)); - * + * * // client.js * brokerClient.api.moduleVendor.moduleName.addNumber(3, 5) * .then((result) => { @@ -701,8 +674,8 @@ module.exports = class BrokerClient extends BrokerBase { * RealityHub will look for an `index.js` file in this path. This script file will be imported * by RealityHub's `index.html` via a `