commit 39851515f69ad5df6ef70e7f4946a5ebd56d534d Author: crusader Date: Wed May 16 20:47:43 2018 +0900 ing diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..aa63e93 --- /dev/null +++ b/.gitignore @@ -0,0 +1,46 @@ +# See http://help.github.com/ignore-files/ for more about ignoring files. + +# compiled output +/dist +/dist-server +/tmp +/out-tsc +/docs + +# dependencies +/node_modules + +# IDEs and editors +/.idea +.project +.classpath +.c9/ +*.launch +.settings/ +*.sublime-workspace + +# IDE - VSCode +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json + +# misc +/.sass-cache +/connect.lock +/coverage +/libpeerconnection.log +npm-debug.log +testem.log +/typings + +# e2e +/e2e/*.js +/e2e/*.map + +# System Files +.DS_Store +Thumbs.db + +yarn.lock \ No newline at end of file diff --git a/.make-package.js b/.make-package.js new file mode 100644 index 0000000..b836c99 --- /dev/null +++ b/.make-package.js @@ -0,0 +1,9 @@ +const fs = require('fs'); +const packageJson = JSON.parse(fs.readFileSync('./package.json').toString()); + +delete packageJson.devDependencies; +delete packageJson.scripts; + +packageJson.private = false; + +fs.writeFileSync('./dist/package.json', JSON.stringify(packageJson, null, 2)); \ No newline at end of file diff --git a/License b/License new file mode 100644 index 0000000..8864d4a --- /dev/null +++ b/License @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..148d9ed --- /dev/null +++ b/package-lock.json @@ -0,0 +1,983 @@ +{ + "name": "@loafer/ng-rpc", + "version": "0.0.1", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@angular/common": { + "version": "5.2.10", + "resolved": "https://nexus.loafle.net/repository/npm-all/@angular/common/-/common-5.2.10.tgz", + "integrity": "sha512-4zgI/Q6bo/KCvrDPf8gc2IpTJ3PFKgd9RF4jZuh1uc+uEYTAj396tDF8o412AJ/iwtYOHWUx+YgzAvT8dHXZ5Q==", + "requires": { + "tslib": "1.9.1" + } + }, + "@angular/core": { + "version": "5.2.10", + "resolved": "https://nexus.loafle.net/repository/npm-all/@angular/core/-/core-5.2.10.tgz", + "integrity": "sha512-glDuTtHTcAVhfU3NVewxz/W+Iweq5IaeW2tnMa+RKLopYk9fRs8eR5iTixTGvegwKR770vfXg/gR7P6Ii5cYGQ==", + "requires": { + "tslib": "1.9.1" + } + }, + "@loafer/core": { + "version": "0.0.1", + "resolved": "https://nexus.loafle.net/repository/npm-all/@loafer/core/-/core-0.0.1.tgz", + "integrity": "sha512-t0Nio5E4l/jqkstQYE4rYqrsnPIgTOWpoxuX6s4UwUvNfAsOnVWxeZ6cSg2ontSZYeEPaObFVfp7IjPiblhX0A==", + "requires": { + "reflect-metadata": "0.1.12" + } + }, + "@loafer/decorator": { + "version": "0.0.1", + "resolved": "https://nexus.loafle.net/repository/npm-all/@loafer/decorator/-/decorator-0.0.1.tgz", + "integrity": "sha512-vN6NX2D3dS5wRevWtznqYSdCteaKpzR7X6YQ25LXXoGEljh5dbAqyBnMBLiULPGmshKV2zp762lZYlrGM5qW0Q==", + "requires": { + "@loafer/core": "0.0.1" + } + }, + "@ngrx/store": { + "version": "5.2.0", + "resolved": "https://nexus.loafle.net/repository/npm-all/@ngrx/store/-/store-5.2.0.tgz", + "integrity": "sha1-Yn7XTJzZVGKTBIXZEqVXEXsjkD4=" + }, + "@types/events": { + "version": "1.2.0", + "resolved": "https://nexus.loafle.net/repository/npm-all/@types/events/-/events-1.2.0.tgz", + "integrity": "sha512-KEIlhXnIutzKwRbQkGWb/I4HFqBuUykAdHgDED6xqwXJfONCjF5VoE0cXEiurh3XauygxzeDzgtXUqvLkxFzzA==", + "dev": true + }, + "@types/fs-extra": { + "version": "5.0.1", + "resolved": "https://nexus.loafle.net/repository/npm-all/@types/fs-extra/-/fs-extra-5.0.1.tgz", + "integrity": "sha512-h3wnflb+jMTipvbbZnClgA2BexrT4w0GcfoCz5qyxd0IRsbqhLSyesM6mqZTAnhbVmhyTm5tuxfRu9R+8l+lGw==", + "dev": true, + "requires": { + "@types/node": "9.6.16" + } + }, + "@types/glob": { + "version": "5.0.35", + "resolved": "https://nexus.loafle.net/repository/npm-all/@types/glob/-/glob-5.0.35.tgz", + "integrity": "sha512-wc+VveszMLyMWFvXLkloixT4n0harUIVZjnpzztaZ0nKLuul7Z32iMt2fUFGAaZ4y1XWjFRMtCI5ewvyh4aIeg==", + "dev": true, + "requires": { + "@types/events": "1.2.0", + "@types/minimatch": "3.0.3", + "@types/node": "9.6.16" + } + }, + "@types/handlebars": { + "version": "4.0.36", + "resolved": "https://nexus.loafle.net/repository/npm-all/@types/handlebars/-/handlebars-4.0.36.tgz", + "integrity": "sha512-LjNiTX7TY7wtuC6y3QwC93hKMuqYhgV9A1uXBKNvZtVC8ZvyWAjZkJ5BvT0K7RKqORRYRLMrqCxpw5RgS+MdrQ==", + "dev": true + }, + "@types/highlight.js": { + "version": "9.12.2", + "resolved": "https://nexus.loafle.net/repository/npm-all/@types/highlight.js/-/highlight.js-9.12.2.tgz", + "integrity": "sha512-y5x0XD/WXDaGSyiTaTcKS4FurULJtSiYbGTeQd0m2LYZGBcZZ/7fM6t5H/DzeUF+kv8y6UfmF6yJABQsHcp9VQ==", + "dev": true + }, + "@types/jasmine": { + "version": "2.8.7", + "resolved": "https://nexus.loafle.net/repository/npm-all/@types/jasmine/-/jasmine-2.8.7.tgz", + "integrity": "sha512-RdbrPcW1aD78UmdLiDa9ZCKrbR5Go8PXh6GCpb4oIOkWVEusubSJJDrP4c5RYOu8m/CBz+ygZpicj6Pgms5a4Q==", + "dev": true + }, + "@types/lodash": { + "version": "4.14.104", + "resolved": "https://nexus.loafle.net/repository/npm-all/@types/lodash/-/lodash-4.14.104.tgz", + "integrity": "sha512-ufQcVg4daO8xQ5kopxRHanqFdL4AI7ondQkV+2f+7mz3gvp0LkBx2zBRC6hfs3T87mzQFmf5Fck7Fi145Ul6NQ==", + "dev": true + }, + "@types/marked": { + "version": "0.3.0", + "resolved": "https://nexus.loafle.net/repository/npm-all/@types/marked/-/marked-0.3.0.tgz", + "integrity": "sha512-CSf9YWJdX1DkTNu9zcNtdCcn6hkRtB5ILjbhRId4ZOQqx30fXmdecuaXhugQL6eyrhuXtaHJ7PHI+Vm7k9ZJjg==", + "dev": true + }, + "@types/minimatch": { + "version": "3.0.3", + "resolved": "https://nexus.loafle.net/repository/npm-all/@types/minimatch/-/minimatch-3.0.3.tgz", + "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==", + "dev": true + }, + "@types/node": { + "version": "9.6.16", + "resolved": "https://nexus.loafle.net/repository/npm-all/@types/node/-/node-9.6.16.tgz", + "integrity": "sha512-fwUW7S8gaSVNpPa1XUdzI0URY71xqXsc90S9vKr2uFIUpVCKV+8ysnN9vvAFK8np4H03A7QGRkHpQvgah0964Q==", + "dev": true + }, + "@types/shelljs": { + "version": "0.7.8", + "resolved": "https://nexus.loafle.net/repository/npm-all/@types/shelljs/-/shelljs-0.7.8.tgz", + "integrity": "sha512-M2giRw93PxKS7YjU6GZjtdV9HASdB7TWqizBXe4Ju7AqbKlWvTr0gNO92XH56D/gMxqD/jNHLNfC5hA34yGqrQ==", + "dev": true, + "requires": { + "@types/glob": "5.0.35", + "@types/node": "9.6.16" + } + }, + "align-text": { + "version": "0.1.4", + "resolved": "https://nexus.loafle.net/repository/npm-all/align-text/-/align-text-0.1.4.tgz", + "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", + "dev": true, + "requires": { + "kind-of": "3.2.2", + "longest": "1.0.1", + "repeat-string": "1.6.1" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://nexus.loafle.net/repository/npm-all/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + } + } + }, + "amdefine": { + "version": "1.0.1", + "resolved": "https://nexus.loafle.net/repository/npm-all/amdefine/-/amdefine-1.0.1.tgz", + "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", + "dev": true + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://nexus.loafle.net/repository/npm-all/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://nexus.loafle.net/repository/npm-all/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://nexus.loafle.net/repository/npm-all/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "1.0.3" + } + }, + "arrify": { + "version": "1.0.1", + "resolved": "https://nexus.loafle.net/repository/npm-all/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", + "dev": true + }, + "async": { + "version": "1.5.2", + "resolved": "https://nexus.loafle.net/repository/npm-all/async/-/async-1.5.2.tgz", + "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", + "dev": true + }, + "babel-code-frame": { + "version": "6.26.0", + "resolved": "https://nexus.loafle.net/repository/npm-all/babel-code-frame/-/babel-code-frame-6.26.0.tgz", + "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", + "dev": true, + "requires": { + "chalk": "1.1.3", + "esutils": "2.0.2", + "js-tokens": "3.0.2" + } + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://nexus.loafle.net/repository/npm-all/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://nexus.loafle.net/repository/npm-all/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "1.0.0", + "concat-map": "0.0.1" + } + }, + "buffer-from": { + "version": "1.0.0", + "resolved": "https://nexus.loafle.net/repository/npm-all/buffer-from/-/buffer-from-1.0.0.tgz", + "integrity": "sha512-83apNb8KK0Se60UE1+4Ukbe3HbfELJ6UlI4ldtOGs7So4KD26orJM8hIY9lxdzP+UpItH1Yh/Y8GUvNFWFFRxA==", + "dev": true + }, + "builtin-modules": { + "version": "1.1.1", + "resolved": "https://nexus.loafle.net/repository/npm-all/builtin-modules/-/builtin-modules-1.1.1.tgz", + "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", + "dev": true + }, + "center-align": { + "version": "0.1.3", + "resolved": "https://nexus.loafle.net/repository/npm-all/center-align/-/center-align-0.1.3.tgz", + "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", + "dev": true, + "optional": true, + "requires": { + "align-text": "0.1.4", + "lazy-cache": "1.0.4" + } + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://nexus.loafle.net/repository/npm-all/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + } + }, + "color-convert": { + "version": "1.9.1", + "resolved": "https://nexus.loafle.net/repository/npm-all/color-convert/-/color-convert-1.9.1.tgz", + "integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://nexus.loafle.net/repository/npm-all/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "commander": { + "version": "2.15.1", + "resolved": "https://nexus.loafle.net/repository/npm-all/commander/-/commander-2.15.1.tgz", + "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://nexus.loafle.net/repository/npm-all/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://nexus.loafle.net/repository/npm-all/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true, + "optional": true + }, + "diff": { + "version": "3.5.0", + "resolved": "https://nexus.loafle.net/repository/npm-all/diff/-/diff-3.5.0.tgz", + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", + "dev": true + }, + "doctrine": { + "version": "0.7.2", + "resolved": "https://nexus.loafle.net/repository/npm-all/doctrine/-/doctrine-0.7.2.tgz", + "integrity": "sha1-fLhgNZujvpDgQLJrcpzkv6ZUxSM=", + "dev": true, + "requires": { + "esutils": "1.1.6", + "isarray": "0.0.1" + }, + "dependencies": { + "esutils": { + "version": "1.1.6", + "resolved": "https://nexus.loafle.net/repository/npm-all/esutils/-/esutils-1.1.6.tgz", + "integrity": "sha1-wBzKqa5LiXxtDD4hCuUvPHqEQ3U=", + "dev": true + }, + "isarray": { + "version": "0.0.1", + "resolved": "https://nexus.loafle.net/repository/npm-all/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + } + } + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://nexus.loafle.net/repository/npm-all/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "esutils": { + "version": "2.0.2", + "resolved": "https://nexus.loafle.net/repository/npm-all/esutils/-/esutils-2.0.2.tgz", + "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", + "dev": true + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://nexus.loafle.net/repository/npm-all/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "glob": { + "version": "7.1.2", + "resolved": "https://nexus.loafle.net/repository/npm-all/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "dev": true, + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } + }, + "graceful-fs": { + "version": "4.1.11", + "resolved": "https://nexus.loafle.net/repository/npm-all/graceful-fs/-/graceful-fs-4.1.11.tgz", + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", + "dev": true + }, + "handlebars": { + "version": "4.0.11", + "resolved": "https://nexus.loafle.net/repository/npm-all/handlebars/-/handlebars-4.0.11.tgz", + "integrity": "sha1-Ywo13+ApS8KB7a5v/F0yn8eYLcw=", + "dev": true, + "requires": { + "async": "1.5.2", + "optimist": "0.6.1", + "source-map": "0.4.4", + "uglify-js": "2.8.29" + }, + "dependencies": { + "source-map": { + "version": "0.4.4", + "resolved": "https://nexus.loafle.net/repository/npm-all/source-map/-/source-map-0.4.4.tgz", + "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", + "dev": true, + "requires": { + "amdefine": "1.0.1" + } + } + } + }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://nexus.loafle.net/repository/npm-all/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "dev": true, + "requires": { + "ansi-regex": "2.1.1" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://nexus.loafle.net/repository/npm-all/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "highlight.js": { + "version": "9.12.0", + "resolved": "https://nexus.loafle.net/repository/npm-all/highlight.js/-/highlight.js-9.12.0.tgz", + "integrity": "sha1-5tnb5Xy+/mB1HwKvM2GVhwyQwB4=", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://nexus.loafle.net/repository/npm-all/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "1.4.0", + "wrappy": "1.0.2" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://nexus.loafle.net/repository/npm-all/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "interpret": { + "version": "1.1.0", + "resolved": "https://nexus.loafle.net/repository/npm-all/interpret/-/interpret-1.1.0.tgz", + "integrity": "sha1-ftGxQQxqDg94z5XTuEQMY/eLhhQ=", + "dev": true + }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://nexus.loafle.net/repository/npm-all/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "jasmine-core": { + "version": "2.8.0", + "resolved": "https://nexus.loafle.net/repository/npm-all/jasmine-core/-/jasmine-core-2.8.0.tgz", + "integrity": "sha1-vMl5rh+f0FcB5F5S5l06XWPxok4=", + "dev": true + }, + "js-tokens": { + "version": "3.0.2", + "resolved": "https://nexus.loafle.net/repository/npm-all/js-tokens/-/js-tokens-3.0.2.tgz", + "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", + "dev": true + }, + "js-yaml": { + "version": "3.11.0", + "resolved": "https://nexus.loafle.net/repository/npm-all/js-yaml/-/js-yaml-3.11.0.tgz", + "integrity": "sha512-saJstZWv7oNeOyBh3+Dx1qWzhW0+e6/8eDzo7p5rDFqxntSztloLtuKu+Ejhtq82jsilwOIZYsCz+lIjthg1Hw==", + "dev": true, + "requires": { + "argparse": "1.0.10", + "esprima": "4.0.0" + }, + "dependencies": { + "esprima": { + "version": "4.0.0", + "resolved": "https://nexus.loafle.net/repository/npm-all/esprima/-/esprima-4.0.0.tgz", + "integrity": "sha512-oftTcaMu/EGrEIu904mWteKIv8vMuOgGYo7EhVJJN00R/EED9DCua/xxHRdYnKtcECzVg7xOWhflvJMnqcFZjw==", + "dev": true + } + } + }, + "jsonfile": { + "version": "4.0.0", + "resolved": "https://nexus.loafle.net/repository/npm-all/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "dev": true, + "requires": { + "graceful-fs": "4.1.11" + } + }, + "lazy-cache": { + "version": "1.0.4", + "resolved": "https://nexus.loafle.net/repository/npm-all/lazy-cache/-/lazy-cache-1.0.4.tgz", + "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=", + "dev": true, + "optional": true + }, + "lodash": { + "version": "4.17.10", + "resolved": "https://nexus.loafle.net/repository/npm-all/lodash/-/lodash-4.17.10.tgz", + "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==", + "dev": true + }, + "longest": { + "version": "1.0.1", + "resolved": "https://nexus.loafle.net/repository/npm-all/longest/-/longest-1.0.1.tgz", + "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=", + "dev": true + }, + "make-error": { + "version": "1.3.4", + "resolved": "https://nexus.loafle.net/repository/npm-all/make-error/-/make-error-1.3.4.tgz", + "integrity": "sha512-0Dab5btKVPhibSalc9QGXb559ED7G7iLjFXBaj9Wq8O3vorueR5K5jaE3hkG6ZQINyhA/JgG6Qk4qdFQjsYV6g==", + "dev": true + }, + "marked": { + "version": "0.3.19", + "resolved": "https://nexus.loafle.net/repository/npm-all/marked/-/marked-0.3.19.tgz", + "integrity": "sha512-ea2eGWOqNxPcXv8dyERdSr/6FmzvWwzjMxpfGB/sbMccXoct+xY+YukPD+QTUZwyvK7BZwcr4m21WBOW41pAkg==", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://nexus.loafle.net/repository/npm-all/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "1.1.11" + } + }, + "minimist": { + "version": "0.0.8", + "resolved": "https://nexus.loafle.net/repository/npm-all/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "dev": true + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://nexus.loafle.net/repository/npm-all/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "dev": true, + "requires": { + "minimist": "0.0.8" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://nexus.loafle.net/repository/npm-all/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1.0.2" + } + }, + "optimist": { + "version": "0.6.1", + "resolved": "https://nexus.loafle.net/repository/npm-all/optimist/-/optimist-0.6.1.tgz", + "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", + "dev": true, + "requires": { + "minimist": "0.0.8", + "wordwrap": "0.0.3" + }, + "dependencies": { + "wordwrap": { + "version": "0.0.3", + "resolved": "https://nexus.loafle.net/repository/npm-all/wordwrap/-/wordwrap-0.0.3.tgz", + "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=", + "dev": true + } + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://nexus.loafle.net/repository/npm-all/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-parse": { + "version": "1.0.5", + "resolved": "https://nexus.loafle.net/repository/npm-all/path-parse/-/path-parse-1.0.5.tgz", + "integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME=", + "dev": true + }, + "prettier": { + "version": "1.12.1", + "resolved": "https://nexus.loafle.net/repository/npm-all/prettier/-/prettier-1.12.1.tgz", + "integrity": "sha1-wa0g6APndJ+vkFpAnSNn4Gu+cyU=", + "dev": true + }, + "progress": { + "version": "2.0.0", + "resolved": "https://nexus.loafle.net/repository/npm-all/progress/-/progress-2.0.0.tgz", + "integrity": "sha1-ihvjZr+Pwj2yvSPxDG/pILQ4nR8=", + "dev": true + }, + "rechoir": { + "version": "0.6.2", + "resolved": "https://nexus.loafle.net/repository/npm-all/rechoir/-/rechoir-0.6.2.tgz", + "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", + "dev": true, + "requires": { + "resolve": "1.7.1" + } + }, + "reflect-metadata": { + "version": "0.1.12", + "resolved": "https://nexus.loafle.net/repository/npm-all/reflect-metadata/-/reflect-metadata-0.1.12.tgz", + "integrity": "sha512-n+IyV+nGz3+0q3/Yf1ra12KpCyi001bi4XFxSjbiWWjfqb52iTTtpGXmCCAOWWIAn9KEuFZKGqBERHmrtScZ3A==" + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "https://nexus.loafle.net/repository/npm-all/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", + "dev": true + }, + "resolve": { + "version": "1.7.1", + "resolved": "https://nexus.loafle.net/repository/npm-all/resolve/-/resolve-1.7.1.tgz", + "integrity": "sha512-c7rwLofp8g1U+h1KNyHL/jicrKg1Ek4q+Lr33AL65uZTinUZHe30D5HlyN5V9NW0JX1D5dXQ4jqW5l7Sy/kGfw==", + "dev": true, + "requires": { + "path-parse": "1.0.5" + } + }, + "right-align": { + "version": "0.1.3", + "resolved": "https://nexus.loafle.net/repository/npm-all/right-align/-/right-align-0.1.3.tgz", + "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=", + "dev": true, + "optional": true, + "requires": { + "align-text": "0.1.4" + } + }, + "rimraf": { + "version": "2.6.2", + "resolved": "https://nexus.loafle.net/repository/npm-all/rimraf/-/rimraf-2.6.2.tgz", + "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", + "dev": true, + "requires": { + "glob": "7.1.2" + } + }, + "rxjs": { + "version": "5.5.10", + "resolved": "https://nexus.loafle.net/repository/npm-all/rxjs/-/rxjs-5.5.10.tgz", + "integrity": "sha512-SRjimIDUHJkon+2hFo7xnvNC4ZEHGzCRwh9P7nzX3zPkCGFEg/tuElrNR7L/rZMagnK2JeH2jQwPRpmyXyLB6A==", + "requires": { + "symbol-observable": "1.0.1" + } + }, + "semver": { + "version": "5.5.0", + "resolved": "https://nexus.loafle.net/repository/npm-all/semver/-/semver-5.5.0.tgz", + "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==", + "dev": true + }, + "shelljs": { + "version": "0.8.2", + "resolved": "https://nexus.loafle.net/repository/npm-all/shelljs/-/shelljs-0.8.2.tgz", + "integrity": "sha512-pRXeNrCA2Wd9itwhvLp5LZQvPJ0wU6bcjaTMywHHGX5XWhVN2nzSu7WV0q+oUY7mGK3mgSkDDzP3MgjqdyIgbQ==", + "dev": true, + "requires": { + "glob": "7.1.2", + "interpret": "1.1.0", + "rechoir": "0.6.2" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://nexus.loafle.net/repository/npm-all/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true, + "optional": true + }, + "source-map-support": { + "version": "0.5.6", + "resolved": "https://nexus.loafle.net/repository/npm-all/source-map-support/-/source-map-support-0.5.6.tgz", + "integrity": "sha512-N4KXEz7jcKqPf2b2vZF11lQIz9W5ZMuUcIOGj243lduidkf2fjkVKJS9vNxVWn3u/uxX38AcE8U9nnH9FPcq+g==", + "dev": true, + "requires": { + "buffer-from": "1.0.0", + "source-map": "0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://nexus.loafle.net/repository/npm-all/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://nexus.loafle.net/repository/npm-all/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://nexus.loafle.net/repository/npm-all/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "2.1.1" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://nexus.loafle.net/repository/npm-all/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + }, + "symbol-observable": { + "version": "1.0.1", + "resolved": "https://nexus.loafle.net/repository/npm-all/symbol-observable/-/symbol-observable-1.0.1.tgz", + "integrity": "sha1-g0D8RwLDEi310iKI+IKD9RPT/dQ=" + }, + "ts-node": { + "version": "6.0.3", + "resolved": "https://nexus.loafle.net/repository/npm-all/ts-node/-/ts-node-6.0.3.tgz", + "integrity": "sha512-ARaOMNFEPKg2ZuC1qJddFvHxHNFVckR0g9xLxMIoMqSSIkDc8iS4/LoV53EdDWWNq2FGwqcEf0bVVGJIWpsznw==", + "dev": true, + "requires": { + "arrify": "1.0.1", + "chalk": "2.4.1", + "diff": "3.5.0", + "make-error": "1.3.4", + "minimist": "1.2.0", + "mkdirp": "0.5.1", + "source-map-support": "0.5.6", + "yn": "2.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://nexus.loafle.net/repository/npm-all/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "1.9.1" + } + }, + "chalk": { + "version": "2.4.1", + "resolved": "https://nexus.loafle.net/repository/npm-all/chalk/-/chalk-2.4.1.tgz", + "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", + "dev": true, + "requires": { + "ansi-styles": "3.2.1", + "escape-string-regexp": "1.0.5", + "supports-color": "5.4.0" + } + }, + "minimist": { + "version": "1.2.0", + "resolved": "https://nexus.loafle.net/repository/npm-all/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + }, + "supports-color": { + "version": "5.4.0", + "resolved": "https://nexus.loafle.net/repository/npm-all/supports-color/-/supports-color-5.4.0.tgz", + "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", + "dev": true, + "requires": { + "has-flag": "3.0.0" + } + } + } + }, + "tslib": { + "version": "1.9.1", + "resolved": "https://nexus.loafle.net/repository/npm-all/tslib/-/tslib-1.9.1.tgz", + "integrity": "sha512-avfPS28HmGLLc2o4elcc2EIq2FcH++Yo5YxpBZi9Yw93BCTGFthI4HPE4Rpep6vSYQaK8e69PelM44tPj+RaQg==" + }, + "tslint": { + "version": "5.10.0", + "resolved": "https://nexus.loafle.net/repository/npm-all/tslint/-/tslint-5.10.0.tgz", + "integrity": "sha1-EeJrzLiK+gLdDZlWyuPUVAtfVMM=", + "dev": true, + "requires": { + "babel-code-frame": "6.26.0", + "builtin-modules": "1.1.1", + "chalk": "2.4.1", + "commander": "2.15.1", + "diff": "3.5.0", + "glob": "7.1.2", + "js-yaml": "3.11.0", + "minimatch": "3.0.4", + "resolve": "1.7.1", + "semver": "5.5.0", + "tslib": "1.9.1", + "tsutils": "2.27.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://nexus.loafle.net/repository/npm-all/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "1.9.1" + } + }, + "chalk": { + "version": "2.4.1", + "resolved": "https://nexus.loafle.net/repository/npm-all/chalk/-/chalk-2.4.1.tgz", + "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", + "dev": true, + "requires": { + "ansi-styles": "3.2.1", + "escape-string-regexp": "1.0.5", + "supports-color": "5.4.0" + } + }, + "supports-color": { + "version": "5.4.0", + "resolved": "https://nexus.loafle.net/repository/npm-all/supports-color/-/supports-color-5.4.0.tgz", + "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", + "dev": true, + "requires": { + "has-flag": "3.0.0" + } + } + } + }, + "tslint-config-prettier": { + "version": "1.12.0", + "resolved": "https://nexus.loafle.net/repository/npm-all/tslint-config-prettier/-/tslint-config-prettier-1.12.0.tgz", + "integrity": "sha512-7zugK8NWpoDPYT6UNGLDGpQOhk0CSodjkyrTNiHOCjwIAleYKlyQunxpsSXBIoGEs/kFVppd6YzZeQZtrJnyRg==", + "dev": true + }, + "tslint-config-standard": { + "version": "7.0.0", + "resolved": "https://nexus.loafle.net/repository/npm-all/tslint-config-standard/-/tslint-config-standard-7.0.0.tgz", + "integrity": "sha512-QCrLt8WwiRgZpRSgRsk6cExy8/Vipa/5fHespm4Q1ly90EB6Lni04Ub8dkEW10bV3fPN3SkxEwj41ZOe/knCZA==", + "dev": true, + "requires": { + "tslint-eslint-rules": "4.1.1" + } + }, + "tslint-eslint-rules": { + "version": "4.1.1", + "resolved": "https://nexus.loafle.net/repository/npm-all/tslint-eslint-rules/-/tslint-eslint-rules-4.1.1.tgz", + "integrity": "sha1-fDDniC8mvCdr/5HSOEl1xp2viLo=", + "dev": true, + "requires": { + "doctrine": "0.7.2", + "tslib": "1.9.1", + "tsutils": "1.9.1" + }, + "dependencies": { + "tsutils": { + "version": "1.9.1", + "resolved": "https://nexus.loafle.net/repository/npm-all/tsutils/-/tsutils-1.9.1.tgz", + "integrity": "sha1-ufmrROVa+WgYMdXyjQrur1x1DLA=", + "dev": true + } + } + }, + "tsutils": { + "version": "2.27.0", + "resolved": "https://nexus.loafle.net/repository/npm-all/tsutils/-/tsutils-2.27.0.tgz", + "integrity": "sha512-JcyX25oM9pFcb3zh60OqG1St8p/uSqC5Bgipdo3ieacB/Ao4dPhm7hAtKT9NrEu23CyYrrgJPV3CqYfo+/+T4w==", + "dev": true, + "requires": { + "tslib": "1.9.1" + } + }, + "typedoc": { + "version": "0.11.1", + "resolved": "https://nexus.loafle.net/repository/npm-all/typedoc/-/typedoc-0.11.1.tgz", + "integrity": "sha512-jdNIoHm5wkZqxQTe/g9AQ3LKnZyrzHXqu6A/c9GUOeJyBWLxNr7/Dm3rwFvLksuxRNwTvY/0HRDU9sJTa9WQSg==", + "dev": true, + "requires": { + "@types/fs-extra": "5.0.1", + "@types/handlebars": "4.0.36", + "@types/highlight.js": "9.12.2", + "@types/lodash": "4.14.104", + "@types/marked": "0.3.0", + "@types/minimatch": "3.0.3", + "@types/shelljs": "0.7.8", + "fs-extra": "5.0.0", + "handlebars": "4.0.11", + "highlight.js": "9.12.0", + "lodash": "4.17.10", + "marked": "0.3.19", + "minimatch": "3.0.4", + "progress": "2.0.0", + "shelljs": "0.8.2", + "typedoc-default-themes": "0.5.0", + "typescript": "2.7.2" + }, + "dependencies": { + "fs-extra": { + "version": "5.0.0", + "resolved": "https://nexus.loafle.net/repository/npm-all/fs-extra/-/fs-extra-5.0.0.tgz", + "integrity": "sha512-66Pm4RYbjzdyeuqudYqhFiNBbCIuI9kgRqLPSHIlXHidW8NIQtVdkM1yeZ4lXwuhbTETv3EUGMNHAAw6hiundQ==", + "dev": true, + "requires": { + "graceful-fs": "4.1.11", + "jsonfile": "4.0.0", + "universalify": "0.1.1" + } + }, + "typescript": { + "version": "2.7.2", + "resolved": "https://nexus.loafle.net/repository/npm-all/typescript/-/typescript-2.7.2.tgz", + "integrity": "sha512-p5TCYZDAO0m4G344hD+wx/LATebLWZNkkh2asWUFqSsD2OrDNhbAHuSjobrmsUmdzjJjEeZVU9g1h3O6vpstnw==", + "dev": true + } + } + }, + "typedoc-default-themes": { + "version": "0.5.0", + "resolved": "https://nexus.loafle.net/repository/npm-all/typedoc-default-themes/-/typedoc-default-themes-0.5.0.tgz", + "integrity": "sha1-bcJDPnjti+qOiHo6zeLzF4W9Yic=", + "dev": true + }, + "typescript": { + "version": "2.8.3", + "resolved": "https://nexus.loafle.net/repository/npm-all/typescript/-/typescript-2.8.3.tgz", + "integrity": "sha512-K7g15Bb6Ra4lKf7Iq2l/I5/En+hLIHmxWZGq3D4DIRNFxMNV6j2SHSvDOqs2tGd4UvD/fJvrwopzQXjLrT7Itw==", + "dev": true + }, + "uglify-js": { + "version": "2.8.29", + "resolved": "https://nexus.loafle.net/repository/npm-all/uglify-js/-/uglify-js-2.8.29.tgz", + "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", + "dev": true, + "optional": true, + "requires": { + "source-map": "0.5.7", + "uglify-to-browserify": "1.0.2", + "yargs": "3.10.0" + }, + "dependencies": { + "camelcase": { + "version": "1.2.1", + "resolved": "https://nexus.loafle.net/repository/npm-all/camelcase/-/camelcase-1.2.1.tgz", + "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=", + "dev": true, + "optional": true + }, + "cliui": { + "version": "2.1.0", + "resolved": "https://nexus.loafle.net/repository/npm-all/cliui/-/cliui-2.1.0.tgz", + "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", + "dev": true, + "optional": true, + "requires": { + "center-align": "0.1.3", + "right-align": "0.1.3", + "wordwrap": "0.0.2" + } + }, + "wordwrap": { + "version": "0.0.2", + "resolved": "https://nexus.loafle.net/repository/npm-all/wordwrap/-/wordwrap-0.0.2.tgz", + "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=", + "dev": true, + "optional": true + }, + "yargs": { + "version": "3.10.0", + "resolved": "https://nexus.loafle.net/repository/npm-all/yargs/-/yargs-3.10.0.tgz", + "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", + "dev": true, + "optional": true, + "requires": { + "camelcase": "1.2.1", + "cliui": "2.1.0", + "decamelize": "1.2.0", + "window-size": "0.1.0" + } + } + } + }, + "uglify-to-browserify": { + "version": "1.0.2", + "resolved": "https://nexus.loafle.net/repository/npm-all/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", + "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=", + "dev": true, + "optional": true + }, + "universalify": { + "version": "0.1.1", + "resolved": "https://nexus.loafle.net/repository/npm-all/universalify/-/universalify-0.1.1.tgz", + "integrity": "sha1-+nG63UQ3r0wUiEHjs7Fl+enlkLc=", + "dev": true + }, + "window-size": { + "version": "0.1.0", + "resolved": "https://nexus.loafle.net/repository/npm-all/window-size/-/window-size-0.1.0.tgz", + "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=", + "dev": true, + "optional": true + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://nexus.loafle.net/repository/npm-all/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "yn": { + "version": "2.0.0", + "resolved": "https://nexus.loafle.net/repository/npm-all/yn/-/yn-2.0.0.tgz", + "integrity": "sha1-5a2ryKz0CPY4X8dklWhMiOavaJo=", + "dev": true + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..da20d46 --- /dev/null +++ b/package.json @@ -0,0 +1,45 @@ +{ + "name": "@loafer/ng-rpc", + "description": "Angular RPC", + "version": "0.0.1", + "license": "MIT", + "private": true, + "main": "./index.js", + "typings": "./index.d.ts", + "repository": { + "type": "git", + "url": "https://git.loafle.net/loafer/typescript/ng-rpc.git" + }, + "publishConfig": { + "registry": "https://nexus.loafle.net/repository/npm-loafle/" + }, + "keywords": [], + "author": "Loafle ", + "scripts": { + "generate:package": "node .make-package.js", + "generate:doc": "rimraf docs && typedoc --out docs --target es6 --theme minimal --mode file src", + "bundle": "rimraf dist && tsc", + "build": "npm run bundle && npm run generate:package && npm run generate:doc" + }, + "dependencies": { + "@angular/common": "^5.2.9", + "@angular/core": "^5.2.9", + "@loafer/core": "^0.0.1", + "@loafer/decorator": "^0.0.1", + "@ngrx/store": "^5.2.0", + "rxjs": "^5.5.8" + }, + "devDependencies": { + "@types/jasmine": "~2.8.5", + "@types/node": "^9.3.0", + "jasmine-core": "~2.8.0", + "prettier": "^1.12.1", + "rimraf": "^2.6.2", + "ts-node": "^6.0.3", + "tslint": "^5.10.0", + "tslint-config-prettier": "^1.12.0", + "tslint-config-standard": "^7.0.0", + "typedoc": "^0.11.1", + "typescript": "^2.8.3" + } +} diff --git a/src/client/RPCClient.ts b/src/client/RPCClient.ts new file mode 100644 index 0000000..bc353c2 --- /dev/null +++ b/src/client/RPCClient.ts @@ -0,0 +1,146 @@ +import { Observable } from 'rxjs/Observable'; +import { Subject } from 'rxjs/Subject'; + +import { RPCClientError } from '../protocol/RPCError'; +import { RPCClientRWC } from './RPCClientRWC'; + +import { + RPCClientCodec, + RPCClientResponseCodec, + RPCClientNotificationCodec, +} from '../protocol/RPCClientCodec'; + +export interface RPCRequestState { + subject: Subject; + request: { + method: string; + params: any[]; + }; +} + +export abstract class RPCClient { + private _requestID: number; + + private _pendingRequestsCount: number; + private _pendingRequests: Map; + + public constructor( + private _codec: RPCClientCodec, + private _rwc: RPCClientRWC, + ) { + this._requestID = 0; + this._pendingRequestsCount = 0; + this._pendingRequests = new Map(); + } + + private getRequestID(): number { + return ++this._requestID; + } + + /** + * connect + */ + public connect(queryString?: string): void { + this._rwc.connect(queryString); + this._rwc.readResponse().subscribe( + (value: Object) => { + this.onMessage(value); + }, + (error: any) => { + console.error(error); + }, + () => { + + } + ); + } + + /** + * close + */ + public disconnect() { + this._rwc.disconnect(); + } + + /** + * notify + */ + public send(method: string, ...args: any[]): void { + this.sendInternal(false, method, args); + } + + /** + * call + */ + public call(method: string, ...args: any[]): Observable { + return this.sendInternal(true, method, args); + } + + /** + * callTimeout + */ + public callTimeout(ms: number, method: string, ...args: any[]): Observable { + + return undefined; + } + + private sendInternal(hasResponse: boolean, method: string, args?: any[]): Observable | undefined { + let id: number; + let resSubject: Subject; + if (hasResponse) { + id = this.getRequestID(); + resSubject = new Subject(); + const reqState: RPCRequestState = { + subject: resSubject, + request: { + method: method, + params: args, + } + }; + this._pendingRequests.set(id, reqState); + this._pendingRequestsCount++; + } + + const req = this._codec.request(method, args, id); + this._rwc.writeRequest(req); + + if (undefined !== resSubject) { + return resSubject.asObservable(); + } + return undefined; + } + + private onMessage(message: Object): void { + const resCodec = this._codec.response(message); + + if (resCodec.isNotification()) { + this.onNotification(resCodec.notification()); + } else { + this.onResponse(resCodec); + } + } + + protected onResponse(resCodec: RPCClientResponseCodec): void { + const id = resCodec.id(); + const result = resCodec.result(); + const error = resCodec.error(); + + const reqState: RPCRequestState = this._pendingRequests.get(id); + + this._pendingRequests.delete(id); + this._pendingRequestsCount--; + + if (undefined !== result) { + reqState.subject.next(result); + } else if (undefined !== error) { + const rpcClientError: RPCClientError = { + request: reqState.request, + response: error, + }; + console.error(rpcClientError); + reqState.subject.error(rpcClientError); + } + } + + protected abstract onNotification(notiCodec: RPCClientNotificationCodec): void; +} diff --git a/src/client/RPCClientRWC.ts b/src/client/RPCClientRWC.ts new file mode 100644 index 0000000..406d4ee --- /dev/null +++ b/src/client/RPCClientRWC.ts @@ -0,0 +1,11 @@ +import { InjectionToken } from '@angular/core'; + +import { Observable } from 'rxjs/Observable'; + +export interface RPCClientRWC { + connect(queryString?: string): void; + readResponse(): Observable; + writeRequest(data: any): void; + disconnect(): void; + connectionStatus(): Observable; +} diff --git a/src/client/index.ts b/src/client/index.ts new file mode 100644 index 0000000..efdf231 --- /dev/null +++ b/src/client/index.ts @@ -0,0 +1,2 @@ +export * from './RPCClient'; +export * from './RPCClientRWC'; diff --git a/src/client/rwc/websocket/RPCClientWebsocketRWC.ts b/src/client/rwc/websocket/RPCClientWebsocketRWC.ts new file mode 100644 index 0000000..f2d0849 --- /dev/null +++ b/src/client/rwc/websocket/RPCClientWebsocketRWC.ts @@ -0,0 +1,62 @@ +import { Observable } from 'rxjs/Observable'; +import { Subject } from 'rxjs/Subject'; +import { map } from 'rxjs/operator/map'; + +import { + RxWebsocketSubject, + RxWebsocketSubjectConfig, +} from './RxWebsocketSubject'; + +import { RPCClientRWC } from '../../RPCClientRWC'; + +export class RPCClientWebsocketRWC implements RPCClientRWC { + private _wsSocketSubject: RxWebsocketSubject; + private _responseSubject: Subject; + + public constructor( + private _config: RxWebsocketSubjectConfig, + ) { + this._wsSocketSubject = new RxWebsocketSubject(this._config); + } + + public connect(queryString?: string): void { + if (undefined !== queryString) { + this._wsSocketSubject.queryString = queryString; + } + this._wsSocketSubject.connect(); + this._wsSocketSubject.subscribe( + (value: Object) => { + if (undefined !== this._responseSubject) { + this._responseSubject.next(value); + } + }, + (error: any) => { + if (undefined !== this._responseSubject) { + this._responseSubject.error(error); + } + }, + () => { + console.log('sss'); + } + ); + } + + public disconnect(): void { + this._wsSocketSubject.disconnect(); + } + + public connectionStatus(): Observable { + return this._wsSocketSubject.connectionStatus; + } + + public readResponse(): Observable { + if (undefined === this._responseSubject) { + this._responseSubject = new Subject(); + } + return this._responseSubject.asObservable(); + } + + public writeRequest(data: any): void { + this._wsSocketSubject.write(data); + } +} diff --git a/src/client/rwc/websocket/RxWebsocketSubject.ts b/src/client/rwc/websocket/RxWebsocketSubject.ts new file mode 100644 index 0000000..e0d1e39 --- /dev/null +++ b/src/client/rwc/websocket/RxWebsocketSubject.ts @@ -0,0 +1,117 @@ +import { Observable } from 'rxjs/Observable'; +import { Observer } from 'rxjs/Observer'; +import { Subject } from 'rxjs/Subject'; +import { + WebSocketSubject, + WebSocketSubjectConfig +} from 'rxjs/observable/dom/WebSocketSubject'; + +import 'rxjs/add/operator/distinctUntilChanged'; +import 'rxjs/add/operator/share'; +import 'rxjs/add/operator/takeWhile'; +import 'rxjs/add/observable/interval'; + +export interface RxWebsocketSubjectConfig { + url: string; + protocol?: string | Array; + reconnectInterval?: 5000; + reconnectRetry?: 10; +} + +export class RxWebsocketSubject extends Subject { + private _reconnectionObservable: Observable; + private _wsSubjectConfig: WebSocketSubjectConfig; + private _socket: WebSocketSubject; + private _connectionObserver: Observer; + private _connectionStatus: Observable; + private _queryString: string; + + public constructor(private _config: RxWebsocketSubjectConfig) { + super(); + + this._connectionStatus = new Observable((observer) => { + this._connectionObserver = observer; + }).share().distinctUntilChanged(); + + this._wsSubjectConfig = { + url: _config.url, + protocol: _config.protocol, + closeObserver: { + next: (e: CloseEvent) => { + this._socket = null; + this._connectionObserver.next(false); + } + }, + openObserver: { + next: (e: Event) => { + this._connectionObserver.next(true); + } + }, + }; + + this._connectionStatus.subscribe((isConnected: boolean) => { + if (!this._reconnectionObservable && typeof(isConnected) === 'boolean' && !isConnected) { + this.reconnect(); + } + }); + } + + public set queryString(query: string) { + this._queryString = query; + } + + public get queryString(): string | undefined { + return this._queryString; + } + + public get connectionStatus(): Observable { + return this._connectionStatus; + } + + public connect(): void { + const wsSubjectConfig = Object.assign({}, this._wsSubjectConfig); + if (undefined !== this._queryString) { + wsSubjectConfig.url = wsSubjectConfig.url + '?' + this._queryString; + } + + this._socket = new WebSocketSubject(wsSubjectConfig); + this._socket.subscribe( + (m) => { + this.next(m); + }, + (error: Event) => { + if (!this._socket) { + this.reconnect(); + } + } + ); + } + + public disconnect(): void { + this._socket.complete(); + } + + private reconnect(): void { + this._reconnectionObservable = Observable.interval(this._config.reconnectInterval) + .takeWhile((v, index) => { + return index < this._config.reconnectRetry && !this._socket; + }); + this._reconnectionObservable.subscribe( + () => { + this.connect(); + }, + null, + () => { + this._reconnectionObservable = null; + if (!this._socket) { + this.complete(); + this._connectionObserver.complete(); + } + } + ); + } + + public write(data: any): void { + this._socket.next(data); + } +} diff --git a/src/client/rwc/websocket/index.ts b/src/client/rwc/websocket/index.ts new file mode 100644 index 0000000..80de9d0 --- /dev/null +++ b/src/client/rwc/websocket/index.ts @@ -0,0 +1,2 @@ +export * from './RPCClientWebsocketRWC'; +export * from './RxWebsocketSubject'; diff --git a/src/core/error.ts b/src/core/error.ts new file mode 100644 index 0000000..f90aac7 --- /dev/null +++ b/src/core/error.ts @@ -0,0 +1,6 @@ +export class SubscriberParameterError extends Error { + public constructor(message?: string) { + super(message); + Object.setPrototypeOf(this, new.target.prototype); + } +} diff --git a/src/core/index.ts b/src/core/index.ts new file mode 100644 index 0000000..d97df57 --- /dev/null +++ b/src/core/index.ts @@ -0,0 +1,2 @@ +export * from './error'; +export * from './token'; diff --git a/src/core/token.ts b/src/core/token.ts new file mode 100644 index 0000000..fe8bd6e --- /dev/null +++ b/src/core/token.ts @@ -0,0 +1,8 @@ +import { InjectionToken } from '@angular/core'; + +export const RPC_CODEC = new InjectionToken('@loafer/ng-rpc RPC codec'); +export const RPC_RWC = new InjectionToken('@loafer/ng-rpc RPC rwc'); + +export const _ROOT_SUBSCRIBERS = new InjectionToken('@loafer/ng-rpc RPC root subscribers'); +export const _FEATURE_SUBSCRIBERS = new InjectionToken('@loafer/ng-rpc RPC feature subscribers'); + diff --git a/src/decorator/index.ts b/src/decorator/index.ts new file mode 100644 index 0000000..f75f0fd --- /dev/null +++ b/src/decorator/index.ts @@ -0,0 +1 @@ +export * from './rpc-subscriber.decorator'; diff --git a/src/decorator/rpc-subscriber.decorator.ts b/src/decorator/rpc-subscriber.decorator.ts new file mode 100644 index 0000000..8aeadf5 --- /dev/null +++ b/src/decorator/rpc-subscriber.decorator.ts @@ -0,0 +1,26 @@ +import { + Type, + PropertyKeyType, +} from '@loafer/core'; + +import { + Decorator, + DecoratorFactory, +} from '@loafer/decorator'; + +export interface RPCSubscriberDecoratorAttribute { + method: string; +} + +export class RPCSubscriberDecorator extends Decorator { + public constructor(config: RPCSubscriberDecoratorAttribute) { + super(config); + } + + public methodDecorator = (target: Object, propertyKey: PropertyKeyType, + descriptor: TypedPropertyDescriptor): TypedPropertyDescriptor | void => { + } + +} + +export const RPCSubscriber = DecoratorFactory.create(RPCSubscriberDecorator); diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..e96ec44 --- /dev/null +++ b/src/index.ts @@ -0,0 +1 @@ +export * from './ng-rpc.module'; diff --git a/src/ng-rpc.module.ts b/src/ng-rpc.module.ts new file mode 100644 index 0000000..34f2139 --- /dev/null +++ b/src/ng-rpc.module.ts @@ -0,0 +1,109 @@ +import { + NgModule, + ModuleWithProviders, + Type, + Inject, + InjectionToken, +} from '@angular/core'; + +import { + RPC_CODEC, + RPC_RWC, + + _ROOT_SUBSCRIBERS, + _FEATURE_SUBSCRIBERS, +} from './core'; + +import { + RPCClientRWC, +} from './client/RPCClientRWC'; + +import { + RPCClientCodec, +} from './protocol'; + +import { + SERVICES, RPCService, +} from './service'; + +import { + SUBSCRIBERS, RPCSubscribeService, +} from './subscribe'; + +export interface RPCFeatureModuleConfig { + subscribers?: Type[]; +} + +export interface RPCRootModuleConfig { + subscribers?: Type[]; +} + +@NgModule({}) +export class RPCRootModule { + constructor( + private rpcService: RPCService, + private rpcSubscribeService: RPCSubscribeService, + @Inject(_ROOT_SUBSCRIBERS) rootSubscribers: any[], + ) { + rootSubscribers.forEach((subscriber) => { + rpcSubscribeService.addSubscriber(subscriber); + }); + } +} + +@NgModule({}) +export class RPCFeatureModule { + constructor( + private rpcService: RPCService, + private rpcSubscribeService: RPCSubscribeService, + @Inject(_FEATURE_SUBSCRIBERS) featureSubscribersGroups: any[][], + private root: RPCRootModule, + ) { + featureSubscribersGroups.forEach((featureSubscribers) => { + featureSubscribers.forEach((subscriber) => { + rpcSubscribeService.addSubscriber(subscriber); + }); + }); + } +} + +@NgModule({}) +export class RPCModule { + static forRoot(config: RPCRootModuleConfig): ModuleWithProviders { + const subscribers = undefined === config.subscribers ? [] : config.subscribers; + return { + ngModule: RPCRootModule, + providers: [ + subscribers, + { + provide: _ROOT_SUBSCRIBERS, + deps: subscribers, + useFactory: createSourceInstances, + }, + SERVICES, + SUBSCRIBERS, + ], + }; + } + + static forFeature(config: RPCFeatureModuleConfig): ModuleWithProviders { + const subscribers = undefined === config.subscribers ? [] : config.subscribers; + + return { + ngModule: RPCFeatureModule, + providers: [ + subscribers, + { + provide: _FEATURE_SUBSCRIBERS, + multi: true, + deps: subscribers, + useFactory: createSourceInstances, + }, + ], + }; + } +} + +export function createSourceInstances(...instances: any[]) { + return instances; +} diff --git a/src/protocol/RPCClientCodec.ts b/src/protocol/RPCClientCodec.ts new file mode 100644 index 0000000..94af99e --- /dev/null +++ b/src/protocol/RPCClientCodec.ts @@ -0,0 +1,20 @@ +import { RPCError } from './RPCError'; + +export interface RPCClientCodec { + request(method: string, args: any[], id: number): any; + response(res: any): RPCClientResponseCodec; +} + +export interface RPCClientResponseCodec { + id(): number | undefined; + error(): RPCError | undefined; + result(): any | undefined; + + isNotification(): boolean; + notification(): RPCClientNotificationCodec | undefined; +} + +export interface RPCClientNotificationCodec { + method(): string; + params(): any | undefined; +} diff --git a/src/protocol/RPCError.ts b/src/protocol/RPCError.ts new file mode 100644 index 0000000..91f4aac --- /dev/null +++ b/src/protocol/RPCError.ts @@ -0,0 +1,41 @@ +export interface RPCClientError { + request: { + method: string, + params: any[], + }; + response: RPCError; +} + +/** + * Error object representation when a method invocation fails. + */ +export interface RPCError { + /** Indicates the error type that occurred. */ + code: RPCErrorCode; + + /** A short description of the error. */ + message: string; + + /** Additional information about the error */ + data?: any; +}/* + +/** Error codes are same as xml-rpc codes. See http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php */ +export const enum RPCErrorCode { + /** Parse error Invalid JSON was received by the Server. */ + ParseError = -32700, + + /** Invalid Request The JSON sent is not a valid Request object. */ + InvalidRequest = -32600, + + /** The method does not exist / is not available. */ + MethodNotFound = -32601, + + /** Invalid method parameter(s). */ + InvalidParams = - -32602, + + /** Internal JSON-RPC error. */ + InternalError = -32603 + + /** -32000 to -32099: Reserved for implementation-defined Server errors. */ +} diff --git a/src/protocol/index.ts b/src/protocol/index.ts new file mode 100644 index 0000000..a9d3a36 --- /dev/null +++ b/src/protocol/index.ts @@ -0,0 +1,2 @@ +export * from './RPCClientCodec'; +export * from './RPCError'; diff --git a/src/protocol/json/JSONRPCClientCodec.ts b/src/protocol/json/JSONRPCClientCodec.ts new file mode 100644 index 0000000..27d3980 --- /dev/null +++ b/src/protocol/json/JSONRPCClientCodec.ts @@ -0,0 +1,125 @@ +import { + RPCClientCodec, + RPCClientResponseCodec, + RPCClientNotificationCodec, +} from '../RPCClientCodec'; + +import { + RPCError, +} from '../RPCError'; + +export interface ClientNotification { + method: string; + params?: string[]; +} + +export interface ClientRequest { + jsonrpc: string; + method: string; + params?: string[]; + id?: number; +} + +export interface ClientResponse { + jsonrpc: string; + result?: any; + error?: RPCError; + id?: number; +} + +export class JSONRPCClientCodec implements RPCClientCodec { + public request(method: string, args: any[], id?: number): any { + const params = convertParamsToStringArray(args); + + const req: ClientRequest = { + jsonrpc: '2.0', + method: method, + params: 0 === params.length ? null : params, + id: id, + }; + return JSON.stringify(req); + } + public response(res: any): RPCClientResponseCodec { + const _res: ClientResponse = { + id: res.id, + jsonrpc: res.jsonrpc, + result: res.result, + error: res.error, + }; + return new JSONRPCClientResponseCodec(_res); + } +} + +function convertParamsToStringArray(args: any[]): string[] | undefined { + const values: string[] = []; + + if (undefined === args || null === args || 0 === args.length) { + return values; + } + + for (let indexI = 0; indexI < args.length; indexI++) { + const arg = args[indexI]; + + switch (typeof arg) { + case 'boolean': + case 'number': // enum + values.push(String(arg)); + break; + case 'string': + values.push(arg); + break; + case 'object': // array, map + values.push(JSON.stringify(arg)); + break; + default: + throw new Error(`Not supported type[${typeof arg}]`); + } + } + + return values; +} + +export class JSONRPCClientResponseCodec implements RPCClientResponseCodec { + public constructor(private _res: ClientResponse) { + } + + public id(): number | undefined { + return this._res.id; + } + public error(): RPCError | undefined { + return this._res.error; + } + public result(): any | undefined { + return this._res.result; + } + + public isNotification(): boolean { + if (undefined !== this.id() || undefined === this.result()) { + return false; + } + return true; + } + + public notification(): RPCClientNotificationCodec | undefined { + if (undefined !== this.id() || undefined === this.result()) { + return undefined; + } + const _noti: ClientNotification = { + method: this._res.result.method, + params: this._res.result.params, + }; + return new JSONRPCClientNotificationCodec(_noti); + } +} + +export class JSONRPCClientNotificationCodec implements RPCClientNotificationCodec { + public constructor(private _noti: ClientNotification) { + } + + public method(): string { + return this._noti.method; + } + public params(): any[] | undefined { + return this._noti.params; + } +} diff --git a/src/protocol/json/index.ts b/src/protocol/json/index.ts new file mode 100644 index 0000000..2ef50ad --- /dev/null +++ b/src/protocol/json/index.ts @@ -0,0 +1 @@ +export * from './JSONRPCClientCodec'; diff --git a/src/service/index.ts b/src/service/index.ts new file mode 100644 index 0000000..18154a6 --- /dev/null +++ b/src/service/index.ts @@ -0,0 +1,7 @@ +export * from './rpc.service'; + +import { RPCService } from './rpc.service'; + +export const SERVICES = [ + RPCService, +]; diff --git a/src/service/rpc.service.ts b/src/service/rpc.service.ts new file mode 100644 index 0000000..b263dd6 --- /dev/null +++ b/src/service/rpc.service.ts @@ -0,0 +1,39 @@ +import { Injectable, Inject } from '@angular/core'; +import { Store, select } from '@ngrx/store'; + +import { Observable } from 'rxjs/Observable'; +import { Subject } from 'rxjs/Subject'; + +import { + RPC_CODEC, + RPC_RWC, +} from '../core'; + +import { + RPCClient, + RPCClientRWC, +} from '../client'; + +import { + RPCClientCodec, + RPCClientNotificationCodec, +} from '../protocol'; + +import { + RPCSubscribeService, +} from '../subscribe'; + +@Injectable() +export class RPCService extends RPCClient { + constructor( + @Inject(RPC_CODEC) rpcClientCodec: RPCClientCodec, + @Inject(RPC_RWC) rpcClientRWC: RPCClientRWC, + private rpcSubscribeService: RPCSubscribeService, + ) { + super(rpcClientCodec, rpcClientRWC); + } + + protected onNotification(notiCodec: RPCClientNotificationCodec): void { + this.rpcSubscribeService.notify(notiCodec); + } +} diff --git a/src/subscribe/index.ts b/src/subscribe/index.ts new file mode 100644 index 0000000..d877f8d --- /dev/null +++ b/src/subscribe/index.ts @@ -0,0 +1,7 @@ +export * from './subscribe.service'; + +import { RPCSubscribeService } from './subscribe.service'; + +export const SUBSCRIBERS = [ + RPCSubscribeService, +]; diff --git a/src/subscribe/subscribe.service.ts b/src/subscribe/subscribe.service.ts new file mode 100644 index 0000000..f1f7426 --- /dev/null +++ b/src/subscribe/subscribe.service.ts @@ -0,0 +1,161 @@ +import { Injectable, Inject } from '@angular/core'; +import { Store, select } from '@ngrx/store'; + +import { + Type, + PropertyKeyType, +} from '@loafer/core'; + +import { TypeUtil } from '@loafer/core/util/TypeUtil'; + +import { + Class, + Method, + Metadata, +} from '@loafer/core/reflect'; + +import { RPCSubscriberDecorator } from '../decorator'; + +import { + RPCClientNotificationCodec, +} from '../protocol'; +import { SubscriberParameterError } from '../core'; + +export interface SubscriberMethod { + className: PropertyKeyType; + methodName: PropertyKeyType; + parameterTypes: string[]; + + method: Method; + instance: any; +} + +@Injectable() +export class RPCSubscribeService { + private subscriberTypes: Set>; + private subscribers: Set; + private subscriberMethodMap: Map; + + constructor( + ) { + this.subscriberTypes = new Set(); + this.subscribers = new Set(); + this.subscriberMethodMap = new Map(); + } + + public addSubscriber(subscriber: Type): void { + const type = TypeUtil.getType(subscriber); + + if (this.subscriberTypes.has(type)) { + // console.log(`Subscriber[${type.name}] has been added`); + return; + } + + this.subscriberTypes.add(type); + + const clazz = Class.forType(type); + if (undefined === clazz) { + console.log(`Type[${subscriber.name}] is not decorated type`); + return; + } + + const methods = clazz.getMethods(); + methods.forEach((method, propertyKey) => { + const annon = method.getAnnotation(RPCSubscriberDecorator); + if (undefined === annon) { + return; + } + + const subscriberMethodName = annon.attribute.method; + let subscriberMethods: SubscriberMethod[] = this.subscriberMethodMap.get(subscriberMethodName); + if (undefined === subscriberMethods) { + subscriberMethods = []; + this.subscriberMethodMap.set(subscriberMethodName, subscriberMethods); + } + + const paramTypes = this.getParamTypes(method); + + const subscriberMethod: SubscriberMethod = { + className: clazz.getName(), + methodName: method.getName(), + parameterTypes: paramTypes, + method: method, + instance: subscriber, + }; + + subscriberMethods.push(subscriberMethod); + }); + } + + public notify(codec: RPCClientNotificationCodec): void { + const method = codec.method(); + const params = codec.params(); + + const subscriberMethods: SubscriberMethod[] = this.subscriberMethodMap.get(method); + if (undefined === subscriberMethods) { + console.warn(`Subscriber for method[${method}] is not exist`); + return; + } + subscriberMethods.forEach((subscriberMethod) => { + try { + const args = this.converParams(params, subscriberMethod.parameterTypes); + subscriberMethod.method.invoke(subscriberMethod.instance, ...args); + } catch (error) { + console.error(error); + } + }); + } + + private getParamTypes(method: Method): string[] { + if (undefined === method || null === method || 0 === method.getParameterCount()) { + return []; + } + + const parameters = method.getParameters(); + const results: string[] = []; + for (let indexI = 0; indexI < parameters.length; indexI++) { + const paramType = parameters[indexI].getType(); + results.push(paramType.name); + } + return results; + } + + private converParams(params: string[], paramTypes: string[]): any[] { + const results: any[] = []; + + if (undefined === params || null === params || 0 === params.length) { + return results; + } + if (undefined === paramTypes || null === paramTypes || 0 === paramTypes.length) { + return results; + } + if (params.length !== paramTypes.length) { + throw new SubscriberParameterError(`Count is not same from server[${params.length}] and method[${paramTypes.length}]`); + } + for (let indexI = 0; indexI < params.length; indexI++) { + const param = params[indexI]; + const type = paramTypes[indexI]; + switch (type) { + case 'Object': + case 'Array': + case 'Map': + results.push(JSON.parse(param)); + break; + case 'String': + results.push(param); + break; + case 'Number': + results.push(Number(param)); + break; + case 'Boolean': + results.push(Boolean(param)); + break; + case 'Function': + throw new SubscriberParameterError(`Function type [${indexI}] is not allowed`); + default: + throw new SubscriberParameterError(`${type} type parameter[${indexI}] is not allowed`); + } + } + return results; + } +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..5f83597 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,45 @@ +{ + "compileOnSave": false, + "compilerOptions": { + "baseUrl": "./src", + "outDir": "./dist", + "sourceMap": true, + "declaration": true, + "newLine": "LF", + "moduleResolution": "node", + + /* Strict Type-Checking Options */ + // "noImplicitAny": true /* Raise error on expressions and declarations with an implied 'any' type. */, + // "strictNullChecks": true /* Enable strict null checks. */, + // "strictFunctionTypes": true /* Enable strict checking of function types. */, + // "strictPropertyInitialization": true /* Enable strict checking of property initialization in classes. */, + // "noImplicitThis": true /* Raise error on 'this' expressions with an implied 'any' type. */, + // "alwaysStrict": true /* Parse in strict mode and emit "use strict" for each source file. */, + + /* Additional Checks */ + // "noUnusedLocals": true /* Report errors on unused locals. */, + // "noUnusedParameters": true /* Report errors on unused parameters. */, + "noImplicitReturns": true /* Report error when not all code paths in function return a value. */, + "noFallthroughCasesInSwitch": true /* Report errors for fallthrough cases in switch statement. */, + + /* Debugging Options */ + "traceResolution": false /* Report module resolution log messages. */, + "listEmittedFiles": false /* Print names of generated files part of the compilation. */, + "listFiles": false /* Print names of files part of the compilation. */, + "pretty": true /* Stylize errors and messages using color and context. */, + + /* Experimental Options */ + "experimentalDecorators": true /* Enables experimental support for ES7 decorators. */, + "emitDecoratorMetadata": true /* Enables experimental support for emitting type metadata for decorators. */, + + "target": "es5", + "typeRoots": [ + "node_modules/@types" + ], + "types": [ + ], + "lib": [ + "es2015", "es2016", "es2017", "dom" + ] + } +} diff --git a/tslint.json b/tslint.json new file mode 100644 index 0000000..b954a5d --- /dev/null +++ b/tslint.json @@ -0,0 +1,34 @@ +{ + "extends": ["tslint:latest", "tslint-config-prettier", "tslint-immutable"], + "rules": { + "interface-name": [true, "never-prefix"], + // TODO: allow devDependencies only in **/*.spec.ts files: + // waiting on https://github.com/palantir/tslint/pull/3708 + "no-implicit-dependencies": [true, "dev"], + + /* tslint-immutable rules */ + // Recommended built-in rules + "no-var-keyword": true, + "no-parameter-reassignment": true, + "typedef": [true, "call-signature"], + + // Immutability rules + "readonly-keyword": true, + "readonly-array": true, + "no-let": true, + "no-object-mutation": true, + "no-delete": true, + "no-method-signature": true, + + // Functional style rules + "no-this": true, + "no-class": true, + "no-mixed-interface": true, + "no-expression-statement": [ + true, + { "ignore-prefix": ["console.", "process.exit"] } + ], + "no-if-statement": true + /* end tslint-immutable rules */ + } +}