From b6be936f4483cfa1352ec301d7f9b0a7a6c153d7 Mon Sep 17 00:00:00 2001 From: David Kebler Date: Thu, 5 Apr 2018 15:11:59 -0700 Subject: [PATCH] working web browser client (with quasar) uses json packets and specifically works with uci-websocket --- .eslintrc.js | 34 ++++++++++++++++++ .eslintrc.js.off2 | 47 ++++++++++++++++++++++++ .gitignore | 5 +++ .npmignore | 4 +++ browser-client.js | 92 +++++++++++++++++++++++++++++++++++++++++++++++ package.json | 34 ++++++++++++++++++ readme.md | 21 +++++++++++ 7 files changed, 237 insertions(+) create mode 100644 .eslintrc.js create mode 100644 .eslintrc.js.off2 create mode 100644 .gitignore create mode 100644 .npmignore create mode 100644 browser-client.js create mode 100644 package.json create mode 100644 readme.md diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..bf8e8cb --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,34 @@ +module.exports = { + "ecmaFeatures": { + "modules": true, + "spread" : true, + "restParams" : true + }, + "env": { + "es6": true, + "node": true, + "mocha": true + }, + "parserOptions": { + "ecmaVersion": 2017, + "sourceType": "module", + "parser": 'babel-eslint', + }, + "extends": "eslint:recommended", + "rules": { + "indent": [ + "error", + 2 + ], + "no-console": 0, + "semi": ["error", "never"], + "linebreak-style": [ + "error", + "unix" + ], + "quotes": [ + "error", + "single" + ] + } +} diff --git a/.eslintrc.js.off2 b/.eslintrc.js.off2 new file mode 100644 index 0000000..4b1913d --- /dev/null +++ b/.eslintrc.js.off2 @@ -0,0 +1,47 @@ +module.exports = { + root: true, + parserOptions: { + parser: 'babel-eslint', + sourceType: 'module' + }, + env: { + browser: true + }, + extends: [ + // https://github.com/vuejs/eslint-plugin-vue#priority-a-essential-error-prevention + // consider switching to `plugin:vue/strongly-recommended` or `plugin:vue/recommended` for stricter rules. + // 'plugin:vue/essential', + // https://github.com/standard/standard/blob/master/docs/RULES-en.md + 'standard' + ], + // required to lint *.vue files + plugins: [ + // 'vue' + ], + globals: { + 'ga': true, // Google Analytics + 'cordova': true, + '__statics': true + }, + // add your custom rules here + 'rules': { + // allow async-await + 'generator-star-spacing': 'off', + + // allow paren-less arrow functions + 'arrow-parens': 0, + 'one-var': 0, + + 'import/first': 0, + 'import/named': 2, + 'import/namespace': 2, + 'import/default': 2, + 'import/export': 2, + 'import/extensions': 0, + 'import/no-unresolved': 0, + 'import/no-extraneous-dependencies': 0, + + // allow debugger during development + 'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0 + } +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..caddd9f --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +/node_modules/ +/coverage/ +/syncd/ +*.log +/temp/ diff --git a/.npmignore b/.npmignore new file mode 100644 index 0000000..f16fc41 --- /dev/null +++ b/.npmignore @@ -0,0 +1,4 @@ +tests/ +test/ +*.test.js +testing/ diff --git a/browser-client.js b/browser-client.js new file mode 100644 index 0000000..7c97d6f --- /dev/null +++ b/browser-client.js @@ -0,0 +1,92 @@ +// Websocket is a native global for vanilla JS +/* globals WebSocket:true */ + +import btc from 'better-try-catch' +import EventEmitter from 'eventemitter3' +import autoBind from 'auto-bind' + +class Consumer extends EventEmitter { + constructor (url, opts = {}) { + super() + this.name = opts.name || 'browser' + this.instanceID = new Date().getTime() + this.url = url + this.protocol = opts.protocol + autoBind(this) + } + + async connect () { + return new Promise((resolve,reject) => { + const socket = new WebSocket(this.url, this.protocol) + // Connection opened + socket.addEventListener('open', open.bind(this)) + function open () { + this.socket = socket + resolve(`socket open to server at : ${this.url}`) + } + + setTimeout(function () { + reject(new Error('Socket did not connect in 5 seconds')) + }, 5000) + + socket.addEventListener('error', function () { + // console.log('Web Socket error occurred') + reject(new Error('Could not connect to socket server ')) + }) + }) + } + + listen (func) { + this.socket.addEventListener('message', handler.bind(this)) + + function handler (event) { + let packet = {} + if (this.socket.readyState === 1) { + let [err, parsed] = btc(JSON.parse)(event.data) + if (err) packet = {error: `Could not parse JSON: ${event.data}`} + else packet = parsed + } else { + packet = {error: `Connection not Ready, CODE:${this.socket.readyState}`} + } + if (func) func(packet) + this.emit(packet._header.id, packet) + } + } + + async send (packet) { + return new Promise((resolve, reject) => { + if (this.socket.readyState !== 1) reject(new Error(`Connection not Ready, CODE:${this.socket.readyState}`)) + packet._header = + { id: Math.random().toString().slice(2), // need this for when multiple sends for different consumers use same packet instanceack + sender: { name: this.name, instanceID: this.instanceID }, + url: this.url + } + let [err, message] = btc(JSON.stringify)(packet) + if (err) reject(new Error(`Could not JSON stringify: ${packet}`)) + // console.log('message to send', message) + this.socket.send(message) + // listen for when packet comes back with unique header id + this.once(packet._header.id, async function (reply) { + // console.log('reply emitted', reply) + let res = await this._packetProcess(reply) + if (!res) { // if process was not promise returning like just logged to console + res = reply + console.log('consumer function was not promise returning - resolving unprocessed') + } + if (res.cmd ==='reply') resolve(res.response) + else resolve(res) + }) // end reply listener + }) + } + + registerPacketProcessor (func) { + this._packetProcess = func + } + + async _packetProcess (packet) { + return Promise.resolve(packet) + } +} // end Consumer Class + + +export default Consumer diff --git a/package.json b/package.json new file mode 100644 index 0000000..b2dc674 --- /dev/null +++ b/package.json @@ -0,0 +1,34 @@ +{ + "name": "@uci/websocket-client", + "version": "0.1.2", + "description": "JSON packet browser client over web socket", + "main": "browser-client", + "scripts": { + "test": "" + }, + "author": "David Kebler", + "license": "MIT", + "repository": { + "type": "git", + "url": "git+https://github.com/uCOMmandIt/websocket-client.git" + }, + "keywords": [ + "node.js", + "socket", + "websocket", + "net", + "JSON", + "packet", + "serialize", + "TCP" + ], + "bugs": { + "url": "https://github.com/uCOMmandIt/websocket-client/issues" + }, + "homepage": "https://github.com/uCOMmandIt/websocket-client#readme", + "devDependencies": {}, + "dependencies": { + "auto-bind": "^1.2.0", + "eventemitter3": "^3.0.1" + } +} diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..3069ddb --- /dev/null +++ b/readme.md @@ -0,0 +1,21 @@ +# UComandIt Websocket Class for Browsers sending and receiving JSON packets + +## What is it + +## TL/DR; + +## What's it good for + +## Why Bother + +## Getting Started + +"babel-eslint": "^8.2.1", +"eslint": "^4.18.2", +"eslint-config-standard": "^11.0.0", +"eslint-friendly-formatter": "^3.0.0", +"eslint-loader": "^2.0.0", +"eslint-plugin-import": "^2.9.0", +"eslint-plugin-node": "^6.0.1", +"eslint-plugin-promise": "^3.7.0", +"eslint-plugin-standard": "^3.0.1"