diff --git a/examples/.gitignore b/examples/.gitignore deleted file mode 100644 index b049ffa..0000000 --- a/examples/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -*.sock -/node_modules/ diff --git a/examples/client/.babelrc b/examples/client/.babelrc new file mode 100644 index 0000000..ce118af --- /dev/null +++ b/examples/client/.babelrc @@ -0,0 +1,28 @@ +{ + "presets": [ + [ + "@babel/preset-env", { + "modules": false, + "loose": false, + "useBuiltIns": "usage" + } + ], + [ + "@babel/preset-stage-2", { + "modules": false, + "loose": false, + "useBuiltIns": true, + "decoratorsLegacy": true + } + ] + ], + "plugins": [ + [ + "@babel/transform-runtime", { + "polyfill": false, + "regenerator": false + } + ] + ], + "comments": false +} diff --git a/examples/client/.editorconfig b/examples/client/.editorconfig new file mode 100644 index 0000000..9d08a1a --- /dev/null +++ b/examples/client/.editorconfig @@ -0,0 +1,9 @@ +root = true + +[*] +charset = utf-8 +indent_style = space +indent_size = 2 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true diff --git a/examples/client/.eslintignore b/examples/client/.eslintignore new file mode 100644 index 0000000..9b1c8b1 --- /dev/null +++ b/examples/client/.eslintignore @@ -0,0 +1 @@ +/dist diff --git a/examples/client/.eslintrc.js b/examples/client/.eslintrc.js new file mode 100644 index 0000000..9d74edb --- /dev/null +++ b/examples/client/.eslintrc.js @@ -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/examples/client/.gitignore b/examples/client/.gitignore new file mode 100644 index 0000000..c1356d3 --- /dev/null +++ b/examples/client/.gitignore @@ -0,0 +1,19 @@ +.quasar +.DS_Store +.thumbs.db +node_modules +/dist +/src-cordova/platforms +/src-cordova/plugins +/src-cordova/www +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Editor directories and files +.idea +.vscode +*.suo +*.ntvs* +*.njsproj +*.sln diff --git a/examples/client/.postcssrc.js b/examples/client/.postcssrc.js new file mode 100644 index 0000000..1174fe5 --- /dev/null +++ b/examples/client/.postcssrc.js @@ -0,0 +1,8 @@ +// https://github.com/michael-ciniawsky/postcss-load-config + +module.exports = { + plugins: [ + // to edit target browsers: use "browserslist" field in package.json + require('autoprefixer') + ] +} diff --git a/examples/client/.stylintrc b/examples/client/.stylintrc new file mode 100644 index 0000000..ce38d77 --- /dev/null +++ b/examples/client/.stylintrc @@ -0,0 +1,35 @@ +{ + "blocks": "never", + "brackets": "never", + "colons": "never", + "colors": "always", + "commaSpace": "always", + "commentSpace": "always", + "cssLiteral": "never", + "depthLimit": false, + "duplicates": true, + "efficient": "always", + "extendPref": false, + "globalDupe": true, + "indentPref": 2, + "leadingZero": "never", + "maxErrors": false, + "maxWarnings": false, + "mixed": false, + "namingConvention": false, + "namingConventionStrict": false, + "none": "never", + "noImportant": false, + "parenSpace": "never", + "placeholder": false, + "prefixVarsWithDollar": "always", + "quotePref": "single", + "semicolons": "never", + "sortOrder": false, + "stackedProperties": "never", + "trailingWhitespace": "never", + "universal": "never", + "valid": true, + "zeroUnits": "never", + "zIndexNormalize": false +} diff --git a/examples/client/README.md b/examples/client/README.md new file mode 100644 index 0000000..51830ce --- /dev/null +++ b/examples/client/README.md @@ -0,0 +1,3 @@ +# Quasar App + +> WIP diff --git a/examples/client/package.json b/examples/client/package.json new file mode 100644 index 0000000..55f6506 --- /dev/null +++ b/examples/client/package.json @@ -0,0 +1,40 @@ +{ + "name": "client", + "version": "1.0.0", + "description": "simple web socket client tester", + "productName": "client", + "cordovaId": "org.cordova.quasar.app", + "author": "David Kebler ", + "private": true, + "scripts": { + "lint": "eslint --ext .js,.vue src", + "test": "echo \"No test specified\" && exit 0" + }, + "dependencies": { + "@uci/websocket-client": "^0.1.2", + "axios": "^0.18.0", + "better-try-catch": "^0.6.2" + }, + "devDependencies": { + "babel-eslint": "^8.2.1", + "eslint": "^4.18.2", + "eslint-config-standard": "^11.0.0", + "eslint-friendly-formatter": "^4.0.1", + "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", + "eslint-plugin-vue": "^4.3.0", + "quasar-cli": "^0.16.0" + }, + "engines": { + "node": ">= 8.9.0", + "npm": ">= 5.6.0" + }, + "browserslist": [ + "> 1%", + "last 2 versions", + "not ie <= 10" + ] +} diff --git a/examples/client/quasar.conf.js b/examples/client/quasar.conf.js new file mode 100644 index 0000000..2885341 --- /dev/null +++ b/examples/client/quasar.conf.js @@ -0,0 +1,139 @@ +// Configuration for your app + +module.exports = function (ctx) { + return { + // app plugins (/src/plugins) + plugins: [ + ], + css: [ + 'app.styl' + ], + extras: [ + ctx.theme.mat ? 'roboto-font' : null, + 'material-icons', + ctx.theme.ios ? 'ionicons' : null + // 'mdi', + // 'fontawesome' + ], + supportIE: false, + build: { + scopeHoisting: true, + vueRouterMode: 'history', + // vueCompiler: true, + // gzip: true, + // analyze: true, + // extractCSS: false, + extendWebpack (cfg) { + cfg.module.rules.push({ + enforce: 'pre', + test: /\.(js|vue)$/, + loader: 'eslint-loader', + exclude: /(node_modules|quasar)/ + }) + } + }, + devServer: { + // https: true, + // port: 8080, + open: true // opens browser window automatically + }, + // framework: 'all' --- includes everything; for dev only! + framework: { + components: [ + 'QLayout', + 'QLayoutHeader', + 'QLayoutDrawer', + 'QPageContainer', + 'QPage', + 'QToolbar', + 'QToolbarTitle', + 'QBtn', + 'QIcon', + 'QList', + 'QListHeader', + 'QItem', + 'QItemMain', + 'QItemSide', + 'QInput', + 'QField' + ], + directives: [ + 'Ripple' + ], + // Quasar plugins + plugins: [ + 'Notify' + ], + iconSet: ctx.theme.mat ? 'material-icons' : 'ionicons' + }, + // animations: 'all' --- includes all animations + animations: [ + ], + pwa: { + // workboxPluginMode: 'InjectManifest', + // workboxOptions: {}, + manifest: { + // name: 'Quasar App', + // short_name: 'Quasar-PWA', + // description: 'Best PWA App in town!', + display: 'standalone', + orientation: 'portrait', + background_color: '#ffffff', + theme_color: '#027be3', + icons: [ + { + 'src': 'statics/icons/icon-128x128.png', + 'sizes': '128x128', + 'type': 'image/png' + }, + { + 'src': 'statics/icons/icon-192x192.png', + 'sizes': '192x192', + 'type': 'image/png' + }, + { + 'src': 'statics/icons/icon-256x256.png', + 'sizes': '256x256', + 'type': 'image/png' + }, + { + 'src': 'statics/icons/icon-384x384.png', + 'sizes': '384x384', + 'type': 'image/png' + }, + { + 'src': 'statics/icons/icon-512x512.png', + 'sizes': '512x512', + 'type': 'image/png' + } + ] + } + }, + cordova: { + // id: 'org.cordova.quasar.app' + }, + electron: { + // bundler: 'builder', // or 'packager' + extendWebpack (cfg) { + // do something with Electron process Webpack cfg + }, + packager: { + // https://github.com/electron-userland/electron-packager/blob/master/docs/api.md#options + + // OS X / Mac App Store + // appBundleId: '', + // appCategoryType: '', + // osxSign: '', + // protocol: 'myapp://path', + + // Window only + // win32metadata: { ... } + }, + builder: { + // https://www.electron.build/configuration/configuration + + // appId: 'quasar-app' + } + } + } +} diff --git a/examples/client/src/App.vue b/examples/client/src/App.vue new file mode 100644 index 0000000..3fa5037 --- /dev/null +++ b/examples/client/src/App.vue @@ -0,0 +1,14 @@ + + + + + diff --git a/examples/client/src/assets/quasar-logo-full.svg b/examples/client/src/assets/quasar-logo-full.svg new file mode 100644 index 0000000..281d072 --- /dev/null +++ b/examples/client/src/assets/quasar-logo-full.svg @@ -0,0 +1,191 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/client/src/assets/sad.svg b/examples/client/src/assets/sad.svg new file mode 100644 index 0000000..628136f --- /dev/null +++ b/examples/client/src/assets/sad.svg @@ -0,0 +1 @@ + diff --git a/examples/client/src/components/.gitkeep b/examples/client/src/components/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/examples/client/src/css/app.styl b/examples/client/src/css/app.styl new file mode 100644 index 0000000..e3e5a1e --- /dev/null +++ b/examples/client/src/css/app.styl @@ -0,0 +1 @@ +// app global css diff --git a/examples/client/src/css/themes/common.variables.styl b/examples/client/src/css/themes/common.variables.styl new file mode 100644 index 0000000..4d659aa --- /dev/null +++ b/examples/client/src/css/themes/common.variables.styl @@ -0,0 +1,25 @@ +// App Shared Variables +// -------------------------------------------------- +// To customize the look and feel of this app, you can override +// the Stylus variables found in Quasar's source Stylus files. Setting +// variables before Quasar's Stylus will use these variables rather than +// Quasar's default Stylus variable values. Stylus variables specific +// to the themes belong in either the variables.ios.styl or variables.mat.styl files. + +// Check documentation for full list of Quasar variables + + +// App Shared Color Variables +// -------------------------------------------------- +// It's highly recommended to change the default colors +// to match your app's branding. + +$primary = #027be3 +$secondary = #26A69A +$tertiary = #555 + +$neutral = #E0E1E2 +$positive = #21BA45 +$negative = #DB2828 +$info = #31CCEC +$warning = #F2C037 diff --git a/examples/client/src/css/themes/variables.ios.styl b/examples/client/src/css/themes/variables.ios.styl new file mode 100644 index 0000000..953b825 --- /dev/null +++ b/examples/client/src/css/themes/variables.ios.styl @@ -0,0 +1,7 @@ +// App Shared Variables +// -------------------------------------------------- +// Shared Stylus variables go in the common.variables.styl file +@import 'common.variables' + +// iOS only Quasar variables overwrites +// ----------------------------------------- diff --git a/examples/client/src/css/themes/variables.mat.styl b/examples/client/src/css/themes/variables.mat.styl new file mode 100644 index 0000000..8169a52 --- /dev/null +++ b/examples/client/src/css/themes/variables.mat.styl @@ -0,0 +1,7 @@ +// App Shared Variables +// -------------------------------------------------- +// Shared Stylus variables go in the common.variables.styl file +@import 'common.variables' + +// Material only Quasar variables overwrites +// ----------------------------------------- diff --git a/examples/client/src/index.template.html b/examples/client/src/index.template.html new file mode 100644 index 0000000..be92ba0 --- /dev/null +++ b/examples/client/src/index.template.html @@ -0,0 +1,25 @@ + + + + + + + + + <%= htmlWebpackPlugin.options.productName %> + + + + + + + + + +
+ + + + diff --git a/examples/client/src/layouts/default.vue b/examples/client/src/layouts/default.vue new file mode 100644 index 0000000..2a37c35 --- /dev/null +++ b/examples/client/src/layouts/default.vue @@ -0,0 +1,38 @@ + + + + + diff --git a/examples/client/src/pages/404.vue b/examples/client/src/pages/404.vue new file mode 100644 index 0000000..6484cb2 --- /dev/null +++ b/examples/client/src/pages/404.vue @@ -0,0 +1,16 @@ + diff --git a/examples/client/src/pages/index.vue b/examples/client/src/pages/index.vue new file mode 100644 index 0000000..bd4a70a --- /dev/null +++ b/examples/client/src/pages/index.vue @@ -0,0 +1,62 @@ + + + + diff --git a/examples/client/src/plugins/.gitkeep b/examples/client/src/plugins/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/examples/client/src/plugins/socket.js b/examples/client/src/plugins/socket.js new file mode 100644 index 0000000..fc60a57 --- /dev/null +++ b/examples/client/src/plugins/socket.js @@ -0,0 +1,11 @@ +// import WebSocket from '@uci/websocket-client' +import WebSocket from '../../../../../uci-websocket-client/browser-client' + +const ws = new WebSocket(process.env.WSS || 'ws://0.0.0.0:8090') + +export { ws } + +// leave the export, even if you don't use it +export default ({ Vue }) => { + Vue.prototype.$socket = ws +} diff --git a/examples/client/src/plugins/ssaxios.js b/examples/client/src/plugins/ssaxios.js new file mode 100644 index 0000000..325a88e --- /dev/null +++ b/examples/client/src/plugins/ssaxios.js @@ -0,0 +1,10 @@ +import axios from 'axios' + +export default ({ Vue }) => { + // we add it to Vue prototype + // so we can reference it in Vue files + // without the need to import axios + Vue.prototype.$axios = axios + + // Example: this.$axios will reference Axios now so you don't need stuff like vue-axios +} diff --git a/examples/client/src/router/index.js b/examples/client/src/router/index.js new file mode 100644 index 0000000..67486b2 --- /dev/null +++ b/examples/client/src/router/index.js @@ -0,0 +1,24 @@ +import Vue from 'vue' +import VueRouter from 'vue-router' + +import routes from './routes' + +Vue.use(VueRouter) + +const Router = new VueRouter({ + /* + * NOTE! Change Vue Router mode from quasar.conf.js -> build -> vueRouterMode + * + * When going with "history" mode, please also make sure "build.publicPath" + * is set to something other than an empty string. + * Example: '/' instead of '' + */ + + // Leave as is and change from quasar.conf.js instead! + mode: process.env.VUE_ROUTER_MODE, + base: process.env.VUE_ROUTER_BASE, + scrollBehavior: () => ({ y: 0 }), + routes +}) + +export default Router diff --git a/examples/client/src/router/routes.js b/examples/client/src/router/routes.js new file mode 100644 index 0000000..e098954 --- /dev/null +++ b/examples/client/src/router/routes.js @@ -0,0 +1,15 @@ + +export default [ + { + path: '/', + component: () => import('layouts/default'), + children: [ + { path: '', component: () => import('pages/index') } + ] + }, + + { // Always leave this as last one + path: '*', + component: () => import('pages/404') + } +] diff --git a/examples/client/src/statics/icons/apple-icon-152x152.png b/examples/client/src/statics/icons/apple-icon-152x152.png new file mode 100644 index 0000000..c918acd Binary files /dev/null and b/examples/client/src/statics/icons/apple-icon-152x152.png differ diff --git a/examples/client/src/statics/icons/favicon-16x16.png b/examples/client/src/statics/icons/favicon-16x16.png new file mode 100644 index 0000000..177c86e Binary files /dev/null and b/examples/client/src/statics/icons/favicon-16x16.png differ diff --git a/examples/client/src/statics/icons/favicon-32x32.png b/examples/client/src/statics/icons/favicon-32x32.png new file mode 100644 index 0000000..b8e6cdf Binary files /dev/null and b/examples/client/src/statics/icons/favicon-32x32.png differ diff --git a/examples/client/src/statics/icons/icon-128x128.png b/examples/client/src/statics/icons/icon-128x128.png new file mode 100644 index 0000000..590e8ce Binary files /dev/null and b/examples/client/src/statics/icons/icon-128x128.png differ diff --git a/examples/client/src/statics/icons/icon-192x192.png b/examples/client/src/statics/icons/icon-192x192.png new file mode 100644 index 0000000..2a9b50b Binary files /dev/null and b/examples/client/src/statics/icons/icon-192x192.png differ diff --git a/examples/client/src/statics/icons/icon-256x256.png b/examples/client/src/statics/icons/icon-256x256.png new file mode 100644 index 0000000..8450959 Binary files /dev/null and b/examples/client/src/statics/icons/icon-256x256.png differ diff --git a/examples/client/src/statics/icons/icon-384x384.png b/examples/client/src/statics/icons/icon-384x384.png new file mode 100644 index 0000000..2d6dd7b Binary files /dev/null and b/examples/client/src/statics/icons/icon-384x384.png differ diff --git a/examples/client/src/statics/icons/icon-512x512.png b/examples/client/src/statics/icons/icon-512x512.png new file mode 100644 index 0000000..a57354e Binary files /dev/null and b/examples/client/src/statics/icons/icon-512x512.png differ diff --git a/examples/client/src/statics/icons/ms-icon-144x144.png b/examples/client/src/statics/icons/ms-icon-144x144.png new file mode 100644 index 0000000..b0880e8 Binary files /dev/null and b/examples/client/src/statics/icons/ms-icon-144x144.png differ diff --git a/examples/client/src/statics/quasar-logo.png b/examples/client/src/statics/quasar-logo.png new file mode 100644 index 0000000..590e8ce Binary files /dev/null and b/examples/client/src/statics/quasar-logo.png differ diff --git a/examples/server.js b/examples/server.js index bd8770c..060a0d7 100644 --- a/examples/server.js +++ b/examples/server.js @@ -1,35 +1,24 @@ import { Socket } from '../src' - ; +async function packetProcess (packet) { + return new Promise(resolve => { + let res = {} + if (packet.cmd !== 'doit') { + res.response = `the payload sent was: ${packet.payload} with command ${packet.cmd}` + } else { + res.cmd = 'doit' + res.response = 'take some action in browser like color a button green' } + resolve (res) + }) +} + +// let test = new Test() +let test = new Socket({port:8090, clientTracking:true}) +test.registerPacketProcessor(packetProcess) + +; (async () => { - class Test extends Socket { - constructor(opts) { - super(opts) - } - - // async _packetProcess(packet) { - // console.log('packet being processed at socket', packet) - // if (packet.cmd) return await this[packet.cmd](packet.data,packet.name) - // return {error: 'no command in packet', packet: packet } - // } - // - // async doit(data,name) { - // return new Promise(resolve => { - // let res = {} - // console.log('data sent to doit = ', data) - // res.status ='success' - // res.name = name - // res.cmd = 'reply' - // res.data = data - // resolve(res) - // }) - // } - - } - - // let test = new Test() - let test = new Test({port:8090}) console.log(await test.create()) })().catch(err => { diff --git a/package.json b/package.json index 9ede516..050e466 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@uci/websocket", - "version": "0.1.6", + "version": "0.1.9", "description": "JSON packet host websocket server", "main": "src", "scripts": { @@ -35,16 +35,16 @@ "chai": "^4.1.2", "chai-as-promised": "^7.1.1", "codecov": "^3.0.0", - "esm": "^3.0.21", + "esm": "^3.0.34", "istanbul": "^0.4.5", "mocha": "^5.0.1", "nodemon": "^1.15.1" }, "dependencies": { - "@uci/logger": "0.0.1", + "@uci/logger": "0.0.3", "better-try-catch": "^0.6.2", "clone": "^2.1.1", "death": "^1.1.0", - "ws": "^5.1.0" + "ws": "^5.1.1" } } diff --git a/src/socket.js b/src/socket.js index 04f955d..995b857 100644 --- a/src/socket.js +++ b/src/socket.js @@ -10,7 +10,8 @@ let log = {} export default class Socket extends WebSocket.Server { constructor (opts = {}) { opts.host = opts.host || '0.0.0.0' - opts.port = opts.port || 8080 + opts.port = opts.port || 8090 + opts.clientTracking = opts.clientTracking || true super(opts) this.id = opts.id || opts.name || 'Websocket:'+ new Date().getTime() this.opts = opts // for use to recover from selected errors @@ -43,7 +44,7 @@ export default class Socket extends WebSocket.Server { this.on('listening', async () => { this._listen() log.info('websocket server created and listening at', this.address()) - resolve('websocket ready and listening') + resolve(`websocket ready and listening at ${this.address().address}:${this.address().port}`) }) }) @@ -56,27 +57,37 @@ export default class Socket extends WebSocket.Server { _listen () { - this.on('connection', async (socket) => { - const send = this._send.bind(socket) - log.info('new consumer connecting') - socket.on('message', messageProcess.bind(this)) + this.on('connection', async (socket, req) => { + let send = this._send.bind(socket) + log.info({req:req},'new consumer connecting') + socket.address = req.remoteAddress + socket.on('message', messageProcess.bind(this,socket)) - async function messageProcess (strPacket) { - log.info(' incoming packet on socket side') + async function messageProcess (client,strPacket) { + log.info({packet:strPacket},' incoming packet on web socket side') let res = {} let [err, packet] = btc(JSON.parse)(strPacket) + log.info('packet', err, packet) if (err) { res = {error: `Could not parse JSON: ${packet}`} } else { - res = await this._packetProcess(clone(packet)) - if (Object.keys(res).length === 0) res = { error: 'socket packet command function likely did not return a promise', packet:packet} + if (packet.clientID) { + client.ID = packet.clientID + res.cmd='ackID' + } + else { + res = await this._packetProcess(clone(packet)) || {} + if (Object.keys(res).length === 0) res = { error: 'socket packet command function likely did not return a promise', packet:packet} + } } - res._header = clone(packet._header,false) //make sure return packet has header with id in case it was removed in processing - delete packet._header // remove before adding to response header as request + if (packet) { + res._header = clone(packet._header,false) || {} //make sure return packet has header with id in case it was removed in processing + delete packet._header // remove before adding to response header as request + } else res._header = {} res._header.request = clone(packet,false) res._header.responder = {name:this.name,instanceID:this.id} res._header.socket = this.address() if (!res.cmd) res.cmd = 'reply' // by default return command is 'reply' - log.info(await send(res)) + log.info({packet:res},await send(res)) } }) // end connected consumer @@ -97,14 +108,22 @@ export default class Socket extends WebSocket.Server { }) } - // must have a consumer socket instance bound to call this!! + async push (packet,id) { + this.clients.forEach(async (client) => { + if (client.readyState === WebSocket.OPEN) { + if (!id || id ===client.ID ) await this._send.bind(client)(packet) + } + }) + } + + // must have a consumer socket instance bound to call this function async _send (packet) { return new Promise( (resolve,reject) => { if (this.readyState !== 1 ) reject (`Connection not Ready, CODE:${this.readyState}`) let [err,message] = btc(JSON.stringify)(packet) if (err) reject(`Could not JSON stringify: ${packet}`) this.send(message) - resolve('sent packet', packet) + resolve('sent packet') }) }