2.0 rework
This commit is contained in:
@@ -12,33 +12,58 @@ rpclibrary is a simple to use websocket RPC library.
|
||||
npm i rpclibrary
|
||||
```
|
||||
|
||||
# Quickstart
|
||||
# Quickstart (TL;DR)
|
||||
```typescript
|
||||
import {RPCServer, RPCSocket} from 'rpclibrary'
|
||||
|
||||
// TL;DR
|
||||
const echo = (text) => text
|
||||
const add = (a, b) => a + b
|
||||
const subtract = (a, b) => a - b
|
||||
|
||||
new RPCServer(20000, [{
|
||||
new RPCServer([
|
||||
{
|
||||
name: 'MyRPCGroup1',
|
||||
exportRPCs: () => [
|
||||
RPCs: [
|
||||
echo,
|
||||
add,
|
||||
]
|
||||
}])
|
||||
},
|
||||
name: 'MyOtherRPCGroup',
|
||||
RPCs: [
|
||||
subtract
|
||||
]
|
||||
}
|
||||
]).listen(20000)
|
||||
|
||||
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 sock['MyRPCGroup1'].echo("hello!").then(console.log)
|
||||
const sum = await sock['MyRPCGroup1'].add(1, Math.PI)
|
||||
const result = await sock['MyOtherRPCGroup'].subtract(sum, 4)
|
||||
|
||||
}catch(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?
|
||||
|
||||
rpclibrary offers full support for callbacks and Promises.
|
||||
@@ -69,16 +94,16 @@ const getCallback = (callback) => {
|
||||
return "Please wait for a callback :)"
|
||||
}
|
||||
|
||||
new RPCServer(20000, [{
|
||||
new RPCServer([{
|
||||
name: 'MyRPCGroup1',
|
||||
exportRPCs: () => [
|
||||
RPCs: [
|
||||
getAsync,
|
||||
{
|
||||
name: 'getCallback',
|
||||
hook: getCallback,
|
||||
}
|
||||
]
|
||||
}])
|
||||
}]).listen(20000)
|
||||
|
||||
new RPCSocket(20000, 'localhost').connect().then(async sock => {
|
||||
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
|
||||
|
||||
```typescript
|
||||
new RPCServer(20001, [{
|
||||
new RPCServer([{
|
||||
name: 'MyRPCGroup1',
|
||||
exportRPCs: () => [
|
||||
RPCs: [
|
||||
echo,
|
||||
add,
|
||||
getAsync,
|
||||
{
|
||||
name: 'getCallback',
|
||||
hook: getCallback,
|
||||
@@ -111,11 +134,10 @@ new RPCServer(20001, [{
|
||||
}
|
||||
],
|
||||
}], {
|
||||
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 */ },
|
||||
})
|
||||
}).listen(20001)
|
||||
|
||||
const sock = new RPCSocket(20001, 'localhost')
|
||||
sock.on('error', (e) => { /* handle error */ })
|
||||
@@ -132,184 +154,151 @@ sock.connect().then(_ => { /* ... */})
|
||||
|
||||
# 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
|
||||
|
||||
//Restricting access
|
||||
new RPCServer(20002, [{
|
||||
name: 'MyRPCGroup1',
|
||||
exportRPCs: () => [
|
||||
echo,
|
||||
add,
|
||||
getAsync,
|
||||
{
|
||||
name: 'getCallback',
|
||||
hook: getCallback,
|
||||
}
|
||||
],
|
||||
}], {
|
||||
new RPCServer([ ... ], {
|
||||
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" },
|
||||
})
|
||||
}).listen(20002)
|
||||
|
||||
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))
|
||||
}
|
||||
/* use normally */
|
||||
})
|
||||
|
||||
|
||||
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
|
||||
|
||||
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.
|
||||
Explicit typing is recommended.
|
||||
**NOTE** that your function implementations and Group Names will usually have to be manually typed to make the compiler infer the correct types.
|
||||
|
||||
Example:
|
||||
First apply types to your functions:
|
||||
```typescript
|
||||
echo = (x) => x
|
||||
/*becomes*/
|
||||
/*becomes*/
|
||||
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
|
||||
type MyInterface = {
|
||||
type MyType = {
|
||||
MyRPCGroup1: {
|
||||
echo: (x: string) => string
|
||||
add: (a: number, b: number) => number
|
||||
getAsync: () => Promise<{ topic: string, message: string }>
|
||||
getCallback: (callback:Function) => string
|
||||
}
|
||||
//...further RPC declarations
|
||||
},
|
||||
//... 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.
|
||||
|
||||
/*
|
||||
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
|
||||
```typescript
|
||||
new RPCServer<MyType>([{
|
||||
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'
|
||||
*/
|
||||
|
||||
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{
|
||||
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 => {
|
||||
//TSC knows about MyRPCGroup1 and its members
|
||||
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
|
||||
|
||||
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:
|
||||
# A class-based pattern for modular APIs
|
||||
|
||||
Define a regular Interface. This will function as the source of truth for type definitions.
|
||||
```typescript
|
||||
interface IMyImplementation {
|
||||
interface IComponentA {
|
||||
echo: (x: string) => string
|
||||
add: (a: number, b: number) => number
|
||||
}
|
||||
|
||||
interface IComponentB {
|
||||
getAsync: () => Promise<{ topic: string, message: string }>
|
||||
getCallback: (callback:Function) => string
|
||||
}
|
||||
```
|
||||
|
||||
type MyInterface = {
|
||||
MyRPCGroup1: {
|
||||
echo: IMyImplementation['echo']
|
||||
add: IMyImplementation['add']
|
||||
getAsync: IMyImplementation['getAsync']
|
||||
getCallback: IMyImplementation['getCallback']
|
||||
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.
|
||||
```typescript
|
||||
type TComponentA = {
|
||||
ComponentA: {
|
||||
echo: IComponentA['echo']
|
||||
add: IComponentA['add']
|
||||
}
|
||||
}
|
||||
|
||||
class MyImplementation implements IMyImplementation, RPCExporter<MyInterface>{
|
||||
//"X" as "X" syntax is required to satisfy the type system (as it assumes string to be the true type)
|
||||
name = "MyRpcGroup11" as "MyRPCGroup1"
|
||||
type TComponentB = {
|
||||
ComponentB: {
|
||||
getAsync: IComponentB['getAsync']
|
||||
getCallback: IComponentB['getCallback']
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
//List the functions you declared in MyInterface
|
||||
exportRPCs = () => [
|
||||
Create classes that implement the interfaces you declared alongside ```RPCExporter<MyType>```, where MyType is the RPC pseudo-interface.
|
||||
```typescript
|
||||
class ComponentA implements IComponentA, RPCExporter<TComponentA> {
|
||||
name = "ComponentA" as "ComponentA"
|
||||
|
||||
RPCs = [
|
||||
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 :)"
|
||||
}
|
||||
}
|
||||
|
||||
type ProjectInterface = MyInterface
|
||||
//& MyOtherInterface
|
||||
//& MyOtherOtherInterface
|
||||
// ...
|
||||
;
|
||||
class ComponentB implements IComponentB, RPCExporter<TComponentB> {
|
||||
name = "ComponentB" as "ComponentB"
|
||||
|
||||
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 => {
|
||||
// ...
|
||||
})
|
||||
|
||||
```
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
})
|
||||
Generated
+435
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "rpclibrary",
|
||||
"version": "1.10.2",
|
||||
"version": "1.12.3",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
@@ -631,6 +631,12 @@
|
||||
"integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=",
|
||||
"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": {
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz",
|
||||
@@ -837,6 +843,41 @@
|
||||
"integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==",
|
||||
"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": {
|
||||
"version": "1.1.11",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||
@@ -1001,6 +1042,12 @@
|
||||
"integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=",
|
||||
"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": {
|
||||
"version": "12.0.3",
|
||||
"resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.3.tgz",
|
||||
@@ -1314,6 +1361,21 @@
|
||||
"integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=",
|
||||
"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": {
|
||||
"version": "1.7.0",
|
||||
"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",
|
||||
"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": {
|
||||
"version": "1.0.5",
|
||||
"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": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.0.tgz",
|
||||
@@ -1532,6 +1606,12 @@
|
||||
"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": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz",
|
||||
@@ -1573,6 +1653,12 @@
|
||||
"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": {
|
||||
"version": "6.5.1",
|
||||
"resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.1.tgz",
|
||||
@@ -1600,6 +1686,12 @@
|
||||
"integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=",
|
||||
"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": {
|
||||
"version": "1.4.4",
|
||||
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
|
||||
@@ -1735,6 +1827,12 @@
|
||||
"integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==",
|
||||
"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": {
|
||||
"version": "1.0.5",
|
||||
"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==",
|
||||
"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": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/events/-/events-3.0.0.tgz",
|
||||
@@ -1868,6 +1972,67 @@
|
||||
"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": {
|
||||
"version": "3.0.2",
|
||||
"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": {
|
||||
"version": "2.1.0",
|
||||
"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": {
|
||||
"version": "0.2.1",
|
||||
"resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz",
|
||||
@@ -2114,6 +2317,12 @@
|
||||
"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": {
|
||||
"version": "2.3.0",
|
||||
"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",
|
||||
"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": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz",
|
||||
"integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=",
|
||||
"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": {
|
||||
"version": "1.1.13",
|
||||
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz",
|
||||
@@ -3093,6 +3332,12 @@
|
||||
"integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==",
|
||||
"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": {
|
||||
"version": "0.1.6",
|
||||
"resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
|
||||
@@ -3721,6 +3966,12 @@
|
||||
"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": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz",
|
||||
@@ -3742,6 +3993,18 @@
|
||||
"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": {
|
||||
"version": "3.1.10",
|
||||
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz",
|
||||
@@ -3773,6 +4036,12 @@
|
||||
"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": {
|
||||
"version": "1.44.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz",
|
||||
@@ -3970,6 +4239,12 @@
|
||||
"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": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.1.tgz",
|
||||
@@ -4383,6 +4658,15 @@
|
||||
"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": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||
@@ -4535,6 +4819,12 @@
|
||||
"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": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz",
|
||||
@@ -4577,6 +4867,12 @@
|
||||
"integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
|
||||
"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": {
|
||||
"version": "3.0.17",
|
||||
"resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.17.tgz",
|
||||
@@ -4644,6 +4940,16 @@
|
||||
"integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=",
|
||||
"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": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz",
|
||||
@@ -4703,6 +5009,12 @@
|
||||
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
|
||||
"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": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz",
|
||||
@@ -4734,6 +5046,24 @@
|
||||
"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": {
|
||||
"version": "2.3.6",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
|
||||
@@ -4920,6 +5250,12 @@
|
||||
"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": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz",
|
||||
@@ -4937,12 +5273,64 @@
|
||||
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
|
||||
"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": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-2.1.2.tgz",
|
||||
"integrity": "sha512-rs9OggEUF0V4jUSecXazOYsLfu7OGK2qIn3c7IPBiffz32XniEp/TX9Xmc9LQfK2nQ2QKHvZ2oygKUGU0lG4jQ==",
|
||||
"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": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
|
||||
@@ -4978,6 +5366,12 @@
|
||||
"integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=",
|
||||
"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": {
|
||||
"version": "2.4.11",
|
||||
"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": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz",
|
||||
@@ -5697,6 +6097,12 @@
|
||||
"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": {
|
||||
"version": "5.4.5",
|
||||
"resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-5.4.5.tgz",
|
||||
@@ -5784,6 +6190,16 @@
|
||||
"integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==",
|
||||
"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": {
|
||||
"version": "0.0.6",
|
||||
"resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
|
||||
@@ -5915,6 +6331,12 @@
|
||||
"integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
|
||||
"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": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz",
|
||||
@@ -6023,6 +6445,12 @@
|
||||
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
|
||||
"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": {
|
||||
"version": "3.3.3",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.3.tgz",
|
||||
@@ -6034,6 +6462,12 @@
|
||||
"integrity": "sha512-CNmdbwQMBjwr9Gsmohvm0pbL954tJrNzf6gWL3K+QMQf00PF7ERGrEiLgjuU3mKreLC2MeGhUsNV9ybTbLgd3w==",
|
||||
"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": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.0.tgz",
|
||||
|
||||
+4
-2
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "rpclibrary",
|
||||
"version": "1.12.1",
|
||||
"description": "rpclibrary is a websocket on steroids!",
|
||||
"version": "2.0.0",
|
||||
"description": "rpclibrary is a websocket RPC library",
|
||||
"main": "./js/Index.js",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -38,7 +38,9 @@
|
||||
"@types/expect": "^1.20.4",
|
||||
"@types/mocha": "^5.2.7",
|
||||
"@types/node": "^11.13.19",
|
||||
"express": "^4.17.1",
|
||||
"mocha": "^6.2.0",
|
||||
"node-fetch": "^2.6.0",
|
||||
"nyc": "^15.0.0",
|
||||
"ts-loader": "^5.3.3",
|
||||
"ts-mocha": "^6.0.0",
|
||||
|
||||
@@ -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
@@ -11,12 +11,12 @@ export class RPCServer<
|
||||
> {
|
||||
|
||||
private pio = PromiseIO.createServer()
|
||||
private visibility: T.Visibility
|
||||
private closeHandler: T.CloseHandler
|
||||
private errorHandler: T.ErrorHandler
|
||||
private connectionHandler: T.ConnectionHandler
|
||||
private sesame?: T.SesameFunction
|
||||
private accessFilter: T.AccessFilter<InterfaceT>
|
||||
private attached = false
|
||||
|
||||
/**
|
||||
* @throws On RPC with no name
|
||||
@@ -25,13 +25,9 @@ export class RPCServer<
|
||||
* @param conf A {@link SocketConf} object with optional settings
|
||||
*/
|
||||
constructor(
|
||||
private port: number,
|
||||
private exporters: T.ExporterArray<InterfaceT> = [],
|
||||
private conf: T.ServerConf<InterfaceT> = {},
|
||||
private ws = http.createServer()
|
||||
conf: T.ServerConf<InterfaceT> = {},
|
||||
) {
|
||||
if (!conf.visibility) this.visibility = "0.0.0.0"
|
||||
|
||||
if (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
|
||||
|
||||
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) {
|
||||
throw new Error(`
|
||||
RPC did not provide a name.
|
||||
@@ -68,31 +64,31 @@ export class RPCServer<
|
||||
\n>------------OFFENDING RPC`)
|
||||
}
|
||||
|
||||
|
||||
this.startWebsocket()
|
||||
}
|
||||
|
||||
private startWebsocket() {
|
||||
try {
|
||||
this.pio.attach(this.ws)
|
||||
|
||||
this.pio.on('socket', (socket: I.Socket) => {
|
||||
socket.on('error', (err) => this.errorHandler(socket, err, "system", []))
|
||||
socket.on('close', () => this.closeHandler(socket))
|
||||
this.connectionHandler(socket)
|
||||
this.initRPCs(socket)
|
||||
})
|
||||
|
||||
if(this.conf.selfStart == null || this.conf.selfStart == true)
|
||||
this.pio.listen(this.port)
|
||||
|
||||
} catch (e) {
|
||||
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) => {
|
||||
const rpcs = await Promise.all(this.exporters.map(async exp => {
|
||||
const allowed = await this.accessFilter(sesame, exp)
|
||||
@@ -105,6 +101,5 @@ export class RPCServer<
|
||||
|
||||
close(): void {
|
||||
this.pio.close()
|
||||
this.ws.close()
|
||||
}
|
||||
}
|
||||
+1
-2
@@ -9,13 +9,12 @@ export type RPCExporter<
|
||||
Name extends keyof Ifc = keyof Ifc,
|
||||
> = {
|
||||
name: Name
|
||||
exportRPCs() : T.RPCDefinitions<Ifc>[Name]
|
||||
RPCs : T.RPCDefinitions<Ifc>[Name] | (() => T.RPCDefinitions<Ifc>[Name])
|
||||
}
|
||||
|
||||
export interface Socket {
|
||||
id?: string
|
||||
bind: (name: string, listener: T.PioBindListener) => void
|
||||
|
||||
hook: (rpcname: string, handler: T.PioHookListener) => void
|
||||
unhook: (rpcname: string, listener?:T.AnyFunction) => void
|
||||
call: (rpcname: string, ...args: any[]) => Promise<any>
|
||||
|
||||
@@ -6,6 +6,7 @@ const socketio = require('socket.io')
|
||||
|
||||
export class PromiseIO {
|
||||
io?: Server
|
||||
httpServer: httpServer
|
||||
private listeners: { [eventName in string]: ((...args: any) => void)[] } = {
|
||||
socket: [],
|
||||
connect: []
|
||||
@@ -16,10 +17,8 @@ export class PromiseIO {
|
||||
}
|
||||
|
||||
attach(httpServer: httpServer) {
|
||||
this.httpServer = httpServer
|
||||
this.io = socketio(httpServer)
|
||||
}
|
||||
|
||||
listen(port: number) {
|
||||
this.io!.on('connection', (sock: Socket) => {
|
||||
const pioSock = U.makePioSocket(sock)
|
||||
this.listeners['socket'].forEach(listener => listener(pioSock))
|
||||
@@ -36,7 +35,10 @@ export class PromiseIO {
|
||||
pioSock.on('reconnecting', ()=>console.log('reconnecting'));
|
||||
*/
|
||||
})
|
||||
this.io!.listen(port)
|
||||
}
|
||||
|
||||
listen(port: number) {
|
||||
this.httpServer!.listen(port)
|
||||
}
|
||||
|
||||
on(eventName: string, listener: T.AnyFunction) {
|
||||
|
||||
@@ -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 ServerConf<InterfaceT extends RPCInterface> = {
|
||||
selfStart?: boolean
|
||||
accessFilter?: AccessFilter<InterfaceT>
|
||||
connectionHandler?: ConnectionHandler
|
||||
errorHandler?: ErrorHandler
|
||||
closeHandler?: CloseHandler
|
||||
visibility?: Visibility
|
||||
} & SesameConf
|
||||
|
||||
export type SocketConf = {
|
||||
|
||||
+2
-2
@@ -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[] {
|
||||
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))
|
||||
.map(info => {
|
||||
@@ -262,7 +262,7 @@ export const makePioSocket = (socket: any): I.Socket => {
|
||||
close: () => {
|
||||
socket
|
||||
socket.disconnect(true)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+141
-40
@@ -3,13 +3,16 @@ import { RPCServer, RPCSocket } from '../Index'
|
||||
import { RPCExporter } from "../src/Interfaces";
|
||||
import { ConnectedSocket } from "../src/Types";
|
||||
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) }
|
||||
function makeServer() {
|
||||
let subcallback
|
||||
return new RPCServer(21010, [{
|
||||
const sv = new RPCServer([{
|
||||
name: 'test',
|
||||
exportRPCs: () => [
|
||||
RPCs: [
|
||||
{
|
||||
name: 'echo',
|
||||
call: async (s: string) => s,
|
||||
@@ -37,12 +40,15 @@ function makeServer() {
|
||||
add,
|
||||
function triggerCallback(...messages: any[]): number { return subcallback.apply({}, messages) },
|
||||
]
|
||||
}], {
|
||||
}],
|
||||
{
|
||||
connectionHandler: (socket) => {
|
||||
},
|
||||
closeHandler: (socket) => { },
|
||||
errorHandler: (socket, err) => { throw err }
|
||||
})
|
||||
sv.listen(21010)
|
||||
return sv
|
||||
}
|
||||
|
||||
|
||||
@@ -51,9 +57,9 @@ describe('RPCServer', () => {
|
||||
const echo = (x) => x
|
||||
|
||||
before(done => {
|
||||
server = new RPCServer(21003, [{
|
||||
server = new RPCServer([{
|
||||
name: 'HelloWorldRPCGroup',
|
||||
exportRPCs: () => [
|
||||
RPCs: () => [
|
||||
echo, //named function variable
|
||||
function echof(x) { return x }, //named function
|
||||
{
|
||||
@@ -62,7 +68,7 @@ describe('RPCServer', () => {
|
||||
}
|
||||
]
|
||||
}])
|
||||
|
||||
server.listen(21003)
|
||||
client = new RPCSocket(21003, 'localhost')
|
||||
done()
|
||||
})
|
||||
@@ -83,7 +89,7 @@ describe('RPCServer', () => {
|
||||
|
||||
if (r0 === 'Hello' && r1 === 'World' && r2.join('') === 'RPC!') {
|
||||
done()
|
||||
}else{
|
||||
} else {
|
||||
done(new Error("Bad response"))
|
||||
}
|
||||
})
|
||||
@@ -91,12 +97,13 @@ describe('RPCServer', () => {
|
||||
|
||||
it('new RPCServer() should fail on bad RPC', (done) => {
|
||||
try {
|
||||
new RPCServer(20001, [{
|
||||
const sv = new RPCServer([{
|
||||
name: 'bad',
|
||||
exportRPCs: () => [
|
||||
RPCs: () => [
|
||||
(aaa, bbb, ccc) => { return aaa + bbb + ccc }
|
||||
]
|
||||
}])
|
||||
sv.listen(20001)
|
||||
done(new Error("Didn't fail with bad RPC"))
|
||||
} catch (badRPCError) {
|
||||
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', () => {
|
||||
let client: RPCSocket
|
||||
@@ -111,6 +203,7 @@ describe('RPCSocket', () => {
|
||||
|
||||
before(async () => {
|
||||
server = makeServer()
|
||||
|
||||
client = new RPCSocket(21010, "localhost")
|
||||
return await client.connect()
|
||||
})
|
||||
@@ -183,9 +276,9 @@ describe('It should do unhook', () => {
|
||||
let server: RPCServer
|
||||
|
||||
before(async () => {
|
||||
server = new RPCServer(21010, [{
|
||||
server = new RPCServer([{
|
||||
name: "test",
|
||||
exportRPCs: () => [{
|
||||
RPCs: () => [{
|
||||
name: 'subscribe',
|
||||
hook: async (callback): Promise<void> => {
|
||||
cb = <Function>callback
|
||||
@@ -211,11 +304,13 @@ describe('It should do unhook', () => {
|
||||
function publish(): string { cb(candy); return candy },
|
||||
function unsubscribe(): string { candy = noCandy; cb(candy); cb = () => { }; return candy }
|
||||
]
|
||||
}], {
|
||||
}],
|
||||
{
|
||||
connectionHandler: (socket) => { },
|
||||
closeHandler: (socket) => { },
|
||||
errorHandler: (socket, err) => { throw err }
|
||||
})
|
||||
server.listen(21010)
|
||||
client = new RPCSocket(21010, "localhost")
|
||||
return await client.connect()
|
||||
})
|
||||
@@ -239,14 +334,14 @@ describe('It should do unhook', () => {
|
||||
|
||||
it('Unhook+unsubscribe should stop callbacks', (done) => {
|
||||
|
||||
client['test'].subscribe(function myCallback(c){
|
||||
if(run == 1)
|
||||
client['test'].subscribe(function myCallback(c) {
|
||||
if (run == 1)
|
||||
(myCallback as any).destroy()
|
||||
|
||||
if (c !== expected[run++]) {
|
||||
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 r3 = await client['test'].unsubscribe()
|
||||
const r2 = await client['test'].publish()
|
||||
@@ -266,11 +361,11 @@ type SesameTestIfc = {
|
||||
test: {
|
||||
checkCandy: () => Promise<string>
|
||||
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: {
|
||||
echo: (x:any) => Promise<any>
|
||||
echo: (x: any) => Promise<any>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -281,9 +376,9 @@ describe('Sesame should unlock the socket', () => {
|
||||
let cb: Function = (...args) => { }
|
||||
|
||||
before((done) => {
|
||||
server = new RPCServer<SesameTestIfc>(21004, [{
|
||||
server = new RPCServer<SesameTestIfc>([{
|
||||
name: "test",
|
||||
exportRPCs: () => [
|
||||
RPCs: () => [
|
||||
{
|
||||
name: 'subscribe',
|
||||
hook: async (callback) => {
|
||||
@@ -297,15 +392,16 @@ describe('Sesame should unlock the socket', () => {
|
||||
async function checkCandy() { cb(candy); cb = () => { }; return candy },
|
||||
async function manyParams(a, b, c, d) { return [a, b, c, d] }
|
||||
],
|
||||
},{
|
||||
}, {
|
||||
name: 'other',
|
||||
exportRPCs: () => [
|
||||
async function echo(x){return x}
|
||||
RPCs: () => [
|
||||
async function echo(x) { return x }
|
||||
]
|
||||
|
||||
}], {
|
||||
sesame: (_sesame) => _sesame === 'sesame!'
|
||||
})
|
||||
server.listen(21004)
|
||||
const sock = new RPCSocket<SesameTestIfc>(21004, "localhost")
|
||||
sock.connect('sesame!').then(cli => {
|
||||
client = cli
|
||||
@@ -378,15 +474,14 @@ describe('Error handling', () => {
|
||||
}
|
||||
|
||||
it("RPC throws on client without handler", (done) => {
|
||||
let server = new RPCServer(21004, [{
|
||||
let server = new RPCServer([{
|
||||
name: "createUser",
|
||||
exportRPCs: () => [{
|
||||
RPCs: () => [{
|
||||
name: 'createUser' as 'createUser',
|
||||
call: createUser
|
||||
}]
|
||||
}], {
|
||||
|
||||
})
|
||||
}])
|
||||
server.listen(21004)
|
||||
|
||||
let sock = new RPCSocket(21004, 'localhost')
|
||||
sock.connect().then((cli) => {
|
||||
@@ -413,9 +508,9 @@ describe('Error handling', () => {
|
||||
})
|
||||
|
||||
it("RPC throws on server with handler", (done) => {
|
||||
let server = new RPCServer(21004, [{
|
||||
let server = new RPCServer([{
|
||||
name: "createUser",
|
||||
exportRPCs: () => [{
|
||||
RPCs: () => [{
|
||||
name: 'createUser' as 'createUser',
|
||||
call: createUser
|
||||
}]
|
||||
@@ -424,6 +519,7 @@ describe('Error handling', () => {
|
||||
done()
|
||||
}
|
||||
})
|
||||
server.listen(21004)
|
||||
|
||||
let sock = new RPCSocket(21004, 'localhost')
|
||||
sock.connect().then((cli) => {
|
||||
@@ -457,9 +553,9 @@ describe("Errorhandler functionality", () => {
|
||||
}
|
||||
|
||||
it("correct values are passed to the handler", (done) => {
|
||||
let server = new RPCServer(21004, [{
|
||||
let server = new RPCServer([{
|
||||
name: "createUser",
|
||||
exportRPCs: () => [{
|
||||
RPCs: () => [{
|
||||
name: 'createUser' as 'createUser',
|
||||
call: createUser
|
||||
}]
|
||||
@@ -469,6 +565,7 @@ describe("Errorhandler functionality", () => {
|
||||
done()
|
||||
}
|
||||
})
|
||||
server.listen(21004)
|
||||
|
||||
let sock = new RPCSocket(21004, 'localhost')
|
||||
sock.connect().then((cli) => {
|
||||
@@ -493,9 +590,9 @@ describe("Errorhandler functionality", () => {
|
||||
|
||||
it("handler sees sesame", (done) => {
|
||||
let sesame = "AAAAAAAAAAAAAAA"
|
||||
let server = new RPCServer(21004, [{
|
||||
let server = new RPCServer([{
|
||||
name: "createUser" as "createUser",
|
||||
exportRPCs: () => [{
|
||||
RPCs: () => [{
|
||||
name: 'createUser' as 'createUser',
|
||||
call: createUser
|
||||
}]
|
||||
@@ -507,6 +604,7 @@ describe("Errorhandler functionality", () => {
|
||||
}
|
||||
|
||||
})
|
||||
server.listen(21004)
|
||||
|
||||
let sock = new RPCSocket(21004, 'localhost')
|
||||
sock.connect(sesame).then((cli) => {
|
||||
@@ -547,7 +645,7 @@ describe("Class binding", () => {
|
||||
|
||||
class MyExporter implements RPCExporter<myExporterIfc>{
|
||||
name = "MyExporter" as "MyExporter"
|
||||
exportRPCs = () => [
|
||||
RPCs = () => [
|
||||
this.myRPC
|
||||
]
|
||||
|
||||
@@ -559,7 +657,7 @@ describe("Class binding", () => {
|
||||
|
||||
class MyOtherExporter implements RPCExporter<myExporterIfc>{
|
||||
name = "MyExporter" as "MyExporter"
|
||||
exportRPCs = () => [
|
||||
RPCs = () => [
|
||||
this.myRPC
|
||||
]
|
||||
|
||||
@@ -571,18 +669,21 @@ describe("Class binding", () => {
|
||||
|
||||
before(done => {
|
||||
exporter1 = new MyExporter()
|
||||
serv = new RPCServer<myExporterIfc>(21004, [exporter1], {
|
||||
serv = new RPCServer<myExporterIfc>(
|
||||
[exporter1],
|
||||
{
|
||||
accessFilter: async (sesame, exporter) => {
|
||||
if(exporter.name === 'MyExporter'){
|
||||
if (exporter.name === 'MyExporter') {
|
||||
if (!allowed) return false
|
||||
allowed = false
|
||||
return sesame === 'xxx';
|
||||
}else{
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
},
|
||||
sesame: "xxx"
|
||||
})
|
||||
serv.listen(21004)
|
||||
done()
|
||||
})
|
||||
|
||||
@@ -630,7 +731,7 @@ describe("Class binding", () => {
|
||||
|
||||
|
||||
it("use sesameFilter for available", (done) => {
|
||||
if (sock['MyExporter']){
|
||||
if (sock['MyExporter']) {
|
||||
allowed = false
|
||||
done()
|
||||
}
|
||||
@@ -722,6 +823,6 @@ describe("attaching handlers before connecting", () => {
|
||||
|
||||
describe('finally', () => {
|
||||
it('print open handles (Ignore `DNSCHANNEL` and `Immediate`)', () => {
|
||||
log()
|
||||
//log(console)
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user