diff --git a/.gitignore b/.gitignore index 5c52516b725390e00723198b0b05a8e4c2b0360d..38607d4c434f1c491f4182693e0dd4c6f0a0c1c6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,10 @@ +/.nyc_output +/.dacap-tmp +/coverage +/data +/lib /node_modules -/dist /public -/data /site -/coverage +/test *.tgz \ No newline at end of file diff --git a/.nycrc b/.nycrc new file mode 100644 index 0000000000000000000000000000000000000000..3a8d9d5914ddcf6592afcb43a37bfe3e5dbff2f3 --- /dev/null +++ b/.nycrc @@ -0,0 +1,11 @@ +{ + "reporter": [ + "html", + "text-summary", + "lcovonly" + ], + "cache": true, + "sourceMap": true, + "instrument": true + +} \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json index 440ff2558529aea963d4c893547db23fd003a112..4e98be11fd8de813b8a92e1c34fc1486bba8650f 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,6 +1,7 @@ { "version": "0.2.0", "configurations": [ + { "type": "chrome", "request": "attach", diff --git a/bin/dacap b/bin/dacap index 3365fbde8279c33a638bf9c1fb84a239e56a3aef..851684a6fa4ca09f05435d7477eeb3335612eebb 100755 --- a/bin/dacap +++ b/bin/dacap @@ -3,7 +3,7 @@ const debug = require('debug')('dacap'); const pjson = require('../package.json'); const path = require('path'); -const Server = require('../dist/server').Server; +const Server = require('../lib/server').Server; const storagePath = process.env.data_dir || path.resolve(process.cwd(), 'data'); const proxyPath = process.env.proxy_path || '/ep/'; diff --git a/docker-compose.yml b/docker-compose.yml index 98fb24b7d7b5b32686b3a68c2b7043fd41940f4e..aba8b7a2e485e52c464386009a83fa92322943da 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -5,27 +5,15 @@ services: volumes: - ./:/app:z - npm-cache:/home/dev/.npm:z - - app-data:/var/lib/app:z ports: - 3000:3000 - 127.0.0.1:9229:9229 - networks: - cache-network: - aliases: - - api.ub.uni-leipzig.de environment: - npm_config_registry=https://services.ub.uni-leipzig.de/npm - NODE_ENV=development - - DEBUG=dacap:* - - APP_DATA_DIR=/var/lib/app - - APP_TMP_DIR=/tmp - DEBUG=* command: - npm run inspect-watch - hostname: api.ub.uni-leipzig.de volumes: npm-cache: {} - app-data: {} -networks: - cache-network: {} diff --git a/package-lock.json b/package-lock.json index 8cf78e498d240e748bfc129d017c36a10770788a..32f63fd8d1a512dadff812e228a09d8a02cd4c74 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "dacap", - "version": "1.0.9", + "version": "1.0.10", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -131,12 +131,28 @@ "@types/node": "8.0.50" } }, + "@types/glob": { + "version": "5.0.33", + "resolved": "https://services.ub.uni-leipzig.de/npm/@types%2fglob/-/glob-5.0.33.tgz", + "integrity": "sha512-BcD4yyWz+qmCggaYMSFF0Xn7GkO6tgwm3Fh9Gxk/kQmEU3Z7flQTnVlMyKBUNvXXNTCCyjqK4XT4/2hLd1gQ2A==", + "dev": true, + "requires": { + "@types/minimatch": "3.0.1", + "@types/node": "8.0.50" + } + }, "@types/mime": { "version": "2.0.0", "resolved": "https://services.ub.uni-leipzig.de/npm/@types%2fmime/-/mime-2.0.0.tgz", "integrity": "sha512-A2TAGbTFdBw9azHbpVd+/FkdW2T6msN1uct1O9bH3vTerEHKZhTXJUQXy+hNq1B0RagfU8U+KBdqiZpxjhOUQA==", "dev": true }, + "@types/minimatch": { + "version": "3.0.1", + "resolved": "https://services.ub.uni-leipzig.de/npm/@types%2fminimatch/-/minimatch-3.0.1.tgz", + "integrity": "sha512-rUO/jz10KRSyA9SHoCWQ8WX9BICyj5jZYu1/ucKEJKb4KzLZCKMURdYbadP157Q6Zl1x0vHsrU+Z/O0XlhYQDw==", + "dev": true + }, "@types/mocha": { "version": "2.2.44", "resolved": "https://services.ub.uni-leipzig.de/npm/@types%2fmocha/-/mocha-2.2.44.tgz", @@ -168,6 +184,16 @@ "@types/node": "8.0.50" } }, + "@types/rimraf": { + "version": "2.0.2", + "resolved": "https://services.ub.uni-leipzig.de/npm/@types%2frimraf/-/rimraf-2.0.2.tgz", + "integrity": "sha512-Hm/bnWq0TCy7jmjeN5bKYij9vw5GrDFWME4IuxV08278NtU/VdGbzsBohcCUJ7+QMqmUq5hpRKB39HeQWJjztQ==", + "dev": true, + "requires": { + "@types/glob": "5.0.33", + "@types/node": "8.0.50" + } + }, "@types/serve-static": { "version": "1.13.0", "resolved": "https://services.ub.uni-leipzig.de/npm/@types%2fserve-static/-/serve-static-1.13.0.tgz", @@ -986,6 +1012,32 @@ "source-map": "0.5.7" } }, + "cli": { + "version": "1.0.1", + "resolved": "https://services.ub.uni-leipzig.de/npm/cli/-/cli-1.0.1.tgz", + "integrity": "sha1-IoF1NPJL+klQw01TLUjsvGIbjBQ=", + "dev": true, + "requires": { + "exit": "0.1.2", + "glob": "7.1.2" + }, + "dependencies": { + "glob": { + "version": "7.1.2", + "resolved": "https://services.ub.uni-leipzig.de/npm/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" + } + } + } + }, "cliui": { "version": "2.1.0", "resolved": "https://services.ub.uni-leipzig.de/npm/cliui/-/cliui-2.1.0.tgz", @@ -1921,6 +1973,12 @@ "strip-eof": "1.0.0" } }, + "exit": { + "version": "0.1.2", + "resolved": "https://services.ub.uni-leipzig.de/npm/exit/-/exit-0.1.2.tgz", + "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", + "dev": true + }, "expand-brackets": { "version": "2.1.4", "resolved": "https://services.ub.uni-leipzig.de/npm/expand-brackets/-/expand-brackets-2.1.4.tgz", @@ -3151,6 +3209,82 @@ "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", "dev": true }, + "jshint": { + "version": "2.9.5", + "resolved": "https://services.ub.uni-leipzig.de/npm/jshint/-/jshint-2.9.5.tgz", + "integrity": "sha1-HnJSkVzmgbQIJ+4UJIxG006apiw=", + "dev": true, + "requires": { + "cli": "1.0.1", + "console-browserify": "1.1.0", + "exit": "0.1.2", + "htmlparser2": "3.8.3", + "lodash": "3.7.0", + "minimatch": "3.0.4", + "shelljs": "0.3.0", + "strip-json-comments": "1.0.4" + }, + "dependencies": { + "domhandler": { + "version": "2.3.0", + "resolved": "https://services.ub.uni-leipzig.de/npm/domhandler/-/domhandler-2.3.0.tgz", + "integrity": "sha1-LeWaCCLVAn+r/28DLCsloqir5zg=", + "dev": true, + "requires": { + "domelementtype": "1.3.0" + } + }, + "entities": { + "version": "1.0.0", + "resolved": "https://services.ub.uni-leipzig.de/npm/entities/-/entities-1.0.0.tgz", + "integrity": "sha1-sph6o4ITR/zeZCsk/fyeT7cSvyY=", + "dev": true + }, + "htmlparser2": { + "version": "3.8.3", + "resolved": "https://services.ub.uni-leipzig.de/npm/htmlparser2/-/htmlparser2-3.8.3.tgz", + "integrity": "sha1-mWwosZFRaovoZQGn15dX5ccMEGg=", + "dev": true, + "requires": { + "domelementtype": "1.3.0", + "domhandler": "2.3.0", + "domutils": "1.5.1", + "entities": "1.0.0", + "readable-stream": "1.1.14" + } + }, + "isarray": { + "version": "0.0.1", + "resolved": "https://services.ub.uni-leipzig.de/npm/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + }, + "lodash": { + "version": "3.7.0", + "resolved": "https://services.ub.uni-leipzig.de/npm/lodash/-/lodash-3.7.0.tgz", + "integrity": "sha1-Nni9irmVBXwHreg27S7wh9qBHUU=", + "dev": true + }, + "readable-stream": { + "version": "1.1.14", + "resolved": "https://services.ub.uni-leipzig.de/npm/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", + "dev": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "0.0.1", + "string_decoder": "0.10.31" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://services.ub.uni-leipzig.de/npm/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "dev": true + } + } + }, "json-loader": { "version": "0.5.7", "resolved": "https://services.ub.uni-leipzig.de/npm/json-loader/-/json-loader-0.5.7.tgz", @@ -3836,67 +3970,1661 @@ "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", "dev": true }, - "oauth-sign": { - "version": "0.8.2", - "resolved": "https://services.ub.uni-leipzig.de/npm/oauth-sign/-/oauth-sign-0.8.2.tgz", - "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=" - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://services.ub.uni-leipzig.de/npm/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" - }, - "object-copy": { - "version": "0.1.0", - "resolved": "https://services.ub.uni-leipzig.de/npm/object-copy/-/object-copy-0.1.0.tgz", - "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", + "nyc": { + "version": "11.3.0", + "resolved": "https://services.ub.uni-leipzig.de/npm/nyc/-/nyc-11.3.0.tgz", + "integrity": "sha512-oUu0WHt1k/JMIODvAYXX6C50Mupw2GO34P/Jdg2ty9xrLufBthHiKR2gf08aF+9S0abW1fl24R7iKRBXzibZmg==", "dev": true, "requires": { - "copy-descriptor": "0.1.1", - "define-property": "0.2.5", - "kind-of": "3.2.2" + "archy": "1.0.0", + "arrify": "1.0.1", + "caching-transform": "1.0.1", + "convert-source-map": "1.5.0", + "debug-log": "1.0.1", + "default-require-extensions": "1.0.0", + "find-cache-dir": "0.1.1", + "find-up": "2.1.0", + "foreground-child": "1.5.6", + "glob": "7.1.2", + "istanbul-lib-coverage": "1.1.1", + "istanbul-lib-hook": "1.1.0", + "istanbul-lib-instrument": "1.9.1", + "istanbul-lib-report": "1.1.2", + "istanbul-lib-source-maps": "1.2.2", + "istanbul-reports": "1.1.3", + "md5-hex": "1.3.0", + "merge-source-map": "1.0.4", + "micromatch": "2.3.11", + "mkdirp": "0.5.1", + "resolve-from": "2.0.0", + "rimraf": "2.6.2", + "signal-exit": "3.0.2", + "spawn-wrap": "1.3.8", + "test-exclude": "4.1.1", + "yargs": "10.0.3", + "yargs-parser": "8.0.0" }, "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://services.ub.uni-leipzig.de/npm/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "align-text": { + "version": "0.1.4", + "bundled": true, "dev": true, "requires": { - "is-descriptor": "0.1.6" + "kind-of": "3.2.2", + "longest": "1.0.1", + "repeat-string": "1.6.1" } }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://services.ub.uni-leipzig.de/npm/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "amdefine": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "ansi-regex": { + "version": "2.1.1", + "bundled": true, + "dev": true + }, + "ansi-styles": { + "version": "2.2.1", + "bundled": true, + "dev": true + }, + "append-transform": { + "version": "0.4.0", + "bundled": true, "dev": true, "requires": { - "is-accessor-descriptor": "0.1.6", - "is-data-descriptor": "0.1.4", - "kind-of": "5.1.0" + "default-require-extensions": "1.0.0" + } + }, + "archy": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "arr-diff": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "requires": { + "arr-flatten": "1.1.0" + } + }, + "arr-flatten": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "array-unique": { + "version": "0.2.1", + "bundled": true, + "dev": true + }, + "arrify": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "async": { + "version": "1.5.2", + "bundled": true, + "dev": true + }, + "babel-code-frame": { + "version": "6.26.0", + "bundled": true, + "dev": true, + "requires": { + "chalk": "1.1.3", + "esutils": "2.0.2", + "js-tokens": "3.0.2" + } + }, + "babel-generator": { + "version": "6.26.0", + "bundled": true, + "dev": true, + "requires": { + "babel-messages": "6.23.0", + "babel-runtime": "6.26.0", + "babel-types": "6.26.0", + "detect-indent": "4.0.0", + "jsesc": "1.3.0", + "lodash": "4.17.4", + "source-map": "0.5.7", + "trim-right": "1.0.1" + } + }, + "babel-messages": { + "version": "6.23.0", + "bundled": true, + "dev": true, + "requires": { + "babel-runtime": "6.26.0" + } + }, + "babel-runtime": { + "version": "6.26.0", + "bundled": true, + "dev": true, + "requires": { + "core-js": "2.5.1", + "regenerator-runtime": "0.11.0" + } + }, + "babel-template": { + "version": "6.26.0", + "bundled": true, + "dev": true, + "requires": { + "babel-runtime": "6.26.0", + "babel-traverse": "6.26.0", + "babel-types": "6.26.0", + "babylon": "6.18.0", + "lodash": "4.17.4" + } + }, + "babel-traverse": { + "version": "6.26.0", + "bundled": true, + "dev": true, + "requires": { + "babel-code-frame": "6.26.0", + "babel-messages": "6.23.0", + "babel-runtime": "6.26.0", + "babel-types": "6.26.0", + "babylon": "6.18.0", + "debug": "2.6.9", + "globals": "9.18.0", + "invariant": "2.2.2", + "lodash": "4.17.4" + } + }, + "babel-types": { + "version": "6.26.0", + "bundled": true, + "dev": true, + "requires": { + "babel-runtime": "6.26.0", + "esutils": "2.0.2", + "lodash": "4.17.4", + "to-fast-properties": "1.0.3" + } + }, + "babylon": { + "version": "6.18.0", + "bundled": true, + "dev": true + }, + "balanced-match": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "brace-expansion": { + "version": "1.1.8", + "bundled": true, + "dev": true, + "requires": { + "balanced-match": "1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "1.8.5", + "bundled": true, + "dev": true, + "requires": { + "expand-range": "1.8.2", + "preserve": "0.2.0", + "repeat-element": "1.1.2" + } + }, + "builtin-modules": { + "version": "1.1.1", + "bundled": true, + "dev": true + }, + "caching-transform": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "requires": { + "md5-hex": "1.3.0", + "mkdirp": "0.5.1", + "write-file-atomic": "1.3.4" + } + }, + "camelcase": { + "version": "1.2.1", + "bundled": true, + "dev": true, + "optional": true + }, + "center-align": { + "version": "0.1.3", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "align-text": "0.1.4", + "lazy-cache": "1.0.4" + } + }, + "chalk": { + "version": "1.1.3", + "bundled": true, + "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" + } + }, + "cliui": { + "version": "2.1.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "center-align": "0.1.3", + "right-align": "0.1.3", + "wordwrap": "0.0.2" }, "dependencies": { - "kind-of": { - "version": "5.1.0", - "resolved": "https://services.ub.uni-leipzig.de/npm/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true + "wordwrap": { + "version": "0.0.2", + "bundled": true, + "dev": true, + "optional": true } } }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://services.ub.uni-leipzig.de/npm/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "code-point-at": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "commondir": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "bundled": true, + "dev": true + }, + "convert-source-map": { + "version": "1.5.0", + "bundled": true, + "dev": true + }, + "core-js": { + "version": "2.5.1", + "bundled": true, + "dev": true + }, + "cross-spawn": { + "version": "4.0.2", + "bundled": true, "dev": true, "requires": { - "is-buffer": "1.1.6" + "lru-cache": "4.1.1", + "which": "1.3.0" } - } - } - }, - "object-visit": { + }, + "debug": { + "version": "2.6.9", + "bundled": true, + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "debug-log": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "decamelize": { + "version": "1.2.0", + "bundled": true, + "dev": true + }, + "default-require-extensions": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "requires": { + "strip-bom": "2.0.0" + } + }, + "detect-indent": { + "version": "4.0.0", + "bundled": true, + "dev": true, + "requires": { + "repeating": "2.0.1" + } + }, + "error-ex": { + "version": "1.3.1", + "bundled": true, + "dev": true, + "requires": { + "is-arrayish": "0.2.1" + } + }, + "escape-string-regexp": { + "version": "1.0.5", + "bundled": true, + "dev": true + }, + "esutils": { + "version": "2.0.2", + "bundled": true, + "dev": true + }, + "execa": { + "version": "0.7.0", + "bundled": true, + "dev": true, + "requires": { + "cross-spawn": "5.1.0", + "get-stream": "3.0.0", + "is-stream": "1.1.0", + "npm-run-path": "2.0.2", + "p-finally": "1.0.0", + "signal-exit": "3.0.2", + "strip-eof": "1.0.0" + }, + "dependencies": { + "cross-spawn": { + "version": "5.1.0", + "bundled": true, + "dev": true, + "requires": { + "lru-cache": "4.1.1", + "shebang-command": "1.2.0", + "which": "1.3.0" + } + } + } + }, + "expand-brackets": { + "version": "0.1.5", + "bundled": true, + "dev": true, + "requires": { + "is-posix-bracket": "0.1.1" + } + }, + "expand-range": { + "version": "1.8.2", + "bundled": true, + "dev": true, + "requires": { + "fill-range": "2.2.3" + } + }, + "extglob": { + "version": "0.3.2", + "bundled": true, + "dev": true, + "requires": { + "is-extglob": "1.0.0" + } + }, + "filename-regex": { + "version": "2.0.1", + "bundled": true, + "dev": true + }, + "fill-range": { + "version": "2.2.3", + "bundled": true, + "dev": true, + "requires": { + "is-number": "2.1.0", + "isobject": "2.1.0", + "randomatic": "1.1.7", + "repeat-element": "1.1.2", + "repeat-string": "1.6.1" + } + }, + "find-cache-dir": { + "version": "0.1.1", + "bundled": true, + "dev": true, + "requires": { + "commondir": "1.0.1", + "mkdirp": "0.5.1", + "pkg-dir": "1.0.0" + } + }, + "find-up": { + "version": "2.1.0", + "bundled": true, + "dev": true, + "requires": { + "locate-path": "2.0.0" + } + }, + "for-in": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "for-own": { + "version": "0.1.5", + "bundled": true, + "dev": true, + "requires": { + "for-in": "1.0.2" + } + }, + "foreground-child": { + "version": "1.5.6", + "bundled": true, + "dev": true, + "requires": { + "cross-spawn": "4.0.2", + "signal-exit": "3.0.2" + } + }, + "fs.realpath": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "get-caller-file": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "get-stream": { + "version": "3.0.0", + "bundled": true, + "dev": true + }, + "glob": { + "version": "7.1.2", + "bundled": true, + "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" + } + }, + "glob-base": { + "version": "0.3.0", + "bundled": true, + "dev": true, + "requires": { + "glob-parent": "2.0.0", + "is-glob": "2.0.1" + } + }, + "glob-parent": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "requires": { + "is-glob": "2.0.1" + } + }, + "globals": { + "version": "9.18.0", + "bundled": true, + "dev": true + }, + "graceful-fs": { + "version": "4.1.11", + "bundled": true, + "dev": true + }, + "handlebars": { + "version": "4.0.11", + "bundled": true, + "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", + "bundled": true, + "dev": true, + "requires": { + "amdefine": "1.0.1" + } + } + } + }, + "has-ansi": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "requires": { + "ansi-regex": "2.1.1" + } + }, + "has-flag": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "hosted-git-info": { + "version": "2.5.0", + "bundled": true, + "dev": true + }, + "imurmurhash": { + "version": "0.1.4", + "bundled": true, + "dev": true + }, + "inflight": { + "version": "1.0.6", + "bundled": true, + "dev": true, + "requires": { + "once": "1.4.0", + "wrappy": "1.0.2" + } + }, + "inherits": { + "version": "2.0.3", + "bundled": true, + "dev": true + }, + "invariant": { + "version": "2.2.2", + "bundled": true, + "dev": true, + "requires": { + "loose-envify": "1.3.1" + } + }, + "invert-kv": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "is-arrayish": { + "version": "0.2.1", + "bundled": true, + "dev": true + }, + "is-buffer": { + "version": "1.1.5", + "bundled": true, + "dev": true + }, + "is-builtin-module": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "requires": { + "builtin-modules": "1.1.1" + } + }, + "is-dotfile": { + "version": "1.0.3", + "bundled": true, + "dev": true + }, + "is-equal-shallow": { + "version": "0.1.3", + "bundled": true, + "dev": true, + "requires": { + "is-primitive": "2.0.0" + } + }, + "is-extendable": { + "version": "0.1.1", + "bundled": true, + "dev": true + }, + "is-extglob": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "is-finite": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "requires": { + "number-is-nan": "1.0.1" + } + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "requires": { + "number-is-nan": "1.0.1" + } + }, + "is-glob": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "requires": { + "is-extglob": "1.0.0" + } + }, + "is-number": { + "version": "2.1.0", + "bundled": true, + "dev": true, + "requires": { + "kind-of": "3.2.2" + } + }, + "is-posix-bracket": { + "version": "0.1.1", + "bundled": true, + "dev": true + }, + "is-primitive": { + "version": "2.0.0", + "bundled": true, + "dev": true + }, + "is-stream": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "is-utf8": { + "version": "0.2.1", + "bundled": true, + "dev": true + }, + "isarray": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "isexe": { + "version": "2.0.0", + "bundled": true, + "dev": true + }, + "isobject": { + "version": "2.1.0", + "bundled": true, + "dev": true, + "requires": { + "isarray": "1.0.0" + } + }, + "istanbul-lib-coverage": { + "version": "1.1.1", + "bundled": true, + "dev": true + }, + "istanbul-lib-hook": { + "version": "1.1.0", + "bundled": true, + "dev": true, + "requires": { + "append-transform": "0.4.0" + } + }, + "istanbul-lib-instrument": { + "version": "1.9.1", + "bundled": true, + "dev": true, + "requires": { + "babel-generator": "6.26.0", + "babel-template": "6.26.0", + "babel-traverse": "6.26.0", + "babel-types": "6.26.0", + "babylon": "6.18.0", + "istanbul-lib-coverage": "1.1.1", + "semver": "5.4.1" + } + }, + "istanbul-lib-report": { + "version": "1.1.2", + "bundled": true, + "dev": true, + "requires": { + "istanbul-lib-coverage": "1.1.1", + "mkdirp": "0.5.1", + "path-parse": "1.0.5", + "supports-color": "3.2.3" + }, + "dependencies": { + "supports-color": { + "version": "3.2.3", + "bundled": true, + "dev": true, + "requires": { + "has-flag": "1.0.0" + } + } + } + }, + "istanbul-lib-source-maps": { + "version": "1.2.2", + "bundled": true, + "dev": true, + "requires": { + "debug": "3.1.0", + "istanbul-lib-coverage": "1.1.1", + "mkdirp": "0.5.1", + "rimraf": "2.6.2", + "source-map": "0.5.7" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "bundled": true, + "dev": true, + "requires": { + "ms": "2.0.0" + } + } + } + }, + "istanbul-reports": { + "version": "1.1.3", + "bundled": true, + "dev": true, + "requires": { + "handlebars": "4.0.11" + } + }, + "js-tokens": { + "version": "3.0.2", + "bundled": true, + "dev": true + }, + "jsesc": { + "version": "1.3.0", + "bundled": true, + "dev": true + }, + "kind-of": { + "version": "3.2.2", + "bundled": true, + "dev": true, + "requires": { + "is-buffer": "1.1.5" + } + }, + "lazy-cache": { + "version": "1.0.4", + "bundled": true, + "dev": true, + "optional": true + }, + "lcid": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "requires": { + "invert-kv": "1.0.0" + } + }, + "load-json-file": { + "version": "1.1.0", + "bundled": true, + "dev": true, + "requires": { + "graceful-fs": "4.1.11", + "parse-json": "2.2.0", + "pify": "2.3.0", + "pinkie-promise": "2.0.1", + "strip-bom": "2.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "requires": { + "p-locate": "2.0.0", + "path-exists": "3.0.0" + }, + "dependencies": { + "path-exists": { + "version": "3.0.0", + "bundled": true, + "dev": true + } + } + }, + "lodash": { + "version": "4.17.4", + "bundled": true, + "dev": true + }, + "longest": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "loose-envify": { + "version": "1.3.1", + "bundled": true, + "dev": true, + "requires": { + "js-tokens": "3.0.2" + } + }, + "lru-cache": { + "version": "4.1.1", + "bundled": true, + "dev": true, + "requires": { + "pseudomap": "1.0.2", + "yallist": "2.1.2" + } + }, + "md5-hex": { + "version": "1.3.0", + "bundled": true, + "dev": true, + "requires": { + "md5-o-matic": "0.1.1" + } + }, + "md5-o-matic": { + "version": "0.1.1", + "bundled": true, + "dev": true + }, + "mem": { + "version": "1.1.0", + "bundled": true, + "dev": true, + "requires": { + "mimic-fn": "1.1.0" + } + }, + "merge-source-map": { + "version": "1.0.4", + "bundled": true, + "dev": true, + "requires": { + "source-map": "0.5.7" + } + }, + "micromatch": { + "version": "2.3.11", + "bundled": true, + "dev": true, + "requires": { + "arr-diff": "2.0.0", + "array-unique": "0.2.1", + "braces": "1.8.5", + "expand-brackets": "0.1.5", + "extglob": "0.3.2", + "filename-regex": "2.0.1", + "is-extglob": "1.0.0", + "is-glob": "2.0.1", + "kind-of": "3.2.2", + "normalize-path": "2.1.1", + "object.omit": "2.0.1", + "parse-glob": "3.0.4", + "regex-cache": "0.4.4" + } + }, + "mimic-fn": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "bundled": true, + "dev": true, + "requires": { + "brace-expansion": "1.1.8" + } + }, + "minimist": { + "version": "0.0.8", + "bundled": true, + "dev": true + }, + "mkdirp": { + "version": "0.5.1", + "bundled": true, + "dev": true, + "requires": { + "minimist": "0.0.8" + } + }, + "ms": { + "version": "2.0.0", + "bundled": true, + "dev": true + }, + "normalize-package-data": { + "version": "2.4.0", + "bundled": true, + "dev": true, + "requires": { + "hosted-git-info": "2.5.0", + "is-builtin-module": "1.0.0", + "semver": "5.4.1", + "validate-npm-package-license": "3.0.1" + } + }, + "normalize-path": { + "version": "2.1.1", + "bundled": true, + "dev": true, + "requires": { + "remove-trailing-separator": "1.1.0" + } + }, + "npm-run-path": { + "version": "2.0.2", + "bundled": true, + "dev": true, + "requires": { + "path-key": "2.0.1" + } + }, + "number-is-nan": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "bundled": true, + "dev": true + }, + "object.omit": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "requires": { + "for-own": "0.1.5", + "is-extendable": "0.1.1" + } + }, + "once": { + "version": "1.4.0", + "bundled": true, + "dev": true, + "requires": { + "wrappy": "1.0.2" + } + }, + "optimist": { + "version": "0.6.1", + "bundled": true, + "dev": true, + "requires": { + "minimist": "0.0.8", + "wordwrap": "0.0.3" + } + }, + "os-homedir": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "os-locale": { + "version": "2.1.0", + "bundled": true, + "dev": true, + "requires": { + "execa": "0.7.0", + "lcid": "1.0.0", + "mem": "1.1.0" + } + }, + "p-finally": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "p-limit": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "p-locate": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "requires": { + "p-limit": "1.1.0" + } + }, + "parse-glob": { + "version": "3.0.4", + "bundled": true, + "dev": true, + "requires": { + "glob-base": "0.3.0", + "is-dotfile": "1.0.3", + "is-extglob": "1.0.0", + "is-glob": "2.0.1" + } + }, + "parse-json": { + "version": "2.2.0", + "bundled": true, + "dev": true, + "requires": { + "error-ex": "1.3.1" + } + }, + "path-exists": { + "version": "2.1.0", + "bundled": true, + "dev": true, + "requires": { + "pinkie-promise": "2.0.1" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "path-key": { + "version": "2.0.1", + "bundled": true, + "dev": true + }, + "path-parse": { + "version": "1.0.5", + "bundled": true, + "dev": true + }, + "path-type": { + "version": "1.1.0", + "bundled": true, + "dev": true, + "requires": { + "graceful-fs": "4.1.11", + "pify": "2.3.0", + "pinkie-promise": "2.0.1" + } + }, + "pify": { + "version": "2.3.0", + "bundled": true, + "dev": true + }, + "pinkie": { + "version": "2.0.4", + "bundled": true, + "dev": true + }, + "pinkie-promise": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "requires": { + "pinkie": "2.0.4" + } + }, + "pkg-dir": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "requires": { + "find-up": "1.1.2" + }, + "dependencies": { + "find-up": { + "version": "1.1.2", + "bundled": true, + "dev": true, + "requires": { + "path-exists": "2.1.0", + "pinkie-promise": "2.0.1" + } + } + } + }, + "preserve": { + "version": "0.2.0", + "bundled": true, + "dev": true + }, + "pseudomap": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "randomatic": { + "version": "1.1.7", + "bundled": true, + "dev": true, + "requires": { + "is-number": "3.0.0", + "kind-of": "4.0.0" + }, + "dependencies": { + "is-number": { + "version": "3.0.0", + "bundled": true, + "dev": true, + "requires": { + "kind-of": "3.2.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "bundled": true, + "dev": true, + "requires": { + "is-buffer": "1.1.5" + } + } + } + }, + "kind-of": { + "version": "4.0.0", + "bundled": true, + "dev": true, + "requires": { + "is-buffer": "1.1.5" + } + } + } + }, + "read-pkg": { + "version": "1.1.0", + "bundled": true, + "dev": true, + "requires": { + "load-json-file": "1.1.0", + "normalize-package-data": "2.4.0", + "path-type": "1.1.0" + } + }, + "read-pkg-up": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "requires": { + "find-up": "1.1.2", + "read-pkg": "1.1.0" + }, + "dependencies": { + "find-up": { + "version": "1.1.2", + "bundled": true, + "dev": true, + "requires": { + "path-exists": "2.1.0", + "pinkie-promise": "2.0.1" + } + } + } + }, + "regenerator-runtime": { + "version": "0.11.0", + "bundled": true, + "dev": true + }, + "regex-cache": { + "version": "0.4.4", + "bundled": true, + "dev": true, + "requires": { + "is-equal-shallow": "0.1.3" + } + }, + "remove-trailing-separator": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "repeat-element": { + "version": "1.1.2", + "bundled": true, + "dev": true + }, + "repeat-string": { + "version": "1.6.1", + "bundled": true, + "dev": true + }, + "repeating": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "requires": { + "is-finite": "1.0.2" + } + }, + "require-directory": { + "version": "2.1.1", + "bundled": true, + "dev": true + }, + "require-main-filename": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "resolve-from": { + "version": "2.0.0", + "bundled": true, + "dev": true + }, + "right-align": { + "version": "0.1.3", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "align-text": "0.1.4" + } + }, + "rimraf": { + "version": "2.6.2", + "bundled": true, + "dev": true, + "requires": { + "glob": "7.1.2" + } + }, + "semver": { + "version": "5.4.1", + "bundled": true, + "dev": true + }, + "set-blocking": { + "version": "2.0.0", + "bundled": true, + "dev": true + }, + "shebang-command": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "requires": { + "shebang-regex": "1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "signal-exit": { + "version": "3.0.2", + "bundled": true, + "dev": true + }, + "slide": { + "version": "1.1.6", + "bundled": true, + "dev": true + }, + "source-map": { + "version": "0.5.7", + "bundled": true, + "dev": true + }, + "spawn-wrap": { + "version": "1.3.8", + "bundled": true, + "dev": true, + "requires": { + "foreground-child": "1.5.6", + "mkdirp": "0.5.1", + "os-homedir": "1.0.2", + "rimraf": "2.6.2", + "signal-exit": "3.0.2", + "which": "1.3.0" + } + }, + "spdx-correct": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "requires": { + "spdx-license-ids": "1.2.2" + } + }, + "spdx-expression-parse": { + "version": "1.0.4", + "bundled": true, + "dev": true + }, + "spdx-license-ids": { + "version": "1.2.2", + "bundled": true, + "dev": true + }, + "string-width": { + "version": "2.1.1", + "bundled": true, + "dev": true, + "requires": { + "is-fullwidth-code-point": "2.0.0", + "strip-ansi": "4.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "bundled": true, + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "bundled": true, + "dev": true + }, + "strip-ansi": { + "version": "4.0.0", + "bundled": true, + "dev": true, + "requires": { + "ansi-regex": "3.0.0" + } + } + } + }, + "strip-ansi": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "requires": { + "ansi-regex": "2.1.1" + } + }, + "strip-bom": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "requires": { + "is-utf8": "0.2.1" + } + }, + "strip-eof": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "supports-color": { + "version": "2.0.0", + "bundled": true, + "dev": true + }, + "test-exclude": { + "version": "4.1.1", + "bundled": true, + "dev": true, + "requires": { + "arrify": "1.0.1", + "micromatch": "2.3.11", + "object-assign": "4.1.1", + "read-pkg-up": "1.0.1", + "require-main-filename": "1.0.1" + } + }, + "to-fast-properties": { + "version": "1.0.3", + "bundled": true, + "dev": true + }, + "trim-right": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "uglify-js": { + "version": "2.8.29", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "source-map": "0.5.7", + "uglify-to-browserify": "1.0.2", + "yargs": "3.10.0" + }, + "dependencies": { + "yargs": { + "version": "3.10.0", + "bundled": true, + "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", + "bundled": true, + "dev": true, + "optional": true + }, + "validate-npm-package-license": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "requires": { + "spdx-correct": "1.0.2", + "spdx-expression-parse": "1.0.4" + } + }, + "which": { + "version": "1.3.0", + "bundled": true, + "dev": true, + "requires": { + "isexe": "2.0.0" + } + }, + "which-module": { + "version": "2.0.0", + "bundled": true, + "dev": true + }, + "window-size": { + "version": "0.1.0", + "bundled": true, + "dev": true, + "optional": true + }, + "wordwrap": { + "version": "0.0.3", + "bundled": true, + "dev": true + }, + "wrap-ansi": { + "version": "2.1.0", + "bundled": true, + "dev": true, + "requires": { + "string-width": "1.0.2", + "strip-ansi": "3.0.1" + }, + "dependencies": { + "string-width": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "requires": { + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" + } + } + } + }, + "wrappy": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "write-file-atomic": { + "version": "1.3.4", + "bundled": true, + "dev": true, + "requires": { + "graceful-fs": "4.1.11", + "imurmurhash": "0.1.4", + "slide": "1.1.6" + } + }, + "y18n": { + "version": "3.2.1", + "bundled": true, + "dev": true + }, + "yallist": { + "version": "2.1.2", + "bundled": true, + "dev": true + }, + "yargs": { + "version": "10.0.3", + "bundled": true, + "dev": true, + "requires": { + "cliui": "3.2.0", + "decamelize": "1.2.0", + "find-up": "2.1.0", + "get-caller-file": "1.0.2", + "os-locale": "2.1.0", + "require-directory": "2.1.1", + "require-main-filename": "1.0.1", + "set-blocking": "2.0.0", + "string-width": "2.1.1", + "which-module": "2.0.0", + "y18n": "3.2.1", + "yargs-parser": "8.0.0" + }, + "dependencies": { + "cliui": { + "version": "3.2.0", + "bundled": true, + "dev": true, + "requires": { + "string-width": "1.0.2", + "strip-ansi": "3.0.1", + "wrap-ansi": "2.1.0" + }, + "dependencies": { + "string-width": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "requires": { + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" + } + } + } + } + } + }, + "yargs-parser": { + "version": "8.0.0", + "bundled": true, + "dev": true, + "requires": { + "camelcase": "4.1.0" + }, + "dependencies": { + "camelcase": { + "version": "4.1.0", + "bundled": true, + "dev": true + } + } + } + } + }, + "oauth-sign": { + "version": "0.8.2", + "resolved": "https://services.ub.uni-leipzig.de/npm/oauth-sign/-/oauth-sign-0.8.2.tgz", + "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=" + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://services.ub.uni-leipzig.de/npm/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "object-copy": { + "version": "0.1.0", + "resolved": "https://services.ub.uni-leipzig.de/npm/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", + "dev": true, + "requires": { + "copy-descriptor": "0.1.1", + "define-property": "0.2.5", + "kind-of": "3.2.2" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://services.ub.uni-leipzig.de/npm/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "0.1.6" + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://services.ub.uni-leipzig.de/npm/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "0.1.6", + "is-data-descriptor": "0.1.4", + "kind-of": "5.1.0" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://services.ub.uni-leipzig.de/npm/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://services.ub.uni-leipzig.de/npm/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + } + } + }, + "object-visit": { "version": "1.0.1", "resolved": "https://services.ub.uni-leipzig.de/npm/object-visit/-/object-visit-1.0.1.tgz", "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", @@ -5148,6 +6876,31 @@ "align-text": "0.1.4" } }, + "rimraf": { + "version": "2.6.2", + "resolved": "https://services.ub.uni-leipzig.de/npm/rimraf/-/rimraf-2.6.2.tgz", + "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", + "dev": true, + "requires": { + "glob": "7.1.2" + }, + "dependencies": { + "glob": { + "version": "7.1.2", + "resolved": "https://services.ub.uni-leipzig.de/npm/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" + } + } + } + }, "ripemd160": { "version": "2.0.1", "resolved": "https://services.ub.uni-leipzig.de/npm/ripemd160/-/ripemd160-2.0.1.tgz", @@ -5307,6 +7060,12 @@ "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", "dev": true }, + "shelljs": { + "version": "0.3.0", + "resolved": "https://services.ub.uni-leipzig.de/npm/shelljs/-/shelljs-0.3.0.tgz", + "integrity": "sha1-NZbmMHp4FUT1kfN9phg2DzHbV7E=", + "dev": true + }, "should": { "version": "11.2.1", "resolved": "https://services.ub.uni-leipzig.de/npm/should/-/should-11.2.1.tgz", @@ -5736,6 +7495,12 @@ "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", "dev": true }, + "strip-json-comments": { + "version": "1.0.4", + "resolved": "https://services.ub.uni-leipzig.de/npm/strip-json-comments/-/strip-json-comments-1.0.4.tgz", + "integrity": "sha1-HhX7ysl9Pumb8tc7TGVrCCu6+5E=", + "dev": true + }, "style-loader": { "version": "0.18.2", "resolved": "https://services.ub.uni-leipzig.de/npm/style-loader/-/style-loader-0.18.2.tgz", @@ -5797,6 +7562,12 @@ "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", "dev": true }, + "time-stamp": { + "version": "2.0.0", + "resolved": "https://services.ub.uni-leipzig.de/npm/time-stamp/-/time-stamp-2.0.0.tgz", + "integrity": "sha1-lcakRTDhW6jW9KPsuMOj+sRto1c=", + "dev": true + }, "timers-browserify": { "version": "2.0.4", "resolved": "https://services.ub.uni-leipzig.de/npm/timers-browserify/-/timers-browserify-2.0.4.tgz", @@ -6465,6 +8236,19 @@ } } }, + "webpack-dev-middleware": { + "version": "1.12.0", + "resolved": "https://services.ub.uni-leipzig.de/npm/webpack-dev-middleware/-/webpack-dev-middleware-1.12.0.tgz", + "integrity": "sha1-007++y7dp+HTtdvgcolRMhllFwk=", + "dev": true, + "requires": { + "memory-fs": "0.4.1", + "mime": "1.4.1", + "path-is-absolute": "1.0.1", + "range-parser": "1.2.0", + "time-stamp": "2.0.0" + } + }, "webpack-merge": { "version": "4.1.1", "resolved": "https://services.ub.uni-leipzig.de/npm/webpack-merge/-/webpack-merge-4.1.1.tgz", diff --git a/package.json b/package.json index 2d73a0ecea27f9d866153ffe81f74207946dc7af..a95882ff3df5e944aba45727160fbc45b41a321b 100644 --- a/package.json +++ b/package.json @@ -10,14 +10,16 @@ "test": "test" }, "scripts": { - "webpack-watch": "webpack --config webpack.dev.js --watch", "start": "node bin/dacap", "tsc": "tsc", - "inspect-watch": "tsc-watch --onSuccess 'node --inspect=0.0.0.0:9229 bin/dacap'", - "inspect-brk-watch": "tsc-watch --onSuccess 'node --inspect-brk=0.0.0.0:9229 bin/dacap'", + "inspect-watch": "tsc-watch --sourceMap --onSuccess 'node --inspect=0.0.0.0:9229 bin/dacap'", + "inspect-brk-watch": "tsc-watch --sourceMap --onSuccess 'node --inspect-brk=0.0.0.0:9229 bin/dacap'", "test": "mocha", - "ci": "istanbul cover _mocha -- --timeout 10000", - "build": "rm -rf dist public && tsc --declaration && webpack --config webpack.prod.js" + "test-inspect": "mocha --inspect-brk=0.0.0.0:9229", + "test-watch": "tsc-watch --sourceMap --onSuccess 'nyc mocha'", + "ci": "nyc mocha --timeout=10000", + "build": "rm -rf lib test public && tsc --declaration && webpack --config webpack.prod.js", + "nyc": "nyc" }, "author": "Ulf Seltmann <ulf.seltmann@uni-leipzig.de>", "license": "GPL-2.0", @@ -50,9 +52,11 @@ "@types/debug": "0.0.30", "@types/express": "^4.0.37", "@types/mocha": "^2.2.42", + "@types/nock": "^8.2.1", "@types/node": "^8.0.25", "@types/node-cache": "^4.1.0", "@types/request": "^2.0.3", + "@types/rimraf": "^2.0.2", "@types/should": "^11.2.0", "angular2-template-loader": "^0.6.2", "awesome-typescript-loader": "^3.2.3", @@ -64,8 +68,12 @@ "html-webpack-plugin": "^2.30.1", "istanbul": "^0.4.5", "jquery": "^3.2.1", + "jshint": "^2.9.5", "mocha": "^3.5.0", "mocha-jenkins-reporter": "^0.3.9", + "nock": "^9.1.0", + "nyc": "^11.3.0", + "rimraf": "^2.6.2", "should": "^11.2.1", "source-map-loader": "^0.2.1", "style-loader": "^0.18.2", @@ -73,6 +81,7 @@ "tslint": "^5.7.0", "typescript": "^2.5.1", "webpack": "^3.5.5", + "webpack-dev-middleware": "^1.12.0", "webpack-merge": "^4.1.0" } } diff --git a/src/cache.ts b/src/cache.ts deleted file mode 100644 index a04727ec0ea1d2328d11728fa19252f2ac88d778..0000000000000000000000000000000000000000 --- a/src/cache.ts +++ /dev/null @@ -1,271 +0,0 @@ -import * as debugFactory from 'debug'; -import * as express from 'express'; -import * as NodeCache from 'node-cache'; -import * as crypto from 'crypto'; -import * as fs from 'fs'; -import * as path from 'path'; -import * as Q from 'q'; - -import { Endpoint, EndpointValue } from './endpoint'; - -const debug = debugFactory('dacap:cache'); - -export interface MiddlewareFunction { - (req: express.Request, res: express.Response, next: express.NextFunction): Promise<void> -} - -export interface RegisterObject { - name: string - data: CacheObject -} -export interface CacheObject { - endPoint: string, - cacheOptions: NodeCache.Options, - cache: [ - { - hash: string, - value: any - } - ] -} - -export interface CacheDetails { - hash: string, - uriPath: string, - contentType: string, - ttl: number, - size: number -} - -export interface CacheInfo { - name: string, - apiEndPoint: string, - cacheOptions: NodeCache.Options, - cacheStats: NodeCache.Stats, - cache: CacheDetails[], -} - -export function Middleware(register: Register): MiddlewareFunction { - - return async function (req: express.Request, res: express.Response, next: express.NextFunction): Promise<void> { - const pattern = new RegExp('^/([^/]+)/?(.*)$'); - const match = req.url.match(pattern); - try { - const cache = register.get(match[1]); - const uniQueryString = Object.keys(req.query).sort().map(function (key) { return `${key}=${req.query[key]}` }).join('&'); - const hash = crypto.createHash('sha1').update(req.path + uniQueryString).digest('hex'); - debug(`finding cache-entry for ${hash}`); - const value = await cache.get(hash, match[2]); - res.setHeader('content-type', value.contentType) - res.send(value.data); - } catch (err) { - res.status(404).send(err.message); - } - } -} - -/** - * the cache register - * - * @export - * @class Register - */ -export class Register { - cacheRegister: { [key: string]: Cache } = {}; - - constructor(private storage: string, public name: string) { - fs.existsSync(storage) || fs.mkdirSync(storage); - } - - has = (name: string): boolean => { - return !!this.cacheRegister[name]; - } - /** - * gets register by its name - * - * @memberof Register - */ - get = (name: string): Cache => { - if (!this.has(name)) throw new Error(`register not found: ${name}`); - return this.cacheRegister[name]; - } - - /** - * adds API-Endpoint to cache-register - * - * @memberof Register - */ - add = (name: string, serverUrl: string, cacheOptions: NodeCache.Options) => { - const endpoint: Endpoint = new Endpoint(serverUrl); - const nodeCache: NodeCache = new NodeCache(cacheOptions) - this.cacheRegister[name] = new Cache(endpoint, nodeCache); - } - - delete(name: string) { - if (!this.has(name)) throw new Error(`register not found: ${name}`); - delete this.cacheRegister[name]; - } - - getInfo = (name?: string): CacheInfo|CacheInfo[] => { - return (name) ? this._getInfo(name) : Object.keys(this.cacheRegister).map((key): CacheInfo => { - return this._getInfo(key); - }); - } - - private _getInfo(name: string): CacheInfo { - return { - name: name, - apiEndPoint: this.cacheRegister[name].getEndpoint().toString(), - cacheOptions: this.cacheRegister[name].getCache().options, - cacheStats: this.cacheRegister[name].getCache().getStats(), - cache: <CacheDetails[]>this.cacheRegister[name].getDetails() - } - } - - toObject = (): RegisterObject[] => { - return Object.keys(this.cacheRegister).map((name) => { - return { - name: name, - data: this.cacheRegister[name].toObject() - } - }); - } - - save = (interval:number): void => { - setTimeout(() => { - fs.writeFileSync(path.resolve(this.storage, this.name), JSON.stringify(this.toObject()), { encoding: 'utf8', flag: 'w' }); - debug(`register "${this.name}" saved to "${this.storage}"`); - this.save(interval); - }, interval); - } - - restore = async (): Promise<void> => { - const data = JSON.parse(fs.readFileSync(path.resolve(this.storage, this.name), { encoding: 'utf8' })); - - data.map((cache) => { - this.cacheRegister[cache.name] = new Cache(); - this.cacheRegister[cache.name].fromObject(cache.data); - }); - debug(`register "${this.name}" restored from "${this.storage}"`); - } -} - -export class Cache { - constructor(private endpoint?: Endpoint, private realCache?: NodeCache) { - if (endpoint) this.setEndpoint(endpoint); - if (realCache) this.setCache(realCache); - } - - setEndpoint(endpoint: Endpoint): this { - this.endpoint = endpoint; - return this; - } - - getEndpoint(): Endpoint { - return this.endpoint; - } - - setCache(realCache: NodeCache): this { - this.realCache = realCache; - this.realCache.on('expired', (key, value) => { - debug(`hash "${key}" has expired`); - this.refresh(key, value); - }) - return this; - } - - getCache(): NodeCache { - return this.realCache; - } - - flush() { - return this.realCache.flushAll(); - } - - del(hash) { - return this.realCache.del(hash); - } - - async get(hash: string, path: string): Promise<EndpointValue> { - let value: EndpointValue | undefined = this.realCache.get(hash); - if (value == undefined) { - debug(`no cache hit. fetching ${path}`); - value = { - uriPath: path, - contentType: '', - data: '' - }; - try { - await this.endpoint.request(value); - this.realCache.set(hash, value); - } catch (err) { - console.error(err); - } - } else { - debug(`cache hit. ttl is ${this.realCache.getTtl(hash)}`); - - } - - return value; - } - - async refresh(key, value) { - const newValue = { - uriPath: value.uriPath, - contentType: '', - data: '' - } - await this.endpoint.request(newValue); - value.data = newValue.data; - value.contentType = newValue.contentType; - this.realCache.set(key, value); - debug(`successfully refreshed "${key}"`); - } - - keys = (): string[] => { - return this.realCache.keys(); - } - - getDetails<T>(hash?:string): CacheDetails|CacheDetails[] { - return (hash) ? this._getDetails(hash) : this.realCache.keys().map(this._getDetails.bind(this)); - } - - private _getDetails(hash: string): CacheDetails { - return { - hash: hash, - ttl: this.realCache.getTtl(hash), - uriPath: this.realCache.get<EndpointValue>(hash).uriPath, - contentType: this.realCache.get<EndpointValue>(hash).contentType, - size: this.realCache.get<EndpointValue>(hash).data.length - }; - } - - toObject(): CacheObject { - const data = { - endPoint: this.endpoint.toString(), - cacheOptions: this.realCache.options, - cache: [] - } - - this.realCache.keys().map((hash) => { - data.cache.push({ - hash: hash, - value: this.realCache.get<EndpointValue>(hash) - }); - }); - - return <CacheObject>data; - } - - fromObject(data) { - const endpoint = new Endpoint(data.endPoint); - const realCache = new NodeCache(data.cacheOptions); - this.setEndpoint(endpoint); - - data.cache.map((item) => { - realCache.set(item.hash, item.value); - }); - - this.setCache(realCache); - } -} \ No newline at end of file diff --git a/src/lib/cache.ts b/src/lib/cache.ts new file mode 100644 index 0000000000000000000000000000000000000000..540d5682bb0274260b1596c8e0eb35d2ec0d8be5 --- /dev/null +++ b/src/lib/cache.ts @@ -0,0 +1,156 @@ +import * as debugFactory from 'debug'; +import * as NodeCache from 'node-cache'; + +import { Endpoint, EndpointValue } from './endpoint'; + +const debug = debugFactory('dacap:cache'); + +export interface CacheObject { + endPoint: string, + cacheOptions: NodeCache.Options, + cache: [ + { + hash: string, + value: any + } + ] +} + +export interface CacheDetails { + hash: string, + uriPath: string, + contentType: string, + ttl: number, + size: number +} + +export interface CacheInfo { + name: string, + apiEndPoint: string, + cacheOptions: NodeCache.Options, + cacheStats: NodeCache.Stats, + cache: CacheDetails[], +} + + +export class Cache { + constructor(private endpoint?: Endpoint, private realCache?: NodeCache) { + if (endpoint) this.setEndpoint(endpoint); + if (realCache) this.setCache(realCache); + } + + setEndpoint(endpoint: Endpoint): this { + this.endpoint = endpoint; + return this; + } + + getEndpoint(): Endpoint { + return this.endpoint; + } + + setCache(realCache: NodeCache): this { + this.realCache = realCache; + this.realCache.on('expired', (key, value) => { + debug(`hash "${key}" has expired`); + this.refresh(key, value); + }) + return this; + } + + getCache(): NodeCache { + return this.realCache; + } + + flush() { + return this.realCache.flushAll(); + } + + del(hash) { + return this.realCache.del(hash); + } + + async get(hash: string, path: string): Promise<EndpointValue> { + let value: EndpointValue | undefined = this.realCache.get(hash); + if (value == undefined) { + debug(`no cache hit. fetching ${path}`); + value = { + uriPath: path, + contentType: '', + data: '' + }; + try { + await this.endpoint.request(value); + this.realCache.set(hash, value); + } catch (err) { + console.error(err); + } + } else { + debug(`cache hit. ttl is ${this.realCache.getTtl(hash)}`); + + } + + return value; + } + + async refresh(key, value) { + const newValue = { + uriPath: value.uriPath, + contentType: '', + data: '' + } + await this.endpoint.request(newValue); + value.data = newValue.data; + value.contentType = newValue.contentType; + this.realCache.set(key, value); + debug(`successfully refreshed "${key}"`); + } + + keys(): string[] { + return this.realCache.keys(); + } + + getDetails(): CacheDetails[]; + getDetails(hash: string): CacheDetails; + getDetails(hash?): any { + return (hash) ? this._getDetails(hash) : this.realCache.keys().map(this._getDetails.bind(this)); + } + + private _getDetails(hash: string): CacheDetails { + return { + hash: hash, + ttl: this.realCache.getTtl(hash), + uriPath: this.realCache.get<EndpointValue>(hash).uriPath, + contentType: this.realCache.get<EndpointValue>(hash).contentType, + size: this.realCache.get<EndpointValue>(hash).data.length + }; + } + + toObject(): CacheObject { + const data = { + endPoint: this.endpoint.toString(), + cacheOptions: this.realCache.options, + cache: [] + } + + this.realCache.keys().map((hash) => { + data.cache.push({ + hash: hash, + value: this.realCache.get<EndpointValue>(hash) + }); + }); + + return <CacheObject>data; + } + + fromObject(data) { + const endpoint = new Endpoint(data.endPoint); + const realCache = new NodeCache(data.cacheOptions); + this.setEndpoint(endpoint); + + data.cache.map((item) => { + realCache.set(item.hash, item.value); + }); + + this.setCache(realCache); + } +} \ No newline at end of file diff --git a/src/endpoint.ts b/src/lib/endpoint.ts similarity index 93% rename from src/endpoint.ts rename to src/lib/endpoint.ts index cd94c001f7287ce8e107681118eac67eaf72987d..cf1346278f27e5448fa56998933cf900952aae52 100644 --- a/src/endpoint.ts +++ b/src/lib/endpoint.ts @@ -17,7 +17,7 @@ export class Endpoint { } toString(): string { - return Url.format(this.serverUrl); + return this.serverUrl ? Url.format(this.serverUrl) : ''; } request(value:EndpointValue): Promise<void>{ diff --git a/src/lib/middleware.ts b/src/lib/middleware.ts new file mode 100644 index 0000000000000000000000000000000000000000..1115eef4c4bc9644e10c4ea5c34ccca99430ec53 --- /dev/null +++ b/src/lib/middleware.ts @@ -0,0 +1,29 @@ +import * as express from 'express'; +import * as crypto from 'crypto'; +import { Register } from './register'; +import * as debugFactory from 'debug'; + +const debug = debugFactory('dacap:middleware'); + +export interface MiddlewareFunction { + (req: express.Request, res: express.Response, next: express.NextFunction): Promise<void> +} + +export function Middleware(register: Register): MiddlewareFunction { + + return async function (req: express.Request, res: express.Response, next: express.NextFunction): Promise<void> { + const pattern = new RegExp('^/([^/]+)/?(.*)$'); + const match = req.url.match(pattern); + try { + const cache = register.get(match[1]); + const uniQueryString = Object.keys(req.query).sort().map(function (key) { return `${key}=${req.query[key]}` }).join('&'); + const hash = crypto.createHash('sha1').update(req.path + uniQueryString).digest('hex'); + debug(`finding cache-entry for ${hash}`); + const value = await cache.get(hash, match[2]); + res.setHeader('content-type', value.contentType) + res.send(value.data); + } catch (err) { + res.status(404).send(err.message); + } + } +} diff --git a/src/lib/register.ts b/src/lib/register.ts new file mode 100644 index 0000000000000000000000000000000000000000..415fda80a0d27a54915268918c5ec99bc3b6e8e8 --- /dev/null +++ b/src/lib/register.ts @@ -0,0 +1,117 @@ +import * as debugFactory from 'debug'; +import * as NodeCache from 'node-cache'; +import * as fs from 'fs'; +import * as path from 'path'; +import * as Q from 'q'; + +import { CacheObject, Cache, CacheInfo } from './cache'; +import { Endpoint } from './endpoint'; + +const debug = debugFactory('dacap:register'); + +export interface RegisterObject { + name: string + data: CacheObject +} + +/** + * the cache register + * + * @export + * @class Register + */ +export class Register { + cacheRegister: { [key: string]: Cache } = {}; + saveTimer: NodeJS.Timer; + + constructor(private storage: string, public name: string) { + fs.existsSync(storage) || fs.mkdirSync(storage); + } + + has(name: string): boolean { + return !!this.cacheRegister[name]; + } + /** + * gets register by its name + * + * @memberof Register + */ + get(name: string): Cache { + if (!this.has(name)) throw new Error(`register not found: ${name}`); + return this.cacheRegister[name]; + } + + /** + * adds API-Endpoint to cache-register + * + * @memberof Register + */ + add(name: string, serverUrl: string, cacheOptions: NodeCache.Options) { + const endpoint: Endpoint = new Endpoint(serverUrl); + const nodeCache: NodeCache = new NodeCache(cacheOptions) + this.cacheRegister[name] = new Cache(endpoint, nodeCache); + } + + delete(name: string) { + if (!this.has(name)) throw new Error(`register not found: ${name}`); + delete this.cacheRegister[name]; + } + + getInfo(): CacheInfo[]; + getInfo(name: string): CacheInfo; + getInfo(name?): any { + return (name) ? this._getInfo(name) : Object.keys(this.cacheRegister).map((key): CacheInfo => { + return this._getInfo(key); + }); + } + + private _getInfo(name: string): CacheInfo { + return { + name: name, + apiEndPoint: this.cacheRegister[name].getEndpoint().toString(), + cacheOptions: this.cacheRegister[name].getCache().options, + cacheStats: this.cacheRegister[name].getCache().getStats(), + cache: this.cacheRegister[name].getDetails() + } + } + + toObject(): RegisterObject[] { + return Object.keys(this.cacheRegister).map((name) => { + return { + name: name, + data: this.cacheRegister[name].toObject() + } + }); + } + + save(interval?: number): void { + let success = this._save(); + + if (this.saveTimer) { + clearTimeout(this.saveTimer); + this.saveTimer = undefined; + } + + if (interval && success) { + this.saveTimer = setTimeout(() => { + this.save(interval); + }, interval); + } + } + + _save(): Boolean { + fs.writeFileSync(path.resolve(this.storage, this.name), JSON.stringify(this.toObject()), { encoding: 'utf8', flag: 'w' }); + debug(`register "${this.name}" saved to "${this.storage}"`); + return true; + } + + async restore(): Promise<void> { + const data = JSON.parse(fs.readFileSync(path.resolve(this.storage, this.name), { encoding: 'utf8' })); + + data.map((cache) => { + this.cacheRegister[cache.name] = new Cache(); + this.cacheRegister[cache.name].fromObject(cache.data); + }); + debug(`register "${this.name}" restored from "${this.storage}"`); + } +} \ No newline at end of file diff --git a/src/server.ts b/src/lib/server.ts similarity index 84% rename from src/server.ts rename to src/lib/server.ts index e7116ffe5674cfc38d630922023f5fd46cb11bcc..2d3521be6a9fb21c814bb76fc5f10c53d58dfbe2 100644 --- a/src/server.ts +++ b/src/lib/server.ts @@ -2,16 +2,18 @@ import * as express from 'express'; import * as compression from 'compression'; import * as debugFactory from 'debug'; import * as path from 'path'; -import * as cache from './cache'; import * as bodyparser from 'body-parser'; import * as cors from 'cors'; import * as url from 'url'; import * as PrettyPrint from 'js-object-pretty-print'; +import { Register } from './register'; +import { Middleware } from './middleware'; + const debug = debugFactory('dacap:server'); export class Server { - private register: cache.Register; + private register: Register; private expressApp: express.Application; private corsOptions: cors.CorsOptions; private prePath: string = ''; @@ -42,7 +44,7 @@ export class Server { } private initRegister() { - this.register = new cache.Register(this.config.storagePath, this.config.registerName); + this.register = new Register(this.config.storagePath, this.config.registerName); this.register.restore(); this.register.save(this.config.autosaveInterval * 1000); } @@ -130,9 +132,18 @@ export class Server { } }); - this.expressApp.use(this.prePath + '/admin/', express.static(path.resolve(__dirname, '..', 'public'))); + if (process.env.NODE_ENV === 'development') { + let webpack = require('webpack'); + let WebpackDevMiddleware = require('webpack-dev-middleware'); + let webpackConfig = require('../webpack.dev'); + this.expressApp.use(WebpackDevMiddleware(webpack(webpackConfig), { + publicPath: `${this.prePath}/admin/` + })); + } else { + this.expressApp.use(`${this.prePath}/admin/`, express.static(path.resolve(__dirname, '..', 'public'))); + } - this.expressApp.use(this.prePath + this.config.proxyPath, cache.Middleware(this.register)); + this.expressApp.use(this.prePath + this.config.proxyPath, Middleware(this.register)); } listen(port: number, cb) { diff --git a/src/test/cache.ts b/src/test/cache.ts new file mode 100644 index 0000000000000000000000000000000000000000..d7f5185df4f12748e70058770a3f23272c80b6dc --- /dev/null +++ b/src/test/cache.ts @@ -0,0 +1,123 @@ +/// <reference types="mocha" /> + +import { Cache } from '../lib/cache'; +import { Endpoint } from '../lib/endpoint'; +import * as NodeCache from 'node-cache'; +import * as should from 'should'; +import * as fs from 'fs'; +import * as rimraf from 'rimraf'; +import * as path from 'path'; +import * as nock from 'nock'; + +const tmpPath = '.dacap-tmp'; + +describe('Cache', () => { + describe('instantiate empty', () => { + it('should be an instance of Cache', () => { + let cache = new Cache(); + should(cache).be.instanceof(Cache); + }); + }); + + describe('instantiate with endpoint and node-cache', () => { + it('should be an instance of Cache', () => { + let endpoint = new Endpoint(); + let realCache = new NodeCache(); + let cache = new Cache(endpoint, realCache); + should(cache).be.instanceof(Cache); + }); + }); + + describe('setEndpoint', () => { + it('should set endpoint', () => { + let endpoint = new Endpoint() + let cache = new Cache(); + should(cache.setEndpoint(endpoint)).be.instanceof(Cache); + }); + }); + + describe('getEndpoint', () => { + let cache; + beforeEach(() => { + cache = new Cache(new Endpoint()); + }); + + it('should get endpoint', () => { + should(cache.getEndpoint()).be.instanceof(Endpoint); + }); + }); + + describe('setCache', () => { + let cache; + beforeEach(() => { + cache = new Cache(); + }); + + it('should set cache', () => { + should(cache.setCache(new NodeCache())).be.instanceof(Cache); + }) + }); + + describe('getCache', () => { + let cache; + beforeEach(() => { + cache = new Cache(null, new NodeCache()); + }); + + it('should get cache', () => { + should(cache.getCache()).be.instanceof(NodeCache); + }); + }); + + describe('flush', () => { + let cache, realCache = new NodeCache(); + beforeEach(() => { + cache = new Cache(null, realCache); + realCache.set('validHash', 'validData'); + realCache.set('anotherValidHash', 'anotherValidData'); + }); + + it('should remove all values', () => { + cache.flush(); + should(realCache.get('validHash')).be.undefined; + should(realCache.get('anotherValidHash')).be.undefined; + }); + }); + + describe('delete', () => { + let cache, realCache = new NodeCache(); + beforeEach(() => { + cache = new Cache(null, realCache); + realCache.set('validHash', 'validData'); + realCache.set('anotherValidHash', 'anotherValidData'); + }); + + it('should remove onnly one value', () => { + cache.del('validHash'); + should(realCache.get('validHash')).be.undefined; + should(realCache.get('anotherValidHash')).eql('anotherValidData'); + }); + }); + + describe('get', () => { + let cache; + beforeEach(() => { + nock('https://api.example.com').get('/').reply(200, { + "validKey": "validData" + }); + + cache = new Cache(new Endpoint('https://api.example.com'), new NodeCache); + }); + + it('should return the json',(done) => { + cache.get('newHash', '/').then((value) => { + should(value).eql({ + uriPath: '/', + contentType: '', + data:'validData' + }); + done(); + }).catch(done); + }); + }); +}); \ No newline at end of file diff --git a/src/test/register.ts b/src/test/register.ts new file mode 100644 index 0000000000000000000000000000000000000000..774d9ee4b72080002f3afa4755e237e8b6102b8c --- /dev/null +++ b/src/test/register.ts @@ -0,0 +1,316 @@ +/// <reference types="mocha" /> + +import { Register } from '../lib/register'; +import * as should from 'should'; +import * as fs from 'fs'; +import * as rimraf from 'rimraf'; +import * as path from 'path'; + +const tmpPath = '.dacap-tmp'; + +describe('Register', () => { + describe('instantiate', () => { + describe('with valid folder', () => { + afterEach(() => { + rimraf.sync(tmpPath); + }); + + it('should perform successfully', () => { + let register = new Register(`${tmpPath}/`, 'foo'); + should(register).instanceof(Register); + }); + + it('even without trailing slash', () => { + let register = new Register(tmpPath, 'foo'); + should(register).instanceof(Register); + }); + }); + + describe('with invalid folder name', () => { + it('should throw an exception', () => { + should(() => { + let register = new Register('', 'foo'); + }).throw(/NOENT/); + }); + }); + + describe('with insufficient permissions', () => { + it('should throw an exception', () => { + should(() => { + let register = new Register('/root/.tmp', 'foo'); + }).throw(/EACCES/); + }); + }); + }); + + describe('has', () => { + let register: Register; + beforeEach(() => { + register = new Register(tmpPath, 'foo'); + register.add('validCache', 'http://example.com/', {}); + }); + + describe('existing cache', () => { + it('should return true', () => { + should(register.has('validCache')).eql(true); + }); + }); + + describe('existing cache', () => { + it('should return false', () => { + should(register.has('invalidCache')).eql(false); + }); + }); + }); + + describe('get', () => { + let register: Register; + beforeEach(() => { + register = new Register(tmpPath, 'foo'); + register.add('validCache', 'http://example.com/', {}); + }); + + afterEach(() => { + rimraf.sync(tmpPath) + }); + + describe('existing cache', () => { + it('should be an instance of Cache', () => { + let validCache = register.get('validCache'); + should(validCache).not.undefined; + }); + }); + + describe('non-existing cache', () => { + it('should throw an error', () => { + should(() => { + let validCache = register.get('invalidCache'); + }).throw('register not found: invalidCache'); + }); + }); + }); + + describe('add', () => { + let register: Register; + beforeEach(() => { + register = new Register(tmpPath, 'foo'); + }); + + afterEach(() => { + rimraf.sync(tmpPath) + }); + + describe('new cache', () => { + beforeEach(() => { + register.add('newCache', 'http://example.com/', {}); + }); + it('should have added a new cache', () => { + should(register.get('newCache')).not.be.undefined; + }); + }); + }); + + describe('delete', () => { + let register: Register; + beforeEach(() => { + register = new Register(tmpPath, 'foo'); + register.add('validCache', 'http://example.com/', {}); + }); + + afterEach(() => { + rimraf.sync(tmpPath) + }); + + describe('existing cache', () => { + beforeEach(() => { + register.delete('validCache'); + }); + it('should have removed existing cache', () => { + should(register.cacheRegister['newCache']).eql(undefined); + }); + }); + + describe('non-existing cache', () => { + it('should throw an error', () => { + should(() => { + register.delete('nonExistingCache'); + }).throw('register not found: nonExistingCache'); + }); + }); + }); + + describe('getInfo', () => { + let register: Register; + beforeEach(() => { + register = new Register(tmpPath, 'foo'); + register.add('newCache', 'http://example.com/', {}); + }); + + describe('without passing cache name', () => { + it('should return list of CacheInfo objects', () => { + let cacheInfoList = register.getInfo(); + should(cacheInfoList).be.an.Array(); + should(cacheInfoList[0]).have.keys('name', 'apiEndPoint', 'cacheOptions', 'cacheStats', 'cache'); + }); + }); + + describe('with passing cache name', () => { + it('should return a CacheInfo object', () => { + let cacheInfo = register.getInfo('newCache'); + should(cacheInfo).have.keys('name', 'apiEndPoint', 'cacheOptions', 'cacheStats', 'cache'); + }); + }); + }); + + describe('toObject', () => { + let register: Register; + beforeEach(() => { + register = new Register(tmpPath, 'foo'); + register.add('newCache', 'http://example.com/', {}); + }); + + describe('existing register', () => { + let serializedCache; + beforeEach(() => { + serializedCache = register.toObject(); + }); + + it('should serialize the register', () => { + should(serializedCache).be.an.Array(); + should(serializedCache[0]).containDeep({ + name: "newCache", + data: { + endPoint: 'http://example.com/', + cache: [] + } + }); + + }); + }); + }); + describe('save', () => { + let register: Register; + + afterEach(() => { + rimraf.sync(tmpPath); + }); + + describe('with sufficient permissions', () => { + beforeEach(() => { + register = new Register(tmpPath, 'foo'); + register.add('newCache', 'http://example.com/', {}); + }); + + describe('once', () => { + it('should create a save file', () => { + register.save(); + should(() => { + fs.statSync(path.resolve(tmpPath, 'foo')) + }).not.throw(Error); + }); + }); + + describe('with interval', () => { + let filesize1, filesize2; + + it('should increase file size', (done) => { + register.save(100); + filesize1 = fs.statSync(path.resolve(tmpPath, 'foo')).size + register.add('moreCache', 'https://api.example.com/', {}); + setTimeout(() => { + should(() => { + filesize2 = fs.statSync(path.resolve(tmpPath, 'foo')).size + }).not.throw(Error) + should(filesize2).above(filesize1); + register.add('evenMoreCache', 'https://anotherapi.example.com/', {}); + filesize1 = filesize2; + }, 150); + setTimeout(() => { + should(() => { + filesize2 = fs.statSync(path.resolve(tmpPath, 'foo')).size + }).not.throw(Error) + should(filesize2).above(filesize1); + done(); + }, 250); + }); + }); + + describe('remove interval', () => { + let filesize1, filesize2; + + it('should stop saving file', () => { + register.save(100); + filesize1 = fs.statSync(path.resolve(tmpPath, 'foo')).size + register.save(); + register.add('moreCache', 'https://api.example.com/', {}); + + setTimeout(() => { + should(() => { + filesize2 = fs.statSync(path.resolve(tmpPath, 'foo')).size + }).not.throw(Error) + should(filesize2).equal(filesize1); + }, 150); + }); + }); + }); + + describe('with insufficient permissions', () => { + beforeEach(() => { + register = new Register('/root', 'foo'); + register.add('newCache', 'http://example.com/', {}); + }); + + describe('once', () => { + it('should throw an error', () => { + should(() => { + register.save(); + }).throw(/EACCES/); + }); + }); + }); + }); + + describe('restore', () => { + describe('existing file', () => { + beforeEach(() => { + let register = new Register(tmpPath, 'foo'); + register.add('newCache', 'http://example.com/', {}); + let Cache = register.get('newCache').getCache(); + Cache.set('validHash', { + uriPath: '/foo/bar', + contentType: 'x-mostly/garbage', + data: 'validData' + }); + register.save(); + }); + + afterEach(() => { + rimraf.sync(tmpPath); + }); + + it('should restore the register', (done) => { + let register = new Register(tmpPath, 'foo'); + register.restore().then(() => { + should(register.has('newCache')).be.true; + should(register.getInfo('newCache').apiEndPoint).eql('http://example.com/'); + should(register.get('newCache').getCache().get('validHash')).containEql({ + uriPath: '/foo/bar', + contentType: 'x-mostly/garbage', + data: 'validData' + }); + done(); + }).catch(done); + }); + }); + describe('non-existing file', () => { + it('should reject promise', (done) => { + let register = new Register(tmpPath, 'foo'); + register.restore().then(() => { + done('promise was resolved') + }).catch(() => { + done(); + }) + }); + }); + }); +}); \ No newline at end of file diff --git a/test/cache.js b/test/cache.js deleted file mode 100644 index df2f88d10712a084aacd92e6fd2ce0635c59fcb8..0000000000000000000000000000000000000000 --- a/test/cache.js +++ /dev/null @@ -1,11 +0,0 @@ -const path = require('path'); - -const Middleware = require('../dist/cache').Middleware; -const Register = require('../dist/cache').Register; -const Cache = require('../dist/cache').Cache; - -describe('Middleware', () => { - it('should be a function', (done) => { - done(); - }); -}) \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index 1d467e6ee29a1edda39a4d753e2d390c7c010bd4..af0b79e7177bb985f204d1774156441a7566065f 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -3,10 +3,10 @@ "module": "commonjs", "target": "es6", "noImplicitAny": false, - "outDir": "dist" + "outDir": "./" }, - "include": ["src/*"], + "include": ["src/**/*"], "exclude": [ - "admin" + "src/admin" ] } \ No newline at end of file