diff --git a/.eslintrc.js b/.eslintrc.js index ce75cdf..8365e43 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,18 +1,17 @@ module.exports = { extends: [ - 'standard', - 'prettier', + 'plugin:chai-friendly/recommended', 'plugin:prettier/recommended', 'plugin:import/errors', 'plugin:import/warnings', 'plugin:node/recommended', 'plugin:promise/recommended', ], - plugins: ['import', 'prettier', 'promise'], ignorePatterns: ['examples/**'], env: { node: true, es2020: true, + mocha: true, }, globals: { module: true, @@ -22,13 +21,44 @@ module.exports = { sourceType: 'module', }, rules: { + 'promise/no-callback-in-promise': 'off', 'space-before-function-paren': 'off', - 'new-cap': 'off', - camelcase: 'off', 'import/no-unresolved': ['error', { commonjs: true }], 'import/no-extraneous-dependencies': 'error', 'node/shebang': 'off', 'node/no-unsupported-features/es-syntax': 'off', 'node/no-missing-require': 'off', + 'no-global-assign': ['error', { exceptions: ['require'] }], + 'node/no-unpublished-require': [ + 'error', + { + allowModules: ['redis-mock'], + }, + ], + 'no-empty': ['error', { allowEmptyCatch: true }], + camelcase: 'off', + 'prefer-destructuring': [ + 'error', + { + VariableDeclarator: { + array: false, + object: true, + }, + AssignmentExpression: { + array: true, + object: false, + }, + }, + { + enforceForRenamedProperties: false, + }, + ], + 'require-await': 'error', + 'no-return-await': 'error', + 'no-return-assign': 'error', + eqeqeq: 'error', + 'no-var': 'error', + 'prefer-const': 'error', + 'prefer-arrow-callback': 'error', }, } diff --git a/.husky/pre-push b/.husky/pre-push new file mode 100755 index 0000000..1b411b0 --- /dev/null +++ b/.husky/pre-push @@ -0,0 +1,5 @@ +#!/bin/sh +. "$(dirname "$0")/_/husky.sh" + +pnpm lint +pnpm test diff --git a/.lintstagedrc.js b/.lintstagedrc.js new file mode 100644 index 0000000..89711f4 --- /dev/null +++ b/.lintstagedrc.js @@ -0,0 +1,3 @@ +module.exports = { + 'src/**/*.js': ['prettier --write', 'eslint --fix'], +} diff --git a/.lintstagedrc.yml b/.lintstagedrc.yml deleted file mode 100644 index 68e0346..0000000 --- a/.lintstagedrc.yml +++ /dev/null @@ -1,3 +0,0 @@ -src/**/*.js: - - prettier --write - - eslint --fix diff --git a/package.json b/package.json index e6ce5e2..bc98a11 100644 --- a/package.json +++ b/package.json @@ -27,8 +27,12 @@ "sats", "bitcoin" ], + "engines": { + "node": ">=14", + "pnpm": ">=6" + }, "scripts": { - "test": "echo \"No tests.\"", + "test": "mocha test/index.js --bail --exit", "prepare": "husky install", "eslint": "eslint --fix", "prettier": "prettier --write", @@ -42,15 +46,18 @@ "devDependencies": { "@commitlint/cli": "^13.1.0", "@commitlint/config-conventional": "^13.1.0", + "chai": "^4.3.4", "eslint": "^8.0.1", "eslint-config-prettier": "^8.3.0", "eslint-config-standard": "^16.0.3", + "eslint-plugin-chai-friendly": "^0.7.2", "eslint-plugin-import": "^2.23.4", "eslint-plugin-node": "^11.1.0", "eslint-plugin-prettier": "^4.0.0", "eslint-plugin-promise": "^5.1.1", "husky": "^7.0.1", "lint-staged": "^11.1.2", + "mocha": "^9.1.3", "prettier": "^2.3.2" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a1c101c..32c994d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -3,9 +3,11 @@ lockfileVersion: 5.3 specifiers: '@commitlint/cli': ^13.1.0 '@commitlint/config-conventional': ^13.1.0 + chai: ^4.3.4 eslint: ^8.0.1 eslint-config-prettier: ^8.3.0 eslint-config-standard: ^16.0.3 + eslint-plugin-chai-friendly: ^0.7.2 eslint-plugin-import: ^2.23.4 eslint-plugin-node: ^11.1.0 eslint-plugin-prettier: ^4.0.0 @@ -13,6 +15,7 @@ specifiers: eventemitter3: ^4.0.7 husky: ^7.0.1 lint-staged: ^11.1.2 + mocha: ^9.1.3 prettier: ^2.3.2 ws: ^8.0.0 @@ -23,15 +26,18 @@ dependencies: devDependencies: '@commitlint/cli': 13.2.1 '@commitlint/config-conventional': 13.2.0 + chai: 4.3.4 eslint: 8.0.1 eslint-config-prettier: 8.3.0_eslint@8.0.1 eslint-config-standard: 16.0.3_74219197973d038647d2b2f3e7b02145 + eslint-plugin-chai-friendly: 0.7.2_eslint@8.0.1 eslint-plugin-import: 2.25.2_eslint@8.0.1 eslint-plugin-node: 11.1.0_eslint@8.0.1 eslint-plugin-prettier: 4.0.0_15313026b01cdd263118061498e273ac eslint-plugin-promise: 5.1.1_eslint@8.0.1 husky: 7.0.4 lint-staged: 11.2.3 + mocha: 9.1.3 prettier: 2.4.1 packages: @@ -261,6 +267,10 @@ packages: resolution: {integrity: sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==} dev: true + /@ungap/promise-all-settled/1.1.2: + resolution: {integrity: sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==} + dev: true + /JSONStream/1.3.5: resolution: {integrity: sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==} hasBin: true @@ -331,6 +341,14 @@ packages: color-convert: 2.0.1 dev: true + /anymatch/3.1.2: + resolution: {integrity: sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==} + engines: {node: '>= 8'} + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.0 + dev: true + /arg/4.1.3: resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} dev: true @@ -374,6 +392,10 @@ packages: engines: {node: '>=0.10.0'} dev: true + /assertion-error/1.1.0: + resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==} + dev: true + /astral-regex/2.0.0: resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==} engines: {node: '>=8'} @@ -383,6 +405,11 @@ packages: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} dev: true + /binary-extensions/2.2.0: + resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} + engines: {node: '>=8'} + dev: true + /brace-expansion/1.1.11: resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} dependencies: @@ -397,6 +424,10 @@ packages: fill-range: 7.0.1 dev: true + /browser-stdout/1.3.1: + resolution: {integrity: sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==} + dev: true + /buffer-from/1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} dev: true @@ -427,6 +458,23 @@ packages: engines: {node: '>=6'} dev: true + /camelcase/6.2.0: + resolution: {integrity: sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==} + engines: {node: '>=10'} + dev: true + + /chai/4.3.4: + resolution: {integrity: sha512-yS5H68VYOCtN1cjfwumDSuzn/9c+yza4f3reKXlE5rUg7SFcCEy90gJvydNgOYtblyf4Zi6jIWRnXOgErta0KA==} + engines: {node: '>=4'} + dependencies: + assertion-error: 1.1.0 + check-error: 1.0.2 + deep-eql: 3.0.1 + get-func-name: 2.0.0 + pathval: 1.1.1 + type-detect: 4.0.8 + dev: true + /chalk/2.4.2: resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} engines: {node: '>=4'} @@ -444,6 +492,25 @@ packages: supports-color: 7.2.0 dev: true + /check-error/1.0.2: + resolution: {integrity: sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=} + dev: true + + /chokidar/3.5.2: + resolution: {integrity: sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==} + engines: {node: '>= 8.10.0'} + dependencies: + anymatch: 3.1.2 + braces: 3.0.2 + glob-parent: 5.1.2 + is-binary-path: 2.1.0 + is-glob: 4.0.3 + normalize-path: 3.0.0 + readdirp: 3.6.0 + optionalDependencies: + fsevents: 2.3.2 + dev: true + /clean-stack/2.2.0: resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==} engines: {node: '>=6'} @@ -622,6 +689,18 @@ packages: engines: {node: '>=0.10.0'} dev: true + /decamelize/4.0.0: + resolution: {integrity: sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==} + engines: {node: '>=10'} + dev: true + + /deep-eql/3.0.1: + resolution: {integrity: sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==} + engines: {node: '>=0.12'} + dependencies: + type-detect: 4.0.8 + dev: true + /deep-is/0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} dev: true @@ -638,6 +717,11 @@ packages: engines: {node: '>=0.3.1'} dev: true + /diff/5.0.0: + resolution: {integrity: sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==} + engines: {node: '>=0.3.1'} + dev: true + /doctrine/2.1.0: resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} engines: {node: '>=0.10.0'} @@ -765,6 +849,15 @@ packages: pkg-dir: 2.0.0 dev: true + /eslint-plugin-chai-friendly/0.7.2_eslint@8.0.1: + resolution: {integrity: sha512-LOIfGx5sZZ5FwM1shr2GlYAWV9Omdi+1/3byuVagvQNoGUuU0iHhp7AfjA1uR+4dJ4Isfb4+FwBJgQajIw9iAg==} + engines: {node: '>=0.10.0'} + peerDependencies: + eslint: '>=3.0.0' + dependencies: + eslint: 8.0.1 + dev: true + /eslint-plugin-es/3.0.1_eslint@8.0.1: resolution: {integrity: sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ==} engines: {node: '>=8.10.0'} @@ -1045,6 +1138,11 @@ packages: rimraf: 3.0.2 dev: true + /flat/5.0.2: + resolution: {integrity: sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==} + hasBin: true + dev: true + /flatted/3.2.2: resolution: {integrity: sha512-JaTY/wtrcSyvXJl4IMFHPKyFur1sE9AUqc0QnhOaJ0CxHtAoIV8pYDzeEfAaNEtGkOfq4gr3LBFmdXW5mOQFnA==} dev: true @@ -1062,6 +1160,14 @@ packages: resolution: {integrity: sha1-FQStJSMVjKpA20onh8sBQRmU6k8=} dev: true + /fsevents/2.3.2: + resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + requiresBuild: true + dev: true + optional: true + /function-bind/1.1.1: resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==} dev: true @@ -1075,6 +1181,10 @@ packages: engines: {node: 6.* || 8.* || >= 10.*} dev: true + /get-func-name/2.0.0: + resolution: {integrity: sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=} + dev: true + /get-intrinsic/1.1.1: resolution: {integrity: sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==} dependencies: @@ -1112,6 +1222,13 @@ packages: through2: 4.0.2 dev: true + /glob-parent/5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + dependencies: + is-glob: 4.0.3 + dev: true + /glob-parent/6.0.2: resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} engines: {node: '>=10.13.0'} @@ -1119,6 +1236,17 @@ packages: is-glob: 4.0.3 dev: true + /glob/7.1.7: + resolution: {integrity: sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==} + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.0.4 + once: 1.4.0 + path-is-absolute: 1.0.1 + dev: true + /glob/7.2.0: resolution: {integrity: sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==} dependencies: @@ -1148,6 +1276,11 @@ packages: resolution: {integrity: sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==} dev: true + /growl/1.10.5: + resolution: {integrity: sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==} + engines: {node: '>=4.x'} + dev: true + /hard-rejection/2.1.0: resolution: {integrity: sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==} engines: {node: '>=6'} @@ -1186,6 +1319,11 @@ packages: function-bind: 1.1.1 dev: true + /he/1.2.0: + resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} + hasBin: true + dev: true + /hosted-git-info/2.8.9: resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==} dev: true @@ -1270,6 +1408,13 @@ packages: has-bigints: 1.0.1 dev: true + /is-binary-path/2.1.0: + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} + dependencies: + binary-extensions: 2.2.0 + dev: true + /is-boolean-object/1.1.2: resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==} engines: {node: '>= 0.4'} @@ -1345,6 +1490,11 @@ packages: engines: {node: '>=0.10.0'} dev: true + /is-plain-obj/2.1.0: + resolution: {integrity: sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==} + engines: {node: '>=8'} + dev: true + /is-regex/1.1.4: resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} engines: {node: '>= 0.4'} @@ -1388,6 +1538,11 @@ packages: text-extensions: 1.9.0 dev: true + /is-unicode-supported/0.1.0: + resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} + engines: {node: '>=10'} + dev: true + /is-weakref/1.0.1: resolution: {integrity: sha512-b2jKc2pQZjaeFYWEf7ScFj+Be1I+PXmlu572Q8coTXZ+LD/QQZ7ShPMst8h16riVgyXTQwUsFEl74mDvc/3MHQ==} dependencies: @@ -1536,6 +1691,14 @@ packages: resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} dev: true + /log-symbols/4.1.0: + resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} + engines: {node: '>=10'} + dependencies: + chalk: 4.1.2 + is-unicode-supported: 0.1.0 + dev: true + /log-update/4.0.0: resolution: {integrity: sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==} engines: {node: '>=10'} @@ -1625,6 +1788,37 @@ packages: resolution: {integrity: sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==} dev: true + /mocha/9.1.3: + resolution: {integrity: sha512-Xcpl9FqXOAYqI3j79pEtHBBnQgVXIhpULjGQa7DVb0Po+VzmSIK9kanAiWLHoRR/dbZ2qpdPshuXr8l1VaHCzw==} + engines: {node: '>= 12.0.0'} + hasBin: true + dependencies: + '@ungap/promise-all-settled': 1.1.2 + ansi-colors: 4.1.1 + browser-stdout: 1.3.1 + chokidar: 3.5.2 + debug: 4.3.2_supports-color@8.1.1 + diff: 5.0.0 + escape-string-regexp: 4.0.0 + find-up: 5.0.0 + glob: 7.1.7 + growl: 1.10.5 + he: 1.2.0 + js-yaml: 4.1.0 + log-symbols: 4.1.0 + minimatch: 3.0.4 + ms: 2.1.3 + nanoid: 3.1.25 + serialize-javascript: 6.0.0 + strip-json-comments: 3.1.1 + supports-color: 8.1.1 + which: 2.0.2 + workerpool: 6.1.5 + yargs: 16.2.0 + yargs-parser: 20.2.4 + yargs-unparser: 2.0.0 + dev: true + /ms/2.0.0: resolution: {integrity: sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=} dev: true @@ -1637,6 +1831,12 @@ packages: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} dev: true + /nanoid/3.1.25: + resolution: {integrity: sha512-rdwtIXaXCLFAQbnfqDRnI6jaRHp9fTcYBjtFKE8eezcZ7LuLjhUaQGNeMXf1HmRoCH32CLz6XwX0TtxEOS/A3Q==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + dev: true + /natural-compare/1.4.0: resolution: {integrity: sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=} dev: true @@ -1830,6 +2030,10 @@ packages: engines: {node: '>=8'} dev: true + /pathval/1.1.1: + resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==} + dev: true + /picomatch/2.3.0: resolution: {integrity: sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==} engines: {node: '>=8.6'} @@ -1886,6 +2090,12 @@ packages: engines: {node: '>=8'} dev: true + /randombytes/2.1.0: + resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} + dependencies: + safe-buffer: 5.2.1 + dev: true + /read-pkg-up/7.0.1: resolution: {integrity: sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==} engines: {node: '>=8'} @@ -1914,6 +2124,13 @@ packages: util-deprecate: 1.0.2 dev: true + /readdirp/3.6.0: + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} + dependencies: + picomatch: 2.3.0 + dev: true + /redent/3.0.0: resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==} engines: {node: '>=8'} @@ -2004,6 +2221,12 @@ packages: lru-cache: 6.0.0 dev: true + /serialize-javascript/6.0.0: + resolution: {integrity: sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==} + dependencies: + randombytes: 2.1.0 + dev: true + /shebang-command/2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} @@ -2254,6 +2477,11 @@ packages: prelude-ls: 1.2.1 dev: true + /type-detect/4.0.8: + resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} + engines: {node: '>=4'} + dev: true + /type-fest/0.18.1: resolution: {integrity: sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==} engines: {node: '>=10'} @@ -2343,6 +2571,10 @@ packages: engines: {node: '>=0.10.0'} dev: true + /workerpool/6.1.5: + resolution: {integrity: sha512-XdKkCK0Zqc6w3iTxLckiuJ81tiD/o5rBE/m+nXpRCB+/Sq4DqkfXZ/x0jW02DG1tGsfUGXbTJyZDP+eu67haSw==} + dev: true + /wrap-ansi/6.2.0: resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} engines: {node: '>=8'} @@ -2392,11 +2624,39 @@ packages: engines: {node: '>= 6'} dev: true + /yargs-parser/20.2.4: + resolution: {integrity: sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==} + engines: {node: '>=10'} + dev: true + /yargs-parser/20.2.9: resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} engines: {node: '>=10'} dev: true + /yargs-unparser/2.0.0: + resolution: {integrity: sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==} + engines: {node: '>=10'} + dependencies: + camelcase: 6.2.0 + decamelize: 4.0.0 + flat: 5.0.2 + is-plain-obj: 2.1.0 + dev: true + + /yargs/16.2.0: + resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==} + engines: {node: '>=10'} + dependencies: + cliui: 7.0.4 + escalade: 3.1.1 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 20.2.9 + dev: true + /yargs/17.2.1: resolution: {integrity: sha512-XfR8du6ua4K6uLGm5S6fA+FIJom/MdJcFNVY8geLlp2v8GYbOXD4EB1tPNZsRn4vBzKGMgb5DRZMeWuFc2GO8Q==} engines: {node: '>=12'} diff --git a/src/rest.js b/src/rest.js index 46d0e13..c3dd9fb 100644 --- a/src/rest.js +++ b/src/rest.js @@ -2,6 +2,7 @@ const https = require('https') const { URLSearchParams } = require('url') const getHostname = (network = null) => { + // If this en var is set ovveride LNMARKETS_NETWORK if (process.env.LNMARKETS_API_URL) { return process.env.LNMARKETS_API_URL } else if (network === 'mainnet') { @@ -13,69 +14,90 @@ const getHostname = (network = null) => { } const RestError = class RestError extends Error { - constructor(status, message) { - if (!status) throw new Error('An HTTP Error need a status') - + constructor(statusCode, { code, message }) { super(message) - this.name = 'RestError' - this.status = status + this.name = 'LNMarketsRestError' + this.statusCode = statusCode + this.code = code this.message = message } } module.exports = class LNMarketsRest { constructor(opt = {}) { - const { token, network, version } = opt + const { token, network, version, customHeaders, fullResponse } = opt this.token = token this.network = network || process.env.LNMARKETS_NETWORK || 'mainnet' this.version = version || process.env.LNMARKETS_API_VERSION || 'v1' this.hostname = getHostname(this.network) + this.customHeaders = customHeaders || {} + this.fullResponse = false || fullResponse + this.debug = false || opt.debug } - requestAPI(opt = {}) { + _requestOptions(opt = {}) { const { method, endpoint, params, credentials } = opt - const { hostname, version, token } = this + + const headers = { + 'Content-Type': 'application/json', + ...this.customHeaders, + } const options = { port: 443, - hostname, + hostname: this.hostname, method, - path: `/${version}${endpoint}`, - headers: { - 'Content-Type': 'application/json', - }, + path: `/${this.version}${endpoint}`, + headers, } - if (credentials && token) { - options.headers.Authorization = `Bearer ${token}` + if (credentials && this.token) { + options.headers.Authorization = `Bearer ${this.token}` } if (method.match(/^(GET|DELETE)$/) && params) { options.path += `?${new URLSearchParams(params).toString()}` } + return options + } + + requestAPI(opt = {}) { + const options = this._requestOptions(opt) + return new Promise((resolve, reject) => { - const call = https.request(options, (response) => { + const req = https.request(options, (res) => { + res.setEncoding('utf8') + let data = '' - response.on('data', (chunk) => { + res.on('data', (chunk) => { data += chunk }) - response.on('error', (error) => { + res.on('error', (error) => { reject(error) }) - response.on('end', () => { + res.on('end', () => { + if (this.debug) { + return resolve({ req, res }) + } + try { const body = JSON.parse(data) - if (response.statusCode === 200) { - resolve(body) + if (res.statusCode === 200) { + if (this.fullResponse) { + const { statusCode, headers } = res + resolve({ body, statusCode, headers }) + } else { + resolve(body) + } } else { - reject(new RestError(response.statusCode, body)) + reject(new RestError(res.statusCode, body)) } } catch (error) { error.data = data @@ -84,18 +106,19 @@ module.exports = class LNMarketsRest { }) }) - call.on('error', (error) => { + req.on('error', (error) => { reject(error) }) - if (method.match(/^(PUT|POST)$/) && params) { - call.write(JSON.stringify(params)) + if (options.method.match(/^(PUT|POST)$/) && opt.params) { + req.write(JSON.stringify(opt.params)) } - call.end() + req.end() }) } + // Hook to do stuff before sending a request ... beforeRequestApi(options) { return this.requestAPI(options) } diff --git a/src/websocket.js b/src/websocket.js index 92647e6..23b6da0 100644 --- a/src/websocket.js +++ b/src/websocket.js @@ -41,13 +41,13 @@ module.exports = class LNMarketsWebsocket extends EventEmitter { resolve() } - this.ws.onMessage = this._onMessage.bind(this) + this.ws.onMessage = this.onMessage.bind(this) this.ws.connect() }) } - async _send({ request, id = null }) { + send({ request, id = null }) { // Set a random ID if none is sent Object.assign(request, { jsonrpc: '2.0', @@ -76,7 +76,7 @@ module.exports = class LNMarketsWebsocket extends EventEmitter { }) } - _onMessage(message) { + onMessage(message) { try { const response = JSON.parse(message) if (response && response.id) { @@ -95,7 +95,7 @@ module.exports = class LNMarketsWebsocket extends EventEmitter { params, } - return this._send({ request, id }) + return this.send({ request, id }) } unsubscribe({ params, id }) { @@ -104,7 +104,7 @@ module.exports = class LNMarketsWebsocket extends EventEmitter { params, } - return this._send({ request, id }) + return this.send({ request, id }) } listEvents() { @@ -112,7 +112,7 @@ module.exports = class LNMarketsWebsocket extends EventEmitter { method: '__listEvents', } - return this._send({ request }) + return this.send({ request }) } listMethods() { @@ -120,7 +120,7 @@ module.exports = class LNMarketsWebsocket extends EventEmitter { method: '__listMethods', } - return this._send({ request }) + return this.send({ request }) } terminate() { diff --git a/test/index.js b/test/index.js new file mode 100644 index 0000000..8f092cd --- /dev/null +++ b/test/index.js @@ -0,0 +1,41 @@ +const { expect } = require('chai') +const { LNMarketsRest } = require('../index.js') + +describe('Rest', () => { + it('New LNMarketsRest', () => { + const lnm = new LNMarketsRest() + expect(lnm).to.be.instanceOf(LNMarketsRest) + }) + + it('Domain does not exis', (done) => { + process.env.LNMARKETS_API_URL = 'api.lnmarkets.fr' + + const lnm = new LNMarketsRest() + + lnm.nodeState().catch((error) => { + expect(error).to.be.instanceOf(Error) + expect(error.message).to.be.equal( + 'getaddrinfo ENOTFOUND api.lnmarkets.fr' + ) + done() + }) + + delete process.env.LNMARKETS_API_URL + }) + + it('Good request', async () => { + const lnm = new LNMarketsRest() + const info = await lnm.nodeState() + expect(info).to.be.an('object') + }) + + it('Unauthorized', (done) => { + const lnm = new LNMarketsRest() + lnm.futuresCancelAllPositions().catch((error) => { + expect(error).to.be.instanceOf(Error) + expect(error.statusCode).to.be.equal(401) + expect(error.code).to.be.equal('Unauthorized') + done() + }) + }) +})