koa-ws-socket
Advanced tools
Comparing version 0.0.1 to 0.1.0
@@ -1,11 +0,339 @@ | ||
(function(global, factory) { | ||
typeof exports === 'object' && typeof module !== 'undefined' | ||
? factory() | ||
: typeof define === 'function' && define.amd ? define(factory) : factory(); | ||
})(this, function() { | ||
'use strict'; | ||
(function (global, factory) { | ||
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('ws'), require('crypto')) : | ||
typeof define === 'function' && define.amd ? define(['exports', 'ws', 'crypto'], factory) : | ||
(factory((global.koaWsSocket = {}),global.WebSocket,global.crypto)); | ||
}(this, (function (exports,WebSocket,crypto) { 'use strict'; | ||
Object.defineProperty(exports, '__esModule', { value: true }); | ||
exports.default = {}; | ||
crypto = crypto && crypto.hasOwnProperty('default') ? crypto['default'] : crypto; | ||
/** | ||
* Expose compositor. | ||
*/ | ||
var koaCompose = compose; | ||
/** | ||
* Compose `middleware` returning | ||
* a fully valid middleware comprised | ||
* of all those which are passed. | ||
* | ||
* @param {Array} middleware | ||
* @return {Function} | ||
* @api public | ||
*/ | ||
function compose (middleware) { | ||
if (!Array.isArray(middleware)) throw new TypeError('Middleware stack must be an array!') | ||
for (const fn of middleware) { | ||
if (typeof fn !== 'function') throw new TypeError('Middleware must be composed of functions!') | ||
} | ||
/** | ||
* @param {Object} context | ||
* @return {Promise} | ||
* @api public | ||
*/ | ||
return function (context, next) { | ||
// last called middleware # | ||
let index = -1; | ||
return dispatch(0) | ||
function dispatch (i) { | ||
if (i <= index) return Promise.reject(new Error('next() called multiple times')) | ||
index = i; | ||
let fn = middleware[i]; | ||
if (i === middleware.length) fn = next; | ||
if (!fn) return Promise.resolve() | ||
try { | ||
return Promise.resolve(fn(context, function next () { | ||
return dispatch(i + 1) | ||
})) | ||
} catch (err) { | ||
return Promise.reject(err) | ||
} | ||
} | ||
} | ||
} | ||
function createCommonjsModule(fn, module) { | ||
return module = { exports: {} }, fn(module, module.exports), module.exports; | ||
} | ||
var base64id = createCommonjsModule(function (module, exports) { | ||
/*! | ||
* base64id v0.1.0 | ||
*/ | ||
/** | ||
* Module dependencies | ||
*/ | ||
/** | ||
* Constructor | ||
*/ | ||
var Base64Id = function() { }; | ||
/** | ||
* Get random bytes | ||
* | ||
* Uses a buffer if available, falls back to crypto.randomBytes | ||
*/ | ||
Base64Id.prototype.getRandomBytes = function(bytes) { | ||
var BUFFER_SIZE = 4096; | ||
var self = this; | ||
bytes = bytes || 12; | ||
if (bytes > BUFFER_SIZE) { | ||
return crypto.randomBytes(bytes); | ||
} | ||
var bytesInBuffer = parseInt(BUFFER_SIZE/bytes); | ||
var threshold = parseInt(bytesInBuffer*0.85); | ||
if (!threshold) { | ||
return crypto.randomBytes(bytes); | ||
} | ||
if (this.bytesBufferIndex == null) { | ||
this.bytesBufferIndex = -1; | ||
} | ||
if (this.bytesBufferIndex == bytesInBuffer) { | ||
this.bytesBuffer = null; | ||
this.bytesBufferIndex = -1; | ||
} | ||
// No buffered bytes available or index above threshold | ||
if (this.bytesBufferIndex == -1 || this.bytesBufferIndex > threshold) { | ||
if (!this.isGeneratingBytes) { | ||
this.isGeneratingBytes = true; | ||
crypto.randomBytes(BUFFER_SIZE, function(err, bytes) { | ||
self.bytesBuffer = bytes; | ||
self.bytesBufferIndex = 0; | ||
self.isGeneratingBytes = false; | ||
}); | ||
} | ||
// Fall back to sync call when no buffered bytes are available | ||
if (this.bytesBufferIndex == -1) { | ||
return crypto.randomBytes(bytes); | ||
} | ||
} | ||
var result = this.bytesBuffer.slice(bytes*this.bytesBufferIndex, bytes*(this.bytesBufferIndex+1)); | ||
this.bytesBufferIndex++; | ||
return result; | ||
}; | ||
/** | ||
* Generates a base64 id | ||
* | ||
* (Original version from socket.io <http://socket.io>) | ||
*/ | ||
Base64Id.prototype.generateId = function () { | ||
var rand = new Buffer(15); // multiple of 3 for base64 | ||
if (!rand.writeInt32BE) { | ||
return Math.abs(Math.random() * Math.random() * Date.now() | 0).toString() | ||
+ Math.abs(Math.random() * Math.random() * Date.now() | 0).toString(); | ||
} | ||
this.sequenceNumber = (this.sequenceNumber + 1) | 0; | ||
rand.writeInt32BE(this.sequenceNumber, 11); | ||
if (crypto.randomBytes) { | ||
this.getRandomBytes(12).copy(rand); | ||
} else { | ||
// not secure for node 0.4 | ||
[0, 4, 8].forEach(function(i) { | ||
rand.writeInt32BE(Math.random() * Math.pow(2, 32) | 0, i); | ||
}); | ||
} | ||
return rand.toString('base64').replace(/\//g, '_').replace(/\+/g, '-'); | ||
}; | ||
/** | ||
* Export | ||
*/ | ||
exports = module.exports = new Base64Id(); | ||
}); | ||
var Socket = /** @class */ (function () { | ||
function Socket(ws, request, listeners, middleware) { | ||
this.ws = ws; | ||
this.request = request; | ||
this.middleware = middleware; | ||
this.update(listeners, middleware); | ||
} | ||
Socket.prototype.update = function (listeners, middleware) { | ||
var _this = this; | ||
this.ws.removeAllListeners(); | ||
this.middleware = middleware; | ||
listeners.forEach(function (handlers, event) { | ||
if (event === 'connection') { | ||
return; | ||
} | ||
handlers.forEach(function (handler) { | ||
_this.on(event, handler); | ||
}); | ||
}); | ||
}; | ||
/** | ||
* Adds a specific event and callback to this socket | ||
*/ | ||
Socket.prototype.on = function (event, handler) { | ||
var _this = this; | ||
this.ws.on(event, function (message) { | ||
var ctx = { | ||
event: event, | ||
data: message, | ||
request: _this.request, | ||
socket: _this.ws, | ||
}; | ||
if (!_this.middleware) { | ||
handler(ctx, message); | ||
return; | ||
} | ||
_this.middleware(ctx).then(function () { | ||
handler(ctx, message); | ||
}); | ||
}); | ||
}; | ||
return Socket; | ||
}()); | ||
var WsSocket = /** @class */ (function () { | ||
function WsSocket() { | ||
var _this = this; | ||
this.middleware = []; | ||
this.composed = null; | ||
this.connections = new Map(); | ||
this.listeners = new Map(); | ||
this.wss = null; | ||
this.onConnection = function (ws, request) { | ||
var socketId = base64id.generateId(); | ||
var socketInstance = new Socket(ws, request, _this.listeners, _this.composed); | ||
_this.connections.set(socketId, socketInstance); | ||
ws.on('disconnect', function () { | ||
_this.connections.delete(socketId); | ||
}); | ||
var handlers = _this.listeners.get('connection'); | ||
if (handlers) { | ||
handlers.forEach(function (handler) { | ||
handler({ | ||
event: 'connection', | ||
data: socketInstance, | ||
request: request, | ||
socket: ws, | ||
}, null); | ||
}); | ||
} | ||
}; | ||
} | ||
WsSocket.prototype.attach = function (app, https, tlsOpts) { | ||
if (https === void 0) { https = false; } | ||
if (tlsOpts === void 0) { tlsOpts = {}; } | ||
var enhancedApp = app; | ||
if (enhancedApp.server && | ||
enhancedApp.server.constructor.name !== 'Server') { | ||
throw new Error("app.server already exists but it's not an http server"); | ||
} | ||
if (!enhancedApp.server) { | ||
// Create a server if it doesn't already exists | ||
enhancedApp.server = https | ||
? require('https').createServer(tlsOpts, enhancedApp.callback()) | ||
: require('http').createServer(enhancedApp.callback()); | ||
// Patch `app.listen()` to call `app.server.listen()` | ||
enhancedApp.listen = function listen() { | ||
enhancedApp.server.listen.apply(enhancedApp.server, arguments); | ||
return enhancedApp.server; | ||
}; | ||
} | ||
this.wss = new WebSocket.Server({ server: enhancedApp.server }); | ||
enhancedApp.ws = this; | ||
this.wss.on('connection', this.onConnection); | ||
}; | ||
/** | ||
* Pushes a middleware on to the stack | ||
* @param fn <Function> the middleware function to execute | ||
*/ | ||
WsSocket.prototype.use = function (fn) { | ||
this.middleware.push(fn); | ||
this.composed = koaCompose(this.middleware); | ||
this.updateConnections(); | ||
return this; | ||
}; | ||
/** | ||
* Adds a new listener to the stack | ||
* @param event <String> the event id | ||
* @param handler <Function> the callback to execute | ||
* @return this | ||
*/ | ||
WsSocket.prototype.on = function (event, handler) { | ||
var listeners = this.listeners.get(event); | ||
// If this is a new event then just set it | ||
if (!listeners) { | ||
this.listeners.set(event, [handler]); | ||
this.updateConnections(); | ||
return this; | ||
} | ||
listeners.push(handler); | ||
this.listeners.set(event, listeners); | ||
this.updateConnections(); | ||
return this; | ||
}; | ||
/** | ||
* Removes a listener from the event | ||
* @param event <String> if omitted will remove all listeners | ||
* @param handler <Function> if omitted will remove all from the event | ||
* @return this | ||
*/ | ||
WsSocket.prototype.off = function (event, handler) { | ||
if (!event) { | ||
this.listeners = new Map(); | ||
this.updateConnections(); | ||
return this; | ||
} | ||
if (!handler) { | ||
this.listeners.delete(event); | ||
this.updateConnections(); | ||
return this; | ||
} | ||
var listeners = this.listeners.get(event) || []; | ||
var i = listeners.length - 1; | ||
while (i) { | ||
if (listeners[i] === handler) { | ||
break; | ||
} | ||
i--; | ||
} | ||
listeners.splice(i, 1); | ||
this.updateConnections(); | ||
return this; | ||
}; | ||
/** | ||
* Updates all existing connections with current listeners and middleware | ||
* @private | ||
*/ | ||
WsSocket.prototype.updateConnections = function () { | ||
var _this = this; | ||
this.connections.forEach(function (connection) { | ||
return connection.update(_this.listeners, _this.composed); | ||
}); | ||
}; | ||
return WsSocket; | ||
}()); | ||
exports.WsSocket = WsSocket; | ||
Object.defineProperty(exports, '__esModule', { value: true }); | ||
}))); | ||
//# sourceMappingURL=koa-ws-socket.umd.js.map |
@@ -1,2 +0,51 @@ | ||
declare const _default: {}; | ||
export default _default; | ||
/// <reference types="node" /> | ||
/// <reference types="ws" /> | ||
/// <reference types="koa-compose" /> | ||
/// <reference types="koa" /> | ||
import * as http from 'http'; | ||
import { TlsOptions } from 'tls'; | ||
import * as Koa from 'koa'; | ||
import compose from 'koa-compose'; | ||
import * as WebSocket from 'ws'; | ||
import Socket from './socket'; | ||
export interface SocketContext { | ||
data: any; | ||
event: string; | ||
request: http.ServerRequest; | ||
socket: WebSocket; | ||
} | ||
export declare type EventHandler = (ctx: SocketContext, data: any) => any; | ||
export declare type Middleware = (ctx: SocketContext, next: () => Promise<any>) => any; | ||
export declare class WsSocket { | ||
middleware: Middleware[]; | ||
composed: compose.ComposedMiddleware<SocketContext> | null; | ||
connections: Map<string, Socket>; | ||
listeners: Map<string, EventHandler[]>; | ||
wss: WebSocket.Server | null; | ||
attach(app: Koa, https?: boolean, tlsOpts?: TlsOptions): void; | ||
onConnection: (ws: WebSocket, request: http.IncomingMessage) => void; | ||
/** | ||
* Pushes a middleware on to the stack | ||
* @param fn <Function> the middleware function to execute | ||
*/ | ||
use(fn: Middleware): this; | ||
/** | ||
* Adds a new listener to the stack | ||
* @param event <String> the event id | ||
* @param handler <Function> the callback to execute | ||
* @return this | ||
*/ | ||
on(event: string, handler: EventHandler): this; | ||
/** | ||
* Removes a listener from the event | ||
* @param event <String> if omitted will remove all listeners | ||
* @param handler <Function> if omitted will remove all from the event | ||
* @return this | ||
*/ | ||
off(event: string, handler: EventHandler): this; | ||
/** | ||
* Updates all existing connections with current listeners and middleware | ||
* @private | ||
*/ | ||
updateConnections(): void; | ||
} |
{ | ||
"name": "koa-ws-socket", | ||
"version": "0.0.1", | ||
"description": | ||
"Attaches ws sockets to koa and allows koa-style middleware for them", | ||
"files": ["dist"], | ||
"version": "0.1.0", | ||
"description": "Attaches ws sockets to koa using koa-style middleware", | ||
"files": [ | ||
"dist" | ||
], | ||
"main": "dist/koa-ws-socket.umd.js", | ||
"module": "dist/koa-ws-socket.es5.js", | ||
"typings": "dist/types/koa-ws-socket.d.ts", | ||
"types": "dist/types/index.d.ts", | ||
"repository": "https://github.com/pbomb/koa-ws-socket.git", | ||
@@ -20,6 +21,4 @@ "author": "Matt Parrish <matt.r.parrish@gmail.com>", | ||
"prebuild": "rimraf dist && rimraf compiled", | ||
"build": | ||
"npm run tsc && BUILD_INPUT=compiled/index.js kcd-scripts build --bundle --no-clean", | ||
"lint": | ||
"tslint -c tslint.json -e **/node_modules/**/* -e **/typings/**/* **/*.ts", | ||
"build": "rollup --config rollup.config.ts --no-clean", | ||
"lint": "tslint -c tslint.json -e **/node_modules/**/* -e **/typings/**/* **/*.ts", | ||
"test": "kcd-scripts test", | ||
@@ -38,2 +37,4 @@ "test:cover": "kcd-scripts test --coverage", | ||
"@types/koa": "2.0.39", | ||
"@types/koa-compose": "3.2.2", | ||
"@types/ws": "3.2.0", | ||
"kcd-scripts": "0.24.0", | ||
@@ -43,3 +44,7 @@ "koa": "2.3.0", | ||
"rimraf": "2.6.2", | ||
"rollup": "0.50.0", | ||
"rollup-plugin-commonjs": "8.2.1", | ||
"rollup-plugin-node-resolve": "3.0.0", | ||
"rollup-plugin-sourcemaps": "0.4.2", | ||
"rollup-plugin-typescript2": "0.7.0", | ||
"tslint": "5.7.0", | ||
@@ -53,7 +58,6 @@ "typescript": "2.5.3", | ||
}, | ||
"lint-staged": { | ||
"*.ts": ["prettier --write", "git add"], | ||
"*.js": ["prettier --write", "git add"], | ||
"*.json": ["prettier --write", "git add"] | ||
"dependencies": { | ||
"base64id": "^1.0.0", | ||
"koa-compose": "^4.0.0" | ||
} | ||
} | ||
} |
@@ -0,2 +1,61 @@ | ||
<!-- START doctoc generated TOC please keep comment here to allow auto update --> | ||
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --> | ||
- [koa-ws-socket](#koa-ws-socket) | ||
- [Contributors](#contributors) | ||
- [LICENSE](#license) | ||
<!-- END doctoc generated TOC please keep comment here to allow auto update --> | ||
# koa-ws-socket | ||
Attaches ws sockets to koa and allows koa-style middleware for them | ||
Attaches [ws](https://github.com/websockets/ws) sockets to koa using koa-style middleware. | ||
[![Build Status][build-badge]][build] | ||
[![Code Coverage][coverage-badge]][coverage] | ||
[![version][version-badge]][package] | ||
[![downloads][downloads-badge]][npmcharts] | ||
[![MIT License][license-badge]][LICENSE] | ||
[![All Contributors](https://img.shields.io/badge/all_contributors-1-orange.svg?style=flat-square)](#contributors) | ||
[![PRs Welcome][prs-badge]][prs] | ||
[![Code of Conduct][coc-badge]][coc] | ||
## Contributors | ||
Thanks goes to these people ([emoji key][emojis]): | ||
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section --> | ||
| [<img src="https://avatars0.githubusercontent.com/u/1402095?v=4" width="100px;"/><br /><sub>Matt Parrish</sub>](https://github.com/pbomb)<br />[💻](https://github.com/pbomb/koa-ws-socket/commits?author=pbomb "Code") [📖](https://github.com/pbomb/koa-ws-socket/commits?author=pbomb "Documentation") [⚠️](https://github.com/pbomb/koa-ws-socket/commits?author=pbomb "Tests") [👀](#review-pbomb "Reviewed Pull Requests") | | ||
| :---: | | ||
<!-- ALL-CONTRIBUTORS-LIST:END --> | ||
This project follows the [all-contributors][all-contributors] specification. | ||
Contributions of any kind welcome! | ||
## LICENSE | ||
MIT | ||
[build-badge]: https://img.shields.io/travis/pbomb/koa-ws-socket.svg?style=flat-square | ||
[build]: https://travis-ci.org/pbomb/koa-ws-socket | ||
[coverage-badge]: https://img.shields.io/codecov/c/github/pbomb/koa-ws-socket.svg?style=flat-square | ||
[coverage]: https://codecov.io/github/pbomb/koa-ws-socket | ||
[version-badge]: https://img.shields.io/npm/v/koa-ws-socket.svg?style=flat-square | ||
[package]: https://www.npmjs.com/package/koa-ws-socket | ||
[downloads-badge]: https://img.shields.io/npm/dm/koa-ws-socket.svg?style=flat-square | ||
[npmcharts]: https://npmcharts.com/compare/koa-ws-socket | ||
[license-badge]: https://img.shields.io/npm/l/koa-ws-socket.svg?style=flat-square | ||
[license]: https://github.com/pbomb/koa-ws-socket/blob/master/LICENSE | ||
[prs-badge]: https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square | ||
[prs]: http://makeapullrequest.com | ||
[chat]: https://gitter.im/pbomb/koa-ws-socket | ||
[chat-badge]: https://img.shields.io/gitter/room/pbomb/koa-ws-socket.svg?style=flat-square | ||
[coc-badge]: https://img.shields.io/badge/code%20of-conduct-ff69b4.svg?style=flat-square | ||
[coc]: https://github.com/pbomb/koa-ws-socket/blob/master/other/CODE_OF_CONDUCT.md | ||
[github-watch-badge]: https://img.shields.io/github/watchers/pbomb/koa-ws-socket.svg?style=social | ||
[github-watch]: https://github.com/pbomb/koa-ws-socket/watchers | ||
[github-star-badge]: https://img.shields.io/github/stars/pbomb/koa-ws-socket.svg?style=social | ||
[github-star]: https://github.com/pbomb/koa-ws-socket/stargazers | ||
[emojis]: https://github.com/kentcdodds/all-contributors#emoji-key | ||
[all-contributors]: https://github.com/kentcdodds/all-contributors |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 2 instances in 1 package
62926
667
62
4
16
5
+ Addedbase64id@^1.0.0
+ Addedkoa-compose@^4.0.0
+ Addedbase64id@1.0.0(transitive)