2.0 rework

This commit is contained in:
2020-08-08 04:23:56 +02:00
parent dde95027bc
commit cb974cbc61
11 changed files with 752 additions and 524 deletions
+130 -141
View File
@@ -12,33 +12,58 @@ rpclibrary is a simple to use websocket RPC library.
npm i rpclibrary npm i rpclibrary
``` ```
# Quickstart # Quickstart (TL;DR)
```typescript ```typescript
import {RPCServer, RPCSocket} from 'rpclibrary' import {RPCServer, RPCSocket} from 'rpclibrary'
// TL;DR
const echo = (text) => text const echo = (text) => text
const add = (a, b) => a + b const add = (a, b) => a + b
const subtract = (a, b) => a - b
new RPCServer(20000, [{ new RPCServer([
{
name: 'MyRPCGroup1', name: 'MyRPCGroup1',
exportRPCs: () => [ RPCs: [
echo, echo,
add, add,
] ]
}]) },
name: 'MyOtherRPCGroup',
RPCs: [
subtract
]
}
]).listen(20000)
new RPCSocket(20000, 'localhost').connect().then(async sock => { new RPCSocket(20000, 'localhost').connect().then(async sock => {
try{ try{
const RPCs = sock['MyRPCGroup1']
await RPCs.echo("hello!").then(console.log) await sock['MyRPCGroup1'].echo("hello!").then(console.log)
await RPCs.add(1, Math.PI).then(console.log) const sum = await sock['MyRPCGroup1'].add(1, Math.PI)
const result = await sock['MyOtherRPCGroup'].subtract(sum, 4)
}catch(e){ }catch(e){
console.log(String(e)) console.log(String(e))
} }
}) })
``` ```
# Working with http.Server and express
```typescript
import {RPCServer, RPCSocket} from 'rpclibrary'
import {Server as httpServer} from 'http'
import * as express from 'express'
const app = express()
//...do express things...
const httpServer = new http.Server(app)
const rpcServer = new RPCServer([ ... ])
rpcServer.attach(httpServer)
rpcServer.listen(8080) //identical to httpServer.listen(8080)
```
# Async and Callbacks? # Async and Callbacks?
rpclibrary offers full support for callbacks and Promises. rpclibrary offers full support for callbacks and Promises.
@@ -69,16 +94,16 @@ const getCallback = (callback) => {
return "Please wait for a callback :)" return "Please wait for a callback :)"
} }
new RPCServer(20000, [{ new RPCServer([{
name: 'MyRPCGroup1', name: 'MyRPCGroup1',
exportRPCs: () => [ RPCs: [
getAsync, getAsync,
{ {
name: 'getCallback', name: 'getCallback',
hook: getCallback, hook: getCallback,
} }
] ]
}]) }]).listen(20000)
new RPCSocket(20000, 'localhost').connect().then(async sock => { new RPCSocket(20000, 'localhost').connect().then(async sock => {
try{ try{
@@ -97,12 +122,10 @@ new RPCSocket(20000, 'localhost').connect().then(async sock => {
There are a many things you can hook into to manage your connections There are a many things you can hook into to manage your connections
```typescript ```typescript
new RPCServer(20001, [{ new RPCServer([{
name: 'MyRPCGroup1', name: 'MyRPCGroup1',
exportRPCs: () => [ RPCs: [
echo, echo,
add,
getAsync,
{ {
name: 'getCallback', name: 'getCallback',
hook: getCallback, hook: getCallback,
@@ -111,11 +134,10 @@ new RPCServer(20001, [{
} }
], ],
}], { }], {
visibility: '127.0.0.1', //0.0.0.0
closeHandler: (socket) => { /* global close handler */ }, closeHandler: (socket) => { /* global close handler */ },
connectionHandler: (socket) => { /* new connection made */ }, connectionHandler: (socket) => { /* new connection made */ },
errorHandler: (socket, error, rpcname, argArr) => { /* An error occured inside a RPC */ }, errorHandler: (socket, error, rpcname, argArr) => { /* An error occured inside a RPC */ },
}) }).listen(20001)
const sock = new RPCSocket(20001, 'localhost') const sock = new RPCSocket(20001, 'localhost')
sock.on('error', (e) => { /* handle error */ }) sock.on('error', (e) => { /* handle error */ })
@@ -132,184 +154,151 @@ sock.connect().then(_ => { /* ... */})
# Restricting access # Restricting access
rpclibrary offers some minimalistic permission management rpclibrary offers some minimalistic permission management in the form of a parameter prefix.
The ```sesame``` option will decorate all functions with a check for the sesame password. If the wrong string is provided, the RPC will not be executed or respond.
```typescript ```typescript
new RPCServer([ ... ], {
//Restricting access
new RPCServer(20002, [{
name: 'MyRPCGroup1',
exportRPCs: () => [
echo,
add,
getAsync,
{
name: 'getCallback',
hook: getCallback,
}
],
}], {
sesame: "sesame open", sesame: "sesame open",
/* }).listen(20002)
OR check sesame dynamically
and refine permissioning with accessfilter (optional)
*/
//sesame: (sesame) => true
//accessFilter: (sesame, exporter) => { return exporter.name === "MyRPCGroup1" && sesame === "sesame open" },
})
new RPCSocket(20002, 'localhost').connect("sesame open").then(async sock => { new RPCSocket(20002, 'localhost').connect("sesame open").then(async sock => {
try{ /* use normally */
const RPCs = sock['MyRPCGroup1']
await RPCs.echo("hello!").then(console.log)
await RPCs.add(1, Math.PI).then(console.log)
await RPCs.getAsync().then(console.log)
await RPCs.getCallback(console.log).then(console.log)
}catch(e){
console.log(String(e))
}
}) })
new RPCSocket(20002, 'localhost').connect("WRONG SESAME").then(async sock => {
const RPCs = sock['MyRPCGroup1'] //undefined
})
``` ```
The permissioning can also be performed dynamically with the ```accessfilter``` option and ```sesame``` function.
```typescript
new RPCServer([ ... ], {
sesame: (sesame) => checkIfSesameTokenShouldHaveAnyAccess(sesame),
accessFilter: (sesame, rpcGroupName) => checkIfSesameTokenShouldSeeRPCGroup(sesame, rpcGroupName),
}).listen(20002)
```
# Typescript support # Typescript support
rpclibrary is a typescript-first project and offers full support for typing your RPCs. rpclibrary is a typescript-first project and offers full support for typing your RPCs.
**NOTE** that your function implementations have to be currectly typed to make the compiler agree. **NOTE** that your function implementations and Group Names will usually have to be manually typed to make the compiler infer the correct types.
Explicit typing is recommended.
Example: First apply types to your functions:
```typescript ```typescript
echo = (x) => x echo = (x) => x
/*becomes*/ /*becomes*/
echo = (x:string) : string => x echo = (x:string) : string => x
/* A faulty implementation */
badEcho = (x: boolean) : number => 3
``` ```
Next define a type matching the desired Group-to-RPC structure. This kind of type will be called ```pseudo-interface```:
```typescript ```typescript
type MyInterface = { type MyType = {
MyRPCGroup1: { MyRPCGroup1: {
echo: (x: string) => string echo: (x: string) => string
add: (a: number, b: number) => number //...further RPC declarations
getAsync: () => Promise<{ topic: string, message: string }> },
getCallback: (callback:Function) => string //... further Group declarations
}
}; };
```
If ```MyType``` is provided as generic parameter to ```RPCServer```, ```RPCs``` becomes type-safe.
TSC's stack traces may become very long when errors happen in this section. Don't be intimidated by this.
The relevant information is usually found at the end in a clear manner.
/* ```typescript
exportRPCs is now type safe. Try swapping echo for badEcho. new RPCServer<MyType>([{
Sadly TSC's stack traces aren't the best, but try to scroll to the bottom of them to find useful info like name: 'MyRPCGroup1' as 'MyRPCGroup1', //Otherwise the compiler may infer type 'string'
RPCs: [ badEcho ], //Type '(x: boolean) => number' is not assignable to type '(x: string) => string'
}]).listen(20003)
```
Type '(x: boolean) => number' is not assignable to type '(x: string) => string' Finally also provide ```MyType``` to the client ```RPCSocket``` to gain type safety for the socket object.
*/ ```typescript
new RPCSocket<MyType>(20003, 'localhost').connect().then(async sock => {
const badEcho = (x: boolean) : number => 3 //TSC knows about MyRPCGroup1 and its members
new RPCServer<MyInterface>(20003, [{
name: 'MyRPCGroup1',
exportRPCs: () => [
//badEcho,
echo,
add,
getAsync,
{
name: 'getCallback',
hook: getCallback,
}
],
}])
new RPCSocket<MyInterface>(20003, 'localhost').connect().then(async sock => {
try{
await sock.MyRPCGroup1.echo("hello!").then(console.log) await sock.MyRPCGroup1.echo("hello!").then(console.log)
await sock.MyRPCGroup1.add(1, Math.PI).then(console.log)
await sock.MyRPCGroup1.getAsync().then(console.log)
await sock.MyRPCGroup1.getCallback(console.log).then(console.log)
}catch(e){
console.log(String(e))
}
}) })
``` ```
# A class-based scalable pattern for APIs # A class-based pattern for modular APIs
because long lists of functions quickly become unwieldy, it is smart to break up the RPCs into chunks or features.
A pattern I found to be useful is as follows:
Define a regular Interface. This will function as the source of truth for type definitions.
```typescript ```typescript
interface IMyImplementation { interface IComponentA {
echo: (x: string) => string echo: (x: string) => string
add: (a: number, b: number) => number add: (a: number, b: number) => number
}
interface IComponentB {
getAsync: () => Promise<{ topic: string, message: string }> getAsync: () => Promise<{ topic: string, message: string }>
getCallback: (callback:Function) => string getCallback: (callback:Function) => string
} }
```
type MyInterface = { Create an RPC pseudo-interface type that references the proper interface. This will make sure the types declared by your RPCs are consistent with their implementations.
MyRPCGroup1: { ```typescript
echo: IMyImplementation['echo'] type TComponentA = {
add: IMyImplementation['add'] ComponentA: {
getAsync: IMyImplementation['getAsync'] echo: IComponentA['echo']
getCallback: IMyImplementation['getCallback'] add: IComponentA['add']
} }
} }
class MyImplementation implements IMyImplementation, RPCExporter<MyInterface>{ type TComponentB = {
//"X" as "X" syntax is required to satisfy the type system (as it assumes string to be the true type) ComponentB: {
name = "MyRpcGroup11" as "MyRPCGroup1" getAsync: IComponentB['getAsync']
getCallback: IComponentB['getCallback']
}
}
```
//List the functions you declared in MyInterface Create classes that implement the interfaces you declared alongside ```RPCExporter<MyType>```, where MyType is the RPC pseudo-interface.
exportRPCs = () => [ ```typescript
class ComponentA implements IComponentA, RPCExporter<TComponentA> {
name = "ComponentA" as "ComponentA"
RPCs = [
this.echo, this.echo,
this.add, this.add,
this.getAsync,
this.getCallback
] ]
//Write your implementations as you normally would
echo = (text: string) => text echo = (text: string) => text
add = (a: number, b: number) : number => a + b add = (a: number, b: number) : number => a + b
getAsync = async () : Promise<{topic: string, message:string}>=> await new Promise((res, _) => {
setTimeout(() => {
res({
topic: "Hey!!",
message: "Hello World Async!"
})
}, 250)
})
getCallback = (callback: Function) : string => {
setTimeout(() => {
try{
callback({
topic: "Hey!!",
message: "Hello World Callback!"
})
}catch(e){
console.log(String(e))
}
}, 250)
return "Please wait for a callback :)"
}
} }
type ProjectInterface = MyInterface class ComponentB implements IComponentB, RPCExporter<TComponentB> {
//& MyOtherInterface name = "ComponentB" as "ComponentB"
//& MyOtherOtherInterface
// ...
;
new RPCServer<ProjectInterface>(20004, [new MyImplementation() /*, new MyOtherImplementation(), new MyOtherOtherImplementation() */]) RPCs = [
this.getAsync,
{
name: 'getCallback',
hook: this.getCallback
}
]
getAsync = async () : Promise<{topic: string, message:string}> => {...}
getCallback = (callback: Function) : string => {...}
}
```
Finally, merge all RPC pseudo-interfaces into a project-wide one:
```typescript
type TProject = TComponentA
& TComponentB
new RPCServer<TProject>([new ComponentA(), new ComponentB()]).listen(port)
new RPCSocket<TProject>(port, host).connect().then(async sock => {
new RPCSocket<ProjectInterface>(20004, 'localhost').connect().then(async sock => {
// ...
}) })
``` ```
-239
View File
@@ -1,239 +0,0 @@
import { RPCServer, RPCSocket } from './Index'
import { RPCExporter } from './src/Interfaces'
import { RPCInterface } from './src/Types'
// TL;DR
const echo = (text: string) => text
const add = (a: number, b: number) : number => a + b
const getAsync = async () : Promise<{topic: string, message:string}>=> await new Promise((res, _) => {
setTimeout(() => {
res({
topic: "Hey!!",
message: "Hello World Async!"
})
}, 250)
})
const getCallback = (callback: Function) : string => {
setTimeout(() => {
try{
callback({
topic: "Hey!!",
message: "Hello World Callback!"
})
}catch(e){
console.log(String(e))
}
}, 250)
return "Please wait for a callback :)"
}
new RPCServer(20000, [{
name: 'MyRPCGroup1',
exportRPCs: () => [
echo,
add,
getAsync,
{
name: 'getCallback',
hook: getCallback,
onClose: (response, rpc) => { /* ... */ },
onCallback: (...callbackArgs) => { /* ... */ }
}
]
}])
new RPCSocket(20000, 'localhost').connect().then(async sock => {
try{
const RPCs = sock['MyRPCGroup1']
await RPCs.echo("hello!").then(console.log)
await RPCs.add(1, Math.PI).then(console.log)
await RPCs.getAsync().then(console.log)
await RPCs.getCallback(console.log).then(console.log)
}catch(e){
console.log(String(e))
}
})
//Hooks and events
new RPCServer(20001, [{
name: 'MyRPCGroup1',
exportRPCs: () => [
echo,
add,
getAsync,
{
name: 'getCallback',
hook: getCallback,
onClose: (response, rpc) => { /* client disconnected */ },
onCallback: (...callbackArgs) => { /* callback triggered */ }
}
],
}], {
visibility: '127.0.0.1', //0.0.0.0
closeHandler: (socket) => { /* global close handler */ },
connectionHandler: (socket) => { /* new connection made */ },
errorHandler: (socket, error, rpcname, argArr) => { /* An error occured inside a RPC */ },
})
const sock = new RPCSocket(20001, 'localhost')
sock.on('error', (e) => { /* handle error */ })
sock.on('close', () => { /* handle close event */ })
sock.hook('RPCName', (/* arg0, arg1, ..., argN */) => { /* bind client-side RPCs */ })
//Retrieve the socket from connectionHandler (Server-side) and trigger with
//socket.call('RPCName', arg0, arg1, ..., argN)
sock.connect().then(_ => { /* ... */})
//Restricting access
new RPCServer(20002, [{
name: 'MyRPCGroup1',
exportRPCs: () => [
echo,
add,
getAsync,
{
name: 'getCallback',
hook: getCallback,
}
],
}], {
sesame: "sesame open",
/*
OR check sesame dynamically
and refine permissioning with accessfilter (optional)
*/
//sesame: (sesame) => true
//accessFilter: (sesame, exporter) => { return exporter.name === "MyRPCGroup1" && sesame === "sesame open" },
})
new RPCSocket(20002, 'localhost').connect("sesame open").then(async sock => {
try{
const RPCs = sock['MyRPCGroup1']
await RPCs.echo("hello!").then(console.log)
await RPCs.add(1, Math.PI).then(console.log)
await RPCs.getAsync().then(console.log)
await RPCs.getCallback(console.log).then(console.log)
}catch(e){
console.log(String(e))
}
})
//TypeScript and pseudo-interfaces
type MyInterface = {
MyRPCGroup1: {
echo: (x: string) => string
add: (a: number, b: number) => number
getAsync: () => Promise<{ topic: string, message: string }>
getCallback: (callback:Function) => string
}
};
/*
exportRPCs is now type safe. Try swapping echo for badEcho.
Sadly TSC's stack traces aren't the best, but try to scroll to the bottom of them to find useful info like
Type '(x: boolean) => number' is not assignable to type '(x: string) => string'
*/
const badEcho = (x: boolean) : number => 3
new RPCServer<MyInterface>(20003, [{
name: 'MyRPCGroup1',
exportRPCs: () => [
//badEcho,
echo,
add,
getAsync,
{
name: 'getCallback',
hook: getCallback,
}
],
}])
new RPCSocket<MyInterface>(20003, 'localhost').connect().then(async sock => {
try{
await sock.MyRPCGroup1.echo("hello!").then(console.log)
await sock.MyRPCGroup1.add(1, Math.PI).then(console.log)
await sock.MyRPCGroup1.getAsync().then(console.log)
await sock.MyRPCGroup1.getCallback(console.log).then(console.log)
}catch(e){
console.log(String(e))
}
})
//Class-based pattern
interface IMyImplementation {
echo: (x: string) => string
add: (a: number, b: number) => number
getAsync: () => Promise<{ topic: string, message: string }>
getCallback: (callback:Function) => string
}
type IfcGenerator<Name extends string, Interface, keys extends keyof Interface = keyof Interface> = {
[name in Name]: {
[key in keys] : Interface[key]
}
}
type MyIfc = IfcGenerator<"MyRPCGroup1", IMyImplementation>
class MyImplementation implements IMyImplementation, RPCExporter<MyIfc>{
//"X" as "X" syntax is required to satisfy the type system (as it assumes string)
name = "MyRpcGroup11" as "MyRPCGroup1"
//List the functions you declared in MyIfc
exportRPCs = () => [
this.echo,
this.add,
this.getAsync,
this.getCallback
]
//Write your implementations as you normally would
echo = (text: string) => text
add = (a: number, b: number) : number => a + b
getAsync = async () : Promise<{topic: string, message:string}>=> await new Promise((res, _) => {
setTimeout(() => {
res({
topic: "Hey!!",
message: "Hello World Async!"
})
}, 250)
})
getCallback = (callback: Function) : string => {
setTimeout(() => {
try{
callback({
topic: "Hey!!",
message: "Hello World Callback!"
})
}catch(e){
console.log(String(e))
}
}, 250)
return "Please wait for a callback :)"
}
}
new RPCServer<MyIfc>(20004, [new MyImplementation(), /* ... other RPCExporters */])
new RPCSocket<MyIfc>(20004, 'localhost').connect().then(async sock => {
try{
await sock.MyRPCGroup1.echo("hello!").then(console.log)
await sock.MyRPCGroup1.add(1, Math.PI).then(console.log)
await sock.MyRPCGroup1.getAsync().then(console.log)
await sock.MyRPCGroup1.getCallback(console.log).then(console.log)
}catch(e){
console.log(String(e))
}
})
+435 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "rpclibrary", "name": "rpclibrary",
"version": "1.10.2", "version": "1.12.3",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {
@@ -631,6 +631,12 @@
"integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=",
"dev": true "dev": true
}, },
"array-flatten": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
"integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=",
"dev": true
},
"array-unique": { "array-unique": {
"version": "0.3.2", "version": "0.3.2",
"resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz",
@@ -837,6 +843,41 @@
"integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==", "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==",
"dev": true "dev": true
}, },
"body-parser": {
"version": "1.19.0",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz",
"integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==",
"dev": true,
"requires": {
"bytes": "3.1.0",
"content-type": "~1.0.4",
"debug": "2.6.9",
"depd": "~1.1.2",
"http-errors": "1.7.2",
"iconv-lite": "0.4.24",
"on-finished": "~2.3.0",
"qs": "6.7.0",
"raw-body": "2.4.0",
"type-is": "~1.6.17"
},
"dependencies": {
"debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"dev": true,
"requires": {
"ms": "2.0.0"
}
},
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
"dev": true
}
}
},
"brace-expansion": { "brace-expansion": {
"version": "1.1.11", "version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
@@ -1001,6 +1042,12 @@
"integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=", "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=",
"dev": true "dev": true
}, },
"bytes": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
"integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==",
"dev": true
},
"cacache": { "cacache": {
"version": "12.0.3", "version": "12.0.3",
"resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.3.tgz", "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.3.tgz",
@@ -1314,6 +1361,21 @@
"integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=", "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=",
"dev": true "dev": true
}, },
"content-disposition": {
"version": "0.5.3",
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz",
"integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==",
"dev": true,
"requires": {
"safe-buffer": "5.1.2"
}
},
"content-type": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
"integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==",
"dev": true
},
"convert-source-map": { "convert-source-map": {
"version": "1.7.0", "version": "1.7.0",
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz",
@@ -1328,6 +1390,12 @@
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz",
"integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s="
}, },
"cookie-signature": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
"integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=",
"dev": true
},
"copy-concurrently": { "copy-concurrently": {
"version": "1.0.5", "version": "1.0.5",
"resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz", "resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz",
@@ -1522,6 +1590,12 @@
} }
} }
}, },
"depd": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
"integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=",
"dev": true
},
"des.js": { "des.js": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.0.tgz", "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.0.tgz",
@@ -1532,6 +1606,12 @@
"minimalistic-assert": "^1.0.0" "minimalistic-assert": "^1.0.0"
} }
}, },
"destroy": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
"integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=",
"dev": true
},
"detect-file": { "detect-file": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz",
@@ -1573,6 +1653,12 @@
"stream-shift": "^1.0.0" "stream-shift": "^1.0.0"
} }
}, },
"ee-first": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
"integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=",
"dev": true
},
"elliptic": { "elliptic": {
"version": "6.5.1", "version": "6.5.1",
"resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.1.tgz", "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.1.tgz",
@@ -1600,6 +1686,12 @@
"integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=", "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=",
"dev": true "dev": true
}, },
"encodeurl": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
"integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=",
"dev": true
},
"end-of-stream": { "end-of-stream": {
"version": "1.4.4", "version": "1.4.4",
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
@@ -1735,6 +1827,12 @@
"integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==",
"dev": true "dev": true
}, },
"escape-html": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
"integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=",
"dev": true
},
"escape-string-regexp": { "escape-string-regexp": {
"version": "1.0.5", "version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
@@ -1778,6 +1876,12 @@
"integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
"dev": true "dev": true
}, },
"etag": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
"integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=",
"dev": true
},
"events": { "events": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/events/-/events-3.0.0.tgz", "resolved": "https://registry.npmjs.org/events/-/events-3.0.0.tgz",
@@ -1868,6 +1972,67 @@
"homedir-polyfill": "^1.0.1" "homedir-polyfill": "^1.0.1"
} }
}, },
"express": {
"version": "4.17.1",
"resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz",
"integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==",
"dev": true,
"requires": {
"accepts": "~1.3.7",
"array-flatten": "1.1.1",
"body-parser": "1.19.0",
"content-disposition": "0.5.3",
"content-type": "~1.0.4",
"cookie": "0.4.0",
"cookie-signature": "1.0.6",
"debug": "2.6.9",
"depd": "~1.1.2",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
"finalhandler": "~1.1.2",
"fresh": "0.5.2",
"merge-descriptors": "1.0.1",
"methods": "~1.1.2",
"on-finished": "~2.3.0",
"parseurl": "~1.3.3",
"path-to-regexp": "0.1.7",
"proxy-addr": "~2.0.5",
"qs": "6.7.0",
"range-parser": "~1.2.1",
"safe-buffer": "5.1.2",
"send": "0.17.1",
"serve-static": "1.14.1",
"setprototypeof": "1.1.1",
"statuses": "~1.5.0",
"type-is": "~1.6.18",
"utils-merge": "1.0.1",
"vary": "~1.1.2"
},
"dependencies": {
"cookie": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz",
"integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==",
"dev": true
},
"debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"dev": true,
"requires": {
"ms": "2.0.0"
}
},
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
"dev": true
}
}
},
"extend-shallow": { "extend-shallow": {
"version": "3.0.2", "version": "3.0.2",
"resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
@@ -1995,6 +2160,38 @@
} }
} }
}, },
"finalhandler": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
"integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==",
"dev": true,
"requires": {
"debug": "2.6.9",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"on-finished": "~2.3.0",
"parseurl": "~1.3.3",
"statuses": "~1.5.0",
"unpipe": "~1.0.0"
},
"dependencies": {
"debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"dev": true,
"requires": {
"ms": "2.0.0"
}
},
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
"dev": true
}
}
},
"find-cache-dir": { "find-cache-dir": {
"version": "2.1.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz",
@@ -2105,6 +2302,12 @@
} }
} }
}, },
"forwarded": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
"integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=",
"dev": true
},
"fragment-cache": { "fragment-cache": {
"version": "0.2.1", "version": "0.2.1",
"resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz",
@@ -2114,6 +2317,12 @@
"map-cache": "^0.2.2" "map-cache": "^0.2.2"
} }
}, },
"fresh": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
"integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=",
"dev": true
},
"from2": { "from2": {
"version": "2.3.0", "version": "2.3.0",
"resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz",
@@ -3008,12 +3217,42 @@
"resolved": "https://registry.npmjs.org/http/-/http-0.0.0.tgz", "resolved": "https://registry.npmjs.org/http/-/http-0.0.0.tgz",
"integrity": "sha1-huYybSnF0Dnen6xYSkVon5KfT3I=" "integrity": "sha1-huYybSnF0Dnen6xYSkVon5KfT3I="
}, },
"http-errors": {
"version": "1.7.2",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz",
"integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==",
"dev": true,
"requires": {
"depd": "~1.1.2",
"inherits": "2.0.3",
"setprototypeof": "1.1.1",
"statuses": ">= 1.5.0 < 2",
"toidentifier": "1.0.0"
},
"dependencies": {
"inherits": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
"dev": true
}
}
},
"https-browserify": { "https-browserify": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz",
"integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=", "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=",
"dev": true "dev": true
}, },
"iconv-lite": {
"version": "0.4.24",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
"dev": true,
"requires": {
"safer-buffer": ">= 2.1.2 < 3"
}
},
"ieee754": { "ieee754": {
"version": "1.1.13", "version": "1.1.13",
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz",
@@ -3093,6 +3332,12 @@
"integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==", "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==",
"dev": true "dev": true
}, },
"ipaddr.js": {
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
"integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
"dev": true
},
"is-accessor-descriptor": { "is-accessor-descriptor": {
"version": "0.1.6", "version": "0.1.6",
"resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
@@ -3721,6 +3966,12 @@
"safe-buffer": "^5.1.2" "safe-buffer": "^5.1.2"
} }
}, },
"media-typer": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
"integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=",
"dev": true
},
"mem": { "mem": {
"version": "4.3.0", "version": "4.3.0",
"resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz", "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz",
@@ -3742,6 +3993,18 @@
"readable-stream": "^2.0.1" "readable-stream": "^2.0.1"
} }
}, },
"merge-descriptors": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
"integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=",
"dev": true
},
"methods": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
"integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=",
"dev": true
},
"micromatch": { "micromatch": {
"version": "3.1.10", "version": "3.1.10",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz",
@@ -3773,6 +4036,12 @@
"brorand": "^1.0.1" "brorand": "^1.0.1"
} }
}, },
"mime": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
"dev": true
},
"mime-db": { "mime-db": {
"version": "1.44.0", "version": "1.44.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz",
@@ -3970,6 +4239,12 @@
"semver": "^5.7.0" "semver": "^5.7.0"
} }
}, },
"node-fetch": {
"version": "2.6.0",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz",
"integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==",
"dev": true
},
"node-libs-browser": { "node-libs-browser": {
"version": "2.2.1", "version": "2.2.1",
"resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.1.tgz", "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.1.tgz",
@@ -4383,6 +4658,15 @@
"isobject": "^3.0.1" "isobject": "^3.0.1"
} }
}, },
"on-finished": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
"integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
"dev": true,
"requires": {
"ee-first": "1.1.1"
}
},
"once": { "once": {
"version": "1.4.0", "version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
@@ -4535,6 +4819,12 @@
"better-assert": "~1.0.0" "better-assert": "~1.0.0"
} }
}, },
"parseurl": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
"integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
"dev": true
},
"pascalcase": { "pascalcase": {
"version": "0.1.1", "version": "0.1.1",
"resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz",
@@ -4577,6 +4867,12 @@
"integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
"dev": true "dev": true
}, },
"path-to-regexp": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
"integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=",
"dev": true
},
"pbkdf2": { "pbkdf2": {
"version": "3.0.17", "version": "3.0.17",
"resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.17.tgz", "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.17.tgz",
@@ -4644,6 +4940,16 @@
"integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=", "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=",
"dev": true "dev": true
}, },
"proxy-addr": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz",
"integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==",
"dev": true,
"requires": {
"forwarded": "~0.1.2",
"ipaddr.js": "1.9.1"
}
},
"prr": { "prr": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz",
@@ -4703,6 +5009,12 @@
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
"dev": true "dev": true
}, },
"qs": {
"version": "6.7.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
"integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==",
"dev": true
},
"querystring": { "querystring": {
"version": "0.2.0", "version": "0.2.0",
"resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz",
@@ -4734,6 +5046,24 @@
"safe-buffer": "^5.1.0" "safe-buffer": "^5.1.0"
} }
}, },
"range-parser": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
"integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
"dev": true
},
"raw-body": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz",
"integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==",
"dev": true,
"requires": {
"bytes": "3.1.0",
"http-errors": "1.7.2",
"iconv-lite": "0.4.24",
"unpipe": "1.0.0"
}
},
"readable-stream": { "readable-stream": {
"version": "2.3.6", "version": "2.3.6",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
@@ -4920,6 +5250,12 @@
"ret": "~0.1.10" "ret": "~0.1.10"
} }
}, },
"safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
"dev": true
},
"schema-utils": { "schema-utils": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz",
@@ -4937,12 +5273,64 @@
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
"dev": true "dev": true
}, },
"send": {
"version": "0.17.1",
"resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz",
"integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==",
"dev": true,
"requires": {
"debug": "2.6.9",
"depd": "~1.1.2",
"destroy": "~1.0.4",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
"fresh": "0.5.2",
"http-errors": "~1.7.2",
"mime": "1.6.0",
"ms": "2.1.1",
"on-finished": "~2.3.0",
"range-parser": "~1.2.1",
"statuses": "~1.5.0"
},
"dependencies": {
"debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"dev": true,
"requires": {
"ms": "2.0.0"
},
"dependencies": {
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
"dev": true
}
}
}
}
},
"serialize-javascript": { "serialize-javascript": {
"version": "2.1.2", "version": "2.1.2",
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-2.1.2.tgz", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-2.1.2.tgz",
"integrity": "sha512-rs9OggEUF0V4jUSecXazOYsLfu7OGK2qIn3c7IPBiffz32XniEp/TX9Xmc9LQfK2nQ2QKHvZ2oygKUGU0lG4jQ==", "integrity": "sha512-rs9OggEUF0V4jUSecXazOYsLfu7OGK2qIn3c7IPBiffz32XniEp/TX9Xmc9LQfK2nQ2QKHvZ2oygKUGU0lG4jQ==",
"dev": true "dev": true
}, },
"serve-static": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz",
"integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==",
"dev": true,
"requires": {
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"parseurl": "~1.3.3",
"send": "0.17.1"
}
},
"set-blocking": { "set-blocking": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
@@ -4978,6 +5366,12 @@
"integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=", "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=",
"dev": true "dev": true
}, },
"setprototypeof": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz",
"integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==",
"dev": true
},
"sha.js": { "sha.js": {
"version": "2.4.11", "version": "2.4.11",
"resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz",
@@ -5421,6 +5815,12 @@
} }
} }
}, },
"statuses": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
"integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=",
"dev": true
},
"stream-browserify": { "stream-browserify": {
"version": "2.0.2", "version": "2.0.2",
"resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz", "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz",
@@ -5697,6 +6097,12 @@
"repeat-string": "^1.6.1" "repeat-string": "^1.6.1"
} }
}, },
"toidentifier": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
"integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==",
"dev": true
},
"ts-loader": { "ts-loader": {
"version": "5.4.5", "version": "5.4.5",
"resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-5.4.5.tgz", "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-5.4.5.tgz",
@@ -5784,6 +6190,16 @@
"integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==",
"dev": true "dev": true
}, },
"type-is": {
"version": "1.6.18",
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
"integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
"dev": true,
"requires": {
"media-typer": "0.3.0",
"mime-types": "~2.1.24"
}
},
"typedarray": { "typedarray": {
"version": "0.0.6", "version": "0.0.6",
"resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
@@ -5915,6 +6331,12 @@
"integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
"dev": true "dev": true
}, },
"unpipe": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
"integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=",
"dev": true
},
"unset-value": { "unset-value": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz",
@@ -6023,6 +6445,12 @@
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
"dev": true "dev": true
}, },
"utils-merge": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
"integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=",
"dev": true
},
"uuid": { "uuid": {
"version": "3.3.3", "version": "3.3.3",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.3.tgz", "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.3.tgz",
@@ -6034,6 +6462,12 @@
"integrity": "sha512-CNmdbwQMBjwr9Gsmohvm0pbL954tJrNzf6gWL3K+QMQf00PF7ERGrEiLgjuU3mKreLC2MeGhUsNV9ybTbLgd3w==", "integrity": "sha512-CNmdbwQMBjwr9Gsmohvm0pbL954tJrNzf6gWL3K+QMQf00PF7ERGrEiLgjuU3mKreLC2MeGhUsNV9ybTbLgd3w==",
"dev": true "dev": true
}, },
"vary": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
"integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=",
"dev": true
},
"vm-browserify": { "vm-browserify": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.0.tgz", "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.0.tgz",
+4 -2
View File
@@ -1,7 +1,7 @@
{ {
"name": "rpclibrary", "name": "rpclibrary",
"version": "1.12.1", "version": "2.0.0",
"description": "rpclibrary is a websocket on steroids!", "description": "rpclibrary is a websocket RPC library",
"main": "./js/Index.js", "main": "./js/Index.js",
"repository": { "repository": {
"type": "git", "type": "git",
@@ -38,7 +38,9 @@
"@types/expect": "^1.20.4", "@types/expect": "^1.20.4",
"@types/mocha": "^5.2.7", "@types/mocha": "^5.2.7",
"@types/node": "^11.13.19", "@types/node": "^11.13.19",
"express": "^4.17.1",
"mocha": "^6.2.0", "mocha": "^6.2.0",
"node-fetch": "^2.6.0",
"nyc": "^15.0.0", "nyc": "^15.0.0",
"ts-loader": "^5.3.3", "ts-loader": "^5.3.3",
"ts-mocha": "^6.0.0", "ts-mocha": "^6.0.0",
-53
View File
@@ -1,53 +0,0 @@
import { RPCSocket, RPCServer } from "./Index"
// TL;DR
const echo = (text: string) => {throw new Error("XD")}
const add = (a: number, b: number) : number => a + b
const getAsync = async () : Promise<{topic: string, message:string}>=> await new Promise((res, _) => {
setTimeout(() => {
res({
topic: "Hey!!",
message: "Hello World Async!"
})
}, 250)
})
const getCallback = (cbb: Function) : string => {
setTimeout(() => {
try{
cbb({
topic: "Hey!!",
message: "Hello World Callback!"
})
}catch(e){
console.log(String(e))
}
}, 250)
return "Please wait for a callback :)"
}
new RPCServer(20000, [{
name: 'MyRPCGroup1',
exportRPCs: () => [
echo,
add,
getAsync,
{
name: 'getCallback',
hook: getCallback,
onClose: (response, rpc) => { },
onCallback: (...callbackArgs) => { }
}
]
}])
new RPCSocket(20000, 'localhost').connect()
.then(async sock => {
try{
const val = await sock.call('ABCD', 12345)
console.log("VAL",val);
}catch(e){
console.log("RPC error "+e)
}
}).catch(e => console.log("connect err: "+e))
+15 -20
View File
@@ -11,12 +11,12 @@ export class RPCServer<
> { > {
private pio = PromiseIO.createServer() private pio = PromiseIO.createServer()
private visibility: T.Visibility
private closeHandler: T.CloseHandler private closeHandler: T.CloseHandler
private errorHandler: T.ErrorHandler private errorHandler: T.ErrorHandler
private connectionHandler: T.ConnectionHandler private connectionHandler: T.ConnectionHandler
private sesame?: T.SesameFunction private sesame?: T.SesameFunction
private accessFilter: T.AccessFilter<InterfaceT> private accessFilter: T.AccessFilter<InterfaceT>
private attached = false
/** /**
* @throws On RPC with no name * @throws On RPC with no name
@@ -25,13 +25,9 @@ export class RPCServer<
* @param conf A {@link SocketConf} object with optional settings * @param conf A {@link SocketConf} object with optional settings
*/ */
constructor( constructor(
private port: number,
private exporters: T.ExporterArray<InterfaceT> = [], private exporters: T.ExporterArray<InterfaceT> = [],
private conf: T.ServerConf<InterfaceT> = {}, conf: T.ServerConf<InterfaceT> = {},
private ws = http.createServer()
) { ) {
if (!conf.visibility) this.visibility = "0.0.0.0"
if (conf.sesame) { if (conf.sesame) {
this.sesame = U.makeSesameFunction(conf.sesame) this.sesame = U.makeSesameFunction(conf.sesame)
} }
@@ -57,7 +53,7 @@ export class RPCServer<
exporters.forEach(U.fixNames) //TSC for some reason doesn't preserve name properties of methods exporters.forEach(U.fixNames) //TSC for some reason doesn't preserve name properties of methods
let badRPC = exporters.flatMap(ex => ex.exportRPCs()).find(rpc => !rpc.name) let badRPC = exporters.flatMap(ex => typeof ex.RPCs === "function"?ex.RPCs():(ex as any)).find(rpc => !rpc.name)
if (badRPC) { if (badRPC) {
throw new Error(` throw new Error(`
RPC did not provide a name. RPC did not provide a name.
@@ -68,31 +64,31 @@ export class RPCServer<
\n>------------OFFENDING RPC`) \n>------------OFFENDING RPC`)
} }
this.startWebsocket()
}
private startWebsocket() {
try { try {
this.pio.attach(this.ws)
this.pio.on('socket', (socket: I.Socket) => { this.pio.on('socket', (socket: I.Socket) => {
socket.on('error', (err) => this.errorHandler(socket, err, "system", [])) socket.on('error', (err) => this.errorHandler(socket, err, "system", []))
socket.on('close', () => this.closeHandler(socket)) socket.on('close', () => this.closeHandler(socket))
this.connectionHandler(socket) this.connectionHandler(socket)
this.initRPCs(socket) this.initRPCs(socket)
}) })
if(this.conf.selfStart == null || this.conf.selfStart == true)
this.pio.listen(this.port)
} catch (e) { } catch (e) {
this.errorHandler(this.pio, e, 'system', []) this.errorHandler(this.pio, e, 'system', [])
} }
} }
protected initRPCs(socket: I.Socket) { public attach = (httpServer = new http.Server()) : RPCServer<InterfaceT> => {
this.pio.attach(httpServer)
this.attached = true
return this
}
public listen(port:number) : RPCServer<InterfaceT>{
if(!this.attached) this.attach()
this.pio.listen(port)
return this
}
protected initRPCs(socket: I.Socket) {
socket.hook('info', async (sesame?: string) => { socket.hook('info', async (sesame?: string) => {
const rpcs = await Promise.all(this.exporters.map(async exp => { const rpcs = await Promise.all(this.exporters.map(async exp => {
const allowed = await this.accessFilter(sesame, exp) const allowed = await this.accessFilter(sesame, exp)
@@ -105,6 +101,5 @@ export class RPCServer<
close(): void { close(): void {
this.pio.close() this.pio.close()
this.ws.close()
} }
} }
+1 -2
View File
@@ -9,13 +9,12 @@ export type RPCExporter<
Name extends keyof Ifc = keyof Ifc, Name extends keyof Ifc = keyof Ifc,
> = { > = {
name: Name name: Name
exportRPCs() : T.RPCDefinitions<Ifc>[Name] RPCs : T.RPCDefinitions<Ifc>[Name] | (() => T.RPCDefinitions<Ifc>[Name])
} }
export interface Socket { export interface Socket {
id?: string id?: string
bind: (name: string, listener: T.PioBindListener) => void bind: (name: string, listener: T.PioBindListener) => void
hook: (rpcname: string, handler: T.PioHookListener) => void hook: (rpcname: string, handler: T.PioHookListener) => void
unhook: (rpcname: string, listener?:T.AnyFunction) => void unhook: (rpcname: string, listener?:T.AnyFunction) => void
call: (rpcname: string, ...args: any[]) => Promise<any> call: (rpcname: string, ...args: any[]) => Promise<any>
+6 -4
View File
@@ -6,6 +6,7 @@ const socketio = require('socket.io')
export class PromiseIO { export class PromiseIO {
io?: Server io?: Server
httpServer: httpServer
private listeners: { [eventName in string]: ((...args: any) => void)[] } = { private listeners: { [eventName in string]: ((...args: any) => void)[] } = {
socket: [], socket: [],
connect: [] connect: []
@@ -16,10 +17,8 @@ export class PromiseIO {
} }
attach(httpServer: httpServer) { attach(httpServer: httpServer) {
this.httpServer = httpServer
this.io = socketio(httpServer) this.io = socketio(httpServer)
}
listen(port: number) {
this.io!.on('connection', (sock: Socket) => { this.io!.on('connection', (sock: Socket) => {
const pioSock = U.makePioSocket(sock) const pioSock = U.makePioSocket(sock)
this.listeners['socket'].forEach(listener => listener(pioSock)) this.listeners['socket'].forEach(listener => listener(pioSock))
@@ -36,7 +35,10 @@ export class PromiseIO {
pioSock.on('reconnecting', ()=>console.log('reconnecting')); pioSock.on('reconnecting', ()=>console.log('reconnecting'));
*/ */
}) })
this.io!.listen(port) }
listen(port: number) {
this.httpServer!.listen(port)
} }
on(eventName: string, listener: T.AnyFunction) { on(eventName: string, listener: T.AnyFunction) {
-2
View File
@@ -28,12 +28,10 @@ export type ExporterArray<InterfaceT extends RPCInterface = RPCInterface> = I.RP
export type ConnectedSocket<T extends RPCInterface = RPCInterface> = RPCSocket & AsyncIfc<T> export type ConnectedSocket<T extends RPCInterface = RPCInterface> = RPCSocket & AsyncIfc<T>
export type ServerConf<InterfaceT extends RPCInterface> = { export type ServerConf<InterfaceT extends RPCInterface> = {
selfStart?: boolean
accessFilter?: AccessFilter<InterfaceT> accessFilter?: AccessFilter<InterfaceT>
connectionHandler?: ConnectionHandler connectionHandler?: ConnectionHandler
errorHandler?: ErrorHandler errorHandler?: ErrorHandler
closeHandler?: CloseHandler closeHandler?: CloseHandler
visibility?: Visibility
} & SesameConf } & SesameConf
export type SocketConf = { export type SocketConf = {
+2 -2
View File
@@ -61,7 +61,7 @@ RPC did not provide a name.
*/ */
export function rpcHooker(socket: I.Socket, exporter: I.RPCExporter<any, any>, errorHandler: T.ErrorHandler, sesame?: T.SesameFunction, makeUnique = true): T.ExtendedRpcInfo[] { export function rpcHooker(socket: I.Socket, exporter: I.RPCExporter<any, any>, errorHandler: T.ErrorHandler, sesame?: T.SesameFunction, makeUnique = true): T.ExtendedRpcInfo[] {
const owner = exporter.name const owner = exporter.name
const RPCs = exporter.exportRPCs() const RPCs = typeof exporter.RPCs === "function" ? exporter.RPCs() : exporter.RPCs
return RPCs.map(rpc => rpcToRpcinfo(socket, rpc, owner, errorHandler, sesame)) return RPCs.map(rpc => rpcToRpcinfo(socket, rpc, owner, errorHandler, sesame))
.map(info => { .map(info => {
@@ -262,7 +262,7 @@ export const makePioSocket = (socket: any): I.Socket => {
close: () => { close: () => {
socket socket
socket.disconnect(true) socket.disconnect(true)
}, }
} }
} }
+141 -40
View File
@@ -3,13 +3,16 @@ import { RPCServer, RPCSocket } from '../Index'
import { RPCExporter } from "../src/Interfaces"; import { RPCExporter } from "../src/Interfaces";
import { ConnectedSocket } from "../src/Types"; import { ConnectedSocket } from "../src/Types";
import * as log from 'why-is-node-running'; import * as log from 'why-is-node-running';
import * as http from 'http';
import * as express from 'express';
import * as fetch from 'node-fetch';
const add = (...args: number[]) => { return args.reduce((a, b) => a + b, 0) } const add = (...args: number[]) => { return args.reduce((a, b) => a + b, 0) }
function makeServer() { function makeServer() {
let subcallback let subcallback
return new RPCServer(21010, [{ const sv = new RPCServer([{
name: 'test', name: 'test',
exportRPCs: () => [ RPCs: [
{ {
name: 'echo', name: 'echo',
call: async (s: string) => s, call: async (s: string) => s,
@@ -37,12 +40,15 @@ function makeServer() {
add, add,
function triggerCallback(...messages: any[]): number { return subcallback.apply({}, messages) }, function triggerCallback(...messages: any[]): number { return subcallback.apply({}, messages) },
] ]
}], { }],
{
connectionHandler: (socket) => { connectionHandler: (socket) => {
}, },
closeHandler: (socket) => { }, closeHandler: (socket) => { },
errorHandler: (socket, err) => { throw err } errorHandler: (socket, err) => { throw err }
}) })
sv.listen(21010)
return sv
} }
@@ -51,9 +57,9 @@ describe('RPCServer', () => {
const echo = (x) => x const echo = (x) => x
before(done => { before(done => {
server = new RPCServer(21003, [{ server = new RPCServer([{
name: 'HelloWorldRPCGroup', name: 'HelloWorldRPCGroup',
exportRPCs: () => [ RPCs: () => [
echo, //named function variable echo, //named function variable
function echof(x) { return x }, //named function function echof(x) { return x }, //named function
{ {
@@ -62,7 +68,7 @@ describe('RPCServer', () => {
} }
] ]
}]) }])
server.listen(21003)
client = new RPCSocket(21003, 'localhost') client = new RPCSocket(21003, 'localhost')
done() done()
}) })
@@ -83,7 +89,7 @@ describe('RPCServer', () => {
if (r0 === 'Hello' && r1 === 'World' && r2.join('') === 'RPC!') { if (r0 === 'Hello' && r1 === 'World' && r2.join('') === 'RPC!') {
done() done()
}else{ } else {
done(new Error("Bad response")) done(new Error("Bad response"))
} }
}) })
@@ -91,12 +97,13 @@ describe('RPCServer', () => {
it('new RPCServer() should fail on bad RPC', (done) => { it('new RPCServer() should fail on bad RPC', (done) => {
try { try {
new RPCServer(20001, [{ const sv = new RPCServer([{
name: 'bad', name: 'bad',
exportRPCs: () => [ RPCs: () => [
(aaa, bbb, ccc) => { return aaa + bbb + ccc } (aaa, bbb, ccc) => { return aaa + bbb + ccc }
] ]
}]) }])
sv.listen(20001)
done(new Error("Didn't fail with bad RPC")) done(new Error("Didn't fail with bad RPC"))
} catch (badRPCError) { } catch (badRPCError) {
done() done()
@@ -104,6 +111,91 @@ describe('RPCServer', () => {
}) })
}) })
describe('RPCServer with premade http server', () => {
const echo = (x) => x
const RPCs = [
echo, //named function variable
function echof(x) { return x }, //named function
{
name: 'echoExplicit', //describing object
call: async (x, y, z) => [x, y, z]
}
]
const RPCExporters = [
{
name: 'HelloWorldRPCGroup',
RPCs: RPCs,
}
]
const RPCExporters2 = [
{
name: 'Grp2',
RPCs: [
function test(){ return "test" }
],
}
]
let client:RPCSocket, server:RPCServer
before(done => {
const expressServer = express()
const httpServer = new http.Server(expressServer)
expressServer.get('/REST_ping', (req, res)=>{
return res
.send('REST_pong')
.status(200)
})
server = new RPCServer(
RPCExporters,
)
server.attach(httpServer)
httpServer.listen(8080)
client = new RPCSocket(8080, 'localhost')
done()
})
after(done => {
client.close()
server.close()
done()
})
it('should serve REST', (done) => {
fetch('http://localhost:8080/REST_ping').then(response => {
response.text().then(text => {
if(text === "REST_pong")
done()
else
done(new Error("REST repsonse was "+text))
})
})
})
it('should be able to use all kinds of RPC definitions', (done) => {
client.connect().then(async () => {
const r0 = await client['HelloWorldRPCGroup'].echo('Hello')
const r1 = await client['HelloWorldRPCGroup'].echof('World')
const r2 = await client['HelloWorldRPCGroup'].echoExplicit('R', 'P', 'C!')
if (r0 === 'Hello' && r1 === 'World' && r2.join('') === 'RPC!') {
done()
} else {
done(new Error("Bad response"))
}
})
})
})
describe('RPCSocket', () => { describe('RPCSocket', () => {
let client: RPCSocket let client: RPCSocket
@@ -111,6 +203,7 @@ describe('RPCSocket', () => {
before(async () => { before(async () => {
server = makeServer() server = makeServer()
client = new RPCSocket(21010, "localhost") client = new RPCSocket(21010, "localhost")
return await client.connect() return await client.connect()
}) })
@@ -183,9 +276,9 @@ describe('It should do unhook', () => {
let server: RPCServer let server: RPCServer
before(async () => { before(async () => {
server = new RPCServer(21010, [{ server = new RPCServer([{
name: "test", name: "test",
exportRPCs: () => [{ RPCs: () => [{
name: 'subscribe', name: 'subscribe',
hook: async (callback): Promise<void> => { hook: async (callback): Promise<void> => {
cb = <Function>callback cb = <Function>callback
@@ -211,11 +304,13 @@ describe('It should do unhook', () => {
function publish(): string { cb(candy); return candy }, function publish(): string { cb(candy); return candy },
function unsubscribe(): string { candy = noCandy; cb(candy); cb = () => { }; return candy } function unsubscribe(): string { candy = noCandy; cb(candy); cb = () => { }; return candy }
] ]
}], { }],
{
connectionHandler: (socket) => { }, connectionHandler: (socket) => { },
closeHandler: (socket) => { }, closeHandler: (socket) => { },
errorHandler: (socket, err) => { throw err } errorHandler: (socket, err) => { throw err }
}) })
server.listen(21010)
client = new RPCSocket(21010, "localhost") client = new RPCSocket(21010, "localhost")
return await client.connect() return await client.connect()
}) })
@@ -239,14 +334,14 @@ describe('It should do unhook', () => {
it('Unhook+unsubscribe should stop callbacks', (done) => { it('Unhook+unsubscribe should stop callbacks', (done) => {
client['test'].subscribe(function myCallback(c){ client['test'].subscribe(function myCallback(c) {
if(run == 1) if (run == 1)
(myCallback as any).destroy() (myCallback as any).destroy()
if (c !== expected[run++]) { if (c !== expected[run++]) {
done(new Error(`Wrong candy '${c}' in iteration '${run - 1}'`)) done(new Error(`Wrong candy '${c}' in iteration '${run - 1}'`))
} }
}).then(async function(res){ }).then(async function (res) {
const r1 = await client['test'].publish() const r1 = await client['test'].publish()
const r3 = await client['test'].unsubscribe() const r3 = await client['test'].unsubscribe()
const r2 = await client['test'].publish() const r2 = await client['test'].publish()
@@ -266,11 +361,11 @@ type SesameTestIfc = {
test: { test: {
checkCandy: () => Promise<string> checkCandy: () => Promise<string>
subscribe: (callback: Function) => Promise<topicDTO> subscribe: (callback: Function) => Promise<topicDTO>
manyParams: <A=string,B=number,C=boolean,D=Object>(a:A, b:B, c:C, d:D) => Promise<[A, B, C, D]> manyParams: <A = string, B = number, C = boolean, D = Object>(a: A, b: B, c: C, d: D) => Promise<[A, B, C, D]>
} }
other: { other: {
echo: (x:any) => Promise<any> echo: (x: any) => Promise<any>
} }
} }
@@ -281,9 +376,9 @@ describe('Sesame should unlock the socket', () => {
let cb: Function = (...args) => { } let cb: Function = (...args) => { }
before((done) => { before((done) => {
server = new RPCServer<SesameTestIfc>(21004, [{ server = new RPCServer<SesameTestIfc>([{
name: "test", name: "test",
exportRPCs: () => [ RPCs: () => [
{ {
name: 'subscribe', name: 'subscribe',
hook: async (callback) => { hook: async (callback) => {
@@ -297,15 +392,16 @@ describe('Sesame should unlock the socket', () => {
async function checkCandy() { cb(candy); cb = () => { }; return candy }, async function checkCandy() { cb(candy); cb = () => { }; return candy },
async function manyParams(a, b, c, d) { return [a, b, c, d] } async function manyParams(a, b, c, d) { return [a, b, c, d] }
], ],
},{ }, {
name: 'other', name: 'other',
exportRPCs: () => [ RPCs: () => [
async function echo(x){return x} async function echo(x) { return x }
] ]
}], { }], {
sesame: (_sesame) => _sesame === 'sesame!' sesame: (_sesame) => _sesame === 'sesame!'
}) })
server.listen(21004)
const sock = new RPCSocket<SesameTestIfc>(21004, "localhost") const sock = new RPCSocket<SesameTestIfc>(21004, "localhost")
sock.connect('sesame!').then(cli => { sock.connect('sesame!').then(cli => {
client = cli client = cli
@@ -378,15 +474,14 @@ describe('Error handling', () => {
} }
it("RPC throws on client without handler", (done) => { it("RPC throws on client without handler", (done) => {
let server = new RPCServer(21004, [{ let server = new RPCServer([{
name: "createUser", name: "createUser",
exportRPCs: () => [{ RPCs: () => [{
name: 'createUser' as 'createUser', name: 'createUser' as 'createUser',
call: createUser call: createUser
}] }]
}], { }])
server.listen(21004)
})
let sock = new RPCSocket(21004, 'localhost') let sock = new RPCSocket(21004, 'localhost')
sock.connect().then((cli) => { sock.connect().then((cli) => {
@@ -413,9 +508,9 @@ describe('Error handling', () => {
}) })
it("RPC throws on server with handler", (done) => { it("RPC throws on server with handler", (done) => {
let server = new RPCServer(21004, [{ let server = new RPCServer([{
name: "createUser", name: "createUser",
exportRPCs: () => [{ RPCs: () => [{
name: 'createUser' as 'createUser', name: 'createUser' as 'createUser',
call: createUser call: createUser
}] }]
@@ -424,6 +519,7 @@ describe('Error handling', () => {
done() done()
} }
}) })
server.listen(21004)
let sock = new RPCSocket(21004, 'localhost') let sock = new RPCSocket(21004, 'localhost')
sock.connect().then((cli) => { sock.connect().then((cli) => {
@@ -457,9 +553,9 @@ describe("Errorhandler functionality", () => {
} }
it("correct values are passed to the handler", (done) => { it("correct values are passed to the handler", (done) => {
let server = new RPCServer(21004, [{ let server = new RPCServer([{
name: "createUser", name: "createUser",
exportRPCs: () => [{ RPCs: () => [{
name: 'createUser' as 'createUser', name: 'createUser' as 'createUser',
call: createUser call: createUser
}] }]
@@ -469,6 +565,7 @@ describe("Errorhandler functionality", () => {
done() done()
} }
}) })
server.listen(21004)
let sock = new RPCSocket(21004, 'localhost') let sock = new RPCSocket(21004, 'localhost')
sock.connect().then((cli) => { sock.connect().then((cli) => {
@@ -493,9 +590,9 @@ describe("Errorhandler functionality", () => {
it("handler sees sesame", (done) => { it("handler sees sesame", (done) => {
let sesame = "AAAAAAAAAAAAAAA" let sesame = "AAAAAAAAAAAAAAA"
let server = new RPCServer(21004, [{ let server = new RPCServer([{
name: "createUser" as "createUser", name: "createUser" as "createUser",
exportRPCs: () => [{ RPCs: () => [{
name: 'createUser' as 'createUser', name: 'createUser' as 'createUser',
call: createUser call: createUser
}] }]
@@ -507,6 +604,7 @@ describe("Errorhandler functionality", () => {
} }
}) })
server.listen(21004)
let sock = new RPCSocket(21004, 'localhost') let sock = new RPCSocket(21004, 'localhost')
sock.connect(sesame).then((cli) => { sock.connect(sesame).then((cli) => {
@@ -547,7 +645,7 @@ describe("Class binding", () => {
class MyExporter implements RPCExporter<myExporterIfc>{ class MyExporter implements RPCExporter<myExporterIfc>{
name = "MyExporter" as "MyExporter" name = "MyExporter" as "MyExporter"
exportRPCs = () => [ RPCs = () => [
this.myRPC this.myRPC
] ]
@@ -559,7 +657,7 @@ describe("Class binding", () => {
class MyOtherExporter implements RPCExporter<myExporterIfc>{ class MyOtherExporter implements RPCExporter<myExporterIfc>{
name = "MyExporter" as "MyExporter" name = "MyExporter" as "MyExporter"
exportRPCs = () => [ RPCs = () => [
this.myRPC this.myRPC
] ]
@@ -571,18 +669,21 @@ describe("Class binding", () => {
before(done => { before(done => {
exporter1 = new MyExporter() exporter1 = new MyExporter()
serv = new RPCServer<myExporterIfc>(21004, [exporter1], { serv = new RPCServer<myExporterIfc>(
[exporter1],
{
accessFilter: async (sesame, exporter) => { accessFilter: async (sesame, exporter) => {
if(exporter.name === 'MyExporter'){ if (exporter.name === 'MyExporter') {
if (!allowed) return false if (!allowed) return false
allowed = false allowed = false
return sesame === 'xxx'; return sesame === 'xxx';
}else{ } else {
return false return false
} }
}, },
sesame: "xxx" sesame: "xxx"
}) })
serv.listen(21004)
done() done()
}) })
@@ -630,7 +731,7 @@ describe("Class binding", () => {
it("use sesameFilter for available", (done) => { it("use sesameFilter for available", (done) => {
if (sock['MyExporter']){ if (sock['MyExporter']) {
allowed = false allowed = false
done() done()
} }
@@ -722,6 +823,6 @@ describe("attaching handlers before connecting", () => {
describe('finally', () => { describe('finally', () => {
it('print open handles (Ignore `DNSCHANNEL` and `Immediate`)', () => { it('print open handles (Ignore `DNSCHANNEL` and `Immediate`)', () => {
log() //log(console)
}) })
}) })