diff --git a/package-lock.json b/package-lock.json index b6b2180..b677f28 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "rpclibrary", - "version": "1.9.2", + "version": "1.10.2", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -247,6 +247,14 @@ "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", "dev": true }, + "@types/engine.io": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@types/engine.io/-/engine.io-3.1.4.tgz", + "integrity": "sha512-98rXVukLD6/ozrQ2O80NAlWDGA4INg+tqsEReWJldqyi2fulC9V7Use/n28SWgROXKm6003ycWV4gZHoF8GA6w==", + "requires": { + "@types/node": "*" + } + }, "@types/expect": { "version": "1.20.4", "resolved": "https://registry.npmjs.org/@types/expect/-/expect-1.20.4.tgz", @@ -275,8 +283,21 @@ "@types/node": { "version": "11.15.2", "resolved": "https://registry.npmjs.org/@types/node/-/node-11.15.2.tgz", - "integrity": "sha512-BqCU9uIFkUH9Sgo2uLYbmIiFB1T+VBiM8AI/El3LIAI5KzwtckeSG+3WOYZr9aMoX4UIvRFBWBeSaOu6hFue2Q==", - "dev": true + "integrity": "sha512-BqCU9uIFkUH9Sgo2uLYbmIiFB1T+VBiM8AI/El3LIAI5KzwtckeSG+3WOYZr9aMoX4UIvRFBWBeSaOu6hFue2Q==" + }, + "@types/socket.io": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/@types/socket.io/-/socket.io-2.1.8.tgz", + "integrity": "sha512-NIQfh9WwJuJKlgmby4NgwMpoBOmNPCDgaRNPiLYZBtkbHkszK/9R52B5yGkd5a34rbVdAADuo8FhOS/5AZDemw==", + "requires": { + "@types/engine.io": "*", + "@types/node": "*" + } + }, + "@types/socket.io-client": { + "version": "1.4.33", + "resolved": "https://registry.npmjs.org/@types/socket.io-client/-/socket.io-client-1.4.33.tgz", + "integrity": "sha512-m4LnxkljsI9fMsjwpW5QhRpMixo2BeeLpFmg0AE+sS4H1pzAd/cs/ftTiL60FLZgfFa8PFRPx5KsHu8O0bADKQ==" }, "@webassemblyjs/ast": { "version": "1.8.5", @@ -466,12 +487,26 @@ "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", "dev": true }, + "accepts": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "requires": { + "mime-types": "~2.1.24", + "negotiator": "0.6.2" + } + }, "acorn": { "version": "6.4.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.1.tgz", "integrity": "sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA==", "dev": true }, + "after": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz", + "integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8=" + }, "aggregate-error": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.0.1.tgz", @@ -602,6 +637,11 @@ "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", "dev": true }, + "arraybuffer.slice": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz", + "integrity": "sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog==" + }, "arrify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", @@ -658,6 +698,11 @@ "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==", "dev": true }, + "async-limiter": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", + "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==" + }, "atob": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", @@ -673,6 +718,11 @@ "underscore": ">=1.8.3" } }, + "backo2": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", + "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=" + }, "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", @@ -734,12 +784,30 @@ } } }, + "base64-arraybuffer": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz", + "integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg=" + }, "base64-js": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==", "dev": true }, + "base64id": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", + "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==" + }, + "better-assert": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz", + "integrity": "sha1-QIZrnhueC1W0gYlDEeaPr/rrxSI=", + "requires": { + "callsite": "1.0.0" + } + }, "big.js": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", @@ -752,6 +820,11 @@ "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", "dev": true }, + "blob": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/blob/-/blob-0.0.5.tgz", + "integrity": "sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig==" + }, "bluebird": { "version": "3.7.2", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", @@ -1013,6 +1086,11 @@ } } }, + "callsite": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz", + "integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA=" + }, "camelcase": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", @@ -1191,11 +1269,20 @@ "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", "dev": true }, + "component-bind": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz", + "integrity": "sha1-AMYIq33Nk4l8AAllGx06jh5zu9E=" + }, "component-emitter": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", - "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", - "dev": true + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" + }, + "component-inherit": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz", + "integrity": "sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM=" }, "concat-map": { "version": "0.0.1", @@ -1236,6 +1323,11 @@ "safe-buffer": "~5.1.1" } }, + "cookie": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", + "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" + }, "copy-concurrently": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz", @@ -1517,6 +1609,77 @@ "once": "^1.4.0" } }, + "engine.io": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.4.2.tgz", + "integrity": "sha512-b4Q85dFkGw+TqgytGPrGgACRUhsdKc9S9ErRAXpPGy/CXKs4tYoHDkvIRdsseAF7NjfVwjRFIn6KTnbw7LwJZg==", + "requires": { + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "0.3.1", + "debug": "~4.1.0", + "engine.io-parser": "~2.2.0", + "ws": "^7.1.2" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "requires": { + "ms": "^2.1.1" + } + } + } + }, + "engine.io-client": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.4.3.tgz", + "integrity": "sha512-0NGY+9hioejTEJCaSJZfWZLk4FPI9dN+1H1C4+wj2iuFba47UgZbJzfWs4aNFajnX/qAaYKbe2lLTfEEWzCmcw==", + "requires": { + "component-emitter": "~1.3.0", + "component-inherit": "0.0.3", + "debug": "~4.1.0", + "engine.io-parser": "~2.2.0", + "has-cors": "1.1.0", + "indexof": "0.0.1", + "parseqs": "0.0.5", + "parseuri": "0.0.5", + "ws": "~6.1.0", + "xmlhttprequest-ssl": "~1.5.4", + "yeast": "0.1.2" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "requires": { + "ms": "^2.1.1" + } + }, + "ws": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.1.4.tgz", + "integrity": "sha512-eqZfL+NE/YQc1/ZynhojeV8q+H050oR8AZ2uIev7RU10svA9ZnJUddHcOUZTJLinZ9yEfdA2kSATS2qZK5fhJA==", + "requires": { + "async-limiter": "~1.0.0" + } + } + } + }, + "engine.io-parser": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.2.0.tgz", + "integrity": "sha512-6I3qD9iUxotsC5HEMuuGsKA0cXerGz+4uGcXQEkfBidgKf0amsjrrtwcbwK/nzpZBxclXlV7gGl9dgWvu4LF6w==", + "requires": { + "after": "0.8.2", + "arraybuffer.slice": "~0.0.7", + "base64-arraybuffer": "0.1.5", + "blob": "0.0.5", + "has-binary2": "~1.0.2" + } + }, "enhanced-resolve": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.1.1.tgz", @@ -2694,6 +2857,26 @@ "function-bind": "^1.1.1" } }, + "has-binary2": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.3.tgz", + "integrity": "sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw==", + "requires": { + "isarray": "2.0.1" + }, + "dependencies": { + "isarray": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", + "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=" + } + } + }, + "has-cors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz", + "integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk=" + }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", @@ -2865,6 +3048,11 @@ "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", "dev": true }, + "indexof": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", + "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=" + }, "infer-owner": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", @@ -3585,6 +3773,19 @@ "brorand": "^1.0.1" } }, + "mime-db": { + "version": "1.44.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", + "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==" + }, + "mime-types": { + "version": "2.1.27", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", + "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", + "requires": { + "mime-db": "1.44.0" + } + }, "mimic-fn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", @@ -3714,8 +3915,7 @@ "ms": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", - "dev": true + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" }, "nan": { "version": "2.14.0", @@ -3743,6 +3943,11 @@ "to-regex": "^3.0.1" } }, + "negotiator": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" + }, "neo-async": { "version": "2.6.1", "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.1.tgz", @@ -4084,6 +4289,11 @@ "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", "dev": true }, + "object-component": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/object-component/-/object-component-0.0.3.tgz", + "integrity": "sha1-8MaapQ78lbhmwYb0AKM3acsvEpE=" + }, "object-copy": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", @@ -4309,6 +4519,22 @@ "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=", "dev": true }, + "parseqs": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz", + "integrity": "sha1-1SCKNzjkZ2bikbouoXNoSSGouJ0=", + "requires": { + "better-assert": "~1.0.0" + } + }, + "parseuri": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.5.tgz", + "integrity": "sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo=", + "requires": { + "better-assert": "~1.0.0" + } + }, "pascalcase": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", @@ -4922,6 +5148,130 @@ } } }, + "socket.io": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.3.0.tgz", + "integrity": "sha512-2A892lrj0GcgR/9Qk81EaY2gYhCBxurV0PfmmESO6p27QPrUK1J3zdns+5QPqvUYK2q657nSj0guoIil9+7eFg==", + "requires": { + "debug": "~4.1.0", + "engine.io": "~3.4.0", + "has-binary2": "~1.0.2", + "socket.io-adapter": "~1.1.0", + "socket.io-client": "2.3.0", + "socket.io-parser": "~3.4.0" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "requires": { + "ms": "^2.1.1" + } + } + } + }, + "socket.io-adapter": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-1.1.2.tgz", + "integrity": "sha512-WzZRUj1kUjrTIrUKpZLEzFZ1OLj5FwLlAFQs9kuZJzJi5DKdU7FsWc36SNmA8iDOtwBQyT8FkrriRM8vXLYz8g==" + }, + "socket.io-client": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.3.0.tgz", + "integrity": "sha512-cEQQf24gET3rfhxZ2jJ5xzAOo/xhZwK+mOqtGRg5IowZsMgwvHwnf/mCRapAAkadhM26y+iydgwsXGObBB5ZdA==", + "requires": { + "backo2": "1.0.2", + "base64-arraybuffer": "0.1.5", + "component-bind": "1.0.0", + "component-emitter": "1.2.1", + "debug": "~4.1.0", + "engine.io-client": "~3.4.0", + "has-binary2": "~1.0.2", + "has-cors": "1.1.0", + "indexof": "0.0.1", + "object-component": "0.0.3", + "parseqs": "0.0.5", + "parseuri": "0.0.5", + "socket.io-parser": "~3.3.0", + "to-array": "0.1.4" + }, + "dependencies": { + "component-emitter": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", + "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=" + }, + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "requires": { + "ms": "^2.1.1" + } + }, + "isarray": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", + "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=" + }, + "socket.io-parser": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.3.0.tgz", + "integrity": "sha512-hczmV6bDgdaEbVqhAeVMM/jfUfzuEZHsQg6eOmLgJht6G3mPKMxYm75w2+qhAQZ+4X+1+ATZ+QFKeOZD5riHng==", + "requires": { + "component-emitter": "1.2.1", + "debug": "~3.1.0", + "isarray": "2.0.1" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + } + } + }, + "socket.io-parser": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.4.1.tgz", + "integrity": "sha512-11hMgzL+WCLWf1uFtHSNvliI++tcRUWdoeYuwIl+Axvwy9z2gQM+7nJyN3STj1tLj5JyIUH8/gpDGxzAlDdi0A==", + "requires": { + "component-emitter": "1.2.1", + "debug": "~4.1.0", + "isarray": "2.0.1" + }, + "dependencies": { + "component-emitter": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", + "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=" + }, + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "requires": { + "ms": "^2.1.1" + } + }, + "isarray": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", + "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=" + } + } + }, "source-list-map": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", @@ -5282,6 +5632,11 @@ "setimmediate": "^1.0.4" } }, + "to-array": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz", + "integrity": "sha1-F+bBH3PdTz10zaek/zI46a2b+JA=" + }, "to-arraybuffer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", @@ -5958,6 +6313,16 @@ "typedarray-to-buffer": "^3.1.5" } }, + "ws": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.3.0.tgz", + "integrity": "sha512-iFtXzngZVXPGgpTlP1rBqsUK82p9tKqsWRPg5L56egiljujJT3vGAYnHANvFxBieXrTFavhzhxW52jnaWV+w2w==" + }, + "xmlhttprequest-ssl": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz", + "integrity": "sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4=" + }, "xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", @@ -6043,6 +6408,11 @@ "yargs": "^13.3.0" } }, + "yeast": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz", + "integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk=" + }, "yn": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/yn/-/yn-2.0.0.tgz", diff --git a/package.json b/package.json index 3312b79..78bb14f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rpclibrary", - "version": "1.10.2", + "version": "1.11.0", "description": "rpclibrary is a websocket on steroids!", "main": "./js/Index.js", "repository": { @@ -50,9 +50,13 @@ "why-is-node-running": "^2.1.2" }, "dependencies": { + "@types/socket.io": "^2.1.8", + "@types/socket.io-client": "^1.4.33", "bsock": "^0.1.9", "crypto-js": "^4.0.0", "http": "0.0.0", + "socket.io": "^2.3.0", + "socket.io-client": "^2.3.0", "uuid": "^3.3.3" }, "files": [ diff --git a/scratchpad.ts b/scratchpad.ts new file mode 100644 index 0000000..c751fce --- /dev/null +++ b/scratchpad.ts @@ -0,0 +1,53 @@ +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)) diff --git a/src/Backend.ts b/src/Backend.ts index f5961fe..c15f08d 100644 --- a/src/Backend.ts +++ b/src/Backend.ts @@ -1,17 +1,17 @@ 'use strict' import http = require('http'); -import bsock = require('bsock'); +import { PromiseIO } from "./PromiseIO/Server"; import * as T from './Types'; import * as U from './Utils'; import * as I from './Interfaces'; export class RPCServer< InterfaceT extends T.RPCInterface = T.RPCInterface, -> implements I.Destroyable { +> { private ws = http.createServer() - private io = bsock.createServer() + private pio = PromiseIO.createServer() private visibility: T.Visibility private closeHandler: T.CloseHandler private errorHandler: T.ErrorHandler @@ -42,7 +42,7 @@ export class RPCServer< }) - this.errorHandler = (socket: I.Socket) => (error: any, rpcName: string, args: any[]) => { + this.errorHandler = (socket: I.Socket | PromiseIO) => (error: any, rpcName: string, args: any[]) => { if (conf.errorHandler) conf.errorHandler(socket, error, rpcName, args) else throw error } @@ -72,20 +72,24 @@ export class RPCServer< private startWebsocket() { try { - this.io.attach(this.ws) - this.io.on('socket', (socket: I.Socket) => { + 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) }) - this.ws = this.ws.listen(this.port, this.visibility) + + this.pio.listen(this.port) + } catch (e) { - this.errorHandler(this.io, e, 'system', []) + this.errorHandler(this.pio, e, 'system', []) } } 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) @@ -96,22 +100,8 @@ export class RPCServer< }) } - /** - * Publishes a new list of Exporters. This destroys and restarts the socket - * @param exporters the exporters to publish - public setExporters(exporters: T.ExporterArray): any { - exporters.forEach(U.fixNames) - this.destroy() - this.ws = http.createServer() - this.io = bsock.createServer() - this.exporters = exporters - this.startWebsocket() - } - */ - - - destroy(): void { - this.io.close() + close(): void { + this.pio.close() this.ws.close() } } \ No newline at end of file diff --git a/src/Frontend.ts b/src/Frontend.ts index 52f94a3..1686973 100644 --- a/src/Frontend.ts +++ b/src/Frontend.ts @@ -1,7 +1,6 @@ 'use strict' -import bsock = require('bsock'); - +import { PromiseIOClient } from './PromiseIO/Client' import * as T from './Types'; import * as I from './Interfaces'; import { stripAfterEquals, appendComma } from './Utils'; @@ -18,8 +17,12 @@ export class RPCSocket implements I } private socket: I.Socket - private closeHandlers: T.FrontEndHandlerType['close'][] = [] - private errorHandlers: T.FrontEndHandlerType['error'][] = [] + private handlers : { + [name in string]: T.AnyFunction[] + } = { + error: [], + close: [] + } private hooks : {[name in string]: T.AnyFunction} = {} /** @@ -45,6 +48,19 @@ export class RPCSocket implements I } } + /** + * Hooks a handler to a function name. Use {@link call} to trigger it. + * @param name The function name to listen on + * @param handler The handler to attach + */ + public bind(name: string, handler: (...args:any[]) => any | Promise){ + if(!this.socket){ + this.hooks[name] = handler + }else{ + this.socket.bind(name, handler) + } + } + /** * Removes a {@link hook} listener by name. * @param name The function name @@ -62,13 +78,12 @@ export class RPCSocket implements I * @param type 'error' or 'close' * @param f The listener to attach */ - public on(type: T, f: T.FrontEndHandlerType[T]){ + public on(type: string, f: T.AnyFunction){ if(!this.socket){ - switch(type){ - case "error": this.errorHandlers.push( f); break; - case "close": this.closeHandlers.push( f); break; - default: throw new Error('socket.on only supports ´error´ and ´close´ as first parameter. Got: ´'+type+'´') - } + if(!this.handlers[type]) + this.handlers[type] = [] + + this.handlers[type].push(f) }else{ this.socket.on(type, f) } @@ -84,14 +99,6 @@ export class RPCSocket implements I this.socket.emit(eventName, data) } - /** - * Destroys the socket - */ - public destroy(){ - if(!this.socket) return; - this.socket.destroy() - } - /** * Closes the socket. It may attempt to reconnect. */ @@ -108,7 +115,8 @@ export class RPCSocket implements I public async call (rpcname: string, ...args: any[]) : Promise{ if(!this.socket) throw new Error("The socket is not connected! Use socket.connect() first") try{ - return await this.socket.call.apply(this.socket, [rpcname, ...args]) + const val = await this.socket.call.apply(this.socket, [rpcname, ...args]) + return val }catch(e){ this.emit('error', e) throw e @@ -129,14 +137,23 @@ export class RPCSocket implements I * Connects to the server and attaches available RPCs to this object */ public async connect( sesame?: string ) : Promise> { - this.socket = await bsock.connect(this.port, this.server, this.conf.tls?this.conf.tls:false) - this.errorHandlers.forEach(h => this.socket.on('error', h)) - this.closeHandlers.forEach(h => this.socket.on('close', h)) + + try{ + this.socket = await PromiseIOClient.connect(this.port, this.server, /*this.conf.tls?this.conf.tls:false*/) + }catch(e){ + this.handlers['error'].forEach(h => h(e)) + throw e + } + + Object.entries(this.handlers).forEach(([k,v])=>{ + v.forEach(h => this.socket.on(k, h)) + }) + Object.entries(this.hooks).forEach((kv: [string, T.AnyFunction]) => { this.socket.hook(kv[0], kv[1]) }) - const info:T.ExtendedRpcInfo[] = await this.info(sesame) + info.forEach(i => { let f: any @@ -153,6 +170,8 @@ export class RPCSocket implements I this[i.owner][i.name] = f this[i.owner][i.name].bind(this) }) + + return > (this as any) } diff --git a/src/Interfaces.ts b/src/Interfaces.ts index f642851..8e09549 100644 --- a/src/Interfaces.ts +++ b/src/Interfaces.ts @@ -12,20 +12,15 @@ export type RPCExporter< exportRPCs() : T.RPCDefinitions[Name] } -/** - * Generic socket interface that can apply to bsock as well as RPCSocket - */ -export interface Socket extends Destroyable { - port: number - hook: (rpcname: string, handler: T.AnyFunction) => void - unhook: (rpcname:string) => void - call: (rpcname:string, ...args: any[]) => Promise - fire: (rpcname:string, ...args: any[]) => Promise - on: T.OnFunction - emit: (eventName: string, data:any) => void - close() : void -} - -export interface Destroyable{ - destroy() : void -} \ No newline at end of file +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 + fire: (rpcname: string, ...args: any[]) => Promise + on: (type: string, f: T.AnyFunction)=>any + emit: (eventName: string, data: any) => void + close(): void + } \ No newline at end of file diff --git a/src/PromiseIO/Client.ts b/src/PromiseIO/Client.ts new file mode 100644 index 0000000..81bd128 --- /dev/null +++ b/src/PromiseIO/Client.ts @@ -0,0 +1,38 @@ +import { Socket } from "socket.io" +import * as U from '../Utils' +import * as I from '../Interfaces' +import * as socketio from 'socket.io-client' + +export class PromiseIOClient { + + static connect = (port: number, host = "localhost"): Promise => new Promise((res, rej) => { + try { + const socket = socketio(`http://${host}:${port}`, { + reconnectionAttempts: 2, + reconnectionDelay: 200, + timeout: 450, + reconnection: false + }) + socket.on('connect_error', e => { + sock.emit('error', e) + rej(e) + }) + + const sock = U.makePioSocket(socket) + socket.on('connect', ()=>{ res(sock) }) + + + /* + socket.on('connect_timeout', ()=>console.log('connect_timeout')) + socket.on('disconnect', ()=>console.log('disconnect')) + socket.on('reconnect', ()=>console.log('reconnect')) + socket.on('reconnect_attempt', ()=>console.log('reconnect_attempt')) + socket.on('reconnecting', ()=>console.log('reconnecting')); + socket.on('reconnect_failed', ()=>console.log('reconnect_failed')); + socket.on('reconnecting', ()=>console.log('reconnecting')); + */ + } catch (e) { + rej(e) + } + }) +} \ No newline at end of file diff --git a/src/PromiseIO/Server.ts b/src/PromiseIO/Server.ts new file mode 100644 index 0000000..2fa3d0c --- /dev/null +++ b/src/PromiseIO/Server.ts @@ -0,0 +1,57 @@ +import { Server, Socket } from "socket.io" +import { Server as httpServer } from "http" +import * as U from '../Utils' +import * as T from '../Types' +const socketio = require('socket.io') + +export class PromiseIO { + io?: Server + private listeners: { [eventName in string]: ((...args: any) => void)[] } = { + socket: [], + connect: [] + } + + static createServer(): PromiseIO { + return new PromiseIO(); + } + + attach(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)) + this.listeners['connect'].forEach(listener => listener(pioSock)) + /* + pioSock.on('error', ()=>console.log('error')); + + pioSock.on('connect_timeout', ()=>console.log('connect_timeout')) + pioSock.on('disconnect', ()=>console.log('disconnect')) + pioSock.on('reconnect', ()=>console.log('reconnect')) + pioSock.on('reconnect_attempt', ()=>console.log('reconnect_attempt')) + pioSock.on('reconnecting', ()=>console.log('reconnecting')); + pioSock.on('reconnect_failed', ()=>console.log('reconnect_failed')); + pioSock.on('reconnecting', ()=>console.log('reconnecting')); + */ + }) + this.io!.listen(port) + } + + on(eventName: string, listener: T.AnyFunction) { + if (this.listeners[eventName] == null) { + this.listeners[eventName] = [] + } + this.listeners[eventName].push(listener) + } + + close = () => { + if(this.io){ + this.io.engine.ws.close() + this.io.close() + this.io = undefined + } + } + +} diff --git a/src/Types.ts b/src/Types.ts index 0b3b9b0..1bdccf1 100644 --- a/src/Types.ts +++ b/src/Types.ts @@ -1,12 +1,18 @@ import * as I from "./Interfaces"; import { RPCSocket } from "./Frontend"; +import { PromiseIO } from "./PromiseIO/Server"; + +export type PioBindListener = (...args: any) => void +export type PioHookListener = AnyFunction + + export type AnyFunction = (...args:any) => any export type HookFunction = AnyFunction export type AccessFilter = (sesame:string|undefined, exporter: I.RPCExporter) => Promise | boolean export type Visibility = "127.0.0.1" | "0.0.0.0" export type ConnectionHandler = (socket:I.Socket) => void -export type ErrorHandler = (socket:I.Socket, error:any, rpcName: string, args: any[]) => void +export type ErrorHandler = (socket:I.Socket | PromiseIO, error:any, rpcName: string, args: any[]) => void export type CloseHandler = (socket:I.Socket) => void export type SesameFunction = (sesame : string) => boolean export type SesameConf = { diff --git a/src/Utils.ts b/src/Utils.ts index f814044..4e7213f 100644 --- a/src/Utils.ts +++ b/src/Utils.ts @@ -2,6 +2,8 @@ import * as uuidv4 from "uuid/v4" import * as T from "./Types"; import * as I from "./Interfaces"; +import { Server as ioServer, Socket as ioSocket, Socket } from "socket.io" +import { Socket as ioClientSocket } from "socket.io-client" /** * Translate an RPC to RPCInfo for serialization. @@ -11,18 +13,18 @@ import * as I from "./Interfaces"; * @param sesame optional sesame phrase to prepend before all RPC arguments * @throws Error on RPC without name property */ -export const rpcToRpcinfo = (socket: I.Socket, rpc : T.RPC, owner: string, errorHandler: T.ErrorHandler, sesame?:T.SesameFunction):T.RpcInfo => { - switch (typeof rpc){ - case "object": - if(rpc['call']){ +export const rpcToRpcinfo = (socket: I.Socket, rpc: T.RPC, owner: string, errorHandler: T.ErrorHandler, sesame?: T.SesameFunction): T.RpcInfo => { + switch (typeof rpc) { + case "object": + if (rpc['call']) { return { owner: owner, argNames: extractArgs(rpc['call']), type: "Call", name: rpc.name, - call: sesame?async (_sesame, ...args) => {if(sesame(_sesame)) return await rpc['call'].apply({}, args); socket.destroy()}:rpc['call'], // check & remove sesame + call: sesame ? async (_sesame, ...args) => { if (sesame(_sesame)) return await rpc['call'].apply({}, args); socket.close() } : rpc['call'], // check & remove sesame } - }else{ + } else { const generator = hookGenerator(>rpc, errorHandler, sesame) return { owner: owner, @@ -33,7 +35,7 @@ export const rpcToRpcinfo = (socket: I.Socket, rpc : T.RPC, owner: str } } case "function": - if(!rpc.name) throw new Error(` + if (!rpc.name) throw new Error(` RPC did not provide a name. \nUse 'funtion name(..){ .. }' syntax instead. \n @@ -41,14 +43,14 @@ RPC did not provide a name. \n${rpc.toString()} \n>------------OFFENDING RPC`) return { - owner : owner, + owner: owner, argNames: extractArgs(rpc), type: "Call", name: rpc.name, - call: sesame?async (_sesame, ...args) => {if(sesame(_sesame)) return await rpc.apply({}, args); throw makeError(rpc.name)}:rpc, // check & remove sesame + call: sesame ? async (_sesame, ...args) => { if (sesame(_sesame)) return await rpc.apply({}, args); throw makeError(rpc.name) } : rpc, // check & remove sesame } } - throw new Error("Bad socketIORPC type "+ typeof rpc) + throw new Error("Bad socketIORPC type " + typeof rpc) } /** @@ -57,37 +59,37 @@ RPC did not provide a name. * @param exporter The exporter * @param makeUnique @default true Attach a suffix to RPC names */ -export function rpcHooker(socket: I.Socket, exporter:I.RPCExporter, errorHandler: T.ErrorHandler, sesame?:T.SesameFunction, makeUnique = true):T.ExtendedRpcInfo[]{ +export function rpcHooker(socket: I.Socket, exporter: I.RPCExporter, errorHandler: T.ErrorHandler, sesame?: T.SesameFunction, makeUnique = true): T.ExtendedRpcInfo[] { const owner = exporter.name const RPCs = exporter.exportRPCs() return RPCs.map(rpc => rpcToRpcinfo(socket, rpc, owner, errorHandler, sesame)) - .map(info => { - const suffix = makeUnique?"-"+uuidv4().substr(0,4):"" - const ret:any = info - ret.uniqueName = info.name+suffix - let rpcFunction = info.type === 'Hook'? info.generator(socket) - : info.call + .map(info => { + const suffix = makeUnique ? "-" + uuidv4().substr(0, 4) : "" + const ret: any = info + ret.uniqueName = info.name + suffix + let rpcFunction = info.type === 'Hook' ? info.generator(socket) + : info.call - socket.hook(ret.uniqueName, callGenerator(info.name, socket, rpcFunction, errorHandler)) - return ret - }) + socket.hook(ret.uniqueName, callGenerator(info.name, socket, rpcFunction, errorHandler)) + return ret + }) } /** * Decorate an RPC with the error handler * @param rpcFunction the function to decorate */ -const callGenerator = (rpcName : string, socket: I.Socket, rpcFunction : T.AnyFunction, errorHandler: T.ErrorHandler) : T.AnyFunction => { +const callGenerator = (rpcName: string, socket: I.Socket, rpcFunction: T.AnyFunction, errorHandler: T.ErrorHandler): T.AnyFunction => { const argsArr = extractArgs(rpcFunction) const args = argsArr.join(',') const argsStr = argsArr.map(stripAfterEquals).join(',') - return eval(`async (`+args+`) => { + return eval(`async (` + args + `) => { try{ - return await rpcFunction(`+argsStr+`) + return await rpcFunction(`+ argsStr + `) }catch(e){ - errorHandler(socket)(e, rpcName, [`+args+`]) + errorHandler(socket)(e, rpcName, [`+ args + `]) } }`) } @@ -96,7 +98,7 @@ const callGenerator = (rpcName : string, socket: I.Socket, rpcFunction : T.AnyFu * Utility function to strip parameters like "a = 3" of their defaults * @param str The parameter to modify */ -export function stripAfterEquals(str:string):string{ +export function stripAfterEquals(str: string): string { return str.split("=")[0] } @@ -105,13 +107,14 @@ export function stripAfterEquals(str:string):string{ * @param rpc The RPC to transform * @returns A {@link HookFunction} */ -const hookGenerator = (rpc:T.HookRPC, /*not unused!*/ errorHandler: T.ErrorHandler, sesameFn?: T.SesameFunction): T.HookInfo['generator'] => { +const hookGenerator = (rpc: T.HookRPC, /*not unused!*/ errorHandler: T.ErrorHandler, sesameFn?: T.SesameFunction): T.HookInfo['generator'] => { + let argsArr = extractArgs(rpc.hook) argsArr.pop() //remove 'callback' from the end let callArgs = argsArr.join(',') - const args = sesameFn?(['sesame', ...argsArr].join(',')) - :callArgs + const args = sesameFn ? (['sesame', ...argsArr].join(',')) + : callArgs callArgs = appendComma(callArgs, false) @@ -139,35 +142,35 @@ const hookGenerator = (rpc:T.HookRPC, /*not unused!*/ errorHandler: T. } const makeError = (callName: string) => { - return new Error("Call not found: "+callName+". ; Zone: ; Task: Promise.then ; Value: Error: Call not found: "+callName) + return new Error("Call not found: " + callName + ". ; Zone: ; Task: Promise.then ; Value: Error: Call not found: " + callName) } /** * Extract a string list of parameters from a function * @param f The source function */ -const extractArgs = (f:Function):string[] => { - let fn:string - fn = (fn = String(f)).substr(0, fn.indexOf(")")).substr(fn.indexOf("(")+1) - return fn!==""?fn.split(',') : [] +const extractArgs = (f: Function): string[] => { + let fn: string + fn = (fn = String(f)).substr(0, fn.indexOf(")")).substr(fn.indexOf("(") + 1) + return fn !== "" ? fn.split(',') : [] } -export function makeSesameFunction (sesame : T.SesameFunction | string) : T.SesameFunction { - if(typeof sesame === 'function'){ +export function makeSesameFunction(sesame: T.SesameFunction | string): T.SesameFunction { + if (typeof sesame === 'function') { return sesame } - return (testSesame : string) => { - return testSesame === sesame + return (testSesame: string) => { + return testSesame === sesame } } -export function appendComma(s?:string, turnToString = true):string{ - if(turnToString) - return s?`'${s}',`:"" - return s?`${s},`:"" +export function appendComma(s?: string, turnToString = true): string { + if (turnToString) + return s ? `'${s}',` : "" + return s ? `${s},` : "" } @@ -176,12 +179,94 @@ export function appendComma(s?:string, turnToString = true):string{ * This was supposedly fixed (https://github.com/microsoft/TypeScript/issues/5611) but it still is the case. * This function sets the name value for all object members that are functions. */ -export function fixNames(o:Object):void{ +export function fixNames(o: Object): void { Object.keys(o).forEach(key => { - if(typeof o[key] === 'function' && !o[key].name){ + if (typeof o[key] === 'function' && !o[key].name) { Object.defineProperty(o[key], 'name', { value: key }) } }) -} \ No newline at end of file +} + +export const makePioSocket = (socket: any): I.Socket => { + return { + bind: (name: string, listener: T.PioBindListener) => socket.on(name, (...args: any) => { + const ack = args.pop() + listener.apply(null, args) + ack() + }), + + hook: (name: string, listener: T.PioHookListener) => { + const args = extractArgs(listener) + let argNames + let restParam = args.find(e => e.includes('...')) + if(!restParam){ + argNames = [...args, '...__args__'].join(',') + restParam = '__args__' + }else{ + argNames = [...args].join(',') + restParam = restParam.replace('...','') + } + + const decoratedListener = eval(`(() => async (${argNames}) => { + const __ack__ = ${restParam}.pop() + try{ + const response = await listener.apply(null, [${argNames}]) + __ack__(response) + }catch(e){ + __ack__({ + ...e, + stack: e.stack, + message: e.message, + name: e.name, + }) + } + })()`) + socket.on(name, decoratedListener) + }, + + call: (name: string, ...args: any) => { + return new Promise((res, rej) => { + const params: any = [name, ...args, (resp) => { + if(isError(resp)){ + const err = new Error() + err.stack = resp.stack + err.name = resp.name + err.message = resp.message + return rej(err) + } + res(resp) + }] + socket.emit.apply(socket, params) + }) + }, + + fire: (name: string, ...args: any) => new Promise((res, rej) => { + const params: any = [name, ...args] + socket.emit.apply(socket, params) + res() + }), + + unhook: (name: string, listener?: T.AnyFunction) => { + if (listener) { + socket.removeListener(name, listener) + } else { + socket.removeAllListeners(name) + } + }, + + id: socket.id, + on: (...args) => socket.on.apply(socket, args), + emit: (...args) => socket.emit.apply(socket, args), + close: () => { + socket + socket.disconnect(true) + }, + } +} + +export const isError = function(e){ + return e && e.stack && e.message && typeof e.stack === 'string' + && typeof e.message === 'string'; + } diff --git a/test/Test.ts b/test/Test.ts index 3f8ed0c..0611a72 100644 --- a/test/Test.ts +++ b/test/Test.ts @@ -68,8 +68,8 @@ describe('RPCServer', () => { }) after(done => { - client.destroy() - server.destroy() + client.close() + server.close() done() }) @@ -116,8 +116,8 @@ describe('RPCSocket', () => { }) after(() => { - client.destroy() - server.destroy() + client.close() + server.close() }) @@ -221,8 +221,8 @@ describe('It should do unhook', () => { }) after(() => { - client.destroy() - server.destroy() + client.close() + server.close() }) it('Subscribe with param', (done) => { @@ -314,8 +314,8 @@ describe('Sesame should unlock the socket', () => { }) after(() => { - client.destroy() - server.destroy() + client.close() + server.close() }) it('should work with sesame', (done) => { @@ -337,8 +337,8 @@ describe('Sesame should unlock the socket', () => { else { done(new Error("Function supposed to be removed without sesame")) } - cli.destroy() - sock.destroy() + cli.close() + sock.close() }) }) @@ -350,8 +350,8 @@ describe('Sesame should unlock the socket', () => { else { done(new Error("Function supposed to be removed without sesame")) } - cli.destroy() - sock.destroy() + cli.close() + sock.close() }) }) @@ -405,9 +405,9 @@ describe('Error handling', () => { done(e) }) .finally(() => { - cli.destroy() - sock.destroy() - server.destroy() + cli.close() + sock.close() + server.close() }) }) }) @@ -440,9 +440,9 @@ describe('Error handling', () => { done(e) }) .finally(() => { - cli.destroy() - sock.destroy() - server.destroy() + cli.close() + sock.close() + server.close() }) }) }) @@ -484,9 +484,9 @@ describe("Errorhandler functionality", () => { done(new Error("UNEXPECTED CLIENT ERROR " + e.message)) }) .finally(() => { - cli.destroy() - sock.destroy() - server.destroy() + cli.close() + sock.close() + server.close() }) }) }) @@ -523,9 +523,9 @@ describe("Errorhandler functionality", () => { done(e) }) .finally(() => { - cli.destroy() - sock.destroy() - server.destroy() + cli.close() + sock.close() + server.close() }) }) }) @@ -595,12 +595,12 @@ describe("Class binding", () => { }) afterEach((done) => { - sock.destroy() + sock.close() done() }) after(() => { - serv.destroy() + serv.close() }) /* The server-side socket will enter a 30s timeout if destroyed by a RPC. @@ -663,26 +663,38 @@ describe("attaching handlers before connecting", () => { }).catch(e => { //catch clause fires second if (errorHandleCount != 1) { - console.log("catch clause didn't fire second"); + console.log("catch clause didn't fire second", errorHandleCount); } else { - sock.destroy() + sock.close() done() } }) }) + /* + * ## 1.11.0 breaking ## + * + * API change: Move from bsock to socketio changes underlying API for when errors are thrown. + * socketio does not throw on unknown listener. This behaviour is considered more consistent with the design + * goals of RPClibrary and was thus adopted + * + + it("fires error if call is unknown", (done) => { const serv = new RPCServer(21004) const sock = new RPCSocket(21004, 'localhost') sock.on('error', (err) => { - sock.destroy() - serv.destroy() + sock.close() + serv.close() done() }) sock.connect().then(_ => { - sock.call("unknownRPC123", "AAAAA").catch(e => { /* ignore */ }) + sock.call("unknownRPC123", "AAAAA").catch(e => { }).then(x => { + console.log("X",x); + + }) }).catch(e => { console.log("unexpected connect catch clause"); done(e) @@ -695,8 +707,8 @@ describe("attaching handlers before connecting", () => { sock.connect().then(_ => { sock.call("unknownRPC123", "AAAAA").catch(e => { - sock.destroy() - serv.destroy() + sock.close() + serv.close() done() }) }).catch(e => { @@ -704,6 +716,7 @@ describe("attaching handlers before connecting", () => { done(e) }) }) + */ }) diff --git a/tsconfig.json b/tsconfig.json index 47547de..cfa8ac0 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -9,6 +9,6 @@ "strict": true, "experimentalDecorators": true }, - "include": ["src/**/*.ts", "test/**/*.ts", "Index.ts", "demo.ts"], + "include": ["src/**/*.ts", "test/**/*.ts", "Index.ts", "demo.ts", "scratchpad.ts"], "exclude": ["node_modules"] } \ No newline at end of file