Browse Source

clean up type system, remove subres

master
peter 4 years ago
parent
commit
7d580c4c23
9 changed files with 466 additions and 469 deletions
  1. 55
    35
      package-lock.json
  2. 4
    2
      package.json
  3. 37
    41
      src/Backend.ts
  4. 16
    7
      src/Frontend.ts
  5. 4
    5
      src/Interfaces.ts
  6. 26
    18
      src/Types.ts
  7. 17
    24
      src/Utils.ts
  8. 307
    264
      test/Test.ts
  9. 0
    73
      test/devtest.ts

+ 55
- 35
package-lock.json View File

@@ -1,6 +1,6 @@
1 1
 {
2 2
   "name": "rpclibrary",
3
-  "version": "1.6.1",
3
+  "version": "1.9.2",
4 4
   "lockfileVersion": 1,
5 5
   "requires": true,
6 6
   "dependencies": {
@@ -467,9 +467,9 @@
467 467
       "dev": true
468 468
     },
469 469
     "acorn": {
470
-      "version": "6.3.0",
471
-      "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.3.0.tgz",
472
-      "integrity": "sha512-/czfa8BwS88b9gWQVhc8eknunSA2DoJpJyTQkhheIf5E48u1N0R4q/YxxsAeqRrmK9TQ/uYfgLDfZo91UlANIA==",
470
+      "version": "6.4.1",
471
+      "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.1.tgz",
472
+      "integrity": "sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA==",
473 473
       "dev": true
474 474
     },
475 475
     "aggregate-error": {
@@ -753,9 +753,9 @@
753 753
       "dev": true
754 754
     },
755 755
     "bluebird": {
756
-      "version": "3.7.1",
757
-      "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.1.tgz",
758
-      "integrity": "sha512-DdmyoGCleJnkbp3nkbxTLJ18rjDsE4yCggEwKNXkeV123sPNfOCYeDoeuOY+F2FrSjO1YXcTU+dsy96KMy+gcg==",
756
+      "version": "3.7.2",
757
+      "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
758
+      "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==",
759 759
       "dev": true
760 760
     },
761 761
     "bn.js": {
@@ -952,9 +952,9 @@
952 952
       },
953 953
       "dependencies": {
954 954
         "glob": {
955
-          "version": "7.1.5",
956
-          "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.5.tgz",
957
-          "integrity": "sha512-J9dlskqUXK1OeTOYBEn5s8aMukWMwWfs+rPTn/jn50Ux4MNXVhubL1wu/j2t+H4NVI+cXEcCaYellqaPVGXNqQ==",
955
+          "version": "7.1.6",
956
+          "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
957
+          "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
958 958
           "dev": true,
959 959
           "requires": {
960 960
             "fs.realpath": "^1.0.0",
@@ -1062,9 +1062,9 @@
1062 1062
       }
1063 1063
     },
1064 1064
     "chownr": {
1065
-      "version": "1.1.3",
1066
-      "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.3.tgz",
1067
-      "integrity": "sha512-i70fVHhmV3DtTl6nqvZOnIjbY0Pe4kAUjwHj8z0zAdgBtYrJyYwLKCCuRBQ5ppkyL0AkN7HKRnETdmdp1zqNXw==",
1065
+      "version": "1.1.4",
1066
+      "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
1067
+      "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==",
1068 1068
       "dev": true
1069 1069
     },
1070 1070
     "chrome-trace-event": {
@@ -1331,6 +1331,11 @@
1331 1331
         "randomfill": "^1.0.3"
1332 1332
       }
1333 1333
     },
1334
+    "crypto-js": {
1335
+      "version": "4.0.0",
1336
+      "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.0.0.tgz",
1337
+      "integrity": "sha512-bzHZN8Pn+gS7DQA6n+iUmBfl0hO5DJq++QP3U6uTucDtk/0iGpXd/Gg7CGR0p8tJhofJyaKoWBuJI4eAO00BBg=="
1338
+    },
1334 1339
     "cyclist": {
1335 1340
       "version": "1.0.1",
1336 1341
       "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz",
@@ -2661,9 +2666,9 @@
2661 2666
       "dev": true
2662 2667
     },
2663 2668
     "handlebars": {
2664
-      "version": "4.5.1",
2665
-      "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.5.1.tgz",
2666
-      "integrity": "sha512-C29UoFzHe9yM61lOsIlCE5/mQVGrnIOrOq7maQl76L7tYPCgC1og0Ajt6uWnX4ZTxBPnjw+CUvawphwCfJgUnA==",
2669
+      "version": "4.7.3",
2670
+      "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.3.tgz",
2671
+      "integrity": "sha512-SRGwSYuNfx8DwHD/6InAPzD6RgeruWLT+B8e8a7gGs8FWgHzlExpTFMEq2IA6QpAfOClpKHy6+8IqTjeBCu6Kg==",
2667 2672
       "dev": true,
2668 2673
       "requires": {
2669 2674
         "neo-async": "^2.6.0",
@@ -3388,9 +3393,9 @@
3388 3393
       }
3389 3394
     },
3390 3395
     "kind-of": {
3391
-      "version": "6.0.2",
3392
-      "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
3393
-      "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
3396
+      "version": "6.0.3",
3397
+      "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
3398
+      "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
3394 3399
       "dev": true
3395 3400
     },
3396 3401
     "lcid": {
@@ -4707,9 +4712,9 @@
4707 4712
       "dev": true
4708 4713
     },
4709 4714
     "serialize-javascript": {
4710
-      "version": "1.9.1",
4711
-      "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.9.1.tgz",
4712
-      "integrity": "sha512-0Vb/54WJ6k5v8sSWN09S0ora+Hnr+cX40r9F170nT+mSkaxltoE/7R3OrIdBSUv1OoiobH1QoWQbCnAO+e8J1A==",
4715
+      "version": "2.1.2",
4716
+      "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-2.1.2.tgz",
4717
+      "integrity": "sha512-rs9OggEUF0V4jUSecXazOYsLfu7OGK2qIn3c7IPBiffz32XniEp/TX9Xmc9LQfK2nQ2QKHvZ2oygKUGU0lG4jQ==",
4713 4718
       "dev": true
4714 4719
     },
4715 4720
     "set-blocking": {
@@ -5039,6 +5044,12 @@
5039 5044
         "figgy-pudding": "^3.5.1"
5040 5045
       }
5041 5046
     },
5047
+    "stackback": {
5048
+      "version": "0.0.2",
5049
+      "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz",
5050
+      "integrity": "sha1-Gsig2Ug4SNFpXkGLbQMaPDzmjjs=",
5051
+      "dev": true
5052
+    },
5042 5053
     "static-extend": {
5043 5054
       "version": "0.1.2",
5044 5055
       "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz",
@@ -5094,9 +5105,9 @@
5094 5105
       }
5095 5106
     },
5096 5107
     "stream-shift": {
5097
-      "version": "1.0.0",
5098
-      "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz",
5099
-      "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=",
5108
+      "version": "1.0.1",
5109
+      "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz",
5110
+      "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==",
5100 5111
       "dev": true
5101 5112
     },
5102 5113
     "string-width": {
@@ -5182,9 +5193,9 @@
5182 5193
       "dev": true
5183 5194
     },
5184 5195
     "terser": {
5185
-      "version": "4.3.9",
5186
-      "resolved": "https://registry.npmjs.org/terser/-/terser-4.3.9.tgz",
5187
-      "integrity": "sha512-NFGMpHjlzmyOtPL+fDw3G7+6Ueh/sz4mkaUYa4lJCxOPTNzd0Uj0aZJOmsDYoSQyfuVoWDMSWTPU3huyOm2zdA==",
5196
+      "version": "4.6.6",
5197
+      "resolved": "https://registry.npmjs.org/terser/-/terser-4.6.6.tgz",
5198
+      "integrity": "sha512-4lYPyeNmstjIIESr/ysHg2vUPRGf2tzF9z2yYwnowXVuVzLEamPN1Gfrz7f8I9uEPuHcbFlW4PLIAsJoxXyJ1g==",
5188 5199
       "dev": true,
5189 5200
       "requires": {
5190 5201
         "commander": "^2.20.0",
@@ -5201,16 +5212,16 @@
5201 5212
       }
5202 5213
     },
