jsonrpc2-ws
Advanced tools
Comparing version 1.0.0-beta1 to 1.0.0-beta10
@@ -41,2 +41,36 @@ /** | ||
/** | ||
* JSON-RPC 2.0 Error Response | ||
*/ | ||
export interface ErrorResponse extends Call { | ||
/** | ||
* This member is REQUIRED on error. | ||
* This member MUST NOT exist if there was no error triggered during invocation. | ||
* The value for this member MUST be an Object as defined in section 5.1. | ||
*/ | ||
error: Error; | ||
/** | ||
* This member is REQUIRED. | ||
* It MUST be the same as the value of the id member in the Request Object. | ||
* If there was an error in detecting the id in the Request object (e.g. Parse error/Invalid Request), it MUST be Null. | ||
*/ | ||
id: string | number | null; | ||
} | ||
/** | ||
* JSON-RPC 2.0 Success Response | ||
*/ | ||
export interface SuccessResponse extends Call { | ||
/** | ||
* This member is REQUIRED on success. | ||
* This member MUST NOT exist if there was an error invoking the method. | ||
* The value of this member is determined by the method invoked on the Server. | ||
*/ | ||
result: any; | ||
/** | ||
* This member is REQUIRED on error. | ||
* This member MUST NOT exist if there was no error triggered during invocation. | ||
* The value for this member MUST be an Object as defined in section 5.1. | ||
*/ | ||
id: string | number | null; | ||
} | ||
/** | ||
* JSON-RPC 2.0 Response | ||
@@ -65,2 +99,13 @@ */ | ||
/** | ||
* Check type of call is an Reponse or not | ||
* @param call an Call object which will be checked. | ||
*/ | ||
export declare function isResponse(call: Request | Notification | Response): call is Response; | ||
/** | ||
* Check type of response is SuccessResponse or not | ||
* | ||
* @param response an Response object which will be checked. | ||
*/ | ||
export declare function isSuccessResponse(response: Response): response is SuccessResponse; | ||
/** | ||
* JSON-RPC 2.0 Error Object | ||
@@ -86,2 +131,10 @@ */ | ||
} | ||
export declare const enum ErrorCode { | ||
ParseError = -32700, | ||
InvalidRequest = -32600, | ||
MethodNotFound = -32601, | ||
InvalidPrams = -32602, | ||
InternalError = -32603, | ||
ServerError = -32000 | ||
} | ||
/** | ||
@@ -88,0 +141,0 @@ * JSON-RPC 2.0 Error Codes |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
/** | ||
* Check type of call is an Reponse or not | ||
* @param call an Call object which will be checked. | ||
*/ | ||
function isResponse(call) { | ||
return "id" in call && ("result" in call || "error" in call); | ||
} | ||
exports.isResponse = isResponse; | ||
/** | ||
* Check type of response is SuccessResponse or not | ||
* | ||
* @param response an Response object which will be checked. | ||
*/ | ||
function isSuccessResponse(response) { | ||
return "result" in response && response.id !== null; | ||
} | ||
exports.isSuccessResponse = isSuccessResponse; | ||
/** | ||
* JSON-RPC 2.0 Error Codes | ||
@@ -5,0 +22,0 @@ */ |
export { default as Server } from "./server"; | ||
export { default as Client } from "./client"; |
@@ -5,2 +5,4 @@ "use strict"; | ||
exports.Server = server_1.default; | ||
var client_1 = require("./client"); | ||
exports.Client = client_1.default; | ||
//# sourceMappingURL=index.js.map |
/// <reference types="node" /> | ||
import * as http from "http"; | ||
import { EventEmitter } from "eventemitter3"; | ||
import EventEmitter = require("eventemitter3"); | ||
import WebSocket = require("ws"); | ||
import { Server as WebSocketServer, ServerOptions as WSServerOptions } from "ws"; | ||
import { Error as RPCError, ErrorResponse } from "./common"; | ||
import { Socket as ISocket } from "./Socket"; | ||
import { Options as MessageHandlerOptions } from "./MessageHandler"; | ||
declare type SocketId = string; | ||
declare type MethodFunction = (socket: Socket, params: any) => Promise<any> | any; | ||
export declare enum VERSION_CHECK_MODE { | ||
STRICT = 0, | ||
LOOSE = 1, | ||
IGNORE = 2 | ||
} | ||
export interface Options { | ||
export interface Options extends MessageHandlerOptions { | ||
/** | ||
@@ -19,8 +16,2 @@ * call `#open()` | ||
/** | ||
* `STRICT`: only accepts jsonrpc: `2.0`. | ||
* `LOOSE`: accepts jsonrpc: `2.0` but it's omittable. | ||
* `IGNORE`: ignore jsonrpc property. | ||
*/ | ||
jsonrpcVersionCheck?: VERSION_CHECK_MODE; | ||
/** | ||
* `ws` constructor's options. | ||
@@ -30,3 +21,14 @@ * details: https://github.com/websockets/ws/blob/master/doc/ws.md | ||
wss: WSServerOptions; | ||
/** | ||
* use `uws` (experimental) | ||
*/ | ||
uws?: boolean; | ||
} | ||
export default interface Server { | ||
on(event: "listening", cb: (this: Server) => void): this; | ||
on(event: "connection", cb: (this: Server, socket: Socket, req?: http.IncomingMessage) => void): this; | ||
on(event: "error", cb: (this: Server, error: Error) => void): this; | ||
on(event: "error_response", cb: (this: Server, response: ErrorResponse) => void): this; | ||
on(event: "notification_error", cb: (this: Server, error: RPCError) => void): this; | ||
} | ||
/** | ||
@@ -39,3 +41,4 @@ * JSON-RPC 2.0 WebSocket Server | ||
sockets: Map<SocketId, Socket>; | ||
methods: Map<string, MethodFunction>; | ||
readonly methods: Map<string, (socket: Socket, params: any) => any>; | ||
private _messageHandler; | ||
/** | ||
@@ -70,2 +73,8 @@ * Create a instance. | ||
/** | ||
* Broadcasts a (raw) message to the room. | ||
* @param room The name of the room. | ||
* @param data (raw) message. | ||
*/ | ||
sendTo(room: string, data: any): void; | ||
/** | ||
* Get all sockets in the room. | ||
@@ -75,6 +84,16 @@ * @param room The name of the room. | ||
in(room: string): Map<SocketId, Socket>; | ||
private _wsMessageHandler; | ||
private _processCall; | ||
/** | ||
* Server is open or not | ||
*/ | ||
isOpen(): boolean; | ||
} | ||
export declare class Socket extends EventEmitter { | ||
/** | ||
* Socket of JSON-RPC 2.0 WebSocket Server | ||
*/ | ||
export interface Socket extends ISocket { | ||
on(event: "close", cb: (this: Socket) => void): this; | ||
on(event: "notification_error", cb: (this: Socket, error: RPCError) => void): void; | ||
on(event: "error_response", cb: (this: Socket, response: ErrorResponse) => void): void; | ||
} | ||
export declare class Socket extends EventEmitter implements ISocket { | ||
server: Server; | ||
@@ -96,4 +115,4 @@ ws: WebSocket; | ||
* Sends a (raw) message to the socket. | ||
* @param method The name of the method to be invoked. | ||
* @param params The parameters of the method. | ||
* @param data (raw) message. | ||
* @param binary binary flag. | ||
*/ | ||
@@ -125,3 +144,7 @@ send(data: any, binary?: boolean): void; | ||
terminate(): void; | ||
/** | ||
* Get the connection is open or not | ||
*/ | ||
isOpen(): boolean; | ||
} | ||
export {}; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const eventemitter3_1 = require("eventemitter3"); | ||
const EventEmitter = require("eventemitter3"); | ||
const ws_1 = require("ws"); | ||
const uuidv4 = require("uuid/v4"); | ||
const common_1 = require("./common"); | ||
var VERSION_CHECK_MODE; | ||
(function (VERSION_CHECK_MODE) { | ||
VERSION_CHECK_MODE[VERSION_CHECK_MODE["STRICT"] = 0] = "STRICT"; | ||
VERSION_CHECK_MODE[VERSION_CHECK_MODE["LOOSE"] = 1] = "LOOSE"; | ||
VERSION_CHECK_MODE[VERSION_CHECK_MODE["IGNORE"] = 2] = "IGNORE"; | ||
})(VERSION_CHECK_MODE = exports.VERSION_CHECK_MODE || (exports.VERSION_CHECK_MODE = {})); | ||
const MessageHandler_1 = require("./MessageHandler"); | ||
/** | ||
* JSON-RPC 2.0 WebSocket Server | ||
*/ | ||
class Server extends eventemitter3_1.EventEmitter { | ||
class Server extends EventEmitter { | ||
/** | ||
@@ -25,11 +19,21 @@ * Create a instance. | ||
this.sockets = new Map(); | ||
this.methods = new Map(); | ||
this.options = Object.assign({ | ||
open: true, | ||
jsonrpcVersionCheck: VERSION_CHECK_MODE.STRICT | ||
jsonrpcVersionCheck: 0 /* STRICT */, | ||
uws: false | ||
}, options); | ||
if (options.open) { | ||
this._messageHandler = new MessageHandler_1.default(this.options); | ||
this._messageHandler.on("error_response", (socket, response) => { | ||
this.emit("error_response", socket, response); | ||
socket.emit("error_response", response); | ||
}); | ||
this._messageHandler.on("notification_error", (socket, error) => { | ||
this.emit("notification_error", socket, error); | ||
socket.emit("notification_error", error); | ||
}); | ||
if (this.options.open) { | ||
this.open(callback); | ||
} | ||
} | ||
get methods() { return this._messageHandler.methods; } | ||
/** | ||
@@ -40,6 +44,14 @@ * Create | ||
open(callback) { | ||
this.wss = new ws_1.Server(this.options.wss, callback); | ||
if (this.wss) { | ||
throw new Error("`ws` has already been created"); | ||
} | ||
if (this.options.uws) { | ||
this.wss = new (require("uws").Server)(this.options.wss, callback); | ||
} | ||
else { | ||
this.wss = new ws_1.Server(this.options.wss, callback); | ||
} | ||
this.wss.once("listening", () => this.emit("listening")); | ||
this.wss.on("connection", (ws, req) => { | ||
const socket = new Socket(this, ws, req); | ||
let socket = new Socket(this, ws, req); | ||
this.sockets.set(socket.id, socket); | ||
@@ -49,4 +61,11 @@ ws.once("close", () => { | ||
socket.emit("close"); | ||
socket.removeAllListeners(); | ||
socket.ws = null; | ||
socket.rooms.clear(); | ||
socket.data.clear(); | ||
socket = null; | ||
ws.removeAllListeners(); | ||
ws = null; | ||
}); | ||
ws.on("message", data => this._wsMessageHandler(socket, data)); | ||
ws.on("message", data => this._messageHandler.handleMessage(socket, data).catch(e => this.emit("error", e))); | ||
this.emit("connection", socket, req); | ||
@@ -114,2 +133,14 @@ }); | ||
/** | ||
* Broadcasts a (raw) message to the room. | ||
* @param room The name of the room. | ||
* @param data (raw) message. | ||
*/ | ||
sendTo(room, data) { | ||
for (const socket of this.sockets.values()) { | ||
if (socket.rooms.has(room) === true) { | ||
socket.send(data); | ||
} | ||
} | ||
} | ||
/** | ||
* Get all sockets in the room. | ||
@@ -127,100 +158,11 @@ * @param room The name of the room. | ||
} | ||
async _wsMessageHandler(socket, data) { | ||
const calls = []; | ||
const responses = []; | ||
let isBinary = false; | ||
let isArray = false; | ||
if (data instanceof ArrayBuffer) { | ||
isBinary = true; | ||
data = Buffer.from(data).toString(); | ||
} | ||
try { | ||
const obj = JSON.parse(data); | ||
if (Array.isArray(obj)) { | ||
isArray = true; | ||
if (obj.length === 0) { | ||
const res = { | ||
jsonrpc: "2.0", | ||
error: common_1.createError(-32600, null, "Empty Array"), | ||
id: null | ||
}; | ||
socket.send(JSON.stringify(res), isBinary); | ||
return; | ||
} | ||
calls.push(...obj); | ||
} | ||
else { | ||
calls.push(obj); | ||
} | ||
} | ||
catch (e) { | ||
const res = { | ||
jsonrpc: "2.0", | ||
error: common_1.createError(-32700, null, "Invalid JSON"), | ||
id: null | ||
}; | ||
socket.send(JSON.stringify(res), isBinary); | ||
return; | ||
} | ||
for (const call of calls) { | ||
const res = await this._processCall(socket, call); | ||
if (res) { | ||
responses.push(res); | ||
} | ||
} | ||
if (responses.length === 0) { | ||
return; | ||
} | ||
socket.send(JSON.stringify(isArray ? responses : responses[0]), isBinary); | ||
/** | ||
* Server is open or not | ||
*/ | ||
isOpen() { | ||
return this.wss !== undefined; | ||
} | ||
async _processCall(socket, call) { | ||
const reqId = call.id; | ||
const res = { | ||
jsonrpc: "2.0", | ||
id: reqId === undefined ? null : reqId | ||
}; | ||
if (typeof call !== "object") { | ||
res.error = common_1.createError(-32600); | ||
return res; | ||
} | ||
if (call.jsonrpc !== "2.0" && (this.options.jsonrpcVersionCheck === VERSION_CHECK_MODE.STRICT || | ||
(this.options.jsonrpcVersionCheck === VERSION_CHECK_MODE.LOOSE && call.jsonrpc !== undefined))) { | ||
res.error = common_1.createError(-32600, null, "Invalid JSON-RPC Version"); | ||
return res; | ||
} | ||
if (!call.method) { | ||
res.error = common_1.createError(-32602, null, "Method not specified"); | ||
return res; | ||
} | ||
if (typeof call.method !== "string") { | ||
res.error = common_1.createError(-32600, null, "Invalid type of method name"); | ||
return res; | ||
} | ||
if (typeof call.params !== "object" || call.params === null) { | ||
res.error = common_1.createError(-32600); | ||
return res; | ||
} | ||
try { | ||
res.result = await this.methods.get(call.method)(socket, call.params) || null; | ||
if (reqId === undefined) { | ||
return; | ||
} | ||
return res; | ||
} | ||
catch (e) { | ||
if (reqId === undefined) { | ||
return; | ||
} | ||
if (e instanceof Error) { | ||
res.error = common_1.createError(-32000, e.name, e.message); | ||
} | ||
else { | ||
res.error = e; | ||
} | ||
return res; | ||
} | ||
} | ||
} | ||
exports.default = Server; | ||
class Socket extends eventemitter3_1.EventEmitter { | ||
class Socket extends EventEmitter { | ||
constructor(server, ws, req) { | ||
@@ -251,7 +193,7 @@ super(); | ||
* Sends a (raw) message to the socket. | ||
* @param method The name of the method to be invoked. | ||
* @param params The parameters of the method. | ||
* @param data (raw) message. | ||
* @param binary binary flag. | ||
*/ | ||
send(data, binary = false) { | ||
if (this.ws.readyState === ws_1.OPEN) { | ||
if (this.isOpen()) { | ||
this.ws.send(data, { binary }); | ||
@@ -302,4 +244,10 @@ } | ||
} | ||
/** | ||
* Get the connection is open or not | ||
*/ | ||
isOpen() { | ||
return this.ws !== undefined && this.ws.readyState === ws_1.OPEN; | ||
} | ||
} | ||
exports.Socket = Socket; | ||
//# sourceMappingURL=server.js.map |
{ | ||
"name": "jsonrpc2-ws", | ||
"version": "1.0.0-beta1", | ||
"version": "1.0.0-beta10", | ||
"description": "Yet Another Server Library which Implementation of JSON-RPC 2.0 over WebSocket for Node.js (w/ TypeScript)", | ||
@@ -38,5 +38,6 @@ "keywords": [ | ||
"devDependencies": { | ||
"@types/eventemitter3": "^2.0.2", | ||
"@types/uuid": "^3.4.3", | ||
"@types/ws": "^6.0.0", | ||
"chai": "^4.1.2", | ||
"chai": "^4.2.0", | ||
"mocha": "^5.2.0", | ||
@@ -48,6 +49,12 @@ "rimraf": "^2.6.2", | ||
"dependencies": { | ||
"backo2": "^1.0.2", | ||
"eventemitter3": "^3.1.0", | ||
"isomorphic-ws": "^4.0.1", | ||
"uuid": "^3.3.2", | ||
"ws": "^6.0.0" | ||
"ws": "~6.1.2" | ||
}, | ||
"optionalDependencies": { | ||
"bufferutil": "^4.0.0", | ||
"utf-8-validate": "^5.0.1" | ||
} | ||
} |
# jsonrpc2-ws | ||
Yet Another Server Library which Implementation of JSON-RPC 2.0 over WebSocket for Node.js (w/ TypeScript) | ||
Yet Another Server Library which Implementation of [JSON-RPC 2.0](https://www.jsonrpc.org/specification) over WebSocket for Node.js (w/ TypeScript) | ||
@@ -10,3 +10,3 @@ [![npm][npm-img]][npm-url] | ||
``` | ||
```sh | ||
npm install jsonrpc2-ws --save | ||
@@ -42,3 +42,3 @@ # or | ||
socket.joinTo("general"); | ||
rpc.notifyTo("general", "general.count", { count: rpc.in("chatroom").size }); | ||
rpc.notifyTo("general", "general.count", { count: rpc.in("general").size }); | ||
}); | ||
@@ -45,0 +45,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
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
86076
25
1228
7
8
1
+ Addedbacko2@^1.0.2
+ Addedisomorphic-ws@^4.0.1
+ Addedbacko2@1.0.2(transitive)
+ Addedbufferutil@4.0.9(transitive)
+ Addedisomorphic-ws@4.0.1(transitive)
+ Addednode-gyp-build@4.8.4(transitive)
+ Addedutf-8-validate@5.0.10(transitive)
+ Addedws@6.1.4(transitive)
- Removedws@6.2.3(transitive)
Updatedws@~6.1.2