5203 5214
     "terser-webpack-plugin": {
5204
-      "version": "1.4.1",
5205
-      "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.1.tgz",
5206
-      "integrity": "sha512-ZXmmfiwtCLfz8WKZyYUuuHf3dMYEjg8NrjHMb0JqHVHVOSkzp3cW2/XG1fP3tRhqEqSzMwzzRQGtAPbs4Cncxg==",
5215
+      "version": "1.4.3",
5216
+      "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.3.tgz",
5217
+      "integrity": "sha512-QMxecFz/gHQwteWwSo5nTc6UaICqN1bMedC5sMtUc7y3Ha3Q8y6ZO0iCR8pq4RJC8Hjf0FEPEHZqcMB/+DFCrA==",
5207 5218
       "dev": true,
5208 5219
       "requires": {
5209 5220
         "cacache": "^12.0.2",
5210 5221
         "find-cache-dir": "^2.1.0",
5211 5222
         "is-wsl": "^1.1.0",
5212 5223
         "schema-utils": "^1.0.0",
5213
-        "serialize-javascript": "^1.7.0",
5224
+        "serialize-javascript": "^2.1.2",
5214 5225
         "source-map": "^0.6.1",
5215 5226
         "terser": "^4.1.2",
5216 5227
         "webpack-sources": "^1.4.0",
@@ -5488,9 +5499,9 @@
5488 5499
       "dev": true
5489 5500
     },
5490 5501
     "uglify-js": {
5491
-      "version": "3.6.7",
5492
-      "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.6.7.tgz",
5493
-      "integrity": "sha512-4sXQDzmdnoXiO+xvmTzQsfIiwrjUCSA95rSP4SEd8tDb51W2TiDOlL76Hl+Kw0Ie42PSItCW8/t6pBNCF2R48A==",
5502
+      "version": "3.8.0",
5503
+      "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.8.0.tgz",
5504
+      "integrity": "sha512-ugNSTT8ierCsDHso2jkBHXYrU8Y5/fY2ZUprfrJUiD7YpuFvV4jODLFmb3h4btQjqr5Nh4TX4XtgDfCU1WdioQ==",
5494 5505
       "dev": true,
5495 5506
       "optional": true,
5496 5507
       "requires": {
@@ -5857,6 +5868,15 @@
5857 5868
       "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=",
5858 5869
       "dev": true
5859 5870
     },
5871
+    "why-is-node-running": {
5872
+      "version": "2.1.2",
5873
+      "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.1.2.tgz",
5874
+      "integrity": "sha512-TwUeoRNMAWy8jAD8oFLtgmYKecZkH3yCtbQ17CYVCxd1WaPJAEB6oqkNgm0o+wIzxJi1oHUkOxMk0M/t5jCGeA==",
5875
+      "dev": true,
5876
+      "requires": {
5877
+        "stackback": "0.0.2"
5878
+      }
5879
+    },
5860 5880
     "wide-align": {
5861 5881
       "version": "1.1.3",
5862 5882
       "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz",

+ 4
- 2
package.json View File

@@ -1,6 +1,6 @@
1 1
 {
2 2
   "name": "rpclibrary",
3
-  "version": "1.9.2",
3
+  "version": "1.10.0",
4 4
   "description": "rpclibrary is a websocket on steroids!",
5 5
   "main": "./js/Index.js",
6 6
   "repository": {
@@ -46,10 +46,12 @@
46 46
     "typedoc-plugin-markdown": "^2.2.6",
47 47
     "typescript": "^3.5.3",
48 48
     "webpack": "^4.40.2",
49
-    "webpack-cli": "^3.3.9"
49
+    "webpack-cli": "^3.3.9",
50
+    "why-is-node-running": "^2.1.2"
50 51
   },
51 52
   "dependencies": {
52 53
     "bsock": "^0.1.9",
54
+    "crypto-js": "^4.0.0",
53 55
     "http": "0.0.0",
54 56
     "uuid": "^3.3.3"
55 57
   },

+ 37
- 41
src/Backend.ts View File

@@ -2,30 +2,26 @@
2 2
 
3 3
 import http = require('http');
4 4
 import bsock = require('bsock');
5
-
6
-import * as T from './Types'; 
7
-import * as U from './Utils'; 
5
+import * as T from './Types';
6
+import * as U from './Utils';
8 7
 import * as I from './Interfaces';
9
-import { Socket } from 'dgram';
10 8
 
11
-type Exporters<InterfaceT extends T.RPCInterface = T.RPCInterface, SubResType = {}> = I.RPCExporter<T.RPCInterface<InterfaceT>, keyof InterfaceT, SubResType>[]
12 9
 
13 10
 /**
14 11
  * A Websocket-server-on-steroids with built-in RPC capabilities
15 12
  */
16 13
 export class RPCServer<
17
-    SubResType = {},
18 14
     InterfaceT extends T.RPCInterface = T.RPCInterface,
19
-> implements I.Destroyable{
20
-    
15
+> implements I.Destroyable {
16
+
21 17
     private ws = http.createServer()
22 18
     private io = bsock.createServer()
23
-    private visibility:T.Visibility
24
-    private closeHandler:T.CloseHandler
19
+    private visibility: T.Visibility
20
+    private closeHandler: T.CloseHandler
25 21
     private errorHandler: T.ErrorHandler
26 22
     private connectionHandler: T.ConnectionHandler
27
-    private sesame? : T.SesameFunction
28
-    private accessFilter: T.AccessFilter<InterfaceT, SubResType>
23
+    private sesame?: T.SesameFunction
24
+    private accessFilter: T.AccessFilter<InterfaceT>
29 25
 
30 26
     /**
31 27
      * @throws On RPC with no name
@@ -34,71 +30,70 @@ export class RPCServer<
34 30
      * @param conf A {@link SocketConf} object with optional settings
35 31
      */
36 32
     constructor(
37
-        private port:number,
38
-        private exporters: Exporters<InterfaceT, SubResType> = [],
39
-        conf: T.ServerConf<InterfaceT, SubResType> =  {}
40
-    ){
41
-        if(!conf.visibility) this.visibility = "0.0.0.0"        
42
-        
43
-
44
-        if(conf.sesame){
33
+        private port: number,
34
+        private exporters: T.ExporterArray<InterfaceT> = [],
35
+        conf: T.ServerConf<InterfaceT> = {}
36
+    ) {
37
+        if (!conf.visibility) this.visibility = "0.0.0.0"
38
+
39
+        if (conf.sesame) {
45 40
             this.sesame = U.makeSesameFunction(conf.sesame)
46 41
         }
47 42
 
48 43
         this.accessFilter = conf.accessFilter || (async (sesame) => {
49
-            if(!this.sesame) return true
44
+            if (!this.sesame) return true
50 45
             return this.sesame!(sesame!)
51 46
         })
52 47
 
53 48
 
54
-        this.errorHandler = (socket:I.Socket) => (error:any, rpcName:string, args: any[]) => { 
55
-            if(conf.errorHandler) conf.errorHandler(socket, error, rpcName, args)
49
+        this.errorHandler = (socket: I.Socket) => (error: any, rpcName: string, args: any[]) => {
50
+            if (conf.errorHandler) conf.errorHandler(socket, error, rpcName, args)
56 51
             else throw error
57 52
         }
58
-        
59
-        this.closeHandler = (socket:I.Socket) => { 
60
-            if(conf.closeHandler) conf.closeHandler(socket)
53
+
54
+        this.closeHandler = (socket: I.Socket) => {
55
+            if (conf.closeHandler) conf.closeHandler(socket)
61 56
         }
62
-        
63
-        this.connectionHandler = (socket:I.Socket) => { 
64
-            if(conf.connectionHandler) conf.connectionHandler(socket)
57
+
58
+        this.connectionHandler = (socket: I.Socket) => {
59
+            if (conf.connectionHandler) conf.connectionHandler(socket)
65 60
         }
66 61
 
67 62
         exporters.forEach(U.fixNames) //TSC for some reason doesn't preserve name properties of methods
68 63
 
69 64
         let badRPC = exporters.flatMap(ex => ex.exportRPCs()).find(rpc => !rpc.name)
70
-        if(badRPC){
65
+        if (badRPC) {
71 66
             throw new Error(`
72 67
             RPC did not provide a name. 
73 68
             \nUse 'funtion name(..){ .. }' syntax instead.
74 69
             \n
75 70
             \n<------------OFFENDING RPC:
76
-            \n`+badRPC.toString()+`
71
+            \n`+ badRPC.toString() + `
77 72
             \n>------------OFFENDING RPC`)
78 73
         }
79 74
         this.startWebsocket()
80 75
     }
81 76
 
82
-    private startWebsocket(){
83
-        try{
77
+    private startWebsocket() {
78
+        try {
84 79
             this.io.attach(this.ws)
85
-            this.io.on('socket', (socket:I.Socket) => {
80
+            this.io.on('socket', (socket: I.Socket) => {
86 81
                 socket.on('error', (err) => this.errorHandler(socket, err, "system", []))
87 82
                 socket.on('close', () => this.closeHandler(socket))
88 83
                 this.connectionHandler(socket)
89 84
                 this.initRPCs(socket)
90 85
             })
91
-            this.ws.listen(this.port, this.visibility)
92
-        }catch(e){
86
+            this.ws = this.ws.listen(this.port, this.visibility)
87
+        } catch (e) {
93 88
             this.errorHandler(this.io, e, 'system', [])
94 89
         }
95 90
     }
96 91
 
97
-    protected initRPCs(socket:I.Socket){
98
-        socket.hook('info', async (sesame? : string) => {
92
+    protected initRPCs(socket: I.Socket) {
93
+        socket.hook('info', async (sesame?: string) => {
99 94
             const rpcs = await Promise.all(this.exporters.map(async exp => {
100 95
                 const allowed = await this.accessFilter(sesame, exp)
101
-                if(!allowed) return []
96
+                if (!allowed) return []
102 97
                 return U.rpcHooker(socket, exp, this.errorHandler, this.sesame)
103 98
             }))
104 99
             return rpcs.flat()
@@ -108,8 +103,7 @@ export class RPCServer<
108 103
     /**
109 104
      * Publishes a new list of Exporters. This destroys and restarts the socket
110 105
      * @param exporters the exporters to publish
111
-     */
112
-    public setExporters(exporters: Exporters<InterfaceT, SubResType>):any{
106
+    public setExporters(exporters: T.ExporterArray<InterfaceT>): any {
113 107
         exporters.forEach(U.fixNames)
114 108
         this.destroy()
115 109
         this.ws = http.createServer()
@@ -117,6 +111,8 @@ export class RPCServer<
117 111
         this.exporters = exporters
118 112
         this.startWebsocket()
119 113
     }
114
+    */
115
+
120 116
 
121 117
     destroy(): void {
122 118
         this.io.close()

+ 16
- 7
src/Frontend.ts View File

@@ -7,12 +7,12 @@ import * as I from './Interfaces';
7 7
 import { stripAfterEquals, appendComma } from './Utils';
8 8
 
9 9
 
10
-
11 10
 /**
12 11
  * A websocket-on-steroids with built-in RPC capabilities
13 12
  */
14
-export class RPCSocket implements I.Socket{
15
-    static async makeSocket<T extends T.RPCInterface = T.RPCInterface>(port:number, server: string, sesame?:string, conf?:T.SocketConf): Promise<RPCSocket & T> {
13
+export class RPCSocket<Ifc extends T.RPCInterface = T.RPCInterface> implements I.Socket{
14
+
15
+    static async makeSocket<T extends T.RPCInterface = T.RPCInterface>(port:number, server: string, sesame?:string, conf?:T.SocketConf): Promise<T.ConnectedSocket<T>> {
16 16
         const socket = new RPCSocket(port, server, conf)
17 17
         return await socket.connect<T>(sesame)
18 18
     }
@@ -128,7 +128,7 @@ export class RPCSocket implements I.Socket{
128 128
     /**
129 129
      * Connects to the server and attaches available RPCs to this object
130 130
      */
131
-    public async connect<T extends T.RPCInterface= T.RPCInterface>( sesame?: string ) : Promise<RPCSocket & T>{
131
+    public async connect<T extends T.RPCInterface= Ifc>( sesame?: string ) : Promise<T.ConnectedSocket<T>> {
132 132
         this.socket = await bsock.connect(this.port, this.server, this.conf.tls?this.conf.tls:false)
133 133
         this.errorHandlers.forEach(h => this.socket.on('error', h))
134 134
         this.closeHandlers.forEach(h => this.socket.on('close', h))
@@ -197,10 +197,19 @@ export class RPCSocket implements I.Socket{
197 197
         return eval( `
198 198
         async (${headerArgs} callback) => {
199 199
             const r = await this.call("${fnName}", ${sesame} ${argParams})
200
-            if(r && r.result === 'Success'){
201
-                this.socket.hook(r.uuid, callback)
200
+            try{
201
+                if(r){
202
+                    if(r.uuid){
203
+                        callback['destroy'] = () => { this.socket.unhook(r.uuid) }
204
+                        this.socket.hook(r.uuid, callback)
205
+                    }
206
+                    return r.return
207
+                }else{
208
+                    throw new Error("Empty response")
209
+                }
210
+            }catch(e){
211
+                throw e
202 212
             }
203
-            return r
204 213
         }`)
205 214
     }
206 215
 }

+ 4
- 5
src/Interfaces.ts View File

@@ -4,13 +4,12 @@ import * as I from "./Interfaces"
4 4
 /**
5 5
  * Interface for all classes that export RPCs
6 6
  */
7
-export interface RPCExporter<
7
+export type RPCExporter<
8 8
     Ifc extends T.RPCInterface = T.RPCInterface,
9
-    Name extends keyof Ifc = keyof Ifc,
10
-    SubresT = {}
11
->{
9
+    Name extends keyof Ifc = string,
10
+> = {
12 11
     name: Name
13
-    exportRPCs() : T.RPCInterfaceArray<Ifc, SubresT>[Name]
12
+    exportRPCs() : T.RPCDefinitions<Ifc>[Name]
14 13
 }
15 14
 
16 15
 /**

+ 26
- 18
src/Types.ts View File

@@ -1,8 +1,9 @@
1 1
 import * as I from "./Interfaces";
2
+import { RPCSocket } from "./Frontend";
2 3
 
3 4
 export type AnyFunction = (...args:any) => any
4
-export type HookFunction<F extends AnyFunction = AnyFunction, SubresT = {}> = (...args: Parameters<F>) => Promise<SubscriptionResponse<SubresT> | ErrorResponse>
5
-export type AccessFilter<InterfaceT extends RPCInterface, SubresT> = (sesame:string|undefined, exporter: I.RPCExporter<RPCInterface<InterfaceT>, keyof InterfaceT, SubresT>) => Promise<boolean> 
5
+export type HookFunction = AnyFunction
6
+export type AccessFilter<InterfaceT extends RPCInterface = RPCInterface> = (sesame:string|undefined, exporter: I.RPCExporter<InterfaceT, keyof InterfaceT>) => Promise<boolean> 
6 7
 export type Visibility = "127.0.0.1" | "0.0.0.0"
7 8
 export type ConnectionHandler = (socket:I.Socket) => void
8 9
 export type ErrorHandler = (socket:I.Socket, error:any, rpcName: string, args: any[]) => void
@@ -17,8 +18,12 @@ export type FrontEndHandlerType = {
17 18
     'close' : () => void
18 19
 }
19 20
 
20
-export type ServerConf<InterfaceT extends RPCInterface, SubresT> = {
21
-    accessFilter?: AccessFilter<InterfaceT, SubresT>
21
+export type ExporterArray<InterfaceT extends RPCInterface = RPCInterface> = I.RPCExporter<RPCInterface<InterfaceT>, keyof InterfaceT>[]
22
+
23
+export type ConnectedSocket<T extends RPCInterface = RPCInterface> = RPCSocket & T
24
+
25
+export type ServerConf<InterfaceT extends RPCInterface> = {
26
+    accessFilter?: AccessFilter<InterfaceT>
22 27
     connectionHandler?: ConnectionHandler
23 28
     errorHandler?: ErrorHandler
24 29
     closeHandler?: CloseHandler
@@ -36,23 +41,23 @@ export type Outcome = "Success" | "Error"
36 41
 export type Respose<T> = T & { result: Outcome }
37 42
 export type SuccessResponse<T = {}> = Respose<T> & { result: "Success" } 
38 43
 export type ErrorResponse<T = {}> = Respose<T> & { result: "Error", message?:string }
39
-export type SubscriptionResponse<T = {}> = Respose<T> & { result: "Success"; uuid: string }
40 44
 
41 45
 export type RPCType = 'Hook' | 'Unhook' | 'Call'
42 46
 
43
-export type CallRPC<N, F> = {
44
-    name: N
45
-    call: F
47
+export type CallRPC<Name, Func extends AnyFunction> = {
48
+    name: Name
49
+    call: Func
46 50
 }
47 51
 
48
-export type HookRPC<N, F extends AnyFunction, SubresT = {}> = {
49
-    name: N
50
-    hook: HookFunction<F, SubresT>
52
+
53
+export type HookRPC<Name, Func extends AnyFunction> = {
54
+    name: Name
55
+    hook: Func
51 56
     onCallback?: AnyFunction
52
-    onClose?: HookCloseFunction<SubresT>
57
+    onClose?: HookCloseFunction<ReturnType<Func> extends Promise<infer T> ? T : ReturnType<Func>>
53 58
 }
54 59
 
55
-export type RPC<N, F extends AnyFunction, SubresT = {}> = HookRPC<N, F, SubresT> | CallRPC<N,F> | F
60
+export type RPC<Name, Func extends AnyFunction> = HookRPC<Name, Func> | CallRPC<Name,Func> | Func
56 61
 
57 62
 export type RPCInterface<Impl extends RPCInterface = {}> = {
58 63
     [grp in string] : {
@@ -60,10 +65,13 @@ export type RPCInterface<Impl extends RPCInterface = {}> = {
60 65
     } 
61 66
 } & Impl
62 67
 
63
-export type RPCInterfaceArray<Itfc extends RPCInterface, SubresT = {}> = {
64
-    [grp in keyof Itfc]: Array<
65
-      { [rpc in keyof Itfc[grp]]: RPC<rpc, Itfc[grp][rpc], SubresT> }[keyof Itfc[grp]]
66
-    >
68
+export type exportT = {
69
+    [group in string]: {}
70
+}
71
+
72
+//This probably has lots of issues
73
+export type RPCDefinitions<Ifc extends RPCInterface> = {
74
+    [grp in keyof Ifc]:( { [rpc in keyof Ifc[grp]]: RPC<rpc, Ifc[grp][rpc]> }[keyof Ifc[grp]] )[]
67 75
 }
68 76
 
69 77
 export type BaseInfo = {
@@ -86,4 +94,4 @@ export type RpcInfo = HookInfo |  CallInfo
86 94
 export type ExtendedRpcInfo = RpcInfo & { uniqueName: string } 
87 95
 
88 96
 export type OnFunction = <T extends "error" | "close">(type: T, f: FrontEndHandlerType[T]) => void
89
-export type HookCloseFunction<T = {}> = (res:SubscriptionResponse<T>, rpc:HookRPC<any, any, T>) => any
97
+export type HookCloseFunction<T> = (res: T, rpc:HookRPC<any, any>) => any

+ 17
- 24
src/Utils.ts View File

@@ -2,7 +2,6 @@ import * as uuidv4 from "uuid/v4"
2 2
 
3 3
 import * as T from "./Types";
4 4
 import * as I from "./Interfaces";
5
-import { SubscriptionResponse } from "./Types";
6 5
 
7 6
 /**
8 7
  * Translate an RPC to RPCInfo for serialization.
@@ -12,7 +11,7 @@ import { SubscriptionResponse } from "./Types";
12 11
  * @param sesame optional sesame phrase to prepend before all RPC arguments 
13 12
  * @throws Error on RPC without name property
14 13
  */
15
-export const rpcToRpcinfo = <SubResT = {}>(socket: I.Socket, rpc : T.RPC<any, any, SubResT>, owner: string, errorHandler: T.ErrorHandler, sesame?:T.SesameFunction):T.RpcInfo => {
14
+export const rpcToRpcinfo = (socket: I.Socket, rpc : T.RPC<any, any>, owner: string, errorHandler: T.ErrorHandler, sesame?:T.SesameFunction):T.RpcInfo => {
16 15
     switch (typeof rpc){
17 16
         case  "object":
18 17
             if(rpc['call']){
@@ -24,7 +23,7 @@ export const rpcToRpcinfo = <SubResT = {}>(socket: I.Socket, rpc : T.RPC<any, an
24 23
                     call: sesame?async (_sesame, ...args) => {if(sesame(_sesame)) return await rpc['call'].apply({}, args); socket.destroy()}:rpc['call'], // check & remove sesame 
25 24
                 }
26 25
             }else{
27
-                const generator = hookGenerator(<T.HookRPC<any, any, any>>rpc, errorHandler, sesame)
26
+                const generator = hookGenerator(<T.HookRPC<any, any>>rpc, errorHandler, sesame)
28 27
                 return {
29 28
                     owner: owner,
30 29
                     argNames: extractArgs(generator(undefined)),
@@ -58,10 +57,9 @@ RPC did not provide a name.
58 57
  * @param exporter The exporter
59 58
  * @param makeUnique @default true Attach a suffix to RPC names
60 59
  */
61
-export function rpcHooker<SubResT = {}>(socket: I.Socket, exporter:I.RPCExporter<any, any, SubResT>, errorHandler: T.ErrorHandler, sesame?:T.SesameFunction, makeUnique = true):T.ExtendedRpcInfo[]{
60
+export function rpcHooker(socket: I.Socket, exporter:I.RPCExporter<any, any>, errorHandler: T.ErrorHandler, sesame?:T.SesameFunction, makeUnique = true):T.ExtendedRpcInfo[]{
62 61
     const owner = exporter.name
63
-    const RPCs = [...exporter.exportRPCs()]
64
-
62
+    const RPCs = exporter.exportRPCs()
65 63
 
66 64
     return RPCs.map(rpc => rpcToRpcinfo(socket, rpc, owner, errorHandler, sesame))
67 65
     .map(info => {
@@ -107,7 +105,7 @@ export function stripAfterEquals(str:string):string{
107 105
  * @param rpc The RPC to transform
108 106
  * @returns A {@link HookFunction}
109 107
  */
110
-const hookGenerator = (rpc:T.HookRPC<any, any, any>, errorHandler: T.ErrorHandler, sesameFn?: T.SesameFunction): T.HookInfo['generator'] => { 
108
+const hookGenerator = (rpc:T.HookRPC<any, any>, /*not unused!*/ errorHandler: T.ErrorHandler, sesameFn?: T.SesameFunction): T.HookInfo['generator'] => { 
111 109
     let argsArr = extractArgs(rpc.hook)
112 110
     argsArr.pop() //remove 'callback' from the end
113 111
     let callArgs = argsArr.join(',')
@@ -119,16 +117,23 @@ const hookGenerator = (rpc:T.HookRPC<any, any, any>, errorHandler: T.ErrorHandle
119 117
 
120 118
     //note rpc.hook is the associated RPC, not a socket.hook
121 119
     return eval(`
122
-    (socket) => async (${args}) => {
120
+    (clientSocket) => async (${args}) => {
123 121
         try{
124 122
             if(sesameFn && !sesameFn(sesame)) return
123
+            const uuid = uuidv4()
125 124
             const res = await rpc.hook(${callArgs} (...cbargs) => {
126
-                if(rpc.onCallback) rpc.onCallback.apply({}, cbargs)
127
-                socket.call.apply(socket, [res.uuid, ...cbargs])
125
+                if(rpc.onCallback){
126
+                    rpc.onCallback.apply({}, cbargs)
127
+                }
128
+                clientSocket.call.apply(clientSocket, [uuid, ...cbargs])
128 129
             })
129
-            return res
130
+            if(rpc.onClose){
131
+                clientSocket.on('close', () => rpc.onClose(res, rpc))
132
+            }
133
+            return {'uuid': uuid, 'return': res}
130 134
         }catch(e){
131
-            errorHandler(socket)(e, ${rpc.name}, [${args}])
135
+            //can throw to pass exception to client or swallow to keep it local
136
+            errorHandler(clientSocket)(e, ${rpc.name}, [${args}])
132 137
         }
133 138
     }`)
134 139
 }
@@ -147,18 +152,6 @@ const extractArgs = (f:Function):string[] => {
147 152
     return fn!==""?fn.split(',') : []
148 153
 }
149 154
 
150
-/**
151
- * Simple utility function to create basic {@link SubscriptionResponse}
152
- * @param uuid optional uuid to use, otherwise defaults to uuid/v4
153
- */
154
-export function makeSubResponse<T extends {} = {}>(extension:T):SubscriptionResponse & T{
155
-    return {
156
-        result: "Success",
157
-        uuid: uuidv4(),
158
-        ...extension
159
-    }
160
-}
161
-
162 155
 
163 156
 export function makeSesameFunction (sesame : T.SesameFunction | string) : T.SesameFunction {
164 157
     if(typeof sesame === 'function'){

+ 307
- 264
test/Test.ts View File

@@ -1,45 +1,44 @@
1
-import { describe, it, Func } from "mocha";
2
-
3
-import { RPCServer, RPCSocket, SubscriptionResponse, makeSubResponse } from '../Index'
4
-import * as uuidv4 from "uuid/v4"
5
-import { doesNotReject } from "assert";
6
-import { Socket } from "dgram";
1
+import { describe, it } from "mocha";
2
+import { RPCServer, RPCSocket } from '../Index'
7 3
 import { RPCExporter } from "../src/Interfaces";
4
+import { ConnectedSocket } from "../src/Types";
5
+import * as log from 'why-is-node-running';
8 6
 
9
-const add = (...args:number[]) => {return args.reduce((a,b)=>a+b, 0)}
10
-function makeServer(){
7
+const add = (...args: number[]) => { return args.reduce((a, b) => a + b, 0) }
8
+function makeServer() {
11 9
     let subcallback
12
-    return new RPCServer<{ topic: string }>(21010, [{
13
-        name: "test",
10
+    return new RPCServer(21010, [{
11
+        name: 'test',
14 12
         exportRPCs: () => [
15 13
             {
16 14
                 name: 'echo',
17
-                call: async (s:string) => s,
18
-            },{
15
+                call: async (s: string) => s,
16
+            }, {
19 17
                 name: 'simpleSubscribe',
20
-                hook: async(callback) => {
21
-                    subcallback =  callback
22
-                    return makeSubResponse<{topic: string}>({topic: "test"})
23
-                }
24
-            },{
18
+                hook: async (callback) => {
19
+                    subcallback = callback
20
+                    return { topic: "test" }
21
+                },
22
+                onClose: (res) => { }
23
+            }, {
25 24
                 name: 'subscribe',
26 25
                 hook: async (callback) => {
27 26
                     subcallback = callback
28
-                    return makeSubResponse<{topic: string}>({topic: "test"})
27
+                    return { topic: "test" }
29 28
                 },
30
-                onClose: (res, rpc) => { 
31
-                    console.log("onClose", rpc.name === 'subscribe' && res?"OK":"")
32
-                    subcallback = null 
29
+                onClose: (res, rpc) => {
30
+                    console.log("onClose", rpc.name === 'subscribe' && res ? "OK" : "")
31
+                    subcallback = null
33 32
                 },
34
-                onCallback: (...args:any) => { 
35
-                    console.log("onCallback", args[0] === "test" && args[1] === "callback"?"OK":"")                    
33
+                onCallback: (...args: any) => {
34
+                    console.log("onCallback", args[0] === "test" && args[1] === "callback" ? "OK" : "")
36 35
                 }
37 36
             },
38 37
             add,
39
-            function triggerCallback(...messages:any[]):number {return subcallback.apply({}, messages)},
38
+            function triggerCallback(...messages: any[]): number { return subcallback.apply({}, messages) },
40 39
         ]
41
-    }],{
42
-        connectionHandler: (socket) => {  },
40
+    }], {
41
+        connectionHandler: (socket) => { },
43 42
         closeHandler: (socket) => { },
44 43
         errorHandler: (socket, err) => { throw err }
45 44
     })
@@ -47,57 +46,58 @@ function makeServer(){
47 46
 
48 47
 
49 48
 describe('RPCServer', () => {
50
-    let server: RPCServer<{ topic: string }, any>
49
+    let client, server
50
+    const echo = (x) => x
51 51
 
52
-    before(() => {
53
-        server = makeServer()
54
-    })
55
-    
56
-    after(() => {
57
-        server.destroy()
58
-    })
59
-
60
-    it('should be able to use all kinds of RPC definitions', (done) => {
61
-        
62
-        const echo = (x) => x
63
-        
64
-        const server = new RPCServer(21003, [{
52
+    before(done => {
53
+        server = new RPCServer(21003, [{
65 54
             name: 'HelloWorldRPCGroup',
66
-            exportRPCs: ()  => [
55
+            exportRPCs: () => [
67 56
                 echo, //named function variable
68
-                function echof(x){ return x }, //named function
57
+                function echof(x) { return x }, //named function
69 58
                 {
70 59
                     name: 'echoExplicit', //describing object
71
-                    call: async (x,y,z) => [x,y,z]
60
+                    call: async (x, y, z) => [x, y, z]
72 61
                 }
73 62
             ]
74 63
         }])
75 64
 
76
-        const client = new RPCSocket(21003, 'localhost')
65
+        client = new RPCSocket(21003, 'localhost')
66
+        done()
67
+    })
68
+    
69
+    after(done => {
70
+        client.destroy()
71
+        server.destroy()
72
+
73
+        done()
74
+    })
77 75
 
76
+    it('should be able to use all kinds of RPC definitions', (done) => {
78 77
         client.connect().then(async () => {
79 78
             const r0 = await client['HelloWorldRPCGroup'].echo('Hello')
80 79
             const r1 = await client['HelloWorldRPCGroup'].echof('World')
81
-            const r2 = await client['HelloWorldRPCGroup'].echoExplicit('R','P','C!')
80
+            const r2 = await client['HelloWorldRPCGroup'].echoExplicit('R', 'P', 'C!')
81
+
82 82
 
83
-            if(r0 === 'Hello' && r1 === 'World' && r2.join('') ==='RPC!'){
84
-                client.destroy()
85
-                server.destroy()
83
+            if (r0 === 'Hello' && r1 === 'World' && r2.join('') === 'RPC!') {
86 84
                 done()
85
+            }else{
86
+                done(new Error("Bad response"))
87 87
             }
88 88
         })
89 89
     })
90 90
 
91 91
     it('new RPCServer() should fail on bad RPC', (done) => {
92
-        try{
92
+        try {
93 93
             new RPCServer(20001, [{
94
-                name: "bad",
95
-                exportRPCs: ()  => [
96
-                    (aaa,bbb,ccc) => { return aaa+bbb+ccc }
94
+                name: 'bad',
95
+                exportRPCs: () => [
96
+                    (aaa, bbb, ccc) => { return aaa + bbb + ccc }
97 97
                 ]
98 98
             }])
99 99
             done(new Error("Didn't fail with bad RPC"))
100
-        }catch(badRPCError){
100
+        } catch (badRPCError) {
101 101
             done()
102 102
         }
103 103
     })
@@ -106,9 +106,9 @@ describe('RPCServer', () => {
106 106
 
107 107
 describe('RPCSocket', () => {
108 108
     let client: RPCSocket
109
-    let server: RPCServer<{topic: string}>
109
+    let server: RPCServer
110 110
 
111
-    before(async() => {
111
+    before(async () => {
112 112
         server = makeServer()
113 113
         client = new RPCSocket(21010, "localhost")
114 114
         return await client.connect()
@@ -122,27 +122,27 @@ describe('RPCSocket', () => {
122 122
 
123 123
     it('should have rpc echo', (done) => {
124 124
         client['test'].echo("x").then(x => {
125
-            if(x === 'x')
125
+            if (x === 'x')
126 126
                 done()
127 127
             else
128
-                done(new Error('echo RPC response did not match'))   
128
+                done(new Error('echo RPC response did not match'))
129 129
         })
130 130
     })
131 131
 
132 132
     it('should add up to 6', (done) => {
133
-        client['test'].add(1,2,3).then(x => {
134
-            if(x === 6)
133
+        client['test'].add(1, 2, 3).then(x => {
134
+            if (x === 6)
135 135
                 done()
136 136
             else
137
-                done(new Error('add RPC response did not match'))   
137
+                done(new Error('add RPC response did not match'))
138 138
         })
139 139
     })
140 140
 
141 141
     it('should subscribe with success', (done) => {
142 142
         client['test'].simpleSubscribe(console.log).then(res => {
143
-            if(res.result === 'Success'){
143
+            if (res.topic === 'test') {
144 144
                 done()
145
-            }else{
145
+            } else {
146 146
                 console.error(res)
147 147
                 done(new Error('Subscribe did not return success'))
148 148
             }
@@ -151,72 +151,66 @@ describe('RPCSocket', () => {
151 151
 
152 152
     it('subscribe should call back', (done) => {
153 153
         client['test'].subscribe((...args: any) => {
154
-            if(args[0] === "test" && args[1] === "callback")
154
+            if (args[0] === "test" && args[1] === "callback")
155 155
                 done()
156
-            else   
157
-                done(new Error("Bad callback value "+ args))    
158
-        }).then( async () => {
156
+            else
157
+                done(new Error("Bad callback value " + args))
158
+        }).then(async () => {
159 159
             await client['test'].triggerCallback("test", "callback")
160 160
         })
161 161
     })
162 162
 
163 163
     it('simpleSubscribe should call back', (done) => {
164 164
         client['test'].simpleSubscribe((...args: any) => {
165
-            if(args[0] === "test_" && args[1] === "callback_")
165
+            if (args[0] === "test_" && args[1] === "callback_")
166 166
                 done()
167
-            else   
168
-                done(new Error("Bad callback value "+ args))    
169
-        }).then( async () => {
167
+            else
168
+                done(new Error("Bad callback value " + args))
169
+        }).then(async () => {
170 170
             await client['test'].triggerCallback("test_", "callback_")
171 171
         })
172 172
     })
173 173
 })
174 174
 
175 175
 describe('It should do unhook', () => {
176
-    let candy = "OK"
176
+    const yesCandy = "OK"
177
+    const noCandy = "stolen"
178
+    let candy = yesCandy
177 179
     let cb: Function
178 180
     let cb2: Function
179 181
     let client: RPCSocket
180
-    let server: RPCServer<{topic: string}> 
182
+    let server: RPCServer
181 183
 
182
-    before(async() => {
183
-        server = new RPCServer<{ topic: string }>(21010, [{
184
+    before(async () => {
185
+        server = new RPCServer(21010, [{
184 186
             name: "test",
185 187
             exportRPCs: () => [{
186 188
                 name: 'subscribe',
187
-                hook: async(callback):Promise<SubscriptionResponse<{topic:string}>> => {
188
-                    cb = <Function> callback
189
-                    return {
190
-                        result: "Success",
191
-                        uuid: uuidv4(),
192
-                        topic: "test"
193
-                    }
189
+                hook: async (callback): Promise<void> => {
190
+                    cb = <Function>callback
191
+                    return
194 192
                 }
195
-            }, 
193
+            },
196 194
             {
197 195
                 name: 'subscribeWithParam',
198
-                hook: async(param, callback):Promise<SubscriptionResponse<{topic:string}>> => {
199
-                    
200
-                    if(param != "OK"){
201
-                        console.log("param was"+ param);
196
+                hook: async (param, callback): Promise<{ uuid: string }> => {
197
+
198
+                    if (param != "OK") {
199
+                        console.log("param was" + param);
202 200
                         return {
203
-                            result: "Success",
204 201
                             uuid: "no",
205
-                            topic: "test"
206 202
                         }
207 203
                     }
208
-                    cb2 = <Function> callback
204
+                    cb2 = <Function>callback
209 205
                     return {
210
-                        result: "Success",
211 206
                         uuid: "OK",
212
-                        topic: "test"
213 207
                     }
214 208
                 }
215 209
             },
216
-            function checkCandy():string { cb(candy); return candy },
217
-            function stealCandy():string { candy = "_OK"; cb(candy); cb = () => {}; return candy }
218
-        ]
219
-        }],{
210
+            function publish(): string { cb(candy); return candy },
211
+            function unsubscribe(): string { candy = noCandy; cb(candy); cb = () => { }; return candy }
212
+            ]
213
+        }], {
220 214
             connectionHandler: (socket) => { },
221 215
             closeHandler: (socket) => { },
222 216
             errorHandler: (socket, err) => { throw err }
@@ -231,69 +225,90 @@ describe('It should do unhook', () => {
231 225
     })
232 226
 
233 227
     it('Subscribe with param', (done) => {
234
-        client['test'].subscribeWithParam("OK", c => {}).then( async (res: SubscriptionResponse) => {
235
-            if(res.uuid === "OK"){
228
+        client['test'].subscribeWithParam("OK", c => { }).then(async (res) => {
229
+            if (res.uuid === candy) {
236 230
                 done()
237
-            }else
238
-                done(new Error("Results did not match "+res.uuid))
231
+            } else
232
+                done(new Error("Results did not match " + res.uuid))
239 233
         })
240 234
     })
241 235
 
236
+    let run = 0
237
+    const expected = [yesCandy, noCandy, noCandy, noCandy]
238
+
242 239
     it('Unhook+unsubscribe should stop callbacks', (done) => {
243
-        client['test'].subscribe(c => {}).then( async (res: SubscriptionResponse) => {
244
-            const r1 = await client['test'].checkCandy()
245
-            const r3 = await client['test'].stealCandy()
246
-            client.unhook(res.uuid)
247
-            const r2 = await client['test'].checkCandy()
248
-            const r4 = await client['test'].checkCandy()
249
-
250
-            if(r1 === "OK" && r3 === "_OK" && r2 === "_OK" && r4 === "_OK")
240
+        
241
+        client['test'].subscribe(function myCallback(c){
242
+            if(run == 1)
243
+            (myCallback as any).destroy()
244
+
245
+            if (c !== expected[run++]) {
246
+                done(new Error(`Wrong candy '${c}' in iteration '${run - 1}'`))
247
+            }
248
+        }).then(async function(res){
249
+            const r1 = await client['test'].publish()
250
+            const r3 = await client['test'].unsubscribe()
251
+            const r2 = await client['test'].publish()
252
+            const r4 = await client['test'].publish()
253
+
254
+            if (r1 === yesCandy && r3 === noCandy && r2 === noCandy && r4 === noCandy)
251 255
                 done()
252 256
             else
253
-                done(new Error("Results did not match: "+[r1,r2,r3,r4]))
257
+                done(new Error("Results did not match: " + [r1, r2, r3, r4]))
254 258
         })
255 259
     })
256 260
 })
257 261
 
262
+type topicDTO = { topic: string; }
258 263
 
259
-type SesameTestIfc = { 
260
-    test: { 
261
-        checkCandy: ()=>Promise<string>
262
-        subscribe: (callback) => Promise<SubscriptionResponse<{ topic: string; }>>
263
-    } 
264
+type SesameTestIfc = {
265
+    test: {
266
+        checkCandy: () => Promise<string>
267
+        subscribe: (callback: Function) => Promise<topicDTO>
268
+        manyParams: <A=string,B=number,C=boolean,D=Object>(a:A, b:B, c:C, d:D) => Promise<[A, B, C, D]>
269
+    }
270
+
271
+    other: {
272
+        echo: (x:any) => Promise<any>
273
+    }
264 274
 }
265 275
 
266 276
 describe('Sesame should unlock the socket', () => {
267 277
     let candy = "OK"
268
-    let client: RPCSocket & SesameTestIfc
269
-    let server: RPCServer 
270
-    let cb = (...args) => {}
278
+    let client: ConnectedSocket<SesameTestIfc>
279
+    let server: RPCServer<SesameTestIfc>
280
+    let cb: Function = (...args) => { }
271 281
 
272 282
     before((done) => {
273
-        server = new RPCServer(21004, [{
283
+        server = new RPCServer<SesameTestIfc>(21004, [{
274 284
             name: "test",
275 285
             exportRPCs: () => [
276 286
                 {
277 287
                     name: 'subscribe',
278
-                    hook: async(callback) => {
288
+                    hook: async (callback) => {
279 289
                         cb = callback
280
-                        return <SubscriptionResponse>{
281
-                            result: "Success",
282
-                            uuid: uuidv4(),
290
+                        return {
283 291
                             topic: 'test'
284 292
                         }
285
-                    }
293
+                    },
294
+                    onClose: (a) => { }
286 295
                 },
287
-                async function checkCandy():Promise<string> { cb(candy); cb=()=>{}; return candy },
288
-                async function manyParams(a,b,c,d) {return [a,b,c,d]}
289
-            ]}
290
-        ],{
291
-            sesame: (_sesame) => _sesame === 'sesame!' 
296
+                async function checkCandy() { cb(candy); cb = () => { }; return candy },
297
+                async function manyParams(a, b, c, d) { return [a, b, c, d] }
298
+            ],
299
+        },{
300
+            name: 'other',
301
+            exportRPCs: () => [
302
+                async function echo(x){return x}
303
+            ]
304
+        
305
+        }], {
306
+            sesame: (_sesame) => _sesame === 'sesame!'
292 307
         })
293
-        const sock = new RPCSocket(21004, "localhost")
294
-        sock.connect<SesameTestIfc>('sesame!').then(cli => {
308
+        const sock = new RPCSocket<SesameTestIfc>(21004, "localhost")
309
+        sock.connect('sesame!').then(cli => {
295 310
             client = cli
296
-            done() 
311
+            done()
297 312
         })
298 313
     })
299 314
 
@@ -307,8 +322,8 @@ describe('Sesame should unlock the socket', () => {
307 322
     })
308 323
 
309 324
     it('should work with multiple params', (done) => {
310
-        client.test['manyParams']('a','b','c','d').then(c => {
311
-            if(c[0] == 'a' && c[1] === 'b' && c[2] === 'c'  && c[3] === 'd')
325
+        client.test['manyParams']('a', 'b', 'c', 'd').then(c => {
326
+            if (c[0] == 'a' && c[1] === 'b' && c[2] === 'c' && c[3] === 'd')
312 327
                 done()
313 328
         })
314 329
     })
@@ -316,9 +331,9 @@ describe('Sesame should unlock the socket', () => {
316 331
     it('should not work without sesame', (done) => {
317 332
         const sock = new RPCSocket(21004, "localhost")
318 333
         sock.connect<SesameTestIfc>( /* no sesame */).then(async (cli) => {
319
-            if(!cli.test) 
334
+            if (!cli.test)
320 335
                 done()
321
-            else{
336
+            else {
322 337
                 done(new Error("Function supposed to be removed without sesame"))
323 338
             }
324 339
             cli.destroy()
@@ -329,9 +344,9 @@ describe('Sesame should unlock the socket', () => {
329 344
     it('should fail with wrong sesame', (done) => {
330 345
         const sock = new RPCSocket(21004, "localhost")
331 346
         sock.connect<SesameTestIfc>('abasd').then(async (cli) => {
332
-            if(!cli.test) 
347
+            if (!cli.test)
333 348
                 done()
334
-            else{
349
+            else {
335 350
                 done(new Error("Function supposed to be removed without sesame"))
336 351
             }
337 352
             cli.destroy()
@@ -341,12 +356,12 @@ describe('Sesame should unlock the socket', () => {
341 356
 
342 357
     it('callback should work with sesame', (done) => {
343 358
         client.test.subscribe((c) => {
344
-            if(c === candy){
359
+            if (c === candy) {
345 360
                 done()
346 361
             }
347 362
         }).then(d => {
348
-            if(d.result !== 'Success')
349
-                done('unexpected valid response')
363
+            if (d.topic !== 'test')
364
+                done('unexpected invalid response')
350 365
 
351 366
             client.test.checkCandy()
352 367
         })
@@ -354,51 +369,56 @@ describe('Sesame should unlock the socket', () => {
354 369
 })
355 370
 
356 371
 
357
-describe('Error handling', ()=>{
358
-    
359
-    let createUser = async( user: {a:any,b:any}) => {
360
-        throw new Error("BAD BAD BAD")
372
+describe('Error handling', () => {
373
+    const errtxt = "BAD BAD BAD"
374
+
375
+    let createUser = async (user: { a: any, b: any }) => {
376
+        throw new Error(errtxt)
361 377
     }
362 378
 
363
-    it("RPC throws on client without handler", (done)=>{
364
-        let server = new RPCServer(21004, [ {
365
-            name: 'createUser' as 'createUser',
379
+    it("RPC throws on client without handler", (done) => {
380
+        let server = new RPCServer(21004, [{
381
+            name: "createUser",
366 382
             exportRPCs: () => [{
367 383
                 name: 'createUser' as 'createUser',
368 384
                 call: createUser
369
-        }]}], {
385
+            }]
386
+        }], {
370 387
 
371 388
         })
372 389
 
373 390
         let sock = new RPCSocket(21004, 'localhost')
374 391
         sock.connect().then((cli) => {
375 392
             cli["createUser"]["createUser"]({
376
-                a:'a',
377
-                b:'b'
393
+                a: 'a',
394
+                b: 'b'
378 395
             })
379
-            .then(r => {
380
-                if(r != null)
381
-                done("UNEXPECTED RESULT " + r)
382
-            })
383
-            .catch((e) => {
384
-                //console.log("EXPECTED CLIENT EXCEPTION", String(e));
385
-                done()
386
-            })
387
-            .finally(() => {
388
-                cli.destroy()
389
-                sock.destroy()
390
-                server.destroy()
391
-            })
392
-        })
393
-    })
394
-
395
-    it("RPC throws on server with handler", (done)=>{
396
-        let server = new RPCServer(21004, [ {
397
-            name: 'createUser' as 'createUser',
396
+                .then(r => {
397
+                    if (r != null)
398
+                        done(new Error("UNEXPECTED RESULT " + r))
399
+                })
400
+                .catch((e) => {
401
+                    if (e.message === errtxt)
402
+                        done()
403
+                    else
404
+                        done(e)
405
+                })
406
+                .finally(() => {
407
+                    cli.destroy()
408
+                    sock.destroy()
409
+                    server.destroy()
410
+                })
411
+        })
412
+    })
413
+
414
+    it("RPC throws on server with handler", (done) => {
415
+        let server = new RPCServer(21004, [{
416
+            name: "createUser",
398 417
             exportRPCs: () => [{
399 418
                 name: 'createUser' as 'createUser',
400 419
                 call: createUser
401
-        }]}], {
420
+            }]
421
+        }], {
402 422
             errorHandler: (socket, e, rpcName, args) => {
403 423
                 done()
404 424
             }
@@ -407,41 +427,44 @@ describe('Error handling', ()=>{
407 427
         let sock = new RPCSocket(21004, 'localhost')
408 428
         sock.connect().then((cli) => {
409 429
             cli["createUser"]["createUser"]({
410
-                a:'a',
411
-                b:'b'
412
-            })
413
-            .then(r => {
414
-                if(r != null)
415
-                done("UNEXPECTED RESULT " + r)
416
-            })
417
-            .catch((e) => {
418
-                done("UNEXPECTED CLIENT ERROR " + e)
419
-                done(e)
420
-            })
421
-            .finally(() => {
422
-                cli.destroy()
423
-                sock.destroy()
424
-                server.destroy()
430
+                a: 'a',
431
+                b: 'b'
425 432
             })
433
+                .then(r => {
434
+                    if (r != null)
435
+                        done("UNEXPECTED RESULT " + r)
436
+                })
437
+                .catch((e) => {
438
+                    done("UNEXPECTED CLIENT ERROR " + e)
439
+                    done(e)
440
+                })
441
+                .finally(() => {
442
+                    cli.destroy()
443
+                    sock.destroy()
444
+                    server.destroy()
445
+                })
426 446
         })
427 447
     })
428 448
 })
429 449
 
430 450
 
431
-describe("Errorhandler functionality", ()=>{
432
-    let createUser = async( user: {a:any,b:any}) => {
433
-        throw new Error("BAD BAD BAD")
451
+describe("Errorhandler functionality", () => {
452
+    const errtxt = "BAD BAD BAD"
453
+
454
+    let createUser = async (user: { a: any, b: any }) => {
455
+        throw new Error(errtxt)
434 456
     }
435 457
 
436
-    it("correct values are passed to the handler", (done)=>{
437
-        let server = new RPCServer(21004, [ {
438
-            name: 'createUser' as 'createUser',
458
+    it("correct values are passed to the handler", (done) => {
459
+        let server = new RPCServer(21004, [{
460
+            name: "createUser",
439 461
             exportRPCs: () => [{
440 462
                 name: 'createUser' as 'createUser',
441 463
                 call: createUser
442
-        }]}], {
464
+            }]
465
+        }], {
443 466
             errorHandler: (socket, e, rpcName, args) => {
444
-                if(e.message === "BAD BAD BAD" && rpcName === "createUser" && args[0]['a'] === 'a' && args[0]['b'] === 'b')
467
+                if (e.message === errtxt && rpcName === "createUser" && args[0]['a'] === 'a' && args[0]['b'] === 'b')
445 468
                     done()
446 469
             }
447 470
         })
@@ -449,97 +472,97 @@ describe("Errorhandler functionality", ()=>{
449 472
         let sock = new RPCSocket(21004, 'localhost')
450 473
         sock.connect().then((cli) => {
451 474
             cli["createUser"]["createUser"]({
452
-                a:'a',
453
-                b:'b'
454
-            })
455
-            .then(r => {
456
-                if(r != null)
457
-                done("UNEXPECTED RESULT " + r)
458
-            })
459
-            .catch((e) => {
460
-                done("UNEXPECTED CLIENT ERROR " + e)
461
-                done(e)
475
+                a: 'a',
476
+                b: 'b'
462 477
             })
463
-            .finally(() => {
464
-                cli.destroy()
465
-                sock.destroy()
466
-                server.destroy()
467
-            })
468
-        })
469
-    })
470
-
471
-    it("handler sees sesame", (done)=>{
478
+                .then(r => {
479
+                    if (r != null)
480
+                        done("UNEXPECTED RESULT " + r)
481
+                })
482
+                .catch((e) => {
483
+                    done(new Error("UNEXPECTED CLIENT ERROR " + e.message))
484
+                })
485
+                .finally(() => {
486
+                    cli.destroy()
487
+                    sock.destroy()
488
+                    server.destroy()
489
+                })
490
+        })
491
+    })
492
+
493
+    it("handler sees sesame", (done) => {
472 494
         let sesame = "AAAAAAAAAAAAAAA"
473
-        let server = new RPCServer(21004, [ {
474
-            name: 'createUser' as 'createUser',
495
+        let server = new RPCServer(21004, [{
496
+            name: "createUser" as "createUser",
475 497
             exportRPCs: () => [{
476 498
                 name: 'createUser' as 'createUser',
477 499
                 call: createUser
478
-        }]}], {
500
+            }]
501
+        }], {
479 502
             sesame: sesame,
480 503
             errorHandler: (socket, e, rpcName, args) => {
481
-                if(e.message === "BAD BAD BAD" && rpcName === "createUser" && args[0] === sesame && args[1]['a'] === 'a' && args[1]['b'] === 'b')
504
+                if (e.message === errtxt && rpcName === "createUser" && args[0] === sesame && args[1]['a'] === 'a' && args[1]['b'] === 'b')
482 505
                     done()
483 506
             }
484
-            
507
+
485 508
         })
486 509
 
487 510
         let sock = new RPCSocket(21004, 'localhost')
488 511
         sock.connect(sesame).then((cli) => {
489 512
             cli["createUser"]["createUser"]({
490
-                a:'a',
491
-                b:'b'
492
-            })
493
-            .then(r => {
494
-                if(r != null)
495
-                done("UNEXPECTED RESULT " + r)
496
-            })
497
-            .catch((e) => {
498
-                done("UNEXPECTED CLIENT ERROR " + e)
499
-                done(e)
500
-            })
501
-            .finally(() => {
502
-                cli.destroy()
503
-                sock.destroy()
504
-                server.destroy()
513
+                a: 'a',
514
+                b: 'b'
505 515
             })
516
+                .then(r => {
517
+                    if (r != null)
518
+                        done("UNEXPECTED RESULT " + r)
519
+                })
520
+                .catch((e) => {
521
+                    done("UNEXPECTED CLIENT ERROR " + e)
522
+                    done(e)
523
+                })
524
+                .finally(() => {
525
+                    cli.destroy()
526
+                    sock.destroy()
527
+                    server.destroy()
528
+                })
506 529
         })
507 530
     })
508 531
 })
509 532
 
510 533
 type myExporterIfc = {
511 534
     MyExporter: {
512
-        myRPC: ()=>Promise<string>
535
+        myRPC: () => Promise<string>
513 536
     }
514 537
 }
515 538
 
516 539
 
517
-describe("Class binding", ()=>{
540
+describe("Class binding", () => {
518 541
 
519
-    let exporter1 : MyExporter
520
-    let serv : RPCServer<{}, myExporterIfc>
542
+    let exporter1: MyExporter
543
+    let serv: RPCServer<myExporterIfc>
521 544
     let sock: RPCSocket & myExporterIfc
522 545
     let allowed = true
523 546
 
524 547
     class MyExporter implements RPCExporter<myExporterIfc>{
525
-        name = "MyExporter" as "MyExporter";
548
+        name = "MyExporter" as "MyExporter"
526 549
         exportRPCs = () => [
527 550
             this.myRPC
528 551
         ]
529 552
 
530 553
         myRPC = async () => {
531
-            serv.setExporters([new MyOtherExporter])
554
+            //serv.setExporters([new MyOtherExporter])
532 555
             return "Hello World"
533 556
         }
534 557
     }
535 558
 
536 559
     class MyOtherExporter implements RPCExporter<myExporterIfc>{
537
-        name = "MyExporter" as "MyExporter";
560
+        name = "MyExporter" as "MyExporter"
538 561
         exportRPCs = () => [
539 562
             this.myRPC
540 563
         ]
541 564
 
542
-        myRPC = async () => { 
565
+        myRPC = async () => {
543 566
             return "Hello Borld"
544 567
         }
545 568
 
@@ -547,24 +570,22 @@ describe("Class binding", ()=>{
547 570
 
548 571
     before(done => {
549 572
         exporter1 = new MyExporter()
550
-        serv = new RPCServer<{}, myExporterIfc>(21004, [exporter1], {
551
-            accessFilter: async (sesame,exporter) => {
552
-                switch(exporter.name){
553
-                    case "MyExporter": 
554
-                        if(!allowed) return false
555
-                        allowed = false
556
-                        return sesame==='xxx';
557
-                    default:
558
-                        return false
573
+        serv = new RPCServer<myExporterIfc>(21004, [exporter1], {
574
+            accessFilter: async (sesame, exporter) => {
575
+                if(exporter.name === 'MyExporter'){
576
+                    if (!allowed) return false
577
+                    allowed = false
578
+                    return sesame === 'xxx';
579
+                }else{
580
+                    return false
559 581
                 }
560 582
             },
561 583
             sesame: "xxx"
562 584
         })
563 585
         done()
564 586
     })
565
-        
566
-    beforeEach((done)=>{
567
-        
587
+
588
+    beforeEach((done) => {
568 589
         const s = new RPCSocket(21004, 'localhost')
569 590
         s.connect<myExporterIfc>("xxx").then(conn => {
570 591
             sock = conn
@@ -572,7 +593,7 @@ describe("Class binding", ()=>{
572 593
         })
573 594
     })
574 595
 
575
-    afterEach(done => {
596
+    afterEach((done) => {
576 597
         sock.destroy()
577 598
         done()
578 599
     })
@@ -581,7 +602,11 @@ describe("Class binding", ()=>{
581 602
         serv.destroy()
582 603
     })
583 604
 
584
-    it("binds correctly", (done)=>{
605
+    /* The server-side socket will enter a 30s timeout if destroyed by a RPC.
606
+       to mitigate the impact on testing time these are not run.
607
+
608
+    it("binds correctly", function(done){
609
+        this.timeout(1000)
585 610
         sock['MyExporter'].myRPC().then((res) => {
586 611
             done(new Error(res))
587 612
         }).catch(e => {
@@ -592,31 +617,42 @@ describe("Class binding", ()=>{
592 617
     })
593 618
 
594 619
     it("changes exporters", (done) => {
620
+        
595 621
         sock['MyExporter'].myRPC().then((res) => {
596
-            if(res === "Hello Borld")
622
+            if (res === "Hello Borld")
597 623
                 done()
598 624
             else
599 625
                 done(new Error(res))
600 626
         })
601 627
     })
628
+    */
629
+
630
+
631
+    it("use sesameFilter for available", (done) => {
632
+        if (sock['MyExporter']){
633
+            allowed = false
634
+            done()
635
+        }
636
+        else done(new Error("RPC supposed to be here"))
637
+    })
602 638
 
603 639
     it("use sesameFilter", (done) => {
604
-        if(!sock['MyExporter']) done()
640
+        if (!sock['MyExporter']) done()
605 641
         else done(new Error("RPC supposed to be gone"))
606 642
     })
607 643
 })
608 644
 
609 645
 
610
-describe("attaching handlers before connecting", ()=>{
611
-    it("fires error if server is unreachable", (done)=>{
646
+describe("attaching handlers before connecting", () => {
647
+    it("fires error if server is unreachable", (done) => {
612 648
         const sock = new RPCSocket(21004, 'localhost')
613 649
         let errorHandleCount = 0
614 650
 
615 651
         sock.on('error', (err) => {
616 652
             //attached listener fires first
617
-            if(errorHandleCount != 0){
653
+            if (errorHandleCount != 0) {
618 654
                 console.log("Error handler didn't fire first");
619
-            }else{
655
+            } else {
620 656
                 errorHandleCount++
621 657
             }
622 658
         })
@@ -625,16 +661,16 @@ describe("attaching handlers before connecting", ()=>{
625 661
             console.log("Unexpected successful connect")
626 662
         }).catch(e => {
627 663
             //catch clause fires second
628
-            if(errorHandleCount != 1){
664
+            if (errorHandleCount != 1) {
629 665
                 console.log("catch clause didn't fire second");
630
-            }else{
666
+            } else {
631 667
                 sock.destroy()
632 668
                 done()
633 669
             }
634 670
         })
635 671
     })
636 672
 
637
-    it("fires error if call is unknown", (done)=>{
673
+    it("fires error if call is unknown", (done) => {
638 674
         const serv = new RPCServer(21004)
639 675
         const sock = new RPCSocket(21004, 'localhost')
640 676
 
@@ -645,14 +681,14 @@ describe("attaching handlers before connecting", ()=>{
645 681
         })
646 682
 
647 683
         sock.connect().then(_ => {
648
-            sock.call("unknownRPC123", "AAAAA").catch(e => { /* ignore */})
684
+            sock.call("unknownRPC123", "AAAAA").catch(e => { /* ignore */ })
649 685
         }).catch(e => {
650 686
             console.log("unexpected connect catch clause");
651 687
             done(e)
652 688
         })
653 689
     })
654 690
 
655
-    it("demands catch on method invocation if call is unknown", (done)=>{
691
+    it("demands catch on method invocation if call is unknown", (done) => {
656 692
         const serv = new RPCServer(21004)
657 693
         const sock = new RPCSocket(21004, 'localhost')
658 694
 
@@ -667,4 +703,11 @@ describe("attaching handlers before connecting", ()=>{
667 703
             done(e)
668 704
         })
669 705
     })
706
+
707
+})
708
+
709
+describe('finally', () => {
710
+    it('print open handles (Ignore `DNSCHANNEL` and `Immediate`)', () => {
711
+        log()
712
+    })
670 713
 })

+ 0
- 73
test/devtest.ts View File

@@ -1,73 +0,0 @@
1
-import { RPCServer } from "../src/Backend";
2
-import { SubscriptionResponse, RPCInterface } from "../src/Types";
3
-import { RPCSocket } from "../src/Frontend";
4
-import { makeSubResponse } from "../src/Utils";
5
-
6
-type SubresExtension = {a:string}
7
-
8
-type MyInterface = { 
9
-    Group1: { 
10
-        triggerCallbacks: (...args:any[]) => Promise<void>,
11
-        subscribe: (param:string, callback:Function) => Promise<SubscriptionResponse<SubresExtension>>,
12
-        unsubscribe: (uuid:string) => Promise<void>
13
-    },
14
-    Group2: {
15
-        echo: (x:string) => Promise<string>
16
-    }
17
-}
18
-
19
-new RPCServer<SubresExtension, MyInterface>(20000, 
20
-    [{
21
-        name: 'Group1',
22
-        exportRPCs: () => [{
23
-            name: 'triggerCallbacks',
24
-            call: async () => { /*...*/ }
25
-        },{
26
-            name: 'subscribe',
27
-            hook: async (param, callback) => { return makeSubResponse<SubresExtension>({a: "test"}) }
28
-        },{
29
-            name: 'unsubscribe',
30
-            call: async(uuid) => { }
31
-        }
32
-    ]
33
-    },{
34
-        name: 'Group2',
35
-        exportRPCs: () => [{
36
-            name: 'echo',
37
-            call: async (x) => "..."
38
-        }]
39
-    }]
40
-)
41
-
42
-RPCSocket.makeSocket<MyInterface>(20000, 'localhost').then((async (client) => {
43
-    console.log(client)
44
-    const res = await client.Group1.subscribe('test', async (...args:any) => {
45
-        console.log.apply(console, args)
46
-
47
-        /* close the callbacks once you're done */
48
-        await client.Group1.unsubscribe(res.uuid)
49
-        client.unhook(res.uuid)
50
-    })
51
-
52
-    await client.Group1.triggerCallbacks("Hello", "World", "Callbacks")
53
-}))
54
-
55
-const srv = new RPCServer(30000, [{
56
-    name: 'Group2',
57
-    exportRPCs: () => [{
58
-        name: 'echo',
59
-        call: async (x) => x
60
-    }]
61
-}], {
62
-    sesame: 'open'
63
-})
64
-
65
-const s = new RPCSocket(30000, 'localhost')
66
-s.connect("open").then(async() => {
67
-    s['Group2']['echo']('open', 'dfgfg').then(console.log)
68
-    s['Group2']['echo']('dfgfg').then(console.log)
69
-
70
-    s['Group2']['echo']('dfgfg').then(console.log)
71
-
72
-})
73
-

Loading…
Cancel
Save