diff --git a/.all-contributorsrc b/.all-contributorsrc new file mode 100644 index 00000000..b29ba575 --- /dev/null +++ b/.all-contributorsrc @@ -0,0 +1,456 @@ +{ + "projectName": "node-binance-api", + "projectOwner": "jaggedsoft", + "repoType": "github", + "repoHost": "https://github.com", + "files": [ + "README.md" + ], + "imageSize": 60, + "commit": false, + "commitConvention": "none", + "contributors": [ + { + "login": "jaggedsoft", + "name": "Jon Eyrick", + "avatar_url": "https://avatars3.githubusercontent.com/u/4283360?v=4", + "profile": "https://liquidity.ltd/", + "contributions": [ + "question", "maintenance", "code" + ] + }, + { + "login": "dmzoneill", + "name": "Dave", + "avatar_url": "https://avatars1.githubusercontent.com/u/15192260?v=4", + "profile": "https://www.fio.ie/", + "contributions": [ + "code" + ] + }, + { + "login": "bmino", + "name": "Brandon", + "avatar_url": "https://avatars3.githubusercontent.com/u/10902767?v=4", + "profile": "https://github.com/bmino", + "contributions": [ + "code" + ] + }, + { + "login": "dmitriz", + "name": "Dmitri Zaitsev", + "avatar_url": "https://avatars2.githubusercontent.com/u/2513193?v=4", + "profile": "https://ie.linkedin.com/in/dmitrizaitsev", + "contributions": [ + "code" + ] + }, + { + "login": "keith1024", + "name": "Keith Kirton", + "avatar_url": "https://avatars2.githubusercontent.com/u/4868093?v=4", + "profile": "https://github.com/keith1024", + "contributions": [ + "code" + ] + }, + { + "login": "Eluvade", + "name": "Bunny Eluvade", + "avatar_url": "https://avatars1.githubusercontent.com/u/32546052?v=4", + "profile": "https://source.ac/", + "contributions": [ + "code" + ] + }, + { + "login": "itnok", + "name": "Simone Conti", + "avatar_url": "https://avatars2.githubusercontent.com/u/1726185?v=4", + "profile": "https://github.com/itnok", + "contributions": [ + "code" + ] + }, + { + "login": "GusGold", + "name": "GusGold", + "avatar_url": "https://avatars3.githubusercontent.com/u/2942675?v=4", + "profile": "https://github.com/GusGold", + "contributions": [ + "code" + ] + }, + { + "login": "gunar", + "name": "Gunar Gessner", + "avatar_url": "https://avatars2.githubusercontent.com/u/7684574?v=4", + "profile": "https://github.com/gunar", + "contributions": [ + "code" + ] + }, + { + "login": "kirosc", + "name": "Kiros", + "avatar_url": "https://avatars1.githubusercontent.com/u/15169595?v=4", + "profile": "https://github.com/kirosc", + "contributions": [ + "code" + ] + }, + { + "login": "MadDeveloper", + "name": "Julien Sergent", + "avatar_url": "https://avatars2.githubusercontent.com/u/6815295?v=4", + "profile": "https://juliensergent.com/", + "contributions": [ + "code" + ] + }, + { + "login": "nimanr", + "name": "nimanr", + "avatar_url": "https://avatars3.githubusercontent.com/u/5919190?v=4", + "profile": "https://github.com/nimanr", + "contributions": [ + "code" + ] + }, + { + "login": "robaleman", + "name": "rob", + "avatar_url": "https://avatars3.githubusercontent.com/u/11480600?v=4", + "profile": "https://github.com/robaleman", + "contributions": [ + "code" + ] + }, + { + "login": "gemmell", + "name": "gemmell", + "avatar_url": "https://avatars3.githubusercontent.com/u/3324817?v=4", + "profile": "https://github.com/gemmell", + "contributions": [ + "code" + ] + }, + { + "login": "lht147", + "name": "lht147", + "avatar_url": "https://avatars3.githubusercontent.com/u/5062654?v=4", + "profile": "https://github.com/lht147", + "contributions": [ + "code" + ] + }, + { + "login": "abou7mied", + "name": "Ahmed Sabry", + "avatar_url": "https://avatars1.githubusercontent.com/u/3378281?v=4", + "profile": "https://github.com/abou7mied", + "contributions": [ + "code" + ] + }, + { + "login": "arenddeboer", + "name": "Arend de Boer", + "avatar_url": "https://avatars2.githubusercontent.com/u/7022204?v=4", + "profile": "https://github.com/arenddeboer", + "contributions": [ + "code" + ] + }, + { + "login": "ejfrancis", + "name": "Evan Francis", + "avatar_url": "https://avatars1.githubusercontent.com/u/3171352?v=4", + "profile": "https://github.com/ejfrancis", + "contributions": [ + "code" + ] + }, + { + "login": "charlesdarkwind", + "name": "Jasmin Parent", + "avatar_url": "https://avatars0.githubusercontent.com/u/23563500?v=4", + "profile": "https://github.com/charlesdarkwind", + "contributions": [ + "code" + ] + }, + { + "login": "joaquinnunez", + "name": "Joaquín Núñez", + "avatar_url": "https://avatars3.githubusercontent.com/u/436746?v=4", + "profile": "https://github.com/joaquinnunez", + "contributions": [ + "code" + ] + }, + { + "login": "meetmangukiya", + "name": "Meet Mangukiya", + "avatar_url": "https://avatars0.githubusercontent.com/u/7620533?v=4", + "profile": "https://www.flamy.dev/", + "contributions": [ + "code" + ] + }, + { + "login": "afsharsafavi", + "name": "afsharsafavi", + "avatar_url": "https://avatars3.githubusercontent.com/u/14315351?v=4", + "profile": "https://github.com/afsharsafavi", + "contributions": [ + "code" + ] + }, + { + "login": "boyhagemann", + "name": "boyhagemann", + "avatar_url": "https://avatars1.githubusercontent.com/u/737610?v=4", + "profile": "https://github.com/boyhagemann", + "contributions": [ + "code" + ] + }, + { + "login": "davewang", + "name": "Dave", + "avatar_url": "https://avatars0.githubusercontent.com/u/555195?v=4", + "profile": "http://iapploft.net/", + "contributions": [ + "code" + ] + }, + { + "login": "dbvcode", + "name": "dbvcode", + "avatar_url": "https://avatars2.githubusercontent.com/u/19186078?v=4", + "profile": "https://github.com/dbvcode", + "contributions": [ + "code" + ] + }, + { + "login": "sethyx", + "name": "Gergely Szell", + "avatar_url": "https://avatars1.githubusercontent.com/u/1311824?v=4", + "profile": "https://github.com/sethyx", + "contributions": [ + "code" + ] + }, + { + "login": "andersonaguiar", + "name": "Anderson Aguiar", + "avatar_url": "https://avatars1.githubusercontent.com/u/491544?v=4", + "profile": "https://andersonaguiar.com/", + "contributions": [ + "code" + ] + }, + { + "login": "hoota", + "name": "Andrey", + "avatar_url": "https://avatars3.githubusercontent.com/u/438983?v=4", + "profile": "https://github.com/hoota", + "contributions": [ + "code" + ] + }, + { + "login": "balthazar", + "name": "Balthazar Gronon", + "avatar_url": "https://avatars1.githubusercontent.com/u/6033345?v=4", + "profile": "https://balthazar.dev/", + "contributions": [ + "code" + ] + }, + { + "login": "ifmad", + "name": "Can Madran", + "avatar_url": "https://avatars3.githubusercontent.com/u/3315749?v=4", + "profile": "https://github.com/ifmad", + "contributions": [ + "code" + ] + }, + { + "login": "CollinEstes", + "name": "Collin Estes", + "avatar_url": "https://avatars3.githubusercontent.com/u/1966803?v=4", + "profile": "https://github.com/CollinEstes", + "contributions": [ + "code" + ] + }, + { + "login": "dpereskokov", + "name": "dpereskokov", + "avatar_url": "https://avatars1.githubusercontent.com/u/16366057?v=4", + "profile": "https://github.com/dpereskokov", + "contributions": [ + "code" + ] + }, + { + "login": "Dobberoonski75", + "name": "Shad Dobberoonski", + "avatar_url": "https://avatars2.githubusercontent.com/u/9811447?v=4", + "profile": "https://github.com/Dobberoonski75", + "contributions": [ + "code" + ] + }, + { + "login": "guygberg", + "name": "guygberg", + "avatar_url": "https://avatars2.githubusercontent.com/u/40508367?v=4", + "profile": "https://github.com/guygberg", + "contributions": [ + "code" + ] + }, + { + "login": "joelrich", + "name": "Joel Richard", + "avatar_url": "https://avatars3.githubusercontent.com/u/4216919?v=4", + "profile": "https://github.com/joelrich", + "contributions": [ + "code" + ] + }, + { + "login": "mstijak", + "name": "Marko Stijak", + "avatar_url": "https://avatars2.githubusercontent.com/u/433394?v=4", + "profile": "https://cxjs.io/", + "contributions": [ + "code" + ] + }, + { + "login": "matthewwoop", + "name": "Matthew Woop", + "avatar_url": "https://avatars0.githubusercontent.com/u/11703376?v=4", + "profile": "https://github.com/matthewwoop", + "contributions": [ + "code" + ] + }, + { + "login": "bhnow", + "name": "Miika", + "avatar_url": "https://avatars0.githubusercontent.com/u/15703708?v=4", + "profile": "https://github.com/bhnow", + "contributions": [ + "code" + ] + }, + { + "login": "Rolaus", + "name": "Rolaus", + "avatar_url": "https://avatars2.githubusercontent.com/u/12387674?v=4", + "profile": "https://github.com/Rolaus", + "contributions": [ + "code" + ] + }, + { + "login": "pavlovdog", + "name": "Sergey Potekhin", + "avatar_url": "https://avatars2.githubusercontent.com/u/5871170?v=4", + "profile": "http://sergeypotekhin.com/?utm_source=github&utm_medium=profile-link&utm_campaign=social-networks", + "contributions": [ + "code" + ] + }, + { + "login": "toant13", + "name": "Tony Tran", + "avatar_url": "https://avatars2.githubusercontent.com/u/2158827?v=4", + "profile": "https://github.com/toant13", + "contributions": [ + "code" + ] + }, + { + "login": "Fuzzyma", + "name": "Ulrich-Matthias Schäfer", + "avatar_url": "https://avatars3.githubusercontent.com/u/3540542?v=4", + "profile": "https://github.com/Fuzzyma", + "contributions": [ + "code" + ] + }, + { + "login": "bitoiu", + "name": "Vitor Monteiro", + "avatar_url": "https://avatars0.githubusercontent.com/u/1192590?v=4", + "profile": "https://github.com/bitoiu", + "contributions": [ + "code" + ] + }, + { + "login": "WWWSpider", + "name": "WWWSpider", + "avatar_url": "https://avatars2.githubusercontent.com/u/6440092?v=4", + "profile": "https://github.com/WWWSpider", + "contributions": [ + "code" + ] + }, + { + "login": "dreyescat", + "name": "dreyescat", + "avatar_url": "https://avatars2.githubusercontent.com/u/1313415?v=4", + "profile": "https://github.com/dreyescat", + "contributions": [ + "code" + ] + }, + { + "login": "hems", + "name": "hems.io", + "avatar_url": "https://avatars3.githubusercontent.com/u/27327?v=4", + "profile": "http://hems.io/", + "contributions": [ + "code" + ] + }, + { + "login": "mathlet0x", + "name": "Mathieu", + "avatar_url": "https://avatars1.githubusercontent.com/u/1147652?v=4", + "profile": "https://github.com/mathlet0x", + "contributions": [ + "code" + ] + }, + { + "login": "nurdtechie98", + "name": "Chirag Shetty", + "avatar_url": "https://avatars0.githubusercontent.com/u/21201278?v=4", + "profile": "http://nurdtechie98.github.io/", + "contributions": [ + "code" + ] + }, + { + "login": "tripolskypetr", + "name": "Petr Tripolsky", + "avatar_url": "https://avatars.githubusercontent.com/u/19227776?v=4", + "profile": "https://github.com/tripolskypetr", + "contributions": [ + "maintenance", "code" + ] + }, + ], + "contributorsPerLine": 7 +} diff --git a/.eslintrc.js b/.eslintrc.js deleted file mode 100644 index 0104e7a3..00000000 --- a/.eslintrc.js +++ /dev/null @@ -1,320 +0,0 @@ -module.exports = { - "env": { - "browser": true, - "commonjs": true, - "es6": true, - "node": true - }, - "extends": "eslint:recommended", - "parserOptions": { - "sourceType": "module" - }, - "rules": { - "accessor-pairs": "error", - "array-bracket-newline": "error", - "array-bracket-spacing": [ - "error", - "never" - ], - "array-callback-return": "error", - "array-element-newline": "off", - "arrow-body-style": "error", - "arrow-parens": [ - "error", - "as-needed" - ], - "arrow-spacing": [ - "error", - { - "after": true, - "before": true - } - ], - "block-scoped-var": "error", - "block-spacing": [ - "error", - "never" - ], - "brace-style": [ - "error", - "1tbs", - { - "allowSingleLine": true - } - ], - "callback-return": "off", - "camelcase": "off", - "capitalized-comments": "off", - "class-methods-use-this": "error", - "comma-dangle": "error", - "comma-spacing": "off", - "comma-style": [ - "error", - "last" - ], - "complexity": "error", - "computed-property-spacing": [ - "error", - "never" - ], - "consistent-return": "off", - "consistent-this": "error", - "curly": "off", - "default-case": "error", - "dot-location": "error", - "dot-notation": "error", - "eol-last": "error", - "eqeqeq": [ - "error", - "always" - ], - "for-direction": "error", - "func-call-spacing": "error", - "func-name-matching": "off", - "func-names": "off", - "func-style": [ - "error", - "expression" - ], - "function-paren-newline": "error", - "generator-star-spacing": "error", - "getter-return": "error", - "global-require": "off", - "guard-for-in": "off", - "handle-callback-err": "error", - "id-blacklist": "error", - "id-length": "off", - "id-match": "error", - "implicit-arrow-linebreak": [ - "error", - "beside" - ], - "indent": "off", - "indent-legacy": "off", - "init-declarations": "off", - "jsx-quotes": "error", - "key-spacing": "off", - "keyword-spacing": [ - "error", - { - "after": true, - "before": true - } - ], - "line-comment-position": "off", - "linebreak-style": [ - "error", - "unix" - ], - "lines-around-comment": "off", - "lines-around-directive": "off", - "lines-between-class-members": "error", - "max-depth": "error", - "max-len": "off", - "max-lines": "off", - "max-nested-callbacks": "error", - "max-params": "off", - "max-statements": "off", - "max-statements-per-line": "off", - "multiline-comment-style": [ - "off", - "bare-block" - ], - "new-cap": "error", - "new-parens": "error", - "newline-after-var": [ - "off", - "never" - ], - "newline-before-return": "off", - "newline-per-chained-call": "off", - "no-alert": "error", - "no-array-constructor": "error", - "no-await-in-loop": "error", - "no-bitwise": "off", - "no-buffer-constructor": "error", - "no-caller": "error", - "no-catch-shadow": "off", - "no-confusing-arrow": "error", - "no-console": [ - "warn", - { - allow: [ - "log", - "warn", - "error" - ] - } - ], - "no-continue": "error", - "no-div-regex": "error", - "no-duplicate-imports": "error", - "no-else-return": [ - "error", - { - "allowElseIf": true - } - ], - "no-empty-function": "error", - "no-eq-null": "error", - "no-eval": "error", - "no-extend-native": "error", - "no-extra-bind": "error", - "no-extra-label": "error", - "no-extra-parens": "off", - "no-floating-decimal": "error", - "no-implicit-coercion": "error", - "no-implicit-globals": "error", - "no-implied-eval": "error", - "no-inline-comments": "off", - "no-invalid-this": "off", - "no-iterator": "error", - "no-label-var": "error", - "no-labels": "error", - "no-lone-blocks": "error", - "no-lonely-if": "error", - "no-loop-func": "off", - "no-magic-numbers": "off", - "no-mixed-operators": [ - "error", - { - "allowSamePrecedence": true - } - ], - "no-mixed-requires": "error", - "no-multi-assign": "error", - "no-multi-spaces": "error", - "no-multi-str": "error", - "no-multiple-empty-lines": "error", - "no-native-reassign": "error", - "no-negated-condition": "off", - "no-negated-in-lhs": "error", - "no-nested-ternary": "error", - "no-new": "error", - "no-new-func": "error", - "no-new-object": "error", - "no-new-require": "error", - "no-new-wrappers": "error", - "no-octal-escape": "error", - "no-param-reassign": "off", - "no-path-concat": "error", - "no-plusplus": "off", - "no-process-env": "off", - "no-process-exit": "error", - "no-proto": "error", - "no-prototype-builtins": "error", - "no-restricted-globals": "error", - "no-restricted-imports": "error", - "no-restricted-modules": "error", - "no-restricted-properties": "error", - "no-restricted-syntax": "error", - "no-return-assign": "error", - "no-return-await": "error", - "no-script-url": "error", - "no-self-compare": "error", - "no-sequences": "error", - "no-shadow": "off", - "no-shadow-restricted-names": "error", - "no-spaced-func": "error", - "no-sync": "off", - "no-tabs": "off", - "no-template-curly-in-string": "error", - "no-ternary": "off", - "no-throw-literal": "off", - "no-trailing-spaces": "error", - "no-undef-init": "error", - "no-undefined": "error", - "no-underscore-dangle": "error", - "no-unmodified-loop-condition": "error", - "no-unneeded-ternary": "error", - "no-unused-expressions": "error", - "no-use-before-define": "off", - "no-useless-call": "error", - "no-useless-computed-key": "error", - "no-useless-concat": "error", - "no-useless-constructor": "error", - "no-useless-rename": "error", - "no-useless-return": "error", - "no-var": "error", - "no-void": "error", - "no-warning-comments": "off", - "no-whitespace-before-property": "error", - "no-with": "error", - "nonblock-statement-body-position": "error", - "object-curly-newline": "error", - "object-curly-spacing": "off", - "object-property-newline": [ - "error", - { - "allowMultiplePropertiesPerLine": true - } - ], - "object-shorthand": "off", - "one-var": "off", - "one-var-declaration-per-line": "off", - "operator-assignment": [ - "error", - "always" - ], - "operator-linebreak": "error", - "padded-blocks": "off", - "padding-line-between-statements": "error", - "prefer-arrow-callback": "off", - "prefer-const": "off", - "prefer-destructuring": "off", - "prefer-numeric-literals": "error", - "prefer-promise-reject-errors": "error", - "prefer-reflect": "off", - "prefer-rest-params": "error", - "prefer-spread": "off", - "prefer-template": "off", - "quote-props": "off", - "quotes": [ - "error", - "single" - ], - "radix": "error", - "require-await": "error", - "require-jsdoc": "error", - "rest-spread-spacing": "error", - "semi": "off", - "semi-spacing": [ - "error", - { - "after": false, - "before": false - } - ], - "semi-style": [ - "error", - "last" - ], - "sort-imports": "error", - "sort-keys": "off", - "sort-vars": "off", - "space-before-blocks": "off", - "space-before-function-paren": "off", - "space-in-parens": "off", - "space-infix-ops": "off", - "space-unary-ops": "error", - "spaced-comment": "off", - "strict": "off", - "switch-colon-spacing": "error", - "symbol-description": "error", - "template-curly-spacing": "error", - "template-tag-spacing": "error", - "unicode-bom": [ - "error", - "never" - ], - "valid-jsdoc": "error", - "vars-on-top": "error", - "wrap-iife": "off", - "wrap-regex": "error", - "yield-star-spacing": "error", - "yoda": [ - "error", - "never" - ] - } -}; diff --git a/.github/workflows/js.yml b/.github/workflows/js.yml new file mode 100644 index 00000000..fa8f557b --- /dev/null +++ b/.github/workflows/js.yml @@ -0,0 +1,45 @@ +name: Js + +on: + workflow_dispatch: + push: + branches: [ main, master ] + pull_request: + branches: [ main, master ] + +permissions: + contents: write + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 2 + - uses: actions/setup-node@v4 + with: + node-version: '20' + cache: 'npm' + - name: Install npm dependencies + run: npm i --include=dev + - name: Lint + run: npm run lint + - name: Build + run: npm run build + - name: Crypto Tests + run: npm run crypto-tests + - name: Static Tests (TS ESM) + run: npm run ts-test-static + - name: Static Tests (JS CJS) + run: npm run static-test + - name: Live Tests (TS ESM) + run: npm run ts-test-live + - name: Ws Live Tests (spot) + run: npm run ws-tests-spot + - name: Ws Live Tests (futures) + run: npm run ws-tests-futures + - name: CJS test + run: npm run test-cjs + - name: Package test + run: npm run package-test \ No newline at end of file diff --git a/.gitignore b/.gitignore index 2cc515e9..5a6ab810 100644 --- a/.gitignore +++ b/.gitignore @@ -36,6 +36,8 @@ ehthumbs.db Thumbs.db +dist/ + # Secrets # ########### .secret @@ -59,3 +61,5 @@ node_modules # code coverage" coverage + +*.txt \ No newline at end of file diff --git a/.mocharc.json b/.mocharc.json new file mode 100644 index 00000000..3cfb40ba --- /dev/null +++ b/.mocharc.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://json.schemastore.org/mocharc.json", + "require": "tsx" +} \ No newline at end of file diff --git a/.npmignore b/.npmignore new file mode 100644 index 00000000..bc24b26c --- /dev/null +++ b/.npmignore @@ -0,0 +1,13 @@ +# Initally ignore all files +* + +# Files to include +!package.json +!package-lock.json +!LICENSE.txt +!README.md + +# Folders to include +!src/**/* +!dist/**/* + diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 83888332..00000000 --- a/.travis.yml +++ /dev/null @@ -1,59 +0,0 @@ -language: node_js -node_js: - - "9" - -matrix: - include: - - os: linux - dist: trusty - sudo: required - env: - - QUALITY=yes - - PROXY_VALIDATION=yes - - os: linux - dist: precise - sudo: required - - os: osx - osx_image: xcode7.2 - allow_failures: - - os: osx - -branches: - only: master - -notifications: - webhooks: https://www.travisbuddy.com/?insertMode=update - -before_script: - - if [[ $PROXY_VALIDATION == "yes" ]]; then mkdir -vp /home/travis/.ssh/; fi - - if [[ $PROXY_VALIDATION == "yes" ]]; then ssh-keygen -f /home/travis/.ssh/id_ecdsa -t ecdsa -N ''; fi - - if [[ $PROXY_VALIDATION == "yes" ]]; then cat /home/travis/.ssh/*.pub > /home/travis/.ssh/authorized_keys; fi - - if [[ $PROXY_VALIDATION == "yes" ]]; then chmod 600 /home/travis/.ssh/*; fi - - if [[ $PROXY_VALIDATION == "yes" ]]; then chmod 700 /home/travis/.ssh/.; fi - - if [[ $PROXY_VALIDATION == "yes" ]]; then ssh -f -o "StrictHostKeyChecking no" -D 9999 -q -N travis@localhost; fi - - travis_retry git clone -b gh-pages https://git@$GH_REPO_REF docs - - travis_retry npm install - - travis_retry npm install coveralls istanbul mocha chai codecov codacy-coverage mocha-lcov-reporter eslint jsdoc - - travis_retry npm install -g coveralls istanbul mocha chai codecov codacy-coverage mocha-lcov-reporter eslint jsdoc - - travis_retry sudo apt-get install tinyproxy curl - - if [[ $PROXY_VALIDATION == "yes" ]]; then tinyproxy; fi - - if [[ $PROXY_VALIDATION == "yes" ]]; then travis_retry curl -v -I --proxy socks://127.0.0.1:9999 https://www.google.com; fi - -script: - - travis_retry npm run test - - if [[ $PROXY_VALIDATION == "yes" ]]; then export socks_proxy=socks4://127.0.0.1:9999; fi - - if [[ $PROXY_VALIDATION == "yes" ]]; then travis_retry npm run test; fi - - npm run lint - -after_success: - - if [[ $QUALITY == "yes" ]]; then travis_retry npm run cover; fi - - if [[ $QUALITY == "yes" ]]; then travis_retry npm run coveralls; fi - - if [[ $QUALITY == "yes" ]]; then travis_retry npm run codecov; fi - - if [[ $QUALITY == "yes" ]]; then travis_retry npm run codacy; fi - - if [[ $QUALITY == "yes" ]]; then mv docs/docs.sh .; cd docs; rm -rvf ./*; mv ../docs.sh .; chmod +x docs.sh; ./docs.sh; fi - -env: - global: - - GH_REPO_NAME: node-binance-api - - GH_REPO_REF: github.com/jaggedsoft/node-binance-api.git - - JSDOC_FILES: $TRAVIS_BUILD_DIR/node-binance-api.js diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md deleted file mode 100644 index 0c0ad78d..00000000 --- a/ISSUE_TEMPLATE.md +++ /dev/null @@ -1,4 +0,0 @@ -Support is no longer offered by Binance and my time is focused on other things. -Please do not post an issue unless it is a feature request or you can explain how to reproduce the issue. - -All pull requests are welcome diff --git a/README.md b/README.md index e3221d4f..fcc6f167 100644 --- a/README.md +++ b/README.md @@ -1,44 +1,416 @@ -[![Latest Version](https://img.shields.io/github/release/jaggedsoft/node-binance-api.svg?style=flat-square)](https://github.com/jaggedsoft/node-binance-api/releases) -[![GitHub last commit](https://img.shields.io/github/last-commit/jaggedsoft/node-binance-api.svg?maxAge=2400)](#) -[![npm downloads](https://img.shields.io/npm/dt/node-binance-api.svg?maxAge=7200)](https://www.npmjs.com/package/node-binance-api) -[![NPM](https://nodei.co/npm/node-binance-api.png?compact=true)](https://npmjs.org/package/node-binance-api) + + + + + + -[![Build Status](https://travis-ci.org/jaggedsoft/node-binance-api.svg?branch=master&style=flat-square)](https://travis-ci.org/jaggedsoft/node-binance-api) -[![Coverage Status](https://coveralls.io/repos/github/jaggedsoft/node-binance-api/badge.svg?branch=master&style=flat-square)](https://coveralls.io/github/jaggedsoft/node-binance-api) -[![CodeCov](https://codecov.io/gh/jaggedsoft/node-binance-api/branch/master/graph/badge.svg?style=flat-square)](https://codecov.io/github/jaggedsoft/node-binance-api/) -[![Codacy Badge](https://api.codacy.com/project/badge/Coverage/996757cec66542c0a64fca2b4cf8a936)](https://www.codacy.com/app/dmzoneill/node-binance-api?utm_source=github.com&utm_medium=referral&utm_content=jaggedsoft/node-binance-api&utm_campaign=Badge_Coverage) -[![Codacy Badge](https://api.codacy.com/project/badge/Grade/996757cec66542c0a64fca2b4cf8a936)](https://www.codacy.com/app/dmzoneill/node-binance-api?utm_source=github.com&utm_medium=referral&utm_content=jaggedsoft/node-binance-api&utm_campaign=Badge_Grade) # Node Binance API + +[![telegram](https://patrolavia.github.io/telegram-badge/chat.png)](https://t.me/nodebinanceapi) [![Yearly Downloads](https://img.shields.io/npm/dy/node-binance-api.svg)](https://www.npmjs.com/package/node-binance-api) [![jaggedsoft on X](https://img.shields.io/twitter/follow/jaggedsoft.svg?style=social)](https://x.com/jaggedsoft) + This project is designed to help you make your own projects that interact with the [Binance API](https://github.com/binance-exchange/binance-official-api-docs). You can stream candlestick chart data, market depth, or use other advanced features such as setting stop losses and iceberg orders. This project seeks to have complete API coverage including WebSockets. + + +

+ Futures API & + Streams • + Spot Trading API & + Streams • + Margin API • + Lending API
+ Examples • + Troubleshooting • + Changelog • + Support +

+ #### Installation + ``` -npm install node-binance-api --save +npm install node-binance-api +``` +[![NPM](https://nodei.co/npm/node-binance-api.png?compact=true)](https://npmjs.org/package/node-binance-api) + + +#### Community Telegram Chat + +https://t.me/nodebinanceapi + + +**This project is powered by** + +Actively maintained, typed, and safe SDK for the Binance REST APIs and Websockets. Supports ESM and CJS out of the box. + +### Features +- Spot, Margin, Futures and Delivery API +- Testnet support +- Proxy support (REST and WS) +- Customizable HTTP headers +- Customizable request parameters +- RSA/ECDSA support +- Portfolio Margin API *\*soon*\* +- Websocket handling with automatic reconnection +- RecvWindow and automatic timestamps generation +- Ability to call any endpoint, even if not supported directly by the library +- Overridable hostnames (.us, .jp, etc) +- Verbose mode to debug http requests/responses + +### Upgrading to v1.0.0+ + +**We highly advise you to update from 0.0.X but minor adjustments might be needed.** + +The library was fully refactored to use a modern and typed JavaScript/Typescript version, using the built-in await/async syntax and unifying some methods' signatures. +Some important changes include the removal of callbacks as parameters of REST methods, adaptation of signatures to directly receive some important request values (symbol, orderId, ...), among others. + + +#### Getting started (ESM) +```javascript +import Binance from 'node-binance-api'; +async function run() { + const exchange = new Binance(); + const res = await exchange.futuresTime(); + console.log( res ); +} ``` -#### Getting started +#### Getting started (CJS) ```javascript -const binance = require('node-binance-api')().options({ +const Binance = require('node-binance-api'); +const binance = new Binance({ APIKEY: '', APISECRET: '', - useServerTime: true // If you get timestamp errors, synchronize to server time at startup + test: true, // if you want to use the sandbox/testnet }); ``` -#### Getting latest price of a symbol +# Binance Futures API + +#### Futures Prices ```js -binance.prices('BNBBTC', (error, ticker) => { - console.log("Price of BNB: ", ticker.BNBBTC); -}); +console.info( await binance.futuresPrices() ); +``` + +#### Futures Account Balances & Positions +```js +console.info( await binance.futuresAccount() ); +``` + +#### Futures Balances +```js +console.info( await binance.futuresBalance() ); +``` + +#### Futures Limit Buy +```js +console.info( await binance.futuresBuy( 'LIMIT', 'BTCUSDT', 0.1, 8222 ) ); +``` + +#### Futures Limit Sell +```js +console.info( await binance.futuresSell( 'LIMIT', 'BTCUSDT', 0.5, 11111 ) ); +``` + +#### Futures Market Buy +```js +console.info( await binance.futuresMarketBuy( 'BNBUSDT', 5 ) ); +``` + +#### Futures Market Sell +```js +console.info( await binance.futuresMarketSell( 'TRXUSDT', 1 ) ); +``` + +#### Futures Place Multiple Orders +```js +let orders = [ + { + symbol:"BTCUSDT", + side: "BUY", + type: "MARKET", + quantity: "0.01", + }, + { + symbol:"BNBUSDT", + side: "SELL", + type: "MARKET", + quantity: "0.5", + } +] +console.info( await binance.futuresMultipleOrders(orders) ); +``` + +#### Futures Market Orders: Get the fill price using newOrderRespType +```js +console.info( await binance.futuresMarketBuy( 'BNBUSDT', amount, { newOrderRespType: 'RESULT' } ) ); +``` + +#### Futures reduceOnly Order Example +```js +if ( side == 'LONG' ) order = await binance.futuresMarketSell( obj.symbol, amount, {reduceOnly: true} ) +else order = await binance.futuresMarketBuy( obj.symbol, amount, {reduceOnly: true} ) +``` + +#### Get Futures Positions +```js +console.info( await binance.futuresPositionRisk() ); +``` +
+ View Example + +```js +let position_data = await binance.futuresPositionRisk(), markets = Object.keys( position_data ); +for ( let market of markets ) { + let obj = position_data[market], size = Number( obj.positionAmt ); + if ( size == 0 ) continue; + console.info( `${leverage}x\t${market}\t${obj.unRealizedProfit}` ); + //console.info( obj ); //positionAmt entryPrice markPrice unRealizedProfit liquidationPrice leverage marginType isolatedMargin isAutoAddMargin maxNotionalValue +} +``` +
+ +#### Adjust Leverage (1-125x) +```js +console.info( await binance.futuresLeverage( 'ETHUSDT', 50 ) ); +``` + +#### Adjust Margin Type (ISOLATED, CROSSED) +```js +console.info( await binance.futuresMarginType( 'BTCUSDT', 'ISOLATED' ) ); +``` + +#### Adjust Position Margin +```js +// Type: 1: Add postion margin,2: Reduce postion margin +console.info( await binance.futuresPositionMargin( "TRXUSDT", amount, type ) ); +``` + +```js +console.info( await binance.futuresTime() ); +console.info( await binance.futuresExchangeInfo() ); +console.info( await binance.futuresCandles( "TRXUSDT", "1m" ) ); +console.info( await binance.futuresDepth( "ADAUSDT" ) ); +console.info( await binance.futuresQuote() ); +console.info( await binance.futuresQuote( "BCHUSDT" ) ); +console.info( await binance.futuresDaily() ); +console.info( await binance.futuresOpenInterest( "BTCUSDT" ) ); +console.info( await binance.futuresMarkPrice() ); +console.info( await binance.futuresMarkPrice( "ETHUSDT" ) ); +console.info( await binance.futuresTrades( "LTCUSDT" ) ); +console.info( await binance.futuresAggTrades( "XTZUSDT" ) ); +console.info( await binance.futuresLiquidationOrders() ); +console.info( await binance.futuresFundingRate() ); +console.info( await binance.futuresHistoricalTrades( "XMRUSDT" ) ); +console.info( await binance.futuresLeverageBracket( "LINKUSDT" ) ); +console.info( await binance.futuresIncome() ); +console.info( await binance.futuresCancelAll( "BTCUSDT" ) ); +console.info( await binance.futuresCancel( "BTCUSDT", "1025137386" ) ); +console.info( await binance.futuresCountdownCancelAll( "BTCUSDT", 45000 ) ); +console.info( await binance.futuresOrderStatus( "BTCUSDT", "1025137386") ); +console.info( await binance.futuresOpenOrders() ); +console.info( await binance.futuresOpenOrders( "BTCUSDT" ) ); +console.info( await binance.futuresAllOrders() ); +console.info( await binance.futuresAllOrders( "BTCUSDT" ) ); +console.info( await binance.futuresUserTrades( "BTCUSDT" ) ); +console.info( await binance.futuresGetDataStream() ); +console.info( await binance.futuresPositionMarginHistory( "TRXUSDT" ) ); +console.info( await binance.futuresPublicRequest( 'v1/time' ) ); +console.info( await binance.spotPublicRequest( 'v1/time')); // call any method by providing the path +console.info( await binance.privateFuturesRequest('v3/account')); // custom futures private call +// Batch orders, remaining WebSocket streams, and better documentation will be come later ``` +### Proxy support + +In some specific cases using a proxy is required, for example: +- Exchange is not available in your location +- You need to make a large amount of requests without getting blocked +- ... + +This package supports the following proxy types, `httpsProxy`, `proxyUrl` and `socksProxy` + +#### httpsProxy + +To set a real http(s) proxy for your scripts, you need to have an access to a remote http or https proxy, so calls will be made directly to the target exchange, tunneled through your proxy server: + +```Js +client.httpsProxy = 'http://1.2.3.4:8080/'; +``` + +#### proxyUrl + +This property prepends an url to API requests. It might be useful for simple redirection or bypassing CORS browser restriction. + +```Js +client.proxyUrl = 'YOUR_PROXY_URL'; +``` + +#### socksProxy + +Tou can also use socks proxy with the following format: + +```Js +client.socksProxy = 'socks5://1.2.3.4:8080/'; +``` + +#### Futures Historical Bulk Data Download API + +##### Get Download ID + +```js +console.info( await binance.futuresHistDataId( + "BTCUSDT", { + startTime: new Date().getTime() - 24 * 60 * 60 * 1000, + endTime: new Date().getTime(), + dataType: 'T_TRADE' + } ) +) +``` + +##### Get Download Link + +```js +console.info( await binance.futuresDownloadLink(7343) ) +``` + +# Futures WebSocket Streams + +#### Futures miniTicker stream for all symbols +```js +binance.futuresMiniTickerStream( miniTicker => { + console.info( miniTicker ); +} ); +``` +#### Futures miniTicker stream for a symbol +```js +binance.futuresMiniTickerStream( 'BTCUSDT', console.log ); +``` +#### Futures bookTicker stream for all symbols +```js +binance.futuresBookTickerStream( console.log ); +``` +#### Futures bookTicker stream for a symbol +```js +binance.futuresBookTickerStream( 'BTCUSDT', console.log ); +``` +#### Futures prevDay ticker stream for all symbols +```js +binance.futuresTickerStream( console.log ); +``` +#### Futures prevDay ticker stream for a symbol +```js +binance.futuresTickerStream( 'BTCUSDT', console.log ); +``` +#### Futures mark price stream for all symbols +```js +binance.futuresMarkPriceStream( console.log ); +``` +#### Futures mark price stream for a symbol +```js +binance.futuresMarkPriceStream( 'BTCUSDT', console.log ); +``` +#### Futures aggregate trade stream for a symbol +```js +binance.futuresAggTradeStream( 'BTCUSDT', console.log ); +``` +#### Futures complete chart cache +```js +binance.futuresChart( 'BTCUSDT', '1m', console.log ); +``` +#### Futures Liquidation Stream for all symbols +```js +binance.futuresLiquidationStream( console.log ); +``` +#### Futures Liquidation Stream for a symbol +```js +binance.futuresLiquidationStream( 'BTCUSDT', console.log ); +``` +#### Connect to a custom endpoint. Easier shortcut functions will come later +```js +binance.futuresSubscribe( 'btcusdt@kline_4h', console.log ); +``` +#### Terminate an existing socket +```js +binance.futuresTerminate( 'btcusdt@kline_4h' ); +``` +#### Return active sockets and subscriptions +```js +console.log( binance.futuresSubscriptions() ); +``` + + + +# Delivery API (Futures w/Expiration Date) +``` +deliveryBuy +deliverySell +deliveryMarketBuy +deliveryMarketSell +deliveryPrices +deliveryDaily +deliveryOpenInterest +deliveryExchangeInfo +deliveryOpenOrders +deliveryAllOrders +deliveryCandles +deliveryIndexKlines +deliveryContinuousKlines +deliveryMarkPriceKlines +deliveryMarkPrice +deliveryHistoricalTrades +deliveryTrades +deliveryAggTrades +deliveryUserTrades +deliveryLiquidationOrders +deliveryPositionRisk +deliveryLeverage +deliveryMarginType +deliveryPositionMargin +deliveryPositionMarginHistory +deliveryIncome +deliveryBalance +deliveryAccount +deliveryDepth +deliveryQuote +deliveryLeverageBracket +deliveryOrderStatus +deliveryCancel +deliveryCancelAll +deliveryCountdownCancelAll +deliveryOrder +deliveryGetDataStream +deliveryCloseDataStream +deliveryKeepDataStream +deliveryPing +deliveryTime +deliveryOrder +``` + +# Binance API (Spot Trading) + #### Getting latest price of all symbols ```javascript -binance.prices((error, ticker) => { - console.log("prices()", ticker); - console.log("Price of BTC: ", ticker.BTCUSDT); +let ticker = await binance.prices(); +console.info(`Price of BNB: ${ticker.BNBUSDT}`); +``` + +#### Getting latest price of a symbol +```js +binance.prices('BNBBTC', (error, ticker) => { + console.info("Price of BNB: ", ticker.BNBBTC); }); ```
@@ -112,8 +484,8 @@ binance.prices((error, ticker) => { ```javascript binance.balance((error, balances) => { if ( error ) return console.error(error); - console.log("balances()", balances); - console.log("ETH balance: ", balances.ETH.available); + console.info("balances()", balances); + console.info("ETH balance: ", balances.ETH.available); }); // If you have problems with this function, // see Troubleshooting at the bottom of this page. @@ -179,7 +551,7 @@ binance.balance((error, balances) => { #### Getting bid/ask prices for a symbol ```js binance.bookTickers('BNBBTC', (error, ticker) => { - console.log("bookTickers", ticker); + console.info("bookTickers", ticker); }); ``` @@ -202,8 +574,8 @@ binance.bookTickers('BNBBTC', (error, ticker) => { #### Getting bid/ask prices for all symbols ```js binance.bookTickers((error, ticker) => { - console.log("bookTickers()", ticker); - console.log("Price of BNB: ", ticker.BNBBTC); + console.info("bookTickers()", ticker); + console.info("Price of BNB: ", ticker.BNBBTC); }); ``` @@ -531,7 +903,7 @@ binance.bookTickers((error, ticker) => { #### Get all bid/ask prices ```javascript binance.bookTickers((error, ticker) => { - console.log("bookTickers", ticker); + console.info("bookTickers", ticker); }); ```
@@ -854,7 +1226,7 @@ binance.bookTickers((error, ticker) => { #### Get market depth for a symbol ```javascript binance.depth("BNBBTC", (error, depth, symbol) => { - console.log(symbol+" market depth", depth); + console.info(symbol+" market depth", depth); }); ```
@@ -1069,7 +1441,7 @@ market depth for BNBBTC #### Placing a LIMIT order ```javascript -var quantity = 1, price = 0.069; +let quantity = 1, price = 0.069; binance.buy("ETHBTC", quantity, price); binance.sell("ETHBTC", quantity, price); ``` @@ -1077,17 +1449,17 @@ binance.sell("ETHBTC", quantity, price); #### Placing a MARKET order ```javascript // These orders will be executed at current market price. -var quantity = 1; +let quantity = 1; binance.marketBuy("BNBBTC", quantity); binance.marketSell("ETHBTC", quantity); ``` #### LIMIT order with callback ```javascript -var quantity = 5, price = 0.00402030; +let quantity = 5, price = 0.00402030; binance.buy("BNBETH", quantity, price, {type:'LIMIT'}, (error, response) => { - console.log("Limit Buy response", response); - console.log("order id: " + response.orderId); + console.info("Limit Buy response", response); + console.info("order id: " + response.orderId); }); ``` @@ -1114,10 +1486,10 @@ Limit Buy response { #### Chaining orders together ```js -var quantity = 1; +let quantity = 1; binance.marketBuy("BNBBTC", quantity, (error, response) => { - console.log("Market Buy response", response); - console.log("order id: " + response.orderId); + console.info("Market Buy response", response); + console.info("order id: " + response.orderId); // Now you can limit sell with a stop loss, etc. }); ``` @@ -1133,7 +1505,7 @@ Market Buy response { transactTime: 1509049376261, price: '0.00000000', origQty: '1.00000000', - exeutedQty: '1.00000000', + executedQty: '1.00000000', status: 'FILLED', timeInForce: 'GTC', type: 'MARKET', @@ -1158,36 +1530,34 @@ binance.sell("ETHBTC", quantity, price, {stopPrice: stopPrice, type: type}); #### Placing an ICEBERG order ```javascript // Iceberg orders are intended to conceal the order quantity. -var quantity = 1; -var price = 0.069; +let quantity = 1; +let price = 0.069; binance.sell("ETHBTC", quantity, price, {icebergQty: 10}); ``` #### Cancel an order ```javascript binance.cancel("ETHBTC", orderid, (error, response, symbol) => { - console.log(symbol+" cancel response:", response); + console.info(symbol+" cancel response:", response); }); ``` #### Cancel all open orders ```js -binance.cancelOrders("XMRBTC", (error, response, symbol) => { - console.log(symbol+" cancel response:", response); -}); +console.info( await binance.cancelAll("XMRBTC") ); ``` #### Get open orders for a symbol ```javascript binance.openOrders("ETHBTC", (error, openOrders, symbol) => { - console.log("openOrders("+symbol+")", openOrders); + console.info("openOrders("+symbol+")", openOrders); }); ``` #### Get list of all open orders ```javascript binance.openOrders(false, (error, openOrders) => { - console.log("openOrders()", openOrders); + console.info("openOrders()", openOrders); }); ``` @@ -1195,14 +1565,14 @@ binance.openOrders(false, (error, openOrders) => { ```javascript let orderid = "7610385"; binance.orderStatus("ETHBTC", orderid, (error, orderStatus, symbol) => { - console.log(symbol+" order status:", orderStatus); + console.info(symbol+" order status:", orderStatus); }); ``` -#### Trade history +#### Get your Trade History ```javascript binance.trades("SNMBTC", (error, trades, symbol) => { - console.log(symbol+" trade history", trades); + console.info(symbol+" trade history", trades); }); ```
@@ -1235,24 +1605,24 @@ binance.trades("SNMBTC", (error, trades, symbol) => { #### Get all account orders; active, canceled, or filled. ```javascript binance.allOrders("ETHBTC", (error, orders, symbol) => { - console.log(symbol+" orders:", orders); + console.info(symbol+" orders:", orders); }); ``` #### Get dust log ```javascript binance.dustLog((error, dustlog) => { - console.log(dustlog); + console.info(dustlog); }) ``` #### Get 24hr ticker price change statistics for all symbols ```javascript binance.prevDay(false, (error, prevDay) => { - // console.log(prevDay); // view all data + // console.info(prevDay); // view all data for ( let obj of prevDay ) { let symbol = obj.symbol; - console.log(symbol+" volume:"+obj.volume+" change: "+obj.priceChangePercent+"%"); + console.info(symbol+" volume:"+obj.volume+" change: "+obj.priceChangePercent+"%"); } }); ``` @@ -1260,8 +1630,8 @@ binance.prevDay(false, (error, prevDay) => { #### Get 24hr ticker price change statistics for a symbol ```javascript binance.prevDay("BNBBTC", (error, prevDay, symbol) => { - console.log(symbol+" previous day:", prevDay); - console.log("BNB change since yesterday: "+prevDay.priceChangePercent+"%") + console.info(symbol+" previous day:", prevDay); + console.info("BNB change since yesterday: "+prevDay.priceChangePercent+"%") }); ``` @@ -1272,10 +1642,10 @@ Optional parameters: limit (max/default 500), startTime, endTime. ```javascript // Intervals: 1m,3m,5m,15m,30m,1h,2h,4h,6h,8h,12h,1d,3d,1w,1M binance.candlesticks("BNBBTC", "5m", (error, ticks, symbol) => { - console.log("candlesticks()", ticks); + console.info("candlesticks()", ticks); let last_tick = ticks[ticks.length - 1]; let [time, open, high, low, close, volume, closeTime, assetVolume, trades, buyBaseVolume, buyAssetVolume, ignored] = last_tick; - console.log(symbol+" last close: "+close); + console.info(symbol+" last close: "+close); }, {limit: 500, endTime: 1514764800000}); ``` @@ -1288,11 +1658,11 @@ This function pulls existing chart data before connecting to the WebSocket, and binance.websockets.chart("BNBBTC", "1m", (symbol, interval, chart) => { let tick = binance.last(chart); const last = chart[tick].close; - console.log(chart); + console.info(chart); // Optionally convert 'chart' object to array: // let ohlc = binance.ohlc(chart); - // console.log(symbol, ohlc); - console.log(symbol+" last price: "+last) + // console.info(symbol, ohlc); + console.info(symbol+" last price: "+last) }); ``` @@ -1361,13 +1731,13 @@ binance.websockets.chart("BNBBTC", "1m", (symbol, interval, chart) => { binance.websockets.candlesticks(['BNBBTC'], "1m", (candlesticks) => { let { e:eventType, E:eventTime, s:symbol, k:ticks } = candlesticks; let { o:open, h:high, l:low, c:close, v:volume, n:trades, i:interval, x:isFinal, q:quoteVolume, V:buyVolume, Q:quoteBuyVolume } = ticks; - console.log(symbol+" "+interval+" candlestick update"); - console.log("open: "+open); - console.log("high: "+high); - console.log("low: "+low); - console.log("close: "+close); - console.log("volume: "+volume); - console.log("isFinal: "+isFinal); + console.info(symbol+" "+interval+" candlestick update"); + console.info("open: "+open); + console.info("high: "+high); + console.info("low: "+low); + console.info("close: "+close); + console.info("volume: "+volume); + console.info("isFinal: "+isFinal); }); ``` @@ -1375,14 +1745,14 @@ binance.websockets.candlesticks(['BNBBTC'], "1m", (candlesticks) => { ```javascript binance.websockets.trades(['BNBBTC', 'ETHBTC'], (trades) => { let {e:eventType, E:eventTime, s:symbol, p:price, q:quantity, m:maker, a:tradeId} = trades; - console.log(symbol+" trade update. price: "+price+", quantity: "+quantity+", maker: "+maker); + console.info(symbol+" trade update. price: "+price+", quantity: "+quantity+", maker: "+maker); }); ``` #### Get miniTicker via WebSocket ```js binance.websockets.miniTicker(markets => { - console.log(markets); + console.info(markets); }); ``` @@ -1421,12 +1791,12 @@ binance.websockets.miniTicker(markets => { ```js // For all symbols: binance.websockets.prevDay(false, (error, response) => { - console.log(response); + console.info(response); }); // For a specific symbol: binance.websockets.prevDay('BNBBTC', (error, response) => { - console.log(response); + console.info(response); }); ``` @@ -1464,8 +1834,8 @@ binance.websockets.prevDay('BNBBTC', (error, response) => { ```javascript binance.websockets.depth(['BNBBTC'], (depth) => { let {e:eventType, E:eventTime, s:symbol, u:updateId, b:bidDepth, a:askDepth} = depth; - console.log(symbol+" market depth update"); - console.log(bidDepth, askDepth); + console.info(symbol+" market depth update"); + console.info(bidDepth, askDepth); }); ``` @@ -1474,12 +1844,12 @@ binance.websockets.depth(['BNBBTC'], (depth) => { binance.websockets.depthCache(['BNBBTC'], (symbol, depth) => { let bids = binance.sortBids(depth.bids); let asks = binance.sortAsks(depth.asks); - console.log(symbol+" depth cache update"); - console.log("bids", bids); - console.log("asks", asks); - console.log("best bid: "+binance.first(bids)); - console.log("best ask: "+binance.first(asks)); - console.log("last updated: " + new Date(depth.eventTime)); + console.info(symbol+" depth cache update"); + console.info("bids", bids); + console.info("asks", asks); + console.info("best bid: "+binance.first(bids)); + console.info("best ask: "+binance.first(asks)); + console.info("last updated: " + new Date(depth.eventTime)); }); ``` @@ -1516,40 +1886,49 @@ bids { '0.00025203': 0.201624, ```
+#### bookTickers stream includes the bid/ask price & amount, for all symbols +```js +binance.websockets.bookTickers( console.log ); +``` +#### bookTickers stream includes the bid/ask price & amount, for a symbol +```js +binance.websockets.bookTickers( 'BTCUSDT', console.log ); +``` + ### Deposit & Withdraw #### Get Deposit Address ```js binance.depositAddress("XMR", (error, response) => { - console.log(response); + console.info(response); }); ``` #### Get All Deposit History ```js binance.depositHistory((error, response) => { - console.log(response); + console.info(response); }); ``` #### Get Deposit History for a specific symbol ```js binance.depositHistory((error, response) => { - console.log(response); + console.info(response); }, "VEN"); ``` #### Get All Withdraw History ```js binance.withdrawHistory((error, response) => { - console.log(response); + console.info(response); }); ``` #### Get Withdraw History for a specific symbol ```js binance.withdrawHistory((error, response) => { - console.log(response); + console.info(response); }, "BTC"); ``` @@ -1562,62 +1941,198 @@ let amount = 0.1; binance.withdraw("XMR", address, amount, addressTag); ``` -#### Withdraw with Callback +#### Withdraw ```js -binance.withdraw("ETH", "0x1d2034348c851ea29c7d03731c7968a5bcc91564", 1, false, (error, response) => { - console.log(response); -}); +binance.withdraw("BTC", "1C5gqLRs96Xq4V2ZZAR1347yUCpHie7sa", 0.2); ``` -#### Withdraw +### Universal Transfer / Internal Wallet Transfer +Example Spot account transfer to USDⓈ-M Futures account , use ENUM -> "MAIN_UMFUTURE" ```js -binance.withdraw("BTC", "1C5gqLRs96Xq4V2ZZAR1347yUCpHie7sa", 0.2); +console.info( await binance.universalTransfer("MAIN_UMFUTURE","USDT",10) ); ``` +for more account transfers (ENUMs) see [docs](https://binance-docs.github.io/apidocs/spot/en/#user-universal-transfer) -#### [Advanced Examples](https://github.com/jaggedsoft/node-binance-api/blob/master/examples/advanced.md) -> [exchangeInfo: Pull minimum order size, quantity, etc](https://github.com/jaggedsoft/node-binance-api/blob/master/examples/advanced.md#exchangeinfo-pull-minimum-order-size-quantity-etc)\ -[Clamp order quantities to required amounts via minQty, minNotional, stepSize when placing orders](https://github.com/jaggedsoft/node-binance-api/blob/master/examples/advanced.md#clamp-order-quantities-to-required-amounts-via-minqty-minnotional-stepsize-when-placing-orders)\ -[Show API Rate limits](https://github.com/jaggedsoft/node-binance-api/blob/master/examples/advanced.md#show-api-rate-limits)\ -[Connect to all WebSockets at once](https://github.com/jaggedsoft/node-binance-api/blob/master/examples/advanced.md#connect-to-all-websockets-at-once-thanks-keith1024)\ -[Get last order for a symbol](https://github.com/jaggedsoft/node-binance-api/blob/master/examples/advanced.md#get-last-order-for-a-symbol)\ -[newOrderRespType example](https://github.com/jaggedsoft/node-binance-api/blob/master/examples/advanced.md#neworderresptype-example-when-placing-orders)\ -[Recent Trades (historicalTrades, recentTrades, aggTrades functions)](https://github.com/jaggedsoft/node-binance-api/blob/master/examples/advanced.md#recent-trades-historicaltrades-recenttrades-aggtrades-functions)\ -[Terminate WebSocket connections](https://github.com/jaggedsoft/node-binance-api/blob/master/examples/advanced.md#terminate-websocket-connections)\ -[User Data: Account Balance Updates, Trade Updates, New Orders, Filled Orders, Cancelled Orders via WebSocket](https://github.com/jaggedsoft/node-binance-api/blob/master/examples/advanced.md#user-data-account-balance-updates-trade-updates-new-orders-filled-orders-cancelled-orders-via-websocket) +# Binance Margin API +#### Transfer from Main account to Margin account +```js +binance.mgTransferMainToMargin(asset, amount, (error, response) => { + if ( error ) return console.warn(error); + // Success! Transaction ID: response.tranId +}); -### Proxy Support -For the standard REST API the https_proxy or socks_proxy variable is honoured -*NOTE* proxy package has no dns name support, please use proxy IP address +``` +#### Transfer from Margin account to Main account +```js +binance.mgTransferMarginToMain(asset, amount, (error, response) => { + if ( error ) return console.warn(error); + // Success! Transaction ID: response.tranId +}); +``` + +#### Get maximum transfer-out amount from Margin account to Main account +```js +binance.maxTransferable(asset, (error, response) => { + if ( error ) return console.warn(error); + console.info(`Maximum transfer-out amount: ${response.amount}`); +}); +``` -**Linux** -```bash -export https_proxy=http://ip:port -#export socks_proxy=socks://ip:port -# run your app +#### Get maximum borrow amount +```js +binance.maxBorrowable(asset, (error, response) => { + if ( error ) return console.warn(error); + console.info(`Maximum borrow amount: ${response.amount}`); +}); ``` -**Windows** -```bash -set https_proxy=http://ip:port -#set socks_proxy=socks://ip:port -# run your app +#### Borrow from margin account +```js +binance.mgBorrow(asset, amount, (error, response) => { + if ( error ) return console.warn(error); + // Success! Transaction ID: response.tranId +}); ``` -For web sockets currently only the socks method is functional at this time +#### Repay margin account +```js +binance.mgRepay(asset, amount, (error, response) => { + if ( error ) return console.warn(error); + // Success! Transaction ID: response.tranId +}); +``` + +#### Margin BUY and SELL orders +Instead of `binance.buy()` use `binance.mgBuy()` and instead of `binance.sell()` use `binance.mgSell()`. + +For market orders use `binance.mgMarketBuy()` and `binance.mgMarketSell()`. + +For order operations, use `binance.mgCancel()`, `binance.mgCancelOrders()`, `binance.mgAllOrders()`, `binance.openOrders()`, `binance.mgOrderStatus()`. + +Usage and callbacks are the same as the 'regular account' counterparts. + +#### Get your Trade History for the Margin account +Use `binance.mgTrades()` instead of `binance.trades()`. + +```javascript +binance.mgTrades("ETHUSDT", (error, trades, symbol) => { + console.info(symbol+" trade history", trades); +}); +``` +
+ View Response + +```js +[ { symbol: 'ETHUSDT', + id: 9572, + orderId: 47884, + price: '2063.07', + qty: '1.44877', + commission: '2.98891392', + commissionAsset: 'USDT', + time: 1617900638521, + isBuyer: false, + isMaker: false, + isBestMatch: true, + isIsolated: true }] +``` +
-**linux** -```bash -export socks_proxy=socks://ip:port -# run your app +#### Margin account details +```javascript +binance.mgAccount((error, response) => { + if ( error ) return console.warn(error); + console.info("Account details response:", response) +}) ``` +
+ View response + + ```javascript + { + borrowEnabled: true, + marginLevel: '999.00000000', + totalAssetOfBtc: '0.00000003', + totalLiabilityOfBtc: '0.00000000', + totalNetAssetOfBtc: '0.00000003', + tradeEnabled: true, + transferEnabled: true, + userAssets: [ + { + asset: 'MATIC', + borrowed: '0.00000000', + free: '0.00000000', + interest: '0.00000000', + locked: '0.00000000', + netAsset: '0.00000000' + } + ] + } + ``` +
-**windows** -```bash -set socks_proxy=socks://ip:port -# run your app +# Binance Lending API + +#### Lending Account Details +```javascript +let lendingData = await binance.lending(); ``` +
+ View response + + ```javascript + lendingData { + positionAmountVos: [ + { + amount: '952983.20208997', + amountInBTC: '129.54853649', + amountInUSDT: '952983.20208997', + asset: 'USDT' + } + ], + totalAmountInBTC: '129.54853649', + totalAmountInUSDT: '952983.20208997', + totalFixedAmountInBTC: '13.59400000', + totalFixedAmountInUSDT: '100000.00000000', + totalFlexibleInBTC: '115.95453649', + totalFlexibleInUSDT: '852983.20208997' + } + { + positionAmountVos: [], + totalAmountInBTC: '0.00000000', + totalAmountInUSDT: '0.00000000', + totalFixedAmountInBTC: '0.00000000', + totalFixedAmountInUSDT: '0.00000000', + totalFlexibleInBTC: '0.00000000', + totalFlexibleInUSDT: '0.00000000' + } + { + positionAmountVos: [], + totalAmountInBTC: '0.00000000', + totalAmountInUSDT: '0.00000000', + totalFixedAmountInBTC: '0.00000000', + totalFixedAmountInUSDT: '0.00000000', + totalFlexibleInBTC: '0.00000000', + totalFlexibleInUSDT: '0.00000000' + } + ``` +
+ +#### [Advanced Examples](https://github.com/jaggedsoft/node-binance-api/blob/master/examples/advanced.md) +> [exchangeInfo: Pull minimum order size, quantity, etc](https://github.com/jaggedsoft/node-binance-api/blob/master/examples/advanced.md#exchangeinfo-pull-minimum-order-size-quantity-etc)\ +[Clamp order quantities to required amounts via minQty, minNotional, stepSize when placing orders](https://github.com/jaggedsoft/node-binance-api/blob/master/examples/advanced.md#clamp-order-quantities-to-required-amounts-via-minqty-minnotional-stepsize-when-placing-orders)\ +[Show API Rate limits](https://github.com/jaggedsoft/node-binance-api/blob/master/examples/advanced.md#show-api-rate-limits)\ +[Connect to all WebSockets at once](https://github.com/jaggedsoft/node-binance-api/blob/master/examples/advanced.md#connect-to-all-websockets-at-once-thanks-keith1024)\ +[Get last order for a symbol](https://github.com/jaggedsoft/node-binance-api/blob/master/examples/advanced.md#get-last-order-for-a-symbol)\ +[newOrderRespType example](https://github.com/jaggedsoft/node-binance-api/blob/master/examples/advanced.md#neworderresptype-example-when-placing-orders)\ +[Recent Trades (historicalTrades, recentTrades, aggTrades functions)](https://github.com/jaggedsoft/node-binance-api/blob/master/examples/advanced.md#recent-trades-historicaltrades-recenttrades-aggtrades-functions)\ +[Terminate WebSocket connections](https://github.com/jaggedsoft/node-binance-api/blob/master/examples/advanced.md#terminate-websocket-connections)\ +[User Data: Account Balance Updates, Trade Updates, New Orders, Filled Orders, Cancelled Orders via WebSocket](https://github.com/jaggedsoft/node-binance-api/blob/master/examples/advanced.md#user-data-account-balance-updates-trade-updates-new-orders-filled-orders-cancelled-orders-via-websocket) +[Margin User Data: Account Balance Updates, Trade Updates, New Orders, Filled Orders, Cancelled Orders via WebSocket](https://github.com/jaggedsoft/node-binance-api/blob/master/examples/advanced.md#margin-user-data-account-balance-updates-trade-updates-new-orders-filled-orders-cancelled-orders-via-websocket) +[Asynchronous Syntax Options](https://github.com/jaggedsoft/node-binance-api/blob/master/examples/advanced.md#asynchronous-syntax-options) + ### Troubleshooting Verify that your system time is correct. If you have any suggestions don't hesitate to file an issue. @@ -1637,24 +2152,32 @@ binance.options({ Problems getting your balance? Wrap the entry point of your application in useServerTime: ```js -binance.useServerTime(function() { - binance.balance((error, balances) => { - if ( error ) return console.error(error); - console.log("balances()", balances); - console.log("BNB balance: ", balances.BNB.available); - }); +await binance.useServerTime(); +binance.balance((error, balances) => { + if ( error ) return console.error(error); + console.info("balances()", balances); + console.info("BNB balance: ", balances.BNB.available); }); ``` -Thank you to all contributors: dmzoneill, dmitriz, keith1024, pavlovdog, usama33, yanislk, learnathoner, vaielab, nickreese, Tuitio, grandmore, itnok, CollinEstes, sethyx, mstijak, MadDeveloper, balthazar, bitoiu, matthewwoop, robaleman, hems and others! +You can enable verbose mode to help with debugging WebSocket streams: +```js +binance.setOption( 'verbose', true ); +``` + +### Sponsors -> # ⚠️ Binance no longer offers support for API projects. -> ## No support is offered. No questions will be answered. Pull requests are still welcome. +[![Vitality](https://github.com/user-attachments/assets/0981aae2-3e12-4b57-8d2f-c5ae2b3b8b1c)](https://vitalitycrypto.com/) -## Stargazers over time +## Star History -[![Stargazers over time](https://starcharts.herokuapp.com/jaggedsoft/node-binance-api.svg)](https://starcharts.herokuapp.com/jaggedsoft/node-binance-api) +[![Star History Chart](https://api.star-history.com/svg?repos=jaggedsoft/node-binance-api&type=Timeline)](https://star-history.com/#jaggedsoft/node-binance-api&Timeline) -[![Views](http://hits.dwyl.io/jaggedsoft/node-binance-api.svg)](http://hits.dwyl.io/jaggedsoft/node-binance-api) +## Contribution +- Give us a star :star: +- Fork and Clone! Awesome +- Select existing [issues](https://github.com/jaggedsoft/node-binance-api/issues) or create a [new issue](https://github.com/jaggedsoft/node-binance-api/issues/new) and give us a PR with your bugfix or improvement after. We love it ❤️ +![Downloads](https://img.shields.io/npm/dt/node-binance-api.svg?style=for-the-badge&maxAge=86400) ![Stars](https://img.shields.io/github/stars/jaggedsoft/node-binance-api.svg?style=for-the-badge&label=Stars) ![Contributors](https://img.shields.io/github/contributors/jaggedsoft/node-binance-api.svg?style=for-the-badge&maxAge=86400) +[![jaggedsoft on X](https://img.shields.io/twitter/follow/jaggedsoft.svg?style=social)](https://x.com/jaggedsoft) diff --git a/async.js b/async.js deleted file mode 100644 index 5ee4b480..00000000 --- a/async.js +++ /dev/null @@ -1,115 +0,0 @@ -/* -(async () => { - let api = require('./async.js'); - api.options('options.json'); - await api.useServerTime(); - console.log(await api.prices()); - console.log(await api.time()); - //console.log(await api.openOrders()); - //console.log(await api.balance()); -})(); -*/ - -// consider moving all websocket functions to binance-websockets - -module.exports = function() { - 'use strict'; - const file = require('fs'); - const axios = require('axios'); - const crypto = require('crypto'); - let options = {}; - - const defaults = { - proxy: false, - recvWindow: 5000, - timeOffset: 0, - useServerTime: false, - reconnect: true, - verbose: false, - test: false, - log: function() { - console.log(Array.prototype.slice.call(arguments)); - } - }; - - async function request(url, data = {}, flags = {}) { - const base = 'https://api.binance.com/api/'; - const wapi = 'https://api.binance.com/wapi/'; - const userAgent = 'Mozilla/4.0 (compatible; Node Binance API)'; - const contentType = 'application/x-www-form-urlencoded'; - let headers = { - 'User-Agent': userAgent, - 'Content-type': contentType - }; - if ( typeof flags.method === 'undefined' ) flags.method = 'GET'; // GET POST PUT DELETE - if ( typeof flags.type === 'undefined' ) flags.type = false; // Endpoint security: TRADE, MARKET, SIGNED - else { - if ( typeof data.recvWindow === 'undefined' ) data.recvWindow = options.recvWindow; - headers['X-MBX-APIKEY'] = options.APIKEY; - if ( !options.APIKEY ) return new Error('Invalid API Key'); - } - if ( flags.type === 'SIGNED' ) { - if ( !options.APISECRET ) return new Error('Invalid API Secret'); - data.timestamp = new Date().getTime() + options.timeOffset; - let query = Object.keys(data).reduce(function(a,k){a.push(k+'='+encodeURIComponent(data[k]));return a},[]).join('&'); - data.signature = crypto.createHmac('sha256', options.APISECRET).update(query).digest('hex'); // HMAC hash header - } - try { - const response = await axios.request({ - url: url, - params: data, - method: flags.method, - headers: headers, - timeout: options.recvWindow, - proxy: options.proxy, - baseURL: typeof flags.wapi === 'undefined' ? base : wapi, - }); - if ( response && response.status !== 200 ) return new Error(JSON.stringify(response.data)); - return response.data; - } catch ( error ) { - return new Error(JSON.stringify(error.response.data)); // error.message - } - } - - return { - options: function(opt) { - // Accept options as string (load json from file) or object - if ( typeof opt === 'string' ) options = JSON.parse(file.readFileSync(opt)); - else options = opt; - // Set default options - for ( let key in defaults ) { - if ( typeof options[key] === 'undefined' ) { - options[key] = defaults[key]; - } - } - }, - - useServerTime: async function useServerTime() { - const response = await request('/v1/time'); - options.timeOffset = response.serverTime - new Date().getTime(); - return response.serverTime; - }, - - time: async function time() { - const response = await request('/v1/time'); - return response.serverTime; - }, - - // prices({symbol:'BTCUSDT'}) or prices() - prices: async function prices(flags = {}) { - let url = '/v3/ticker/price'; - if ( typeof flags.symbol !== 'undefined' ) url+= '?symbol=' + flags.symbol; - return await request(url); - }, - - // openOrders({symbol:'BTCUSDT'}) or openOrders() - openOrders: async function(symbol = false) { - let parameters = symbol ? {symbol:symbol} : {}; - return await request('/v3/openOrders', parameters, {type: 'SIGNED'}); - }, - - balance: async function() { - return request('/v3/account', {}, {type: 'SIGNED'}); - } - } -}(); diff --git a/demo.js b/demo.js new file mode 100644 index 00000000..a9ff4920 --- /dev/null +++ b/demo.js @@ -0,0 +1,10 @@ +const Binance = require( './node-binance-api.js' ); + +async function run() { + const exchange = new Binance(); + const res = await exchange.futuresTime(); + console.log( res ); +} + + +run(); diff --git a/deprecated/node-binance-api.d.ts b/deprecated/node-binance-api.d.ts new file mode 100644 index 00000000..67f424b7 --- /dev/null +++ b/deprecated/node-binance-api.d.ts @@ -0,0 +1,1593 @@ +/** + * @author tripolskypetr + * @see https://github.com/tripolskypetr + */ +declare module "node-binance-api" { + + type _callback = (...args: any) => any; + + type _symbol = string; + + type _interval = keyof { + '1m': never; + '3m': never; + '5m': never; + '15m': never; + '30m': never; + '1h': never; + '2h': never; + '4h': never; + '6h': never; + '8h': never; + '12h': never; + '1d': never; + '3d': never; + '1w': never; + '1M': never; + }; + + interface IWebsockets { + /** + * Userdata websockets function + * @param {function} callback - the callback function + * @param {function} execution_callback - optional execution callback + * @param {function} subscribed_callback - subscription callback + * @param {function} list_status_callback - status callback + * @return {undefined} + */ + userData(callback: _callback, execution_callback: _callback, subscribed_callback: _callback, list_status_callback: _callback): any; + userData(...args: any): any; + + /** + * Margin Userdata websockets function + * @param {function} callback - the callback function + * @param {function} execution_callback - optional execution callback + * @param {function} subscribed_callback - subscription callback + * @param {function} list_status_callback - status callback + * @return {undefined} + */ + userMarginData(callback: _callback, execution_callback: _callback, subscribed_callback: _callback, list_status_callback: _callback): any; + userMarginData(...args: any): any; + + /** + * Future Userdata websockets function + * @param {function} margin_call_callback + * @param {function} account_update_callback + * @param {function} order_update_callback + * @param {Function} subscribed_callback - subscription callback + */ + userFutureData(margin_call_callback: _callback, account_update_callback: _callback, order_update_callback: _callback, subscribed_callback: _callback): any; + userFutureData(...args: any): any; + + /** + * Delivery Userdata websockets function + * @param {function} margin_call_callback + * @param {function} account_update_callback + * @param {function} order_update_callback + * @param {Function} subscribed_callback - subscription callback + */ + userDeliveryData(margin_call_callback: _callback, account_update_callback: _callback, order_update_callback: _callback, subscribed_callback: _callback): any; + userDeliveryData(...args: any): any; + + /** + * Subscribe to a generic websocket + * @param {string} url - the websocket endpoint + * @param {function} callback - optional execution callback + * @param {boolean} reconnect - subscription callback + * @return {WebSocket} the websocket reference + */ + subscribe(url: string, callback: _callback, reconnect?: boolean): any; + subscribe(...args: any): any; + + /** + * Subscribe to a generic combined websocket + * @param {string} url - the websocket endpoint + * @param {function} callback - optional execution callback + * @param {boolean} reconnect - subscription callback + * @return {WebSocket} the websocket reference + */ + subscribeCombined(url: string, callback: _callback, reconnect?: boolean): any; + subscribeCombined(...args: any): any; + + /** + * Returns the known websockets subscriptions + * @return {array} array of web socket subscriptions + */ + subscriptions(...args: any): any[]; + subscriptions(...args: any): any; + + /** + * Terminates a web socket + * @param {string} endpoint - the string associated with the endpoint + * @return {undefined} + */ + terminate(endpoint: string): any; + terminate(...args: any): any; + + depth(...args: any): any; + depth(...args: any): any; + + /** + * Websocket depth chart + * @param {array/string} symbols - an array or string of symbols to query + * @param {function} callback - callback function + * @return {string} the websocket endpoint + */ + depthCache(symbols: _symbol[] | _symbol, ): any; + depthCache(...args: any): any; + + /** + * Clear Websocket depth cache + * @param {String|Array} symbols - a single symbol, or an array of symbols, to clear the cache of + * @returns {void} + */ + clearDepthCache(symbols: _symbol | _symbol[]): any; + clearDepthCache(...args: any): any; + + /** + * Websocket staggered depth cache + * @param {array/string} symbols - an array of symbols to query + * @param {function} callback - callback function + * @param {int} limit - the number of entries + * @param {int} stagger - ms between each depth cache + * @return {Promise} the websocket endpoint + */ + depthCacheStaggered(symbols: _symbol | _symbol[], callback: _callback, limit?: number, stagger?: number): Promise; + depthCacheStaggered(...args: any): any; + + /** + * Websocket aggregated trades + * @param {array/string} symbols - an array or string of symbols to query + * @param {function} callback - callback function + * @return {string} the websocket endpoint + */ + aggTrades(symbols: _symbol | _symbol[], callback: _callback): any; + aggTrades(...args: any): any; + + /** + * Websocket raw trades + * @param {array/string} symbols - an array or string of symbols to query + * @param {function} callback - callback function + * @return {string} the websocket endpoint + */ + trades(symbols: _symbol | _symbol[], callback: _callback): string; + trades(...args: any): any; + + /** + * Websocket klines + * @param {array/string} symbols - an array or string of symbols to query + * @param {string} interval - the time interval + * @param {function} callback - callback function + * @param {int} limit - maximum results, no more than 1000 + * @return {string} the websocket endpoint + */ + chart(symbols: _symbol | _symbol[], interval: _interval, callback: _callback, limit?: number): string; + chart(...args: any): any; + + /** + * Websocket candle sticks + * @param {array/string} symbols - an array or string of symbols to query + * @param {string} interval - the time interval + * @param {function} callback - callback function + * @return {string} the websocket endpoint + */ + candlesticks(symbols: _symbol | _symbol[], interval: _interval, callback: _callback): string; + candlesticks(...args: any): any; + + /** + * Websocket mini ticker + * @param {function} callback - callback function + * @return {string} the websocket endpoint + */ + miniTicker(callback: _callback): string; + miniTicker(...args: any): any; + + /** + * Spot WebSocket bookTicker (bid/ask quotes including price & amount) + * @param {symbol} symbol name or false. can also be a callback + * @param {function} callback - callback function + * @return {string} the websocket endpoint + */ + bookTickers(symbol: _symbol, callback: _callback): string; + bookTickers(...args: any): any; + + /** + * Websocket prevday percentage + * @param {array/string} symbols - an array or string of symbols to query + * @param {function} callback - callback function + * @param {boolean} singleCallback - avoid call one callback for each symbol in data array + * @return {string} the websocket endpoint + */ + prevDay(symbols: _symbol | _symbol[], callback: _callback, singleCallback: boolean): string; + prevDay(...args: any): any; + } + + interface IConstructorArgs { + recvWindow: number; + useServerTime: boolean; + reconnect: boolean; + test: boolean; + hedgeMode: boolean; + log: (...args: any[]) => void; + verbose: boolean; + keepAlive: boolean; + localAddress: boolean; + family: boolean; + urls: Partial<{ + base: string; + wapi: string; + sapi: string; + fapi: string; + fapiTest: string; + stream: string; + combineStream: string; + fstream: string; + fstreamSingle: string; + fstreamTest: string; + fstreamSingleTest: string; + dstream: string; + dstreamSingle: string; + dstreamTest: string; + dstreamSingleTest: string; + }>; + timeOffset: number; + } + + class Binance { + + constructor(options?: Partial); + constructor(pathToFile?: string); + constructor(...args: any); + + /** + * Gets depth cache for given symbol + * @param {symbol} symbol - get depch cache for this symbol + * @return {object} - object + */ + depthCache(symbols: _symbol | _symbol[], callback: _callback, limit: number): string; + depthCache(...args: any): any; + + /** + * Gets depth volume for given symbol + * @param {symbol} symbol - get depch volume for this symbol + * @return {object} - object + */ + depthVolume(symbol: _symbol): any; + depthVolume(...args: any): any; + + /** + * Count decimal places + * @param {float} float - get the price precision point + * @return {int} - number of place + */ + getPrecision(float: number): number; + getPrecision(...args: any): any; + + /** + * rounds number with given step + * @param {float} qty - quantity to round + * @param {float} stepSize - stepSize as specified by exchangeInfo + * @return {float} - number + */ + roundStep(qty: number, stepSize: number): number; + roundStep(...args: any): any; + + /** + * rounds price to required precision + * @param {float} price - price to round + * @param {float} tickSize - tickSize as specified by exchangeInfo + * @return {float} - number + */ + roundTicks(price: number, tickSize: number): any; + roundTicks(...args: any): any; + + /** + * Gets percentage of given numbers + * @param {float} min - the smaller number + * @param {float} max - the bigger number + * @param {int} width - percentage width + * @return {float} - percentage + */ + percent(min: number, max: number, width?: number): any; + percent(...args: any): any; + + /** + * Gets the sum of an array of numbers + * @param {array} array - the number to add + * @return {float} - sum + */ + sum(array: number[]): number; + sum(...args: any): any; + + /** + * Reverses the keys of an object + * @param {object} object - the object + * @return {object} - the object + */ + reverse(object: any): any; + reverse(...args: any): any; + + /** + * Converts an object to an array + * @param {object} obj - the object + * @return {array} - the array + */ + array(obj: any): any[]; + array(...args: any): any; + + /** + * Sorts bids + * @param {string} symbol - the object + * @param {int} max - the max number of bids + * @param {string} baseValue - the object + * @return {object} - the object + */ + sortBids(symbol: any, max?: number, baseValue?: string): any; + sortBids(...args: any): any; + + /** + * Sorts asks + * @param {string} symbol - the object + * @param {int} max - the max number of bids + * @param {string} baseValue - the object + * @return {object} - the object + */ + sortAsks(symbol: any, max?: number, baseValue?: string): any; + sortAsks(...args: any): any; + + /** + * Returns the first property of an object + * @param {object} object - the object to get the first member + * @return {string} - the object key + */ + first(object: any): string; + first(...args: any): any; + + /** + * Returns the last property of an object + * @param {object} object - the object to get the first member + * @return {string} - the object key + */ + last(object: any): string; + last(...args: any): any; + + /** + * Returns an array of properties starting at start + * @param {object} object - the object to get the properties form + * @param {int} start - the starting index + * @return {array} - the array of entires + */ + slice(object: any, start?: number): any[]; + slice(...args: any): any; + + /** + * Gets the minimum key form object + * @param {object} object - the object to get the properties form + * @return {string} - the minimum key + */ + min(object: any): string; + min(...args: any): any; + + /** + * Gets the maximum key form object + * @param {object} object - the object to get the properties form + * @return {string} - the minimum key + */ + max(object: any): string; + max(...args: any): any; + + /** + * Sets an option given a key and value + * @param {string} key - the key to set + * @param {object} value - the value of the key + * @return {undefined} + */ + setOption(key: string, value: any): any; + setOption(...args: any): any; + + /** + * Gets an option given a key + * @param {string} key - the key to set + * @return {undefined} + */ + getOption(key: string): any; + getOption(...args: any): any; + + /** + * Returns the entire info object + * @return {object} - the info object + */ + getInfo(key: string, value: any): any; + getInfo(...args: any): any; + + /** + * Returns the used weight from the last request + * @return {object} - 1m weight used + */ + usedWeight(): any; + usedWeight(...args: any): any; + + /** + * Returns the status code from the last http response + * @return {object} - status code + */ + statusCode(): any; + statusCode(...args: any): any; + + /** + * Returns the ping time from the last futures request + * @return {object} - latency/ping (2ms) + */ + futuresLatency(): any; + futuresLatency(...args: any): any; + + /** + * Returns the complete URL from the last request + * @return {object} - http address including query string + */ + lastURL(): any; + lastURL(...args: any): any; + + /** + * Returns the order count from the last request + * @return {object} - orders allowed per 1m + */ + orderCount(...args: any): any; + orderCount(...args: any): any; + + /** + * Returns the entire options object + * @return {object} - the options object + */ + getOptions(): any; + getOptions(...args: any): any; + + + options(...args: any): any; + + /** + * Creates an order + * @param {string} side - BUY or SELL + * @param {string} symbol - the symbol to buy + * @param {numeric} quantity - the quantity required + * @param {numeric} price - the price to pay for each unit + * @param {object} flags - aadditionalbuy order flags + * @param {function} callback - the callback function + * @return {promise or undefined} - omitting the callback returns a promise + */ + order(side: 'BUY' | 'SELL', symbol: _symbol, quantity: number, price: number, flags?: any, callback?: _callback): Promise; + order(...args: any): any; + + /** + * Creates a buy order + * @param {string} symbol - the symbol to buy + * @param {numeric} quantity - the quantity required + * @param {numeric} price - the price to pay for each unit + * @param {object} flags - additional buy order flags + * @param {function} callback - the callback function + * @return {promise or undefined} - omitting the callback returns a promise + */ + buy(symbol: _symbol, quantity: number, price: number, flags?: any, callback?: _callback): Promise; + buy(...args: any): any; + + /** + * Creates a sell order + * @param {string} symbol - the symbol to sell + * @param {numeric} quantity - the quantity required + * @param {numeric} price - the price to sell each unit for + * @param {object} flags - additional order flags + * @param {function} callback - the callback function + * @return {promise or undefined} - omitting the callback returns a promise + */ + sell(symbol: _symbol, quantity: number, price: number, flags?: any, callback?: _callback): Promise; + sell(...args: any): any; + + /** + * Creates a market buy order + * @param {string} symbol - the symbol to buy + * @param {numeric} quantity - the quantity required + * @param {object} flags - additional buy order flags + * @param {function} callback - the callback function + * @return {promise or undefined} - omitting the callback returns a promise + */ + marketBuy(symbol: _symbol, quantity: number, flags?: any, callback?: _callback): Promise; + marketBuy(...args: any): any; + + /** + * Creates a market sell order + * @param {string} symbol - the symbol to sell + * @param {numeric} quantity - the quantity required + * @param {object} flags - additional sell order flags + * @param {function} callback - the callback function + * @return {promise or undefined} - omitting the callback returns a promise + */ + marketSell(symbol: _symbol, quantity: number, flags?: any, callback?: _callback): Promise; + marketSell(...args: any): any; + + /** + * Cancels an order + * @param {string} symbol - the symbol to cancel + * @param {string} orderid - the orderid to cancel + * @param {function} callback - the callback function + * @return {promise or undefined} - omitting the callback returns a promise + */ + cancel(symbol: _symbol, orderid: string, callback?: _callback): Promise; + cancel(...args: any): any; + + /** + * Gets the status of an order + * @param {string} symbol - the symbol to check + * @param {string} orderid - the orderid to check + * @param {function} callback - the callback function + * @param {object} flags - any additional flags + * @return {promise or undefined} - omitting the callback returns a promise + */ + orderStatus(symbol: _symbol, orderid: string, callback?: _callback, flags?: any): Promise; + orderStatus(...args: any): any; + + /** + * Gets open orders + * @param {string} symbol - the symbol to get + * @param {function} callback - the callback function + * @return {promise or undefined} - omitting the callback returns a promise + */ + openOrders(symbol: _symbol, callback?: _callback): Promise; + openOrders(...args: any): any; + + /** + * Cancels all orders of a given symbol + * @param {string} symbol - the symbol to cancel all orders for + * @param {function} callback - the callback function + * @return {promise or undefined} - omitting the callback returns a promise + */ + cancelAll(symbol: _symbol, callback?: _callback): Promise; + cancelAll(...args: any): any; + + /** + * Cancels all orders of a given symbol + * @param {string} symbol - the symbol to cancel all orders for + * @param {function} callback - the callback function + * @return {promise or undefined} - omitting the callback returns a promise + */ + cancelOrders(symbol: _symbol, callback?: _callback): Promise; + cancelOrders(...args: any): any; + + /** + * Gets all order of a given symbol + * @param {string} symbol - the symbol + * @param {function} callback - the callback function (can also accept options) + * @param {object} options - additional options + * @return {promise or undefined} - omitting the callback returns a promise + */ + allOrders(symbol: _symbol, callback?: _callback, options?: any): Promise; + allOrders(...args: any): any; + + /** + * Gets the depth information for a given symbol + * @param {string} symbol - the symbol + * @param {function} callback - the callback function + * @param {int} limit - limit the number of returned orders + * @return {promise or undefined} - omitting the callback returns a promise + */ + depth(symbol: _symbol, callback?: _callback, limit?: number): Promise; + depth(...args: any): any; + + /** + * Gets the average prices of a given symbol + * @param {string} symbol - the symbol + * @param {function} callback - the callback function + * @return {promise or undefined} - omitting the callback returns a promise + */ + avgPrice(symbol: _symbol, callback?: _callback): Promise; + avgPrice(...args: any): any; + + /** + * Gets the prices of a given symbol(s) + * @param {string} symbol - the symbol + * @param {function} callback - the callback function + * @return {promise or undefined} - omitting the callback returns a promise + */ + prices(symbol?: _symbol, callback?: _callback): Promise; + prices(...args: any): any; + + /** + * Gets the book tickers of given symbol(s) + * @param {string} symbol - the symbol + * @param {function} callback - the callback function + * @return {promise or undefined} - omitting the callback returns a promise + */ + bookTickers(symbol: _symbol, callback?: _callback): Promise; + bookTickers(...args: any): any; + + /** + * Gets the prevday percentage change + * @param {string} symbol - the symbol or symbols + * @param {function} callback - the callback function + * @return {promise or undefined} - omitting the callback returns a promise + */ + prevDay(symbol: _symbol, callback?: _callback): Promise; + prevDay(...args: any): any; + + /** + * Gets the the exchange info + * @param {function} callback - the callback function + * @return {promise or undefined} - omitting the callback returns a promise + */ + exchangeInfo(callback?: _callback): Promise; + exchangeInfo(...args: any): any; + + /** + * Gets the dust log for user + * @param {function} callback - the callback function + * @return {promise or undefined} - omitting the callback returns a promise + */ + dustLog(callback?: _callback): Promise; + dustLog(...args: any): any; + + dustTransfer(...args: any): any; + assetDividendRecord(...args: any): any; + + /** + * Gets the the system status + * @param {function} callback - the callback function + * @return {promise or undefined} - omitting the callback returns a promise + */ + systemStatus(callback?: _callback): Promise; + systemStatus(...args: any): any; + + /** + * Withdraws asset to given wallet id + * @param {string} asset - the asset symbol + * @param {string} address - the wallet to transfer it to + * @param {number} amount - the amount to transfer + * @param {string} addressTag - and addtional address tag + * @param {function} callback - the callback function + * @param {string} name - the name to save the address as. Set falsy to prevent Binance saving to address book + * @return {promise or undefined} - omitting the callback returns a promise + */ + withdraw(asset: string, address: string, amount: number, addressTag?: string, callback?: _callback, name?: string): Promise; + withdraw(...args: any): any; + + /** + * Get the Withdraws history for a given asset + * @param {function} callback - the callback function + * @param {object} params - supports limit and fromId parameters + * @return {promise or undefined} - omitting the callback returns a promise + */ + withdrawHistory(callback?: _callback, params?: any): any; + withdrawHistory(...args: any): any; + + /** + * Get the deposit history + * @param {function} callback - the callback function + * @param {object} params - additional params + * @return {promise or undefined} - omitting the callback returns a promise + */ + depositHistory(callback?: _callback, params?: any): Promise; + depositHistory(...args: any): any; + + /** + * Get the deposit history for given asset + * @see https://developers.binance.com/docs/wallet/capital/deposite-address + * @param {string} code - the asset + * @param {function} callback - the callback function + * @return {promise or undefined} - omitting the callback returns a promise + */ + depositAddress(code: string, callback?: _callback): Promise; + depositAddress(...args: any): any; + + /** + * Get the account status + * @param {function} callback - the callback function + * @return {promise or undefined} - omitting the callback returns a promise + */ + accountStatus(callback?: _callback): Promise; + accountStatus(...args: any): any; + + /** + * Get the trade fee + * @param {function} callback - the callback function + * @param {string} symbol (optional) + * @return {promise or undefined} - omitting the callback returns a promise + */ + tradeFee(...args: any): any; + tradeFee(...args: any): any; + + /** + * Fetch asset detail (minWithdrawAmount, depositStatus, withdrawFee, withdrawStatus, depositTip) + * @param {function} callback - the callback function + * @return {promise or undefined} - omitting the callback returns a promise + */ + assetDetail(callback?: _callback): Promise; + assetDetail(...args: any): any; + + /** + * Get the account + * @param {function} callback - the callback function + * @return {promise or undefined} - omitting the callback returns a promise + */ + account(callback?: _callback): Promise; + account(...args: any): any; + + /** + * Get the balance data + * @param {function} callback - the callback function + * @return {promise or undefined} - omitting the callback returns a promise + */ + balance(callback?: _callback): Promise; + balance(...args: any): any; + + /** + * Get trades for a given symbol + * @param {string} symbol - the symbol + * @param {function} callback - the callback function + * @param {object} options - additional options + * @return {promise or undefined} - omitting the callback returns a promise + */ + trades(symbol: _symbol, callback: _callback, options?: any): Promise; + trades(...args: any): any; + + /** + * Tell api to use the server time to offset time indexes + * @param {function} callback - the callback function + * @return {promise or undefined} - omitting the callback returns a promise + */ + useServerTime(callback?: _callback): Promise; + useServerTime(...args: any): any; + + /** + * Get Binance server time + * @param {function} callback - the callback function + * @return {promise or undefined} - omitting the callback returns a promise + */ + time(callback?: _callback): Promise; + time(...args: any): any; + + /** + * Get agg trades for given symbol + * @param {string} symbol - the symbol + * @param {object} options - additional optoins + * @param {function} callback - the callback function + * @return {promise or undefined} - omitting the callback returns a promise + */ + aggTrades(symbol: _symbol, options?: any, callback?: _callback): Promise; + aggTrades(...args: any): any; + + /** + * Get the recent trades + * @param {string} symbol - the symbol + * @param {function} callback - the callback function + * @param {int} limit - limit the number of items returned + * @return {promise or undefined} - omitting the callback returns a promise + */ + recentTrades(symbol: _symbol, callback?: _callback, limit?: number): Promise; + recentTrades(...args: any): any; + + /** + * Get the historical trade info + * @param {string} symbol - the symbol + * @param {function} callback - the callback function + * @param {int} limit - limit the number of items returned + * @param {int} fromId - from this id + * @return {promise or undefined} - omitting the callback returns a promise + */ + historicalTrades(symbol: _symbol, callback?: _callback, limit?: number, fromId?: boolean): Promise; + historicalTrades(...args: any): any; + + /** + * Convert chart data to highstock array [timestamp,open,high,low,close] + * @param {object} chart - the chart + * @param {boolean} include_volume - to include the volume or not + * @return {array} - an array + */ + highstock(chart: any, include_volume?: boolean): any[]; + highstock(...args: any): any; + + /** + * Populates OHLC information + * @param {object} chart - the chart + * @return {object} - object with candle information + */ + ohlc(chart: any): any; + ohlc(...args: any): any; + + /** + * Gets the candles information for a given symbol + * intervals: 1m,3m,5m,15m,30m,1h,2h,4h,6h,8h,12h,1d,3d,1w,1M + * @param {string} symbol - the symbol + * @param {function} interval - the callback function + * @param {function} callback - the callback function + * @param {object} options - additional options + * @return {promise or undefined} - omitting the callback returns a promise + */ + candlesticks(symbol: _symbol, interval: _interval, callback?: _callback, options?: any): Promise; + candlesticks(...args: any): any; + + /** + * Queries the public api + * @param {string} url - the public api endpoint + * @param {object} data - the data to send + * @param {function} callback - the callback function + * @param {string} method - the http method + * @return {promise or undefined} - omitting the callback returns a promise + */ + publicRequest(url: string, data: any, callback?: _callback, method?: string): Promise; + publicRequest(...args: any): any; + + /** + * Queries the futures API by default + * @param {string} url - the signed api endpoint + * @param {object} data - the data to send + * @param {object} flags - type of request, authentication method and endpoint url + */ + promiseRequest(url: string, data?: any, flags?: any): Promise; + promiseRequest(...args: any): any; + + /** + * Queries the signed api + * @param {string} url - the signed api endpoint + * @param {object} data - the data to send + * @param {function} callback - the callback function + * @param {string} method - the http method + * @param {boolean} noDataInSignature - Prevents data from being added to signature + * @return {promise or undefined} - omitting the callback returns a promise + */ + signedRequest(url: string, data: any, callback?: _callback, method?: string, noDataInSignature?: boolean): Promise; + signedRequest(...args: any): any; + + /** + * Gets the market asset of given symbol + * @param {string} symbol - the public api endpoint + * @return {string or undefined} + */ + getMarket(symbol: _symbol): string | undefined; + getMarket(...args: any): any; + + /** + * Get the account binance lending information + * @param {object} params - the callback function + * @return {promise or undefined} - omitting the callback returns a promise + */ + lending(params?: any): Promise; + lending(...args: any): any; + + futuresPing(params?: any): Promise; + futuresPing(...args: any): any; + + futuresTime(params?: any): Promise; + futuresTime(...args: any): any; + + futuresExchangeInfo(): Promise; + futuresExchangeInfo(...args: any): any; + + futuresPrices(params?: any): Promise; + futuresPrices(...args: any): any; + + futuresDaily(symbol?: _symbol, params?: any): Promise; + futuresDaily(...args: any): any; + + futuresOpenInterest(symbol: _symbol): Promise; + futuresOpenInterest(...args: any): any; + + futuresCandles(symbol: _symbol, interval?: _interval, params?: any): Promise; + futuresCandles(...args: any): any; + + futuresMarkPrice(_symbol?: _symbol): Promise; + futuresMarkPrice(...args: any): any; + + futuresTrades(symbol: _symbol, params?: any): Promise; + futuresTrades(...args: any): any; + + futuresHistoricalTrades(symbol: _symbol, params?: any): Promise; + futuresHistoricalTrades(...args: any): any; + + futuresAggTrades(symbol: _symbol, params?: any): Promise; + futuresAggTrades(...args: any): any; + + futuresForceOrders(params?: any): Promise; + futuresForceOrders(...args: any): any; + + futuresDeleverageQuantile(params?: any): Promise; + futuresDeleverageQuantile(...args: any): any; + + futuresUserTrades(symbol: _symbol, params?: any): Promise; + futuresUserTrades(...args: any): any; + + futuresGetDataStream(params?: any): Promise; + futuresGetDataStream(...args: any): any; + + futuresKeepDataStream(params?: any): Promise; + futuresKeepDataStream(...args: any): any; + + futuresCloseDataStream(params?: any): Promise; + futuresCloseDataStream(...args: any): any; + + futuresLiquidationOrders(symbol?: _symbol, params?: any): Promise; + futuresLiquidationOrders(...args: any): any; + + futuresPositionRisk(params?: any, useV2 = false): Promise; + futuresPositionRisk(...args: any): any; + + futuresPositionRiskV2(params?: any): Promise; + futuresPositionRiskV2(...args: any): any; + + futuresFundingRate(symbol: _symbol, params?: any): Promise; + futuresFundingRate(...args: any): any; + + futuresLeverageBracket(symbol?: _symbol, params?: any): Promise; + futuresLeverageBracket(...args: any): any; + + futuresTradingStatus(symbol?: _symbol, params?: any): Promise; + futuresTradingStatus(...args: any): any; + + futuresCommissionRate(symbol?: _symbol, params?: any): Promise; + futuresCommissionRate(...args: any): any; + + /** + * @see leverage 1 to 125 + */ + futuresLeverage(symbol: _symbol, leverage: number, params?: any): Promise; + futuresLeverage(...args: any): any; + + futuresMarginType(symbol: _symbol, marginType: 'ISOLATED' | 'CROSSED', params?: any): Promise; + futuresMarginType(...args: any): any; + + futuresPositionMargin(symbol: _symbol, amount: number, type?: number, params?: any): Promise; + futuresPositionMargin(...args: any): any; + + futuresPositionMarginHistory(symbol: _symbol, params?: any): Promise; + futuresPositionMarginHistory(...args: any): any; + + futuresIncome(params?: any): Promise; + futuresIncome(...args: any): any; + + futuresBalance(params?: any): Promise; + futuresBalance(...args: any): any; + + futuresAccount(params?: any): Promise; + futuresAccount(...args: any): any; + + futuresDepth(symbol: _symbol, params?: any): Promise; + futuresDepth(...args: any): any; + + futuresQuote(symbol?: _symbol, params?: any): Promise; + futuresQuote(...args: any): any; + + futuresBuy(symbol: _symbol, quantity: number, price: number, params?: any): Promise; + futuresBuy(...args: any): any; + + futuresSell(symbol: _symbol, quantity: number, price: number, params?: any): Promise; + futuresSell(...args: any): any; + + futuresMarketBuy(symbol: _symbol, quantity: number, params?: any): Promise; + futuresMarketBuy(...args: any): any; + + futuresMarketSell(symbol: _symbol, quantity: number, params?: any): Promise; + futuresMarketSell(...args: any): any; + + futuresOrder(side: 'BUY' | 'SELL', symbol: _symbol, price?: number, params?: any): Promise; + futuresOrder(...args: any): any; + + futuresOrderStatus(symbol: _symbol, params?: any): Promise; + futuresOrderStatus(...args: any): any; + + futuresCancel(symbol: _symbol, params?: any): Promise; + futuresCancel(...args: any): any; + + futuresCancelAll(symbol: _symbol, params?: any): Promise; + futuresCancelAll(...args: any): any; + + futuresCountdownCancelAll(symbol: _symbol, countdownTime?: number, params?: any): Promise; + futuresCountdownCancelAll(...args: any): any; + + futuresOpenOrders(symbol?: _symbol, params?: any): Promise; + futuresOpenOrders(...args: any): any; + + futuresAllOrders(symbol?: _symbol, params?: any): Promise; + futuresAllOrders(...args: any): any; + + futuresPositionSideDual(params?: any): Promise; + futuresPositionSideDual(...args: any): any; + + futuresChangePositionSideDual(dualSidePosition: any, params?: any): Promise; + futuresChangePositionSideDual(...args: any): any; + + futuresTransferAsset(asset: any, amount: any, type: any): Promise; + futuresTransferAsset(...args: any): any; + + futuresHistDataId(symbol?: _symbol, params?: any): Promise; + futuresHistDataId(...args: any): any; + + futuresDownloadLink(downloadId: string): Promise; + futuresDownloadLink(...args: any): any; + + deliveryPing(params?: any): Promise; + deliveryPing(...args: any): any; + + deliveryTime(params?: any): Promise; + deliveryTime(...args: any): any; + + deliveryExchangeInfo(): Promise; + deliveryExchangeInfo(...args: any): any; + + deliveryPrices(params?: any): Promise; + deliveryPrices(...args: any): any; + + deliveryDaily(symbol?: _symbol, params?: any): Promise; + deliveryDaily(...args: any): any; + + deliveryOpenInterest(symbol: _symbol): Promise; + deliveryOpenInterest(...args: any): any; + + deliveryCandles(symbol: _symbol, interval?: _interval, params?: any): Promise; + deliveryCandles(...args: any): any; + + deliveryContinuousKlines(pair: any, contractType: 'CURRENT_QUARTER' | string, interval: _interval, params?: any): Promise; + deliveryContinuousKlines(...args: any): any; + + deliveryIndexKlines(pair: any, interval: _interval, params?: any): Promise; + deliveryIndexKlines(...args: any): any; + + deliveryMarkPriceKlines(symbol: _symbol, interval?: _interval, params?: any): Promise; + deliveryMarkPriceKlines(...args: any): any; + + deliveryMarkPrice(symbol?: _symbol): Promise; + deliveryMarkPrice(...args: any): any; + + deliveryTrades(symbol: _symbol, params?: any): Promise; + deliveryTrades(...args: any): any; + + deliveryHistoricalTrades(symbol: _symbol, params?: any): Promise; + deliveryHistoricalTrades(...args: any): any; + + deliveryAggTrades(symbol: _symbol, params?: any): Promise; + deliveryAggTrades(...args: any): any; + + deliveryUserTrades(symbol: _symbol, params?: any): Promise; + deliveryUserTrades(...args: any): any; + + deliveryGetDataStream(params?: any): Promise; + deliveryGetDataStream(...args: any): any; + + deliveryKeepDataStream(params?: any): Promise; + deliveryKeepDataStream(...args: any): any; + + deliveryCloseDataStream(params?: any): Promise; + deliveryCloseDataStream(...args: any): any; + + deliveryLiquidationOrders(symbol?: _symbol, params?: any): Promise; + deliveryLiquidationOrders(...args: any): any; + + deliveryPositionRisk(params?: any): Promise; + deliveryPositionRisk(...args: any): any; + + deliveryLeverageBracket(symbol?: _symbol, params?: any): Promise; + deliveryLeverageBracket(...args: any): any; + + deliveryLeverageBracketSymbols(symbol?: _symbol, params?: any): Promise; + deliveryLeverageBracketSymbols(...args: any): any; + + deliveryLeverage(symbol: _symbol, leverage: any, params?: any): Promise; + deliveryLeverage(...args: any): any; + + deliveryMarginType(symbol: _symbol, marginType: any, params?: any): Promise; + deliveryMarginType(...args: any): any; + + deliveryPositionMargin(symbol: _symbol, amount: number, type?: number, params?: any): Promise; + deliveryPositionMargin(...args: any): any; + + deliveryPositionMarginHistory(symbol: _symbol, params?: any): Promise; + deliveryPositionMarginHistory(...args: any): any; + + deliveryIncome(params?: any): Promise; + deliveryIncome(...args: any): any; + + deliveryBalance(params?: any): Promise; + deliveryBalance(...args: any): any; + + deliveryAccount(params?: any): Promise; + deliveryAccount(...args: any): any; + + deliveryDepth(symbol: _symbol, params?: any): Promise; + deliveryDepth(...args: any): any; + + deliveryQuote(symbol?: _symbol, params?: any): Promise; + deliveryQuote(...args: any): any; + + deliveryBuy(symbol: _symbol, quantity: number, price: number, params?: any): Promise; + deliveryBuy(...args: any): any; + + deliverySell(symbol: _symbol, quantity: number, price: number, params?: any): Promise; + deliverySell(...args: any): any; + + deliveryMarketBuy(symbol: _symbol, quantity: number, params?: any): Promise; + deliveryMarketBuy(...args: any): any; + + deliveryMarketSell(symbol: _symbol, quantity: number, params?: any): Promise; + deliveryMarketSell(...args: any): any; + + deliveryOrder(side: 'BUY' | 'SELL', symbol: _symbol, quantity: number, price?: number, params?: any): Promise; + deliveryOrder(...args: any): any; + + deliveryOrderStatus(symbol: _symbol, params?: any): Promise; + deliveryOrderStatus(...args: any): any; + + deliveryCancel(symbol: _symbol, params?: any): Promise; + deliveryCancel(...args: any): any; + + deliveryCancelAll(symbol: _symbol, params?: any): Promise; + deliveryCancelAll(...args: any): any; + + deliveryCountdownCancelAll(symbol: _symbol, countdownTime?: number, params?: any): Promise; + deliveryCountdownCancelAll(...args: any): any; + + deliveryOpenOrders(symbol?: boolean, params?: any): Promise; + deliveryOpenOrders(...args: any): any; + + deliveryAllOrders(symbol?: _symbol, params?: any): Promise; + deliveryAllOrders(...args: any): any; + + deliveryPositionSideDual(params?: any): Promise; + deliveryPositionSideDual(...args: any): any; + + deliveryChangePositionSideDual(dualSidePosition: any, params?: any): Promise; + deliveryChangePositionSideDual(...args: any): any; + + /** + * Creates an order + * @param {string} side - BUY or SELL + * @param {string} symbol - the symbol to buy + * @param {numeric} quantity - the quantity required + * @param {numeric} price - the price to pay for each unit + * @param {object} flags - additional buy order flags + * @param {function} callback - the callback function + * @param {string} isIsolated - the isolate margin option + * @return {undefined} + */ + mgOrder(side: 'BUY' | 'SELL', symbol: _symbol, quantity: number, price: number, flags?: any, callback?: _callback, isIsolated?: string): any; + mgOrder(...args: any): any; + + /** + * Creates a buy order + * @param {string} symbol - the symbol to buy + * @param {numeric} quantity - the quantity required + * @param {numeric} price - the price to pay for each unit + * @param {object} flags - additional buy order flags + * @param {function} callback - the callback function + * @param {string} isIsolated - the isolate margin option + * @return {undefined} + */ + mgBuy(side: 'BUY' | 'SELL', symbol: _symbol, quantity: number, price: number, flags?: any, callback?: _callback, isIsolated?: string): any; + mgBuy(...args: any): any; + + /** + * Creates a sell order + * @param {string} symbol - the symbol to sell + * @param {numeric} quantity - the quantity required + * @param {numeric} price - the price to sell each unit for + * @param {object} flags - additional order flags + * @param {function} callback - the callback function + * @param {string} isIsolated - the isolate margin option + * @return {undefined} + */ + mgSell(side: 'BUY' | 'SELL', symbol: _symbol, quantity: number, price: number, flags?: any, callback?: _callback, isIsolated?: string): any; + mgSell(...args: any): any; + + /** + * Creates a market buy order + * @param {string} symbol - the symbol to buy + * @param {numeric} quantity - the quantity required + * @param {object} flags - additional buy order flags + * @param {function} callback - the callback function + * @param {string} isIsolated - the isolate margin option + * @return {undefined} + */ + mgMarketBuy(symbol: _symbol, quantity: number, price: number, flags?: any, callback?: _callback, isIsolated?: string): any; + mgMarketBuy(...args: any): any; + + /** + * Creates a market sell order + * @param {string} symbol - the symbol to sell + * @param {numeric} quantity - the quantity required + * @param {object} flags - additional sell order flags + * @param {function} callback - the callback function + * @param {string} isIsolated - the isolate margin option + * @return {undefined} + */ + mgMarketSell(symbol: _symbol, quantity: number, price: number, flags?: any, callback?: _callback, isIsolated?: string): any; + mgMarketSell(...args: any): any; + + /** + * Cancels an order + * @param {string} symbol - the symbol to cancel + * @param {string} orderid - the orderid to cancel + * @param {function} callback - the callback function + * @return {undefined} + */ + mgCancel(symbol: _symbol, orderid: string, callback?: _callback, isIsolated?: string): any; + mgCancel(...args: any): any; + + /** + * Gets all order of a given symbol + * @param {string} symbol - the symbol + * @param {function} callback - the callback function + * @param {object} options - additional options + * @return {promise or undefined} - omitting the callback returns a promise + */ + mgAllOrders(symbol: _symbol, callback?: _callback, options?: any): Promise; + mgAllOrders(...args: any): any; + + /** + * Gets the status of an order + * @param {string} symbol - the symbol to check + * @param {string} orderid - the orderid to check + * @param {function} callback - the callback function + * @param {object} flags - any additional flags + * @return {undefined} + */ + mgOrderStatus(symbol: _symbol, orderid: string, callback?: _callback, flags?: any): Promise; + mgOrderStatus(...args: any): any; + + /** + * Gets open orders + * @param {string} symbol - the symbol to get + * @param {function} callback - the callback function + * @return {undefined} + */ + mgOpenOrders(symbol: _symbol, callback: _callback): Promise; + mgOpenOrders(...args: any): any; + + /** + * Cancels all order of a given symbol + * @param {string} symbol - the symbol to cancel all orders for + * @param {function} callback - the callback function + * @return {undefined} + */ + mgCancelOrders(symbol: _symbol, callback?: _callback): Promise; + mgCancelOrders(...args: any): any; + + /** + * Transfer from main account to margin account + * @param {string} asset - the asset + * @param {number} amount - the asset + * @param {function} callback - the callback function + * @param {object} options - additional options + * @return {undefined} + */ + mgTransferMainToMargin(asset: string, amount: number, callback?: _callback): any; + mgTransferMainToMargin(...args: any): any; + + /** + * Transfer from main account to margin account + * @param {string} asset - the asset + * @param {number} amount - the asset + * @param {function} callback - the callback function + * @param {object} options - additional options + * @return {undefined} + */ + mgTransferMarginToMain(asset: string, amount: number, callback?: _callback, options?: any): any; + mgTransferMarginToMain(...args: any): any; + + /** + * Transfer from margin account to main account + * @param {string} asset - the asset + * @param {number} amount - the asset + * @param {function} callback - the callback function + * @return {undefined} + */ + transferMainToFutures(asset: string, amount: number, callback?: _callback): any; + transferMainToFutures(...args: any): any; + + /** + * Transfer from main account to delivery account + * @param {string} asset - the asset + * @param {number} amount - the asset + * @param {function} callback - the callback function (optionnal) + * @param {object} options - additional options + * @return {undefined} + */ + transferFuturesToMain(asset: string, amount: number, callback?: _callback, options?: any): any; + transferFuturesToMain(...args: any): any; + + /** + * Transfer from delivery account to main account + * @param {string} asset - the asset + * @param {number} amount - the asset + * @param {function} callback - the callback function (optionnal) + * @return {undefined} + */ + transferMainToDelivery(asset: string, amount: number, callback?: _callback): any; + transferMainToDelivery(...args: any): any; + + /** + * Transfer from main account to delivery account + * @param {string} asset - the asset + * @param {number} amount - the asset + * @param {function} callback - the callback function (optionnal) + * @param {object} options - additional options + * @return {undefined} + */ + transferDeliveryToMain(asset: string, amount: number, callback?: _callback): any; + transferDeliveryToMain(...args: any): any; + + /** + * Get maximum transfer-out amount of an asset + * @param {string} asset - the asset + * @param {function} callback - the callback function + * @return {undefined} + */ + maxTransferable(asset: string, callback?: _callback): any; + maxTransferable(...args: any): any; + + /** + * Margin account borrow/loan + * @param {string} asset - the asset + * @param {number} amount - the asset + * @param {function} callback - the callback function + * @param {string} isIsolated - the isolated option + * @param {string} symbol - symbol for isolated margin + * @return {undefined} + */ + mgBorrow(asset: string, amount: number, callback: _callback, isIsolated?: string, symbol?: _symbol): any; + mgBorrow(...args: any): any; + + /** + * Margin account repay + * @param {string} asset - the asset + * @param {number} amount - the asset + * @param {function} callback - the callback function + * @param {string} isIsolated - the isolated option + * @param {string} symbol - symbol for isolated margin + * @return {undefined} + */ + mgRepay(asset: string, amount: number, callback: _callback, isIsolated?: string, symbol?: _symbol): any; + mgRepay(...args: any): any; + + /** + * Margin account details + * @param {function} callback - the callback function + * @param {boolean} isIsolated - the callback function + * @return {undefined} + */ + mgAccount(callback: _callback, isIsolated?: boolean): any; + mgAccount(...args: any): any; + + /** + * Get maximum borrow amount of an asset + * @param {string} asset - the asset + * @param {function} callback - the callback function + * @return {undefined} + */ + maxBorrowable(asset: string, callback: _callback): any; + maxBorrowable(...args: any): any; + + /** + * Subscribe to a single futures websocket + * @param {string} url - the futures websocket endpoint + * @param {function} callback - optional execution callback + * @param {object} params - Optional reconnect {boolean} (whether to reconnect on disconnect), openCallback {function}, id {string} + * @return {WebSocket} the websocket reference + */ + futuresSubscribeSingle(url: string, callback: _callback, params?: any): WebSocket; + futuresSubscribeSingle(...args: any): any; + + /** + * Subscribe to a combined futures websocket + * @param {string} streams - the list of websocket endpoints to connect to + * @param {function} callback - optional execution callback + * @param {object} params - Optional reconnect {boolean} (whether to reconnect on disconnect), openCallback {function}, id {string} + * @return {WebSocket} the websocket reference + */ + futuresSubscribe(streams: string, callback: _callback, params?: any ): WebSocket; + futuresSubscribe(...args: any): any; + + /** + * Returns the known futures websockets subscriptions + * @return {array} array of futures websocket subscriptions + */ + futuresSubscriptions(): any[]; + futuresSubscriptions(...args: any): any; + + /** + * Terminates a futures websocket + * @param {string} endpoint - the string associated with the endpoint + * @return {undefined} + */ + futuresTerminate(endpoint: string): any; + futuresTerminate(...args: any): any; + + /** + * Futures WebSocket aggregated trades + * @param {array/string} symbols - an array or string of symbols to query + * @param {function} callback - callback function + * @return {string} the websocket endpoint + */ + futuresAggTradeStream(symbols: _symbol | _symbol[], callback: _callback): string; + futuresAggTradeStream(...args: any): any; + + /** + * Futures WebSocket mark price + * @param {symbol} symbol name or false. can also be a callback + * @param {function} callback - callback function + * @param {string} speed - 1 second updates (@1s). leave blank for default 3 seconds + * @return {string} the websocket endpoint + */ + futuresMarkPriceStream(symbol?: _symbol, callback?: _callback, speed?: string): string; + futuresMarkPriceStream(...args: any): any; + + /** + * Futures WebSocket liquidations stream + * @param {symbol} symbol name or false. can also be a callback + * @param {function} callback - callback function + * @return {string} the websocket endpoint + */ + futuresLiquidationStream(symbol?: _symbol, callback?: _callback): string; + futuresLiquidationStream(...args: any): any; + + /** + * Futures WebSocket prevDay ticker + * @param {symbol} symbol name or false. can also be a callback + * @param {function} callback - callback function + * @return {string} the websocket endpoint + */ + futuresTickerStream(symbol?: _symbol, callback?: _callback): string; + futuresTickerStream(...args: any): any; + + /** + * Futures WebSocket miniTicker + * @param {symbol} symbol name or false. can also be a callback + * @param {function} callback - callback function + * @return {string} the websocket endpoint + */ + futuresMiniTickerStream(symbol?: _symbol, callback?: _callback): string; + futuresMiniTickerStream(...args: any): any; + + /** + * Futures WebSocket bookTicker + * @param {symbol} symbol name or false. can also be a callback + * @param {function} callback - callback function + * @return {string} the websocket endpoint + */ + futuresBookTickerStream(symbol: _symbol, callback?: _callback): string; + futuresBookTickerStream(...args: any): any; + + /** + * Websocket futures klines + * @param {array/string} symbols - an array or string of symbols to query + * @param {string} interval - the time interval + * @param {function} callback - callback function + * @param {int} limit - maximum results, no more than 1000 + * @return {string} the websocket endpoint + */ + futuresChart(symbols: _symbol | _symbol[], interval: _interval, callback: _callback, limit?: number): string; + futuresChart(...args: any): any; + + /** + * Websocket futures candlesticks + * @param {array/string} symbols - an array or string of symbols to query + * @param {string} interval - the time interval + * @param {function} callback - callback function + * @return {string} the websocket endpoint + */ + futuresCandlesticks(symbols?: _symbol | _symbol[], interval?: _interval, callback?: _callback): string; + futuresCandlesticks(...args: any): any; + + /** + * Subscribe to a single delivery websocket + * @param {string} url - the delivery websocket endpoint + * @param {function} callback - optional execution callback + * @param {object} params - Optional reconnect {boolean} (whether to reconnect on disconnect), openCallback {function}, id {string} + * @return {WebSocket} the websocket reference + */ + deliverySubscribeSingle(url: string, callback: _callback, params?: any): any; + deliverySubscribeSingle(...args: any): any; + + /** + * Subscribe to a combined delivery websocket + * @param {string} streams - the list of websocket endpoints to connect to + * @param {function} callback - optional execution callback + * @param {object} params - Optional reconnect {boolean} (whether to reconnect on disconnect), openCallback {function}, id {string} + * @return {WebSocket} the websocket reference + */ + deliverySubscribe(streams: string, callback: _callback, params?: any): WebSocket; + deliverySubscribe(...args: any): any; + + /** + * Returns the known delivery websockets subscriptions + * @return {array} array of delivery websocket subscriptions + */ + deliverySubscriptions(): any[]; + deliverySubscriptions(...args: any): any; + + /** + * Terminates a delivery websocket + * @param {string} endpoint - the string associated with the endpoint + * @return {undefined} + */ + deliveryTerminate(endpoint: string); + deliveryTerminate(...args: any): any; + + /** + * Delivery WebSocket aggregated trades + * @param {array/string} symbols - an array or string of symbols to query + * @param {function} callback - callback function + * @return {string} the websocket endpoint + */ + deliveryAggTradeStream(symbols: _symbol | _symbol[], callback: _callback): string; + deliveryAggTradeStream(...args: any): any; + + /** + * Delivery WebSocket mark price + * @param {symbol} symbol name or false. can also be a callback + * @param {function} callback - callback function + * @param {string} speed - 1 second updates (@1s). leave blank for default 3 seconds + * @return {string} the websocket endpoint + */ + deliveryMarkPriceStream(symbol?: _symbol, callback?: _callback, speed?: string): string; + deliveryMarkPriceStream(...args: any): any; + + /** + * Delivery WebSocket liquidations stream + * @param {symbol} symbol name or false. can also be a callback + * @param {function} callback - callback function + * @return {string} the websocket endpoint + */ + deliveryLiquidationStream(symbol?: _symbol, callback?: _callback): string; + deliveryLiquidationStream(...args: any): any; + + /** + * Delivery WebSocket prevDay ticker + * @param {symbol} symbol name or false. can also be a callback + * @param {function} callback - callback function + * @return {string} the websocket endpoint + */ + deliveryTickerStream(symbol?: _symbol, callback?: _callback): string; + deliveryTickerStream(...args: any): any; + + /** + * Delivery WebSocket miniTicker + * @param {symbol} symbol name or false. can also be a callback + * @param {function} callback - callback function + * @return {string} the websocket endpoint + */ + deliveryMiniTickerStream(symbol?: _symbol, callback?: _callback): string; + deliveryMiniTickerStream(...args: any): any; + + /** + * Delivery WebSocket bookTicker + * @param {symbol} symbol name or false. can also be a callback + * @param {function} callback - callback function + * @return {string} the websocket endpoint + */ + deliveryBookTickerStream(symbol?: _symbol, callback?: _callback): string; + deliveryBookTickerStream(...args: any): any; + + /** + * Websocket delivery klines + * @param {array/string} symbols - an array or string of symbols to query + * @param {string} interval - the time interval + * @param {function} callback - callback function + * @param {int} limit - maximum results, no more than 1000 + * @return {string} the websocket endpoint + */ + deliveryChart(symbols?: _symbol | _symbol[], interval?: _interval, callback?: _callback, limit?: number): string; + deliveryChart(...args: any): any; + + /** + * Websocket delivery candlesticks + * @param {array/string} symbols - an array or string of symbols to query + * @param {string} interval - the time interval + * @param {function} callback - callback function + * @return {string} the websocket endpoint + */ + deliveryCandlesticks(symbols: _symbol | _symbol[], interval: _interval, callback: _callback ): string; + deliveryCandlesticks(...args: any): any; + + websockets: IWebsockets; + + } + + export default Binance; + +} + diff --git a/deprecated/node-binance-api.js b/deprecated/node-binance-api.js new file mode 100644 index 00000000..550a1ebd --- /dev/null +++ b/deprecated/node-binance-api.js @@ -0,0 +1,6037 @@ +/* ============================================================ + * node-binance-api + * https://github.com/jaggedsoft/node-binance-api + * ============================================================ + * Copyright 2017-, Jon Eyrick + * Released under the MIT License + * ============================================================ + * @module jaggedsoft/node-binance-api + * @return {object} instance to class object */ +let api = function Binance(options = {}) { + if (!new.target) return new api(options); // Legacy support for calling the constructor without 'new' + let Binance = this; // eslint-disable-line consistent-this + const WebSocket = require('ws'); + const request = require('request'); + const crypto = require('crypto'); + const file = require('fs'); + const url = require('url'); + const JSONbig = require('json-bigint'); + const HttpsProxyAgent = require('https-proxy-agent'); + const SocksProxyAgent = require('socks-proxy-agent'); + const stringHash = require('string-hash'); + const async = require('async'); + let base = 'https://api.binance.com/api/'; + let baseTest = 'https://testnet.binance.vision/api/'; + let wapi = 'https://api.binance.com/wapi/'; + let sapi = 'https://api.binance.com/sapi/'; + let fapi = 'https://fapi.binance.com/fapi/'; + let dapi = 'https://dapi.binance.com/dapi/'; + let fapiTest = 'https://testnet.binancefuture.com/fapi/'; + let dapiTest = 'https://testnet.binancefuture.com/dapi/'; + let fstream = 'wss://fstream.binance.com/stream?streams='; + let fstreamSingle = 'wss://fstream.binance.com/ws/'; + let fstreamSingleTest = 'wss://stream.binancefuture.com/ws/'; + let fstreamTest = 'wss://stream.binancefuture.com/stream?streams='; + let dstream = 'wss://dstream.binance.com/stream?streams='; + let dstreamSingle = 'wss://dstream.binance.com/ws/'; + let dstreamSingleTest = 'wss://dstream.binancefuture.com/ws/'; + let dstreamTest = 'wss://dstream.binancefuture.com/stream?streams='; + let stream = 'wss://stream.binance.com:9443/ws/'; + let combineStream = 'wss://stream.binance.com:9443/stream?streams='; + const userAgent = 'Mozilla/4.0 (compatible; Node Binance API)'; + const contentType = 'application/x-www-form-urlencoded'; + const SPOT_PREFIX = "x-HNA2TXFJ" + const CONTRACT_PREFIX = "x-Cb7ytekJ" + Binance.subscriptions = {}; + Binance.futuresSubscriptions = {}; + Binance.futuresInfo = {}; + Binance.futuresMeta = {}; + Binance.futuresTicks = {}; + Binance.futuresRealtime = {}; + Binance.futuresKlineQueue = {}; + Binance.deliverySubscriptions = {}; + Binance.deliveryInfo = {}; + Binance.deliveryMeta = {}; + Binance.deliveryTicks = {}; + Binance.deliveryRealtime = {}; + Binance.deliveryKlineQueue = {}; + Binance.depthCache = {}; + Binance.depthCacheContext = {}; + Binance.ohlcLatest = {}; + Binance.klineQueue = {}; + Binance.ohlc = {}; + + const default_options = { + recvWindow: 5000, + useServerTime: false, + reconnect: true, + keepAlive: true, + verbose: false, + test: false, + hedgeMode: false, + localAddress: false, + family: 4, + log: function (...args) { + console.log(Array.prototype.slice.call(args)); + } + }; + Binance.options = default_options; + Binance.info = { + usedWeight: 0, + futuresLatency: false, + lastRequest: false, + lastURL: false, + statusCode: 0, + orderCount1s: 0, + orderCount1m: 0, + orderCount1h: 0, + orderCount1d: 0, + timeOffset: 0 + }; + Binance.socketHeartbeatInterval = null; + if (options) setOptions(options); + + function setOptions(opt = {}, callback = false) { + if (typeof opt === 'string') { // Pass json config filename + Binance.options = JSON.parse(file.readFileSync(opt)); + } else Binance.options = opt; + if (typeof Binance.options.recvWindow === 'undefined') Binance.options.recvWindow = default_options.recvWindow; + if (typeof Binance.options.useServerTime === 'undefined') Binance.options.useServerTime = default_options.useServerTime; + if (typeof Binance.options.reconnect === 'undefined') Binance.options.reconnect = default_options.reconnect; + if (typeof Binance.options.test === 'undefined') Binance.options.test = default_options.test; + if (typeof Binance.options.hedgeMode === 'undefined') Binance.options.hedgeMode = default_options.hedgeMode; + if (typeof Binance.options.log === 'undefined') Binance.options.log = default_options.log; + if (typeof Binance.options.verbose === 'undefined') Binance.options.verbose = default_options.verbose; + if (typeof Binance.options.keepAlive === 'undefined') Binance.options.keepAlive = default_options.keepAlive; + if (typeof Binance.options.localAddress === 'undefined') Binance.options.localAddress = default_options.localAddress; + if (typeof Binance.options.family === 'undefined') Binance.options.family = default_options.family; + if (typeof Binance.options.urls !== 'undefined') { + const { urls } = Binance.options; + if (typeof urls.base === 'string') base = urls.base; + if (typeof urls.wapi === 'string') wapi = urls.wapi; + if (typeof urls.sapi === 'string') sapi = urls.sapi; + if (typeof urls.fapi === 'string') fapi = urls.fapi; + if (typeof urls.fapiTest === 'string') fapiTest = urls.fapiTest; + if (typeof urls.stream === 'string') stream = urls.stream; + if (typeof urls.combineStream === 'string') combineStream = urls.combineStream; + if (typeof urls.fstream === 'string') fstream = urls.fstream; + if (typeof urls.fstreamSingle === 'string') fstreamSingle = urls.fstreamSingle; + if (typeof urls.fstreamTest === 'string') fstreamTest = urls.fstreamTest; + if (typeof urls.fstreamSingleTest === 'string') fstreamSingleTest = urls.fstreamSingleTest; + if (typeof urls.dstream === 'string') dstream = urls.dstream; + if (typeof urls.dstreamSingle === 'string') dstreamSingle = urls.dstreamSingle; + if (typeof urls.dstreamTest === 'string') dstreamTest = urls.dstreamTest; + if (typeof urls.dstreamSingleTest === 'string') dstreamSingleTest = urls.dstreamSingleTest; + } + if (Binance.options.useServerTime) { + publicRequest(getSpotUrl() + 'v3/time', {}, function (error, response) { + Binance.info.timeOffset = response.serverTime - new Date().getTime(); + //Binance.options.log("server time set: ", response.serverTime, Binance.info.timeOffset); + if (callback) callback(); + }); + } else if (callback) callback(); + return this; + } + + const getSpotUrl = () => { + if (Binance.options.test) return baseTest; + return base; + } + + const uuid22 = (a) => { + return a ? (a ^ Math.random() * 16 >> a / 4).toString(16) : ([1e7] + 1e3 + 4e3 + 8e5).replace(/[018]/g, uuid22); + }; + + /** + * Replaces socks connection uri hostname with IP address + * @param {string} connString - socks connection string + * @return {string} modified string with ip address + */ + const proxyReplacewithIp = connString => { + return connString; + } + + /** + * Returns an array in the form of [host, port] + * @param {string} connString - connection string + * @return {array} array of host and port + */ + const parseProxy = connString => { + let arr = connString.split('/'); + let host = arr[2].split(':')[0]; + let port = arr[2].split(':')[1]; + return [arr[0], host, port]; + } + + /** + * Checks to see of the object is iterable + * @param {object} obj - The object check + * @return {boolean} true or false is iterable + */ + const isIterable = obj => { + if (obj === null) return false; + return typeof obj[Symbol.iterator] === 'function'; + } + + const addProxy = opt => { + if (Binance.options.proxy) { + const proxyauth = Binance.options.proxy.auth ? `${Binance.options.proxy.auth.username}:${Binance.options.proxy.auth.password}@` : ''; + opt.proxy = `http://${proxyauth}${Binance.options.proxy.host}:${Binance.options.proxy.port}`; + } + return opt; + } + + const reqHandler = cb => (error, response, body) => { + Binance.info.lastRequest = new Date().getTime(); + if (response) { + Binance.info.statusCode = response.statusCode || 0; + if (response.request) Binance.info.lastURL = response.request.uri.href; + if (response.headers) { + Binance.info.usedWeight = response.headers['x-mbx-used-weight-1m'] || 0; + Binance.info.orderCount1s = response.headers['x-mbx-order-count-1s'] || 0; + Binance.info.orderCount1m = response.headers['x-mbx-order-count-1m'] || 0; + Binance.info.orderCount1h = response.headers['x-mbx-order-count-1h'] || 0; + Binance.info.orderCount1d = response.headers['x-mbx-order-count-1d'] || 0; + } + } + if (!cb) return; + if (error) return cb(error, {}); + if (response && response.statusCode !== 200) return cb(response, {}); + return cb(null, JSONbig.parse(body)); + } + + const proxyRequest = (opt, cb) => { + const req = request(addProxy(opt), reqHandler(cb)).on('error', (err) => { cb(err, {}) }); + return req; + } + + const reqObj = (url, data = {}, method = 'GET', key) => ({ + url: url, + qs: data, + method: method, + family: Binance.options.family, + localAddress: Binance.options.localAddress, + timeout: Binance.options.recvWindow, + forever: Binance.options.keepAlive, + headers: { + 'User-Agent': userAgent, + 'Content-type': contentType, + 'X-MBX-APIKEY': key || '' + } + }) + const reqObjPOST = (url, data = {}, method = 'POST', key) => ({ + url: url, + form: data, + method: method, + family: Binance.options.family, + localAddress: Binance.options.localAddress, + timeout: Binance.options.recvWindow, + forever: Binance.options.keepAlive, + qsStringifyOptions: { + arrayFormat: 'repeat' + }, + headers: { + 'User-Agent': userAgent, + 'Content-type': contentType, + 'X-MBX-APIKEY': key || '' + } + }) + /** + * Create a http request to the public API + * @param {string} url - The http endpoint + * @param {object} data - The data to send + * @param {function} callback - The callback method to call + * @param {string} method - the http method + * @return {undefined} + */ + const publicRequest = (url, data = {}, callback, method = 'GET') => { + let opt = reqObj(url, data, method); + proxyRequest(opt, callback); + }; + + // XXX: This one works with array (e.g. for dust.transfer) + // XXX: I _guess_ we could use replace this function with the `qs` module + const makeQueryString = q => + Object.keys(q) + .reduce((a, k) => { + if (Array.isArray(q[k])) { + q[k].forEach(v => { + a.push(k + "=" + encodeURIComponent(v)) + }) + } else if (q[k] !== undefined) { + a.push(k + "=" + encodeURIComponent(q[k])); + } + return a; + }, []) + .join("&"); + + /** + * Create a http request to the public API + * @param {string} url - The http endpoint + * @param {object} data - The data to send + * @param {function} callback - The callback method to call + * @param {string} method - the http method + * @return {undefined} + */ + const apiRequest = (url, data = {}, callback, method = 'GET') => { + requireApiKey('apiRequest'); + let opt = reqObj( + url, + data, + method, + Binance.options.APIKEY + ); + proxyRequest(opt, callback); + }; + + // Check if API key is empty or invalid + const requireApiKey = function (source = 'requireApiKey', fatalError = true) { + if (!Binance.options.APIKEY) { + if (fatalError) throw Error(`${source}: Invalid API Key!`); + return false; + } + return true; + } + + // Check if API secret is present + const requireApiSecret = function (source = 'requireApiSecret', fatalError = true) { + if (!Binance.options.APIKEY) { + if (fatalError) throw Error(`${source}: Invalid API Key!`); + return false; + } + if (!Binance.options.APISECRET) { + if (fatalError) throw Error(`${source}: Invalid API Secret!`); + return false; + } + return true; + } + + /** + * Make market request + * @param {string} url - The http endpoint + * @param {object} data - The data to send + * @param {function} callback - The callback method to call + * @param {string} method - the http method + * @return {undefined} + */ + const marketRequest = (url, data = {}, callback, method = 'GET') => { + requireApiKey('marketRequest'); + let query = makeQueryString(data); + let opt = reqObj( + url + (query ? '?' + query : ''), + data, + method, + Binance.options.APIKEY + ); + proxyRequest(opt, callback); + }; + + /** + * Create a signed http request + * @param {string} url - The http endpoint + * @param {object} data - The data to send + * @param {function} callback - The callback method to call + * @param {string} method - the http method + * @param {boolean} noDataInSignature - Prevents data from being added to signature + * @return {undefined} + */ + const signedRequest = (url, data = {}, callback, method = 'GET', noDataInSignature = false) => { + requireApiSecret('signedRequest'); + data.timestamp = new Date().getTime() + Binance.info.timeOffset; + if (typeof data.recvWindow === 'undefined') data.recvWindow = Binance.options.recvWindow; + let query = method === 'POST' && noDataInSignature ? '' : makeQueryString(data); + let signature = crypto.createHmac('sha256', Binance.options.APISECRET).update(query).digest('hex'); // set the HMAC hash header + if (method === 'POST') { + let opt = reqObjPOST( + url, + data, + method, + Binance.options.APIKEY + ); + opt.form.signature = signature; + proxyRequest(opt, callback); + } else { + let opt = reqObj( + url + '?' + query + '&signature=' + signature, + data, + method, + Binance.options.APIKEY + ); + proxyRequest(opt, callback); + } + }; + + /** + * Create a signed spot order + * @param {string} side - BUY or SELL + * @param {string} symbol - The symbol to buy or sell + * @param {string} quantity - The quantity to buy or sell + * @param {string} price - The price per unit to transact each unit at + * @param {object} flags - additional order settings + * @param {function} callback - the callback function + * @return {undefined} + */ + const order = (side, symbol, quantity, price, flags = {}, callback = false) => { + let endpoint = flags.type === 'OCO' ? 'v3/orderList/oco' : 'v3/order'; + if (typeof flags.test && flags.test) endpoint += '/test'; + let opt = { + symbol: symbol, + side: side, + type: 'LIMIT' + }; + if (typeof flags.quoteOrderQty !== undefined && flags.quoteOrderQty > 0) + opt.quoteOrderQty = flags.quoteOrderQty + else + opt.quantity = quantity + if (typeof flags.type !== 'undefined') opt.type = flags.type; + if (opt.type.includes('LIMIT')) { + opt.price = price; + if (opt.type !== 'LIMIT_MAKER') { + opt.timeInForce = 'GTC'; + } + } + if (opt.type == 'MARKET' && typeof flags.quoteOrderQty !== 'undefined') { + opt.quoteOrderQty = flags.quoteOrderQty + delete opt.quantity; + } + if (opt.type === 'OCO') { + opt.price = price; + opt.stopLimitPrice = flags.stopLimitPrice; + opt.stopLimitTimeInForce = 'GTC'; + delete opt.type; + if (typeof flags.listClientOrderId !== 'undefined') opt.listClientOrderId = flags.listClientOrderId; + if (typeof flags.limitClientOrderId !== 'undefined') opt.limitClientOrderId = flags.limitClientOrderId; + if (typeof flags.stopClientOrderId !== 'undefined') opt.stopClientOrderId = flags.stopClientOrderId; + } + if (typeof flags.timeInForce !== 'undefined') opt.timeInForce = flags.timeInForce; + if (typeof flags.newOrderRespType !== 'undefined') opt.newOrderRespType = flags.newOrderRespType; + if (typeof flags.newClientOrderId !== 'undefined') { + opt.newClientOrderId = flags.newClientOrderId; + } else { + opt.newClientOrderId = SPOT_PREFIX + uuid22(); + } + + /* + * STOP_LOSS + * STOP_LOSS_LIMIT + * TAKE_PROFIT + * TAKE_PROFIT_LIMIT + * LIMIT_MAKER + */ + if (typeof flags.icebergQty !== 'undefined') opt.icebergQty = flags.icebergQty; + if (typeof flags.stopPrice !== 'undefined') { + opt.stopPrice = flags.stopPrice; + if (opt.type === 'LIMIT') throw Error('stopPrice: Must set "type" to one of the following: STOP_LOSS, STOP_LOSS_LIMIT, TAKE_PROFIT, TAKE_PROFIT_LIMIT'); + } + signedRequest(getSpotUrl() + endpoint, opt, (error, response) => { + if (!response) { + if (callback) callback(error, response); + else Binance.options.log('Order() error:', error); + return; + } + if (typeof response.msg !== 'undefined' && response.msg === 'Filter failure: MIN_NOTIONAL') { + Binance.options.log('Order quantity too small. See exchangeInfo() for minimum amounts'); + } + if (callback) callback(error, response); + else Binance.options.log(side + '(' + symbol + ',' + quantity + ',' + price + ') ', response); + }, 'POST'); + }; + + /** + * Create a signed margin order + * @param {string} side - BUY or SELL + * @param {string} symbol - The symbol to buy or sell + * @param {string} quantity - The quantity to buy or sell + * @param {string} price - The price per unit to transact each unit at + * @param {object} flags - additional order settings + * @param {function} callback - the callback function + * @return {undefined} + */ + const marginOrder = (side, symbol, quantity, price, flags = {}, callback = false) => { + let endpoint = 'v1/margin/order'; + if (Binance.options.test) endpoint += '/test'; + let opt = { + symbol: symbol, + side: side, + type: 'LIMIT', + quantity: quantity + }; + if (typeof flags.type !== 'undefined') opt.type = flags.type; + if (typeof flags.isIsolated !== 'undefined') opt.isIsolated = flags.isIsolated; + if (opt.type.includes('LIMIT')) { + opt.price = price; + if (opt.type !== 'LIMIT_MAKER') { + opt.timeInForce = 'GTC'; + } + } + + if (typeof flags.timeInForce !== 'undefined') opt.timeInForce = flags.timeInForce; + if (typeof flags.newOrderRespType !== 'undefined') opt.newOrderRespType = flags.newOrderRespType; + // if ( typeof flags.newClientOrderId !== 'undefined' ) opt.newClientOrderId = flags.newClientOrderId; + if (typeof flags.newClientOrderId !== 'undefined') { + opt.newClientOrderId = flags.newClientOrderId; + } else { + opt.newClientOrderId = SPOT_PREFIX + uuid22(); + } + if (typeof flags.sideEffectType !== 'undefined') opt.sideEffectType = flags.sideEffectType; + + /* + * STOP_LOSS + * STOP_LOSS_LIMIT + * TAKE_PROFIT + * TAKE_PROFIT_LIMIT + */ + if (typeof flags.icebergQty !== 'undefined') opt.icebergQty = flags.icebergQty; + if (typeof flags.stopPrice !== 'undefined') { + opt.stopPrice = flags.stopPrice; + if (opt.type === 'LIMIT') throw Error('stopPrice: Must set "type" to one of the following: STOP_LOSS, STOP_LOSS_LIMIT, TAKE_PROFIT, TAKE_PROFIT_LIMIT'); + } + signedRequest(sapi + endpoint, opt, function (error, response) { + if (!response) { + if (callback) callback(error, response); + else Binance.options.log('Order() error:', error); + return; + } + if (typeof response.msg !== 'undefined' && response.msg === 'Filter failure: MIN_NOTIONAL') { + Binance.options.log('Order quantity too small. See exchangeInfo() for minimum amounts'); + } + if (callback) callback(error, response); + else Binance.options.log(side + '(' + symbol + ',' + quantity + ',' + price + ') ', response); + }, 'POST'); + }; + + // Futures internal functions + const futuresOrder = async (side, symbol, quantity, price = false, params = {}) => { + params.symbol = symbol; + params.side = side; + if (quantity) params.quantity = quantity; + // if in the binance futures setting Hedged mode is active, positionSide parameter is mandatory + if (typeof params.positionSide === 'undefined' && Binance.options.hedgeMode) { + params.positionSide = side === 'BUY' ? 'LONG' : 'SHORT'; + } + // LIMIT STOP MARKET STOP_MARKET TAKE_PROFIT TAKE_PROFIT_MARKET + // reduceOnly stopPrice + if (price) { + params.price = price; + if (typeof params.type === 'undefined') params.type = 'LIMIT'; + } else { + if (typeof params.type === 'undefined') params.type = 'MARKET'; + } + if (!params.timeInForce && (params.type.includes('LIMIT') || params.type === 'STOP' || params.type === 'TAKE_PROFIT')) { + params.timeInForce = 'GTX'; // Post only by default. Use GTC for limit orders. + } + + if (!params.newClientOrderId) { + params.newClientOrderId = CONTRACT_PREFIX + uuid22(); + } + + return promiseRequest('v1/order', params, { base: fapi, type: 'TRADE', method: 'POST' }); + }; + const deliveryOrder = async (side, symbol, quantity, price = false, params = {}) => { + params.symbol = symbol; + params.side = side; + params.quantity = quantity; + // if in the binance futures setting Hedged mode is active, positionSide parameter is mandatory + if (Binance.options.hedgeMode) { + params.positionSide = side === 'BUY' ? 'LONG' : 'SHORT'; + } + // LIMIT STOP MARKET STOP_MARKET TAKE_PROFIT TAKE_PROFIT_MARKET + // reduceOnly stopPrice + if (price) { + params.price = price; + if (typeof params.type === 'undefined') params.type = 'LIMIT'; + } else { + if (typeof params.type === 'undefined') params.type = 'MARKET'; + } + if (!params.timeInForce && (params.type.includes('LIMIT') || params.type === 'STOP' || params.type === 'TAKE_PROFIT')) { + params.timeInForce = 'GTX'; // Post only by default. Use GTC for limit orders. + } + + if (!params.newClientOrderId) { + params.newClientOrderId = CONTRACT_PREFIX + uuid22(); + } + return promiseRequest('v1/order', params, { base: dapi, type: 'TRADE', method: 'POST' }); + }; + const promiseRequest = async (url, data = {}, flags = {}) => { + return new Promise((resolve, reject) => { + let query = '', headers = { + 'User-Agent': userAgent, + 'Content-type': 'application/x-www-form-urlencoded' + }; + if (typeof flags.method === 'undefined') flags.method = 'GET'; // GET POST PUT DELETE + if (typeof flags.type === 'undefined') flags.type = false; // TRADE, SIGNED, MARKET_DATA, USER_DATA, USER_STREAM + else { + if (typeof data.recvWindow === 'undefined') data.recvWindow = Binance.options.recvWindow; + requireApiKey('promiseRequest'); + headers['X-MBX-APIKEY'] = Binance.options.APIKEY; + } + let baseURL = typeof flags.base === 'undefined' ? base : flags.base; + if (Binance.options.test && baseURL === base) baseURL = baseTest; + if (Binance.options.test && baseURL === fapi) baseURL = fapiTest; + if (Binance.options.test && baseURL === dapi) baseURL = dapiTest; + let opt = { + headers, + url: baseURL + url, + method: flags.method, + timeout: Binance.options.recvWindow, + followAllRedirects: true + }; + if (flags.type === 'SIGNED' || flags.type === 'TRADE' || flags.type === 'USER_DATA') { + if (!requireApiSecret('promiseRequest')) return reject('promiseRequest: Invalid API Secret!'); + data.timestamp = new Date().getTime() + Binance.info.timeOffset; + query = makeQueryString(data); + data.signature = crypto.createHmac('sha256', Binance.options.APISECRET).update(query).digest('hex'); // HMAC hash header + opt.url = `${baseURL}${url}?${query}&signature=${data.signature}`; + } + opt.qs = data; + /*if ( flags.method === 'POST' ) { + opt.form = data; + } else { + opt.qs = data; + }*/ + try { + request(addProxy(opt), (error, response, body) => { + if (error) return reject(error); + try { + Binance.info.lastRequest = new Date().getTime(); + if (response) { + Binance.info.statusCode = response.statusCode || 0; + if (response.request) Binance.info.lastURL = response.request.uri.href; + if (response.headers) { + Binance.info.usedWeight = response.headers['x-mbx-used-weight-1m'] || 0; + Binance.info.futuresLatency = response.headers['x-response-time'] || 0; + } + } + if (!error && response.statusCode == 200) return resolve(JSONbig.parse(body)); + if (typeof response.body !== 'undefined') { + return resolve(JSONbig.parse(response.body)); + } + return reject(response); + } catch (err) { + return reject(`promiseRequest error #${response.statusCode}`); + } + }).on('error', reject); + } catch (err) { + return reject(err); + } + }); + }; + + /** + * No-operation function + * @return {undefined} + */ + const noop = () => { }; // Do nothing. + + /** + * Reworked Tuitio's heartbeat code into a shared single interval tick + * @return {undefined} + */ + const socketHeartbeat = () => { + /* Sockets removed from `subscriptions` during a manual terminate() + will no longer be at risk of having functions called on them */ + for (let endpointId in Binance.subscriptions) { + const ws = Binance.subscriptions[endpointId]; + if (ws.isAlive) { + ws.isAlive = false; + if (ws.readyState === WebSocket.OPEN) ws.ping(noop); + } else { + if (Binance.options.verbose) Binance.options.log('Terminating inactive/broken WebSocket: ' + ws.endpoint); + if (ws.readyState === WebSocket.OPEN) ws.terminate(); + } + } + }; + + /** + * Called when socket is opened, subscriptions are registered for later reference + * @param {function} opened_callback - a callback function + * @return {undefined} + */ + const handleSocketOpen = function (opened_callback) { + this.isAlive = true; + if (Object.keys(Binance.subscriptions).length === 0) { + Binance.socketHeartbeatInterval = setInterval(socketHeartbeat, 30000); + } + Binance.subscriptions[this.endpoint] = this; + if (typeof opened_callback === 'function') opened_callback(this.endpoint); + }; + + /** + * Called when socket is closed, subscriptions are de-registered for later reference + * @param {boolean} reconnect - true or false to reconnect the socket + * @param {string} code - code associated with the socket + * @param {string} reason - string with the response + * @return {undefined} + */ + const handleSocketClose = function (reconnect, code, reason) { + delete Binance.subscriptions[this.endpoint]; + if (Binance.subscriptions && Object.keys(Binance.subscriptions).length === 0) { + clearInterval(Binance.socketHeartbeatInterval); + } + Binance.options.log('WebSocket closed: ' + this.endpoint + + (code ? ' (' + code + ')' : '') + + (reason ? ' ' + reason : '')); + if (Binance.options.reconnect && this.reconnect && reconnect) { + if (this.endpoint && parseInt(this.endpoint.length, 10) === 60) Binance.options.log('Account data WebSocket reconnecting...'); + else Binance.options.log('WebSocket reconnecting: ' + this.endpoint + '...'); + try { + reconnect(); + } catch (error) { + Binance.options.log('WebSocket reconnect error: ' + error.message); + } + } + }; + + /** + * Called when socket errors + * @param {object} error - error object message + * @return {undefined} + */ + const handleSocketError = function (error) { + /* Errors ultimately result in a `close` event. + see: https://github.com/websockets/ws/blob/828194044bf247af852b31c49e2800d557fedeff/lib/websocket.js#L126 */ + Binance.options.log('WebSocket error: ' + this.endpoint + + (error.code ? ' (' + error.code + ')' : '') + + (error.message ? ' ' + error.message : '')); + }; + + /** + * Called on each socket heartbeat + * @return {undefined} + */ + const handleSocketHeartbeat = function () { + this.isAlive = true; + }; + + /** + * Used to subscribe to a single websocket endpoint + * @param {string} endpoint - endpoint to connect to + * @param {function} callback - the function to call when information is received + * @param {boolean} reconnect - whether to reconnect on disconnect + * @param {object} opened_callback - the function to call when opened + * @return {WebSocket} - websocket reference + */ + const subscribe = function (endpoint, callback, reconnect = false, opened_callback = false) { + let httpsproxy = process.env.https_proxy || false; + let socksproxy = process.env.socks_proxy || false; + let ws = false; + + if (socksproxy !== false) { + socksproxy = proxyReplacewithIp(socksproxy); + if (Binance.options.verbose) Binance.options.log('using socks proxy server ' + socksproxy); + let agent = new SocksProxyAgent({ + protocol: parseProxy(socksproxy)[0], + host: parseProxy(socksproxy)[1], + port: parseProxy(socksproxy)[2] + }); + ws = new WebSocket(stream + endpoint, { agent: agent }); + } else if (httpsproxy !== false) { + let config = url.parse(httpsproxy); + let agent = new HttpsProxyAgent(config); + if (Binance.options.verbose) Binance.options.log('using proxy server ' + agent); + ws = new WebSocket(stream + endpoint, { agent: agent }); + } else { + ws = new WebSocket(stream + endpoint); + } + + if (Binance.options.verbose) Binance.options.log('Subscribed to ' + endpoint); + ws.reconnect = Binance.options.reconnect; + ws.endpoint = endpoint; + ws.isAlive = false; + ws.on('open', handleSocketOpen.bind(ws, opened_callback)); + ws.on('pong', handleSocketHeartbeat); + ws.on('error', handleSocketError); + ws.on('close', handleSocketClose.bind(ws, reconnect)); + ws.on('message', data => { + try { + callback(JSON.parse(data)); + } catch (error) { + Binance.options.log('Parse error: ' + error.message); + } + }); + return ws; + }; + + /** + * Used to subscribe to a combined websocket endpoint + * @param {string} streams - streams to connect to + * @param {function} callback - the function to call when information is received + * @param {boolean} reconnect - whether to reconnect on disconnect + * @param {object} opened_callback - the function to call when opened + * @return {WebSocket} - websocket reference + */ + const subscribeCombined = function (streams, callback, reconnect = false, opened_callback = false) { + let httpsproxy = process.env.https_proxy || false; + let socksproxy = process.env.socks_proxy || false; + const queryParams = streams.join('/'); + let ws = false; + if (socksproxy !== false) { + socksproxy = proxyReplacewithIp(socksproxy); + if (Binance.options.verbose) Binance.options.log('using socks proxy server ' + socksproxy); + let agent = new SocksProxyAgent({ + protocol: parseProxy(socksproxy)[0], + host: parseProxy(socksproxy)[1], + port: parseProxy(socksproxy)[2] + }); + ws = new WebSocket(combineStream + queryParams, { agent: agent }); + } else if (httpsproxy !== false) { + if (Binance.options.verbose) Binance.options.log('using proxy server ' + httpsproxy); + let config = url.parse(httpsproxy); + let agent = new HttpsProxyAgent(config); + ws = new WebSocket(combineStream + queryParams, { agent: agent }); + } else { + ws = new WebSocket(combineStream + queryParams); + } + + ws.reconnect = Binance.options.reconnect; + ws.endpoint = stringHash(queryParams); + ws.isAlive = false; + if (Binance.options.verbose) { + Binance.options.log('CombinedStream: Subscribed to [' + ws.endpoint + '] ' + queryParams); + } + ws.on('open', handleSocketOpen.bind(ws, opened_callback)); + ws.on('pong', handleSocketHeartbeat); + ws.on('error', handleSocketError); + ws.on('close', handleSocketClose.bind(ws, reconnect)); + ws.on('message', data => { + try { + callback(JSON.parse(data).data); + } catch (error) { + Binance.options.log('CombinedStream: Parse error: ' + error.message); + } + }); + return ws; + }; + + /** + * Used to terminate a web socket + * @param {string} endpoint - endpoint identifier associated with the web socket + * @param {boolean} reconnect - auto reconnect after termination + * @return {undefined} + */ + const terminate = function (endpoint, reconnect = false) { + let ws = Binance.subscriptions[endpoint]; + if (!ws) return; + ws.removeAllListeners('message'); + ws.reconnect = reconnect; + ws.terminate(); + } + + + /** + * Futures heartbeat code with a shared single interval tick + * @return {undefined} + */ + const futuresSocketHeartbeat = () => { + /* Sockets removed from subscriptions during a manual terminate() + will no longer be at risk of having functions called on them */ + for (let endpointId in Binance.futuresSubscriptions) { + const ws = Binance.futuresSubscriptions[endpointId]; + if (ws.isAlive) { + ws.isAlive = false; + if (ws.readyState === WebSocket.OPEN) ws.ping(noop); + } else { + if (Binance.options.verbose) Binance.options.log(`Terminating zombie futures WebSocket: ${ws.endpoint}`); + if (ws.readyState === WebSocket.OPEN) ws.terminate(); + } + } + }; + + /** + * Called when a futures socket is opened, subscriptions are registered for later reference + * @param {function} openCallback - a callback function + * @return {undefined} + */ + const handleFuturesSocketOpen = function (openCallback) { + this.isAlive = true; + if (Object.keys(Binance.futuresSubscriptions).length === 0) { + Binance.socketHeartbeatInterval = setInterval(futuresSocketHeartbeat, 30000); + } + Binance.futuresSubscriptions[this.endpoint] = this; + if (typeof openCallback === 'function') openCallback(this.endpoint); + }; + + /** + * Called when futures websocket is closed, subscriptions are de-registered for later reference + * @param {boolean} reconnect - true or false to reconnect the socket + * @param {string} code - code associated with the socket + * @param {string} reason - string with the response + * @return {undefined} + */ + const handleFuturesSocketClose = function (reconnect, code, reason) { + delete Binance.futuresSubscriptions[this.endpoint]; + if (Binance.futuresSubscriptions && Object.keys(Binance.futuresSubscriptions).length === 0) { + clearInterval(Binance.socketHeartbeatInterval); + } + Binance.options.log('Futures WebSocket closed: ' + this.endpoint + + (code ? ' (' + code + ')' : '') + + (reason ? ' ' + reason : '')); + if (Binance.options.reconnect && this.reconnect && reconnect) { + if (this.endpoint && parseInt(this.endpoint.length, 10) === 60) Binance.options.log('Futures account data WebSocket reconnecting...'); + else Binance.options.log('Futures WebSocket reconnecting: ' + this.endpoint + '...'); + try { + reconnect(); + } catch (error) { + Binance.options.log('Futures WebSocket reconnect error: ' + error.message); + } + } + }; + + /** + * Called when a futures websocket errors + * @param {object} error - error object message + * @return {undefined} + */ + const handleFuturesSocketError = function (error) { + Binance.options.log('Futures WebSocket error: ' + this.endpoint + + (error.code ? ' (' + error.code + ')' : '') + + (error.message ? ' ' + error.message : '')); + }; + + /** + * Called on each futures socket heartbeat + * @return {undefined} + */ + const handleFuturesSocketHeartbeat = function () { + this.isAlive = true; + }; + + /** + * Used to subscribe to a single futures websocket endpoint + * @param {string} endpoint - endpoint to connect to + * @param {function} callback - the function to call when information is received + * @param {object} params - Optional reconnect {boolean} (whether to reconnect on disconnect), openCallback {function}, id {string} + * @return {WebSocket} - websocket reference + */ + const futuresSubscribeSingle = function (endpoint, callback, params = {}) { + if (typeof params === 'boolean') params = { reconnect: params }; + if (!params.reconnect) params.reconnect = false; + if (!params.openCallback) params.openCallback = false; + if (!params.id) params.id = false; + let httpsproxy = process.env.https_proxy || false; + let socksproxy = process.env.socks_proxy || false; + let ws = false; + + if (socksproxy !== false) { + socksproxy = proxyReplacewithIp(socksproxy); + if (Binance.options.verbose) Binance.options.log(`futuresSubscribeSingle: using socks proxy server: ${socksproxy}`); + let agent = new SocksProxyAgent({ + protocol: parseProxy(socksproxy)[0], + host: parseProxy(socksproxy)[1], + port: parseProxy(socksproxy)[2] + }); + ws = new WebSocket((Binance.options.test ? fstreamSingleTest : fstreamSingle) + endpoint, { agent }); + } else if (httpsproxy !== false) { + let config = url.parse(httpsproxy); + let agent = new HttpsProxyAgent(config); + if (Binance.options.verbose) Binance.options.log(`futuresSubscribeSingle: using proxy server: ${agent}`); + ws = new WebSocket((Binance.options.test ? fstreamSingleTest : fstreamSingle) + endpoint, { agent }); + } else { + ws = new WebSocket((Binance.options.test ? fstreamSingleTest : fstreamSingle) + endpoint); + } + + if (Binance.options.verbose) Binance.options.log('futuresSubscribeSingle: Subscribed to ' + endpoint); + ws.reconnect = Binance.options.reconnect; + ws.endpoint = endpoint; + ws.isAlive = false; + ws.on('open', handleFuturesSocketOpen.bind(ws, params.openCallback)); + ws.on('pong', handleFuturesSocketHeartbeat); + ws.on('error', handleFuturesSocketError); + ws.on('close', handleFuturesSocketClose.bind(ws, params.reconnect)); + ws.on('message', data => { + try { + callback(JSONbig.parse(data)); + } catch (error) { + Binance.options.log('Parse error: ' + error.message); + } + }); + return ws; + }; + + /** + * Used to subscribe to a combined futures websocket endpoint + * @param {string} streams - streams to connect to + * @param {function} callback - the function to call when information is received + * @param {object} params - Optional reconnect {boolean} (whether to reconnect on disconnect), openCallback {function}, id {string} + * @return {WebSocket} - websocket reference + */ + const futuresSubscribe = function (streams, callback, params = {}) { + if (typeof streams === 'string') return futuresSubscribeSingle(streams, callback, params); + if (typeof params === 'boolean') params = { reconnect: params }; + if (!params.reconnect) params.reconnect = false; + if (!params.openCallback) params.openCallback = false; + if (!params.id) params.id = false; + let httpsproxy = process.env.https_proxy || false; + let socksproxy = process.env.socks_proxy || false; + const queryParams = streams.join('/'); + let ws = false; + if (socksproxy !== false) { + socksproxy = proxyReplacewithIp(socksproxy); + if (Binance.options.verbose) Binance.options.log(`futuresSubscribe: using socks proxy server ${socksproxy}`); + let agent = new SocksProxyAgent({ + protocol: parseProxy(socksproxy)[0], + host: parseProxy(socksproxy)[1], + port: parseProxy(socksproxy)[2] + }); + ws = new WebSocket((Binance.options.test ? fstreamTest : fstream) + queryParams, { agent }); + } else if (httpsproxy !== false) { + if (Binance.options.verbose) Binance.options.log(`futuresSubscribe: using proxy server ${httpsproxy}`); + let config = url.parse(httpsproxy); + let agent = new HttpsProxyAgent(config); + ws = new WebSocket((Binance.options.test ? fstreamTest : fstream) + queryParams, { agent }); + } else { + ws = new WebSocket((Binance.options.test ? fstreamTest : fstream) + queryParams); + } + + ws.reconnect = Binance.options.reconnect; + ws.endpoint = stringHash(queryParams); + ws.isAlive = false; + if (Binance.options.verbose) { + Binance.options.log(`futuresSubscribe: Subscribed to [${ws.endpoint}] ${queryParams}`); + } + ws.on('open', handleFuturesSocketOpen.bind(ws, params.openCallback)); + ws.on('pong', handleFuturesSocketHeartbeat); + ws.on('error', handleFuturesSocketError); + ws.on('close', handleFuturesSocketClose.bind(ws, params.reconnect)); + ws.on('message', data => { + try { + callback(JSON.parse(data).data); + } catch (error) { + Binance.options.log(`futuresSubscribe: Parse error: ${error.message}`); + } + }); + return ws; + }; + + /** + * Used to terminate a futures websocket + * @param {string} endpoint - endpoint identifier associated with the web socket + * @param {boolean} reconnect - auto reconnect after termination + * @return {undefined} + */ + const futuresTerminate = function (endpoint, reconnect = false) { + let ws = Binance.futuresSubscriptions[endpoint]; + if (!ws) return; + ws.removeAllListeners('message'); + ws.reconnect = reconnect; + ws.terminate(); + } + + /** + * Combines all futures OHLC data with the latest update + * @param {string} symbol - the symbol + * @param {string} interval - time interval + * @return {array} - interval data for given symbol + */ + const futuresKlineConcat = (symbol, interval) => { + let output = Binance.futuresTicks[symbol][interval]; + if (typeof Binance.futuresRealtime[symbol][interval].time === 'undefined') return output; + const time = Binance.futuresRealtime[symbol][interval].time; + const last_updated = Object.keys(Binance.futuresTicks[symbol][interval]).pop(); + if (time >= last_updated) { + output[time] = Binance.futuresRealtime[symbol][interval]; + //delete output[time].time; + output[last_updated].isFinal = true; + output[time].isFinal = false; + } + return output; + }; + + /** + * Used for websocket futures @kline + * @param {string} symbol - the symbol + * @param {object} kline - object with kline info + * @param {string} firstTime - time filter + * @return {undefined} + */ + const futuresKlineHandler = (symbol, kline, firstTime = 0) => { + // eslint-disable-next-line no-unused-vars + let { e: eventType, E: eventTime, k: ticks } = kline; + // eslint-disable-next-line no-unused-vars + let { o: open, h: high, l: low, c: close, v: volume, i: interval, x: isFinal, q: quoteVolume, V: takerBuyBaseVolume, Q: takerBuyQuoteVolume, n: trades, t: time, T: closeTime } = ticks; + if (time <= firstTime) return; + if (!isFinal) { + // if ( typeof Binance.futuresRealtime[symbol][interval].time !== 'undefined' ) { + // if ( Binance.futuresRealtime[symbol][interval].time > time ) return; + // } + Binance.futuresRealtime[symbol][interval] = { time, closeTime, open, high, low, close, volume, quoteVolume, takerBuyBaseVolume, takerBuyQuoteVolume, trades, isFinal }; + return; + } + const first_updated = Object.keys(Binance.futuresTicks[symbol][interval]).shift(); + if (first_updated) delete Binance.futuresTicks[symbol][interval][first_updated]; + Binance.futuresTicks[symbol][interval][time] = { time, closeTime, open, high, low, close, volume, quoteVolume, takerBuyBaseVolume, takerBuyQuoteVolume, trades, isFinal: false }; + }; + + /** + * Converts the futures liquidation stream data into a friendly object + * @param {object} data - liquidation data callback data type + * @return {object} - user friendly data type + */ + const fLiquidationConvertData = data => { + let eventType = data.e, eventTime = data.E; + let { + s: symbol, + S: side, + o: orderType, + f: timeInForce, + q: origAmount, + p: price, + ap: avgPrice, + X: orderStatus, + l: lastFilledQty, + z: totalFilledQty, + T: tradeTime + } = data.o; + return { symbol, side, orderType, timeInForce, origAmount, price, avgPrice, orderStatus, lastFilledQty, totalFilledQty, eventType, tradeTime, eventTime }; + }; + + /** + * Converts the futures ticker stream data into a friendly object + * @param {object} data - user data callback data type + * @return {object} - user friendly data type + */ + const fTickerConvertData = data => { + let friendlyData = data => { + let { + e: eventType, + E: eventTime, + s: symbol, + p: priceChange, + P: percentChange, + w: averagePrice, + c: close, + Q: closeQty, + o: open, + h: high, + l: low, + v: volume, + q: quoteVolume, + O: openTime, + C: closeTime, + F: firstTradeId, + L: lastTradeId, + n: numTrades + } = data; + return { + eventType, + eventTime, + symbol, + priceChange, + percentChange, + averagePrice, + close, + closeQty, + open, + high, + low, + volume, + quoteVolume, + openTime, + closeTime, + firstTradeId, + lastTradeId, + numTrades + }; + } + if (Array.isArray(data)) { + const result = []; + for (let obj of data) { + result.push(friendlyData(obj)); + } + return result; + } + return friendlyData(data); + } + + /** + * Converts the futures miniTicker stream data into a friendly object + * @param {object} data - user data callback data type + * @return {object} - user friendly data type + */ + const fMiniTickerConvertData = data => { + let friendlyData = data => { + let { + e: eventType, + E: eventTime, + s: symbol, + c: close, + o: open, + h: high, + l: low, + v: volume, + q: quoteVolume + } = data; + return { + eventType, + eventTime, + symbol, + close, + open, + high, + low, + volume, + quoteVolume + }; + } + if (Array.isArray(data)) { + const result = []; + for (let obj of data) { + result.push(friendlyData(obj)); + } + return result; + } + return friendlyData(data); + } + + /** + * Converts the futures bookTicker stream data into a friendly object + * @param {object} data - user data callback data type + * @return {object} - user friendly data type + */ + const fBookTickerConvertData = data => { + let { + u: updateId, + s: symbol, + b: bestBid, + B: bestBidQty, + a: bestAsk, + A: bestAskQty + } = data; + return { + updateId, + symbol, + bestBid, + bestBidQty, + bestAsk, + bestAskQty + }; + }; + + /** + * Converts the futures UserData stream MARGIN_CALL data into a friendly object + * @param {object} data - user data callback data type + * @return {object} - user friendly data type + */ + const fUserDataMarginConvertData = data => { + let { + e: eventType, + E: eventTime, + cw: crossWalletBalance, // only pushed with crossed position margin call + p: positions + } = data; + let positionConverter = position => { + let { + s: symbol, + ps: positionSide, + pa: positionAmount, + mt: marginType, + iw: isolatedWallet, // if isolated position + mp: markPrice, + up: unrealizedPnL, + mm: maintenanceMargin // maintenance margin required + } = position; + return { + symbol, + positionSide, + positionAmount, + marginType, + isolatedWallet, + markPrice, + unrealizedPnL, + maintenanceMargin + } + }; + const convertedPositions = []; + for (let position of positions) { + convertedPositions.push(positionConverter(position)); + } + positions = convertedPositions; + return { + eventType, + eventTime, + crossWalletBalance, + positions + }; + }; + + /** + * Converts the futures UserData stream ACCOUNT_CONFIG_UPDATE into a friendly object + * @param {object} data - user config callback data type + * @return {object} - user friendly data type + */ + const fUserConfigDataAccountUpdateConvertData = data => { + return { + eventType: data.e, + eventTime: data.E, + transactionTime: data.T, + ac: { + symbol: data.ac.s, + leverage: data.ac.l + } + }; + }; + + /** + * Converts the futures UserData stream ACCOUNT_UPDATE data into a friendly object + * @param {object} data - user data callback data type + * @return {object} - user friendly data type + */ + const fUserDataAccountUpdateConvertData = data => { + let { + e: eventType, + E: eventTime, + T: transaction, + a: updateData + } = data; + let updateConverter = updateData => { + let { + m: eventReasonType, + B: balances, + P: positions + } = updateData; + let positionConverter = position => { + let { + s: symbol, + pa: positionAmount, + ep: entryPrice, + cr: accumulatedRealized, // (Pre-fee) Accumulated Realized + up: unrealizedPnL, + mt: marginType, + iw: isolatedWallet, // if isolated position + ps: positionSide + } = position; + return { + symbol, + positionAmount, + entryPrice, + accumulatedRealized, + unrealizedPnL, + marginType, + isolatedWallet, + positionSide + }; + }; + let balanceConverter = balance => { + let { + a: asset, + wb: walletBalance, + cw: crossWalletBalance, + bc: balanceChange + } = balance; + return { + asset, + walletBalance, + crossWalletBalance, + balanceChange + }; + }; + + const balanceResult = []; + const positionResult = []; + + for (let balance of balances) { + balanceResult.push(balanceConverter(balance)); + } + for (let position of positions) { + positionResult.push(positionConverter(position)); + } + + balances = balanceResult; + positions = positionResult; + return { + eventReasonType, + balances, + positions + }; + }; + updateData = updateConverter(updateData); + return { + eventType, + eventTime, + transaction, + updateData + }; + }; + + /** + * Converts the futures UserData stream ORDER_TRADE_UPDATE data into a friendly object + * @param {object} data - user data callback data type + * @return {object} - user friendly data type + */ + const fUserDataOrderUpdateConvertData = data => { + let { + e: eventType, + E: eventTime, + T: transaction, // transaction time + o: order + } = data; + + let orderConverter = order => { + let { + s: symbol, + c: clientOrderId, + // special client order id: + // starts with "autoclose-": liquidation order + // "adl_autoclose": ADL auto close order + S: side, + o: orderType, + f: timeInForce, + q: originalQuantity, + p: originalPrice, + ap: averagePrice, + sp: stopPrice, // please ignore with TRAILING_STOP_MARKET order, + x: executionType, + X: orderStatus, + i: orderId, + l: orderLastFilledQuantity, + z: orderFilledAccumulatedQuantity, + L: lastFilledPrice, + N: commissionAsset, // will not push if no commission + n: commission, // will not push if no commission + T: orderTradeTime, + t: tradeId, + b: bidsNotional, + a: askNotional, + m: isMakerSide, // is this trade maker side + R: isReduceOnly, // is this reduce only + wt: stopPriceWorkingType, + ot: originalOrderType, + ps: positionSide, + cp: closeAll, // if close-all, pushed with conditional order + AP: activationPrice, // only pushed with TRAILING_STOP_MARKET order + cr: callbackRate, // only pushed with TRAILING_STOP_MARKET order + rp: realizedProfit + } = order; + return { + symbol, + clientOrderId, + side, + orderType, + timeInForce, + originalQuantity, + originalPrice, + averagePrice, + stopPrice, + executionType, + orderStatus, + orderId, + orderLastFilledQuantity, + orderFilledAccumulatedQuantity, + lastFilledPrice, + commissionAsset, + commission, + orderTradeTime, + tradeId, + bidsNotional, + askNotional, + isMakerSide, + isReduceOnly, + stopPriceWorkingType, + originalOrderType, + positionSide, + closeAll, + activationPrice, + callbackRate, + realizedProfit + }; + }; + order = orderConverter(order); + return { + eventType, + eventTime, + transaction, + order + }; + }; + + /** + * Converts the futures markPrice stream data into a friendly object + * @param {object} data - user data callback data type + * @return {object} - user friendly data type + */ + const fMarkPriceConvertData = data => { + let friendlyData = data => { + let { + e: eventType, + E: eventTime, + s: symbol, + p: markPrice, + i: indexPrice, + r: fundingRate, + T: fundingTime + } = data; + return { + eventType, + eventTime, + symbol, + markPrice, + indexPrice, + fundingRate, + fundingTime + }; + } + if (Array.isArray(data)) { + const result = []; + for (let obj of data) { + result.push(friendlyData(obj)); + } + return result; + } + return friendlyData(data); + } + + /** + * Converts the futures aggTrade stream data into a friendly object + * @param {object} data - user data callback data type + * @return {object} - user friendly data type + */ + const fAggTradeConvertData = data => { + let friendlyData = data => { + let { + e: eventType, + E: eventTime, + s: symbol, + a: aggTradeId, + p: price, + q: amount, + f: firstTradeId, + l: lastTradeId, + T: timestamp, + m: maker + } = data; + return { + eventType, + eventTime, + symbol, + aggTradeId, + price, + amount, + total: price * amount, + firstTradeId, + lastTradeId, + timestamp, + maker + }; + } + if (Array.isArray(data)) { + const result = []; + for (let obj of data) { + result.push(friendlyData(obj)); + } + return result; + } + return friendlyData(data); + } + + /** + * Delivery heartbeat code with a shared single interval tick + * @return {undefined} + */ + const deliverySocketHeartbeat = () => { + /* Sockets removed from subscriptions during a manual terminate() + will no longer be at risk of having functions called on them */ + for (let endpointId in Binance.deliverySubscriptions) { + const ws = Binance.deliverySubscriptions[endpointId]; + if (ws.isAlive) { + ws.isAlive = false; + if (ws.readyState === WebSocket.OPEN) ws.ping(noop); + } else { + if (Binance.options.verbose) Binance.options.log(`Terminating zombie delivery WebSocket: ${ws.endpoint}`); + if (ws.readyState === WebSocket.OPEN) ws.terminate(); + } + } + }; + + /** + * Called when a delivery socket is opened, subscriptions are registered for later reference + * @param {function} openCallback - a callback function + * @return {undefined} + */ + const handleDeliverySocketOpen = function (openCallback) { + this.isAlive = true; + if (Object.keys(Binance.deliverySubscriptions).length === 0) { + Binance.socketHeartbeatInterval = setInterval(deliverySocketHeartbeat, 30000); + } + Binance.deliverySubscriptions[this.endpoint] = this; + if (typeof openCallback === 'function') openCallback(this.endpoint); + }; + + /** + * Called when delivery websocket is closed, subscriptions are de-registered for later reference + * @param {boolean} reconnect - true or false to reconnect the socket + * @param {string} code - code associated with the socket + * @param {string} reason - string with the response + * @return {undefined} + */ + const handleDeliverySocketClose = function (reconnect, code, reason) { + delete Binance.deliverySubscriptions[this.endpoint]; + if (Binance.deliverySubscriptions && Object.keys(Binance.deliverySubscriptions).length === 0) { + clearInterval(Binance.socketHeartbeatInterval); + } + Binance.options.log('Delivery WebSocket closed: ' + this.endpoint + + (code ? ' (' + code + ')' : '') + + (reason ? ' ' + reason : '')); + if (Binance.options.reconnect && this.reconnect && reconnect) { + if (this.endpoint && parseInt(this.endpoint.length, 10) === 60) Binance.options.log('Delivery account data WebSocket reconnecting...'); + else Binance.options.log('Delivery WebSocket reconnecting: ' + this.endpoint + '...'); + try { + reconnect(); + } catch (error) { + Binance.options.log('Delivery WebSocket reconnect error: ' + error.message); + } + } + }; + + /** + * Called when a delivery websocket errors + * @param {object} error - error object message + * @return {undefined} + */ + const handleDeliverySocketError = function (error) { + Binance.options.log('Delivery WebSocket error: ' + this.endpoint + + (error.code ? ' (' + error.code + ')' : '') + + (error.message ? ' ' + error.message : '')); + }; + + /** + * Called on each delivery socket heartbeat + * @return {undefined} + */ + const handleDeliverySocketHeartbeat = function () { + this.isAlive = true; + }; + + /** + * Used to subscribe to a single delivery websocket endpoint + * @param {string} endpoint - endpoint to connect to + * @param {function} callback - the function to call when information is received + * @param {object} params - Optional reconnect {boolean} (whether to reconnect on disconnect), openCallback {function}, id {string} + * @return {WebSocket} - websocket reference + */ + const deliverySubscribeSingle = function (endpoint, callback, params = {}) { + if (typeof params === 'boolean') params = { reconnect: params }; + if (!params.reconnect) params.reconnect = false; + if (!params.openCallback) params.openCallback = false; + if (!params.id) params.id = false; + let httpsproxy = process.env.https_proxy || false; + let socksproxy = process.env.socks_proxy || false; + let ws = false; + if (socksproxy !== false) { + socksproxy = proxyReplacewithIp(socksproxy); + if (Binance.options.verbose) Binance.options.log(`deliverySubscribeSingle: using socks proxy server: ${socksproxy}`); + let agent = new SocksProxyAgent({ + protocol: parseProxy(socksproxy)[0], + host: parseProxy(socksproxy)[1], + port: parseProxy(socksproxy)[2] + }); + ws = new WebSocket((Binance.options.test ? dstreamSingleTest : dstreamSingle) + endpoint, { agent }); + } else if (httpsproxy !== false) { + let config = url.parse(httpsproxy); + let agent = new HttpsProxyAgent(config); + if (Binance.options.verbose) Binance.options.log(`deliverySubscribeSingle: using proxy server: ${agent}`); + ws = new WebSocket((Binance.options.test ? dstreamSingleTest : dstreamSingle) + endpoint, { agent }); + } else { + ws = new WebSocket((Binance.options.test ? dstreamSingleTest : dstreamSingle) + endpoint); + } + + if (Binance.options.verbose) Binance.options.log('deliverySubscribeSingle: Subscribed to ' + endpoint); + ws.reconnect = Binance.options.reconnect; + ws.endpoint = endpoint; + ws.isAlive = false; + ws.on('open', handleDeliverySocketOpen.bind(ws, params.openCallback)); + ws.on('pong', handleDeliverySocketHeartbeat); + ws.on('error', handleDeliverySocketError); + ws.on('close', handleDeliverySocketClose.bind(ws, params.reconnect)); + ws.on('message', data => { + try { + callback(JSON.parse(data)); + } catch (error) { + Binance.options.log('Parse error: ' + error.message); + } + }); + return ws; + }; + + /** + * Used to subscribe to a combined delivery websocket endpoint + * @param {string} streams - streams to connect to + * @param {function} callback - the function to call when information is received + * @param {object} params - Optional reconnect {boolean} (whether to reconnect on disconnect), openCallback {function}, id {string} + * @return {WebSocket} - websocket reference + */ + const deliverySubscribe = function (streams, callback, params = {}) { + if (typeof streams === 'string') return deliverySubscribeSingle(streams, callback, params); + if (typeof params === 'boolean') params = { reconnect: params }; + if (!params.reconnect) params.reconnect = false; + if (!params.openCallback) params.openCallback = false; + if (!params.id) params.id = false; + let httpsproxy = process.env.https_proxy || false; + let socksproxy = process.env.socks_proxy || false; + const queryParams = streams.join('/'); + let ws = false; + if (socksproxy !== false) { + socksproxy = proxyReplacewithIp(socksproxy); + if (Binance.options.verbose) Binance.options.log(`deliverySubscribe: using socks proxy server ${socksproxy}`); + let agent = new SocksProxyAgent({ + protocol: parseProxy(socksproxy)[0], + host: parseProxy(socksproxy)[1], + port: parseProxy(socksproxy)[2] + }); + ws = new WebSocket((Binance.options.test ? dstreamTest : dstream) + queryParams, { agent }); + } else if (httpsproxy !== false) { + if (Binance.options.verbose) Binance.options.log(`deliverySubscribe: using proxy server ${httpsproxy}`); + let config = url.parse(httpsproxy); + let agent = new HttpsProxyAgent(config); + ws = new WebSocket((Binance.options.test ? dstreamTest : dstream) + queryParams, { agent }); + } else { + ws = new WebSocket((Binance.options.test ? dstreamTest : dstream) + queryParams); + } + + ws.reconnect = Binance.options.reconnect; + ws.endpoint = stringHash(queryParams); + ws.isAlive = false; + if (Binance.options.verbose) { + Binance.options.log(`deliverySubscribe: Subscribed to [${ws.endpoint}] ${queryParams}`); + } + ws.on('open', handleDeliverySocketOpen.bind(ws, params.openCallback)); + ws.on('pong', handleDeliverySocketHeartbeat); + ws.on('error', handleDeliverySocketError); + ws.on('close', handleDeliverySocketClose.bind(ws, params.reconnect)); + ws.on('message', data => { + try { + callback(JSON.parse(data).data); + } catch (error) { + Binance.options.log(`deliverySubscribe: Parse error: ${error.message}`); + } + }); + return ws; + }; + + /** + * Used to terminate a delivery websocket + * @param {string} endpoint - endpoint identifier associated with the web socket + * @param {boolean} reconnect - auto reconnect after termination + * @return {undefined} + */ + const deliveryTerminate = function (endpoint, reconnect = false) { + let ws = Binance.deliverySubscriptions[endpoint]; + if (!ws) return; + ws.removeAllListeners('message'); + ws.reconnect = reconnect; + ws.terminate(); + } + + /** + * Combines all delivery OHLC data with the latest update + * @param {string} symbol - the symbol + * @param {string} interval - time interval + * @return {array} - interval data for given symbol + */ + const deliveryKlineConcat = (symbol, interval) => { + let output = Binance.deliveryTicks[symbol][interval]; + if (typeof Binance.deliveryRealtime[symbol][interval].time === 'undefined') return output; + const time = Binance.deliveryRealtime[symbol][interval].time; + const last_updated = Object.keys(Binance.deliveryTicks[symbol][interval]).pop(); + if (time >= last_updated) { + output[time] = Binance.deliveryRealtime[symbol][interval]; + //delete output[time].time; + output[last_updated].isFinal = true; + output[time].isFinal = false; + } + return output; + }; + + /** + * Used for websocket delivery @kline + * @param {string} symbol - the symbol + * @param {object} kline - object with kline info + * @param {string} firstTime - time filter + * @return {undefined} + */ + const deliveryKlineHandler = (symbol, kline, firstTime = 0) => { + // eslint-disable-next-line no-unused-vars + let { e: eventType, E: eventTime, k: ticks } = kline; + // eslint-disable-next-line no-unused-vars + let { o: open, h: high, l: low, c: close, v: volume, i: interval, x: isFinal, q: quoteVolume, V: takerBuyBaseVolume, Q: takerBuyQuoteVolume, n: trades, t: time, T: closeTime } = ticks; + if (time <= firstTime) return; + if (!isFinal) { + // if ( typeof Binance.futuresRealtime[symbol][interval].time !== 'undefined' ) { + // if ( Binance.futuresRealtime[symbol][interval].time > time ) return; + // } + Binance.deliveryRealtime[symbol][interval] = { time, closeTime, open, high, low, close, volume, quoteVolume, takerBuyBaseVolume, takerBuyQuoteVolume, trades, isFinal }; + return; + } + const first_updated = Object.keys(Binance.deliveryTicks[symbol][interval]).shift(); + if (first_updated) delete Binance.deliveryTicks[symbol][interval][first_updated]; + Binance.deliveryTicks[symbol][interval][time] = { time, closeTime, open, high, low, close, volume, quoteVolume, takerBuyBaseVolume, takerBuyQuoteVolume, trades, isFinal: false }; + }; + + /** + * Converts the delivery liquidation stream data into a friendly object + * @param {object} data - liquidation data callback data type + * @return {object} - user friendly data type + */ + const dLiquidationConvertData = data => { + let eventType = data.e, eventTime = data.E; + let { + s: symbol, + S: side, + o: orderType, + f: timeInForce, + q: origAmount, + p: price, + ap: avgPrice, + X: orderStatus, + l: lastFilledQty, + z: totalFilledQty, + T: tradeTime + } = data.o; + return { symbol, side, orderType, timeInForce, origAmount, price, avgPrice, orderStatus, lastFilledQty, totalFilledQty, eventType, tradeTime, eventTime }; + }; + + /** + * Converts the delivery ticker stream data into a friendly object + * @param {object} data - user data callback data type + * @return {object} - user friendly data type + */ + const dTickerConvertData = data => { + let friendlyData = data => { + let { + e: eventType, + E: eventTime, + s: symbol, + p: priceChange, + P: percentChange, + w: averagePrice, + c: close, + Q: closeQty, + o: open, + h: high, + l: low, + v: volume, + q: quoteVolume, + O: openTime, + C: closeTime, + F: firstTradeId, + L: lastTradeId, + n: numTrades + } = data; + return { + eventType, + eventTime, + symbol, + priceChange, + percentChange, + averagePrice, + close, + closeQty, + open, + high, + low, + volume, + quoteVolume, + openTime, + closeTime, + firstTradeId, + lastTradeId, + numTrades + }; + } + if (Array.isArray(data)) { + const result = []; + for (let obj of data) { + result.push(friendlyData(obj)); + } + return result; + } + return friendlyData(data); + } + + /** + * Converts the delivery miniTicker stream data into a friendly object + * @param {object} data - user data callback data type + * @return {object} - user friendly data type + */ + const dMiniTickerConvertData = data => { + let friendlyData = data => { + let { + e: eventType, + E: eventTime, + s: symbol, + c: close, + o: open, + h: high, + l: low, + v: volume, + q: quoteVolume + } = data; + return { + eventType, + eventTime, + symbol, + close, + open, + high, + low, + volume, + quoteVolume + }; + } + if (Array.isArray(data)) { + const result = []; + for (let obj of data) { + result.push(friendlyData(obj)); + } + return result; + } + return friendlyData(data); + } + + /** + * Converts the delivery bookTicker stream data into a friendly object + * @param {object} data - user data callback data type + * @return {object} - user friendly data type + */ + const dBookTickerConvertData = data => { + let { + u: updateId, + s: symbol, + b: bestBid, + B: bestBidQty, + a: bestAsk, + A: bestAskQty + } = data; + return { + updateId, + symbol, + bestBid, + bestBidQty, + bestAsk, + bestAskQty + }; + } + + /** + * Converts the delivery markPrice stream data into a friendly object + * @param {object} data - user data callback data type + * @return {object} - user friendly data type + */ + const dMarkPriceConvertData = data => { + let friendlyData = data => { + let { + e: eventType, + E: eventTime, + s: symbol, + p: markPrice, + r: fundingRate, + T: fundingTime + } = data; + return { + eventType, + eventTime, + symbol, + markPrice, + fundingRate, + fundingTime + }; + } + if (Array.isArray(data)) { + const result = []; + for (let obj of data) { + result.push(friendlyData(obj)); + } + return result; + } + return friendlyData(data); + } + + /** + * Converts the delivery aggTrade stream data into a friendly object + * @param {object} data - user data callback data type + * @return {object} - user friendly data type + */ + const dAggTradeConvertData = data => { + let friendlyData = data => { + let { + e: eventType, + E: eventTime, + s: symbol, + a: aggTradeId, + p: price, + q: amount, + f: firstTradeId, + l: lastTradeId, + T: timestamp, + m: maker + } = data; + return { + eventType, + eventTime, + symbol, + aggTradeId, + price, + amount, + total: price * amount, + firstTradeId, + lastTradeId, + timestamp, + maker + }; + } + if (Array.isArray(data)) { + const result = []; + for (let obj of data) { + result.push(friendlyData(obj)); + } + return result; + } + return friendlyData(data); + } + + /** + * Converts the delivery UserData stream ORDER_TRADE_UPDATE data into a friendly object + * @param {object} data - user data callback data type + * @return {object} - user friendly data type + */ + const dUserDataOrderUpdateConvertData = (data) => { + let { + e: eventType, + E: eventTime, + T: transaction, // transaction time + o: order, + } = data; + + let orderConverter = (order) => { + let { + s: symbol, + c: clientOrderId, + // special client order id: + // starts with "autoclose-": liquidation order + // "adl_autoclose": ADL auto close order + S: side, + o: orderType, + f: timeInForce, + q: originalQuantity, + p: originalPrice, + ap: averagePrice, + sp: stopPrice, // please ignore with TRAILING_STOP_MARKET order, + x: executionType, + X: orderStatus, + i: orderId, + l: orderLastFilledQuantity, + z: orderFilledAccumulatedQuantity, + L: lastFilledPrice, + ma: marginAsset, + N: commissionAsset, // will not push if no commission + n: commission, // will not push if no commission + T: orderTradeTime, + t: tradeId, + rp: realizedProfit, + b: bidsNotional, + a: askNotional, + m: isMakerSide, // is this trade maker side + R: isReduceOnly, // is this reduce only + wt: stopPriceWorkingType, + ot: originalOrderType, + ps: positionSide, + cp: closeAll, // if close-all, pushed with conditional order + AP: activationPrice, // only pushed with TRAILING_STOP_MARKET order + cr: callbackRate, // only pushed with TRAILING_STOP_MARKET order + pP: priceProtect, // If conditional order trigger is protected + } = order; + return { + symbol, + clientOrderId, + side, + orderType, + timeInForce, + originalQuantity, + originalPrice, + averagePrice, + stopPrice, + executionType, + orderStatus, + orderId, + orderLastFilledQuantity, + orderFilledAccumulatedQuantity, + lastFilledPrice, + marginAsset, + commissionAsset, + commission, + orderTradeTime, + tradeId, + bidsNotional, + askNotional, + isMakerSide, + isReduceOnly, + stopPriceWorkingType, + originalOrderType, + positionSide, + closeAll, + activationPrice, + callbackRate, + realizedProfit, + priceProtect, + }; + }; + order = orderConverter(order); + return { + eventType, + eventTime, + transaction, + order, + }; + }; + + /** + * Used as part of the user data websockets callback + * @param {object} data - user data callback data type + * @return {undefined} + */ + const userDataHandler = data => { + let type = data.e; + if (type === 'outboundAccountInfo') { + // XXX: Deprecated in 2020-09-08 + } else if (type === 'executionReport') { + if (Binance.options.execution_callback) Binance.options.execution_callback(data); + } else if (type === 'listStatus') { + if (Binance.options.list_status_callback) Binance.options.list_status_callback(data); + } else if (type === 'outboundAccountPosition' || type === 'balanceUpdate') { + Binance.options.balance_callback(data); + } else { + Binance.options.log('Unexpected userData: ' + type); + } + }; + + /** + * Used as part of the user data websockets callback + * @param {object} data - user data callback data type + * @return {undefined} + */ + const userMarginDataHandler = data => { + let type = data.e; + if (type === 'outboundAccountInfo') { + // XXX: Deprecated in 2020-09-08 + } else if (type === 'executionReport') { + if (Binance.options.margin_execution_callback) Binance.options.margin_execution_callback(data); + } else if (type === 'listStatus') { + if (Binance.options.margin_list_status_callback) Binance.options.margin_list_status_callback(data); + } else if (type === 'outboundAccountPosition' || type === 'balanceUpdate') { + Binance.options.margin_balance_callback(data); + } else { + Binance.options.log('Unexpected userMarginData: ' + type); + } + }; + + /** + * Used as part of the user data websockets callback + * @param {object} data - user data callback data type + * @return {undefined} + */ + const userFutureDataHandler = data => { + let type = data.e; + if (type === 'MARGIN_CALL') { + Binance.options.future_margin_call_callback(fUserDataMarginConvertData(data)); + } else if (type === 'ACCOUNT_UPDATE') { + if (Binance.options.future_account_update_callback) { + Binance.options.future_account_update_callback(fUserDataAccountUpdateConvertData(data)); + } + } else if (type === 'ORDER_TRADE_UPDATE') { + if (Binance.options.future_order_update_callback) { + Binance.options.future_order_update_callback(fUserDataOrderUpdateConvertData(data)); + } + } else if (type === 'ACCOUNT_CONFIG_UPDATE') { + if (Binance.options.future_account_config_update_callback) { + Binance.options.future_account_config_update_callback(fUserConfigDataAccountUpdateConvertData(data)); + } + } else { + Binance.options.log('Unexpected userFutureData: ' + type); + } + }; + + /** + * Used as part of the user data websockets callback + * @param {object} data - user data callback data type + * @return {undefined} + */ + const userDeliveryDataHandler = (data) => { + let type = data.e; + if (type === "MARGIN_CALL") { + Binance.options.delivery_margin_call_callback( + fUserDataMarginConvertData(data) + ); + } else if (type === "ACCOUNT_UPDATE") { + if (Binance.options.delivery_account_update_callback) { + Binance.options.delivery_account_update_callback( + fUserDataAccountUpdateConvertData(data) + ); + } + } else if (type === "ORDER_TRADE_UPDATE") { + if (Binance.options.delivery_order_update_callback) { + Binance.options.delivery_order_update_callback( + dUserDataOrderUpdateConvertData(data) + ); + } + } else { + Binance.options.log("Unexpected userDeliveryData: " + type); + } + }; + + /** + * Universal Transfer requires API permissions enabled + * @param {string} type - ENUM , example MAIN_UMFUTURE for SPOT to USDT futures, see https://binance-docs.github.io/apidocs/spot/en/#user-universal-transfer + * @param {string} asset - the asset - example :USDT * + * @param {number} amount - the callback function + * @param {function} callback - the callback function + * @return {promise} + */ + const universalTransfer = (type, asset, amount, callback = false) => { + let parameters = Object.assign({ + asset, + amount, + type, + }); + if (!callback) { + return new Promise((resolve, reject) => { + signedRequest( + sapi + "v1/asset/transfer", + parameters, + function (error, data) { + if (error) return reject(error); + return resolve(data); + }, + "POST" + ); + }); + } + signedRequest( + sapi + "v1/asset/transfer", + parameters, + function (error, data) { + if (callback) return callback(error, data); + }, + "POST" + ); + + } + + /** + * Transfer between main account and futures/delivery accounts + * @param {string} asset - the asset + * @param {number} amount - the asset + * @param {function} callback - the callback function + * @param {object} options - additional options + * @return {undefined} + */ + const transferBetweenMainAndFutures = function ( + asset, + amount, + type, + callback + ) { + let parameters = Object.assign({ + asset, + amount, + type, + }); + if (!callback) { + return new Promise((resolve, reject) => { + signedRequest( + sapi + "v1/futures/transfer", + parameters, + function (error, data) { + if (error) return reject(error); + return resolve(data); + }, + "POST" + ); + }); + } + signedRequest( + sapi + "v1/futures/transfer", + parameters, + function (error, data) { + if (callback) return callback(error, data); + }, + "POST" + ); + }; + + /** + * Converts the previous day stream into friendly object + * @param {object} data - user data callback data type + * @return {object} - user friendly data type + */ + const prevDayConvertData = data => { + let convertData = data => { + let { + e: eventType, + E: eventTime, + s: symbol, + p: priceChange, + P: percentChange, + w: averagePrice, + x: prevClose, + c: close, + Q: closeQty, + b: bestBid, + B: bestBidQty, + a: bestAsk, + A: bestAskQty, + o: open, + h: high, + l: low, + v: volume, + q: quoteVolume, + O: openTime, + C: closeTime, + F: firstTradeId, + L: lastTradeId, + n: numTrades + } = data; + return { + eventType, + eventTime, + symbol, + priceChange, + percentChange, + averagePrice, + prevClose, + close, + closeQty, + bestBid, + bestBidQty, + bestAsk, + bestAskQty, + open, + high, + low, + volume, + quoteVolume, + openTime, + closeTime, + firstTradeId, + lastTradeId, + numTrades + }; + } + if (Array.isArray(data)) { + const result = []; + for (let obj of data) { + let converted = convertData(obj); + result.push(converted); + } + return result; + // eslint-disable-next-line no-else-return + } else { + return convertData(data); + } + } + + /** + * Parses the previous day stream and calls the user callback with friendly object + * @param {object} data - user data callback data type + * @param {function} callback - user data callback data type + * @return {undefined} + */ + const prevDayStreamHandler = (data, callback) => { + const converted = prevDayConvertData(data); + callback(null, converted); + }; + + /** + * Gets the price of a given symbol or symbols + * @param {array} data - array of symbols + * @return {array} - symbols with their current prices + */ + const priceData = (data) => { + const prices = {}; + if (Array.isArray(data)) { + for (let obj of data) { + prices[obj.symbol] = obj.price; + } + } else { // Single price returned + prices[data.symbol] = data.price; + } + return prices; + }; + + /** + * Used by bookTickers to format the bids and asks given given symbols + * @param {array} data - array of symbols + * @return {object} - symbols with their bids and asks data + */ + const bookPriceData = data => { + let prices = {}; + for (let obj of data) { + prices[obj.symbol] = { + bid: obj.bidPrice, + bids: obj.bidQty, + ask: obj.askPrice, + asks: obj.askQty + }; + } + return prices; + }; + + /** + * Used by balance to get the balance data + * @param {array} data - account info object + * @return {object} - balances hel with available, onorder amounts + */ + const balanceData = data => { + let balances = {}; + if (typeof data === 'undefined') return {}; + if (typeof data.balances === 'undefined') { + Binance.options.log('balanceData error', data); + return {}; + } + for (let obj of data.balances) { + balances[obj.asset] = { available: obj.free, onOrder: obj.locked }; + } + return balances; + }; + + /** + * Used by web sockets depth and populates OHLC and info + * @param {string} symbol - symbol to get candlestick info + * @param {string} interval - time interval, 1m, 3m, 5m .... + * @param {array} ticks - tick array + * @return {undefined} + */ + const klineData = (symbol, interval, ticks) => { // Used for /depth + let last_time = 0; + if (isIterable(ticks)) { + for (let tick of ticks) { + // eslint-disable-next-line no-unused-vars + let [time, open, high, low, close, volume, closeTime, assetVolume, trades, buyBaseVolume, buyAssetVolume, ignored] = tick; + Binance.ohlc[symbol][interval][time] = { open: open, high: high, low: low, close: close, volume: volume }; + last_time = time; + } + + Binance.info[symbol][interval].timestamp = last_time; + } + }; + + /** + * Combines all OHLC data with latest update + * @param {string} symbol - the symbol + * @param {string} interval - time interval, 1m, 3m, 5m .... + * @return {array} - interval data for given symbol + */ + const klineConcat = (symbol, interval) => { + let output = Binance.ohlc[symbol][interval]; + if (typeof Binance.ohlcLatest[symbol][interval].time === 'undefined') return output; + const time = Binance.ohlcLatest[symbol][interval].time; + const last_updated = Object.keys(Binance.ohlc[symbol][interval]).pop(); + if (time >= last_updated) { + output[time] = Binance.ohlcLatest[symbol][interval]; + delete output[time].time; + output[time].isFinal = false; + } + return output; + }; + + /** + * Used for websocket @kline + * @param {string} symbol - the symbol + * @param {object} kline - object with kline info + * @param {string} firstTime - time filter + * @return {undefined} + */ + const klineHandler = (symbol, kline, firstTime = 0) => { + // TODO: add Taker buy base asset volume + // eslint-disable-next-line no-unused-vars + let { e: eventType, E: eventTime, k: ticks } = kline; + // eslint-disable-next-line no-unused-vars + let { o: open, h: high, l: low, c: close, v: volume, i: interval, x: isFinal, q: quoteVolume, t: time } = ticks; //n:trades, V:buyVolume, Q:quoteBuyVolume + if (time <= firstTime) return; + if (!isFinal) { + if (typeof Binance.ohlcLatest[symbol][interval].time !== 'undefined') { + if (Binance.ohlcLatest[symbol][interval].time > time) return; + } + Binance.ohlcLatest[symbol][interval] = { open: open, high: high, low: low, close: close, volume: volume, time: time }; + return; + } + // Delete an element from the beginning so we don't run out of memory + const first_updated = Object.keys(Binance.ohlc[symbol][interval]).shift(); + if (first_updated) delete Binance.ohlc[symbol][interval][first_updated]; + Binance.ohlc[symbol][interval][time] = { open: open, high: high, low: low, close: close, volume: volume }; + }; + + + /** + * Used by futures websockets chart cache + * @param {string} symbol - symbol to get candlestick info + * @param {string} interval - time interval, 1m, 3m, 5m .... + * @param {array} ticks - tick array + * @return {undefined} + */ + const futuresKlineData = (symbol, interval, ticks) => { + let last_time = 0; + if (isIterable(ticks)) { + for (let tick of ticks) { + // eslint-disable-next-line no-unused-vars + let [time, open, high, low, close, volume, closeTime, quoteVolume, trades, takerBuyBaseVolume, takerBuyQuoteVolume, ignored] = tick; + Binance.futuresTicks[symbol][interval][time] = { time, closeTime, open, high, low, close, volume, quoteVolume, takerBuyBaseVolume, takerBuyQuoteVolume, trades }; + last_time = time; + } + Binance.futuresMeta[symbol][interval].timestamp = last_time; + } + }; + + /** + * Used by delivery websockets chart cache + * @param {string} symbol - symbol to get candlestick info + * @param {string} interval - time interval, 1m, 3m, 5m .... + * @param {array} ticks - tick array + * @return {undefined} + */ + const deliveryKlineData = (symbol, interval, ticks) => { + let last_time = 0; + if (isIterable(ticks)) { + for (let tick of ticks) { + // eslint-disable-next-line no-unused-vars + let [time, open, high, low, close, volume, closeTime, quoteVolume, trades, takerBuyBaseVolume, takerBuyQuoteVolume, ignored] = tick; + Binance.deliveryTicks[symbol][interval][time] = { time, closeTime, open, high, low, close, volume, quoteVolume, takerBuyBaseVolume, takerBuyQuoteVolume, trades }; + last_time = time; + } + Binance.deliveryMeta[symbol][interval].timestamp = last_time; + } + }; + + /** + * Used for /depth endpoint + * @param {object} data - containing the bids and asks + * @return {undefined} + */ + const depthData = data => { + if (!data) return { bids: [], asks: [] }; + let bids = {}, asks = {}, obj; + if (typeof data.bids !== 'undefined') { + for (obj of data.bids) { + bids[obj[0]] = parseFloat(obj[1]); + } + } + if (typeof data.asks !== 'undefined') { + for (obj of data.asks) { + asks[obj[0]] = parseFloat(obj[1]); + } + } + return { lastUpdateId: data.lastUpdateId, bids: bids, asks: asks }; + } + + /** + * Used for /depth endpoint + * @param {object} depth - information + * @return {undefined} + */ + const depthHandler = depth => { + let symbol = depth.s, obj; + let context = Binance.depthCacheContext[symbol]; + let updateDepthCache = () => { + Binance.depthCache[symbol].eventTime = depth.E; + for (obj of depth.b) { //bids + if (obj[1] == 0) { + delete Binance.depthCache[symbol].bids[obj[0]]; + } else { + Binance.depthCache[symbol].bids[obj[0]] = parseFloat(obj[1]); + } + } + for (obj of depth.a) { //asks + if (obj[1] == 0) { + delete Binance.depthCache[symbol].asks[obj[0]]; + } else { + Binance.depthCache[symbol].asks[obj[0]] = parseFloat(obj[1]); + } + } + context.skipCount = 0; + context.lastEventUpdateId = depth.u; + context.lastEventUpdateTime = depth.E; + }; + + // This now conforms 100% to the Binance docs constraints on managing a local order book + if (context.lastEventUpdateId) { + const expectedUpdateId = context.lastEventUpdateId + 1; + if (depth.U <= expectedUpdateId) { + updateDepthCache(); + } else { + let msg = 'depthHandler: [' + symbol + '] The depth cache is out of sync.'; + msg += ' Symptom: Unexpected Update ID. Expected "' + expectedUpdateId + '", got "' + depth.U + '"'; + if (Binance.options.verbose) Binance.options.log(msg); + throw new Error(msg); + } + } else if (depth.U > context.snapshotUpdateId + 1) { + /* In this case we have a gap between the data of the stream and the snapshot. + This is an out of sync error, and the connection must be torn down and reconnected. */ + let msg = 'depthHandler: [' + symbol + '] The depth cache is out of sync.'; + msg += ' Symptom: Gap between snapshot and first stream data.'; + if (Binance.options.verbose) Binance.options.log(msg); + throw new Error(msg); + } else if (depth.u < context.snapshotUpdateId + 1) { + /* In this case we've received data that we've already had since the snapshot. + This isn't really an issue, and we can just update the cache again, or ignore it entirely. */ + + // do nothing + } else { + // This is our first legal update from the stream data + updateDepthCache(); + } + }; + + /** + * Gets depth cache for given symbol + * @param {string} symbol - the symbol to fetch + * @return {object} - the depth cache object + */ + const getDepthCache = symbol => { + if (typeof Binance.depthCache[symbol] === 'undefined') return { bids: {}, asks: {} }; + return Binance.depthCache[symbol]; + }; + + /** + * Calculate Buy/Sell volume from DepthCache + * @param {string} symbol - the symbol to fetch + * @return {object} - the depth volume cache object + */ + const depthVolume = symbol => { + let cache = getDepthCache(symbol), quantity, price; + let bidbase = 0, askbase = 0, bidqty = 0, askqty = 0; + for (price in cache.bids) { + quantity = cache.bids[price]; + bidbase += parseFloat((quantity * parseFloat(price)).toFixed(8)); + bidqty += quantity; + } + for (price in cache.asks) { + quantity = cache.asks[price]; + askbase += parseFloat((quantity * parseFloat(price)).toFixed(8)); + askqty += quantity; + } + return { bids: bidbase, asks: askbase, bidQty: bidqty, askQty: askqty }; + }; + + /** + * Checks whether or not an array contains any duplicate elements + * @param {array} array - the array to check + * @return {boolean} - true or false + */ + const isArrayUnique = array => { + return new Set(array).size === array.length; + }; + return { + /** + * Gets depth cache for given symbol + * @param {symbol} symbol - get depch cache for this symbol + * @return {object} - object + */ + depthCache: symbol => { + return getDepthCache(symbol); + }, + + /** + * Gets depth volume for given symbol + * @param {symbol} symbol - get depch volume for this symbol + * @return {object} - object + */ + depthVolume: symbol => { + return depthVolume(symbol); + }, + + /** + * Count decimal places + * @param {float} float - get the price precision point + * @return {int} - number of place + */ + getPrecision: function (float) { + if (!float || Number.isInteger(float)) return 0; + return float.toString().split('.')[1].length || 0; + }, + + /** + * rounds number with given step + * @param {float} qty - quantity to round + * @param {float} stepSize - stepSize as specified by exchangeInfo + * @return {float} - number + */ + roundStep: function (qty, stepSize) { + // Integers do not require rounding + if (Number.isInteger(qty)) return qty; + const qtyString = parseFloat(qty).toFixed(16); + const desiredDecimals = Math.max(stepSize.indexOf('1') - 1, 0); + const decimalIndex = qtyString.indexOf('.'); + return parseFloat(qtyString.slice(0, decimalIndex + desiredDecimals + 1)); + }, + + /** + * rounds price to required precision + * @param {float} price - price to round + * @param {float} tickSize - tickSize as specified by exchangeInfo + * @return {float} - number + */ + roundTicks: function (price, tickSize) { + const formatter = new Intl.NumberFormat('en-US', { style: 'decimal', minimumFractionDigits: 0, maximumFractionDigits: 8 }); + const precision = formatter.format(tickSize).split('.')[1].length || 0; + if (typeof price === 'string') price = parseFloat(price); + return price.toFixed(precision); + }, + + /** + * Gets percentage of given numbers + * @param {float} min - the smaller number + * @param {float} max - the bigger number + * @param {int} width - percentage width + * @return {float} - percentage + */ + percent: function (min, max, width = 100) { + return (min * 0.01) / (max * 0.01) * width; + }, + + /** + * Gets the sum of an array of numbers + * @param {array} array - the number to add + * @return {float} - sum + */ + sum: function (array) { + return array.reduce((a, b) => a + b, 0); + }, + + /** + * Reverses the keys of an object + * @param {object} object - the object + * @return {object} - the object + */ + reverse: function (object) { + let range = Object.keys(object).reverse(), output = {}; + for (let price of range) { + output[price] = object[price]; + } + return output; + }, + + /** + * Converts an object to an array + * @param {object} obj - the object + * @return {array} - the array + */ + array: function (obj) { + return Object.keys(obj).map(function (key) { + return [Number(key), obj[key]]; + }); + }, + + /** + * Sorts bids + * @param {string} symbol - the object + * @param {int} max - the max number of bids + * @param {string} baseValue - the object + * @return {object} - the object + */ + sortBids: function (symbol, max = Infinity, baseValue = false) { + let object = {}, count = 0, cache; + if (typeof symbol === 'object') cache = symbol; + else cache = getDepthCache(symbol).bids; + const sorted = Object.keys(cache).sort((a, b) => parseFloat(b) - parseFloat(a)); + let cumulative = 0; + for (let price of sorted) { + if (!baseValue) object[price] = cache[price]; + else if (baseValue === 'cumulative') { + cumulative += cache[price]; + object[price] = cumulative; + } else object[price] = parseFloat((cache[price] * parseFloat(price)).toFixed(8)); + if (++count >= max) break; + } + return object; + }, + + /** + * Sorts asks + * @param {string} symbol - the object + * @param {int} max - the max number of bids + * @param {string} baseValue - the object + * @return {object} - the object + */ + sortAsks: function (symbol, max = Infinity, baseValue = false) { + let object = {}, count = 0, cache; + if (typeof symbol === 'object') cache = symbol; + else cache = getDepthCache(symbol).asks; + const sorted = Object.keys(cache).sort((a, b) => parseFloat(a) - parseFloat(b)); + let cumulative = 0; + for (let price of sorted) { + if (!baseValue) object[price] = cache[price]; + else if (baseValue === 'cumulative') { + cumulative += cache[price]; + object[price] = cumulative; + } else object[price] = parseFloat((cache[price] * parseFloat(price)).toFixed(8)); + if (++count >= max) break; + } + return object; + }, + + /** + * Returns the first property of an object + * @param {object} object - the object to get the first member + * @return {string} - the object key + */ + first: function (object) { + return Object.keys(object).shift(); + }, + + /** + * Returns the last property of an object + * @param {object} object - the object to get the first member + * @return {string} - the object key + */ + last: function (object) { + return Object.keys(object).pop(); + }, + + /** + * Returns an array of properties starting at start + * @param {object} object - the object to get the properties form + * @param {int} start - the starting index + * @return {array} - the array of entires + */ + slice: function (object, start = 0) { + return Object.keys(object).slice(start); + }, + + /** + * Gets the minimum key form object + * @param {object} object - the object to get the properties form + * @return {string} - the minimum key + */ + min: function (object) { + return Math.min.apply(Math, Object.keys(object)); + }, + + /** + * Gets the maximum key form object + * @param {object} object - the object to get the properties form + * @return {string} - the minimum key + */ + max: function (object) { + return Math.max.apply(Math, Object.keys(object)); + }, + + /** + * Sets an option given a key and value + * @param {string} key - the key to set + * @param {object} value - the value of the key + * @return {undefined} + */ + setOption: function (key, value) { + Binance.options[key] = value; + }, + + /** + * Gets an option given a key + * @param {string} key - the key to set + * @return {undefined} + */ + getOption: key => Binance.options[key], + + /** + * Returns the entire info object + * @return {object} - the info object + */ + getInfo: () => Binance.info, + + /** + * Returns the used weight from the last request + * @return {object} - 1m weight used + */ + usedWeight: () => Binance.info.usedWeight, + + /** + * Returns the status code from the last http response + * @return {object} - status code + */ + statusCode: () => Binance.info.statusCode, + + /** + * Returns the ping time from the last futures request + * @return {object} - latency/ping (2ms) + */ + futuresLatency: () => Binance.info.futuresLatency, + + /** + * Returns the complete URL from the last request + * @return {object} - http address including query string + */ + lastURL: () => Binance.info.lastURL, + + /** + * Returns the order count from the last request + * @return {object} - orders allowed per 1m + */ + orderCount: () => Binance.info.orderCount1m, + + /** + * Returns the entire options object + * @return {object} - the options object + */ + getOptions: () => Binance.options, + + /** + * Gets an option given a key + * @param {object} opt - the object with the class configuration + * @param {function} callback - the callback function + * @return {undefined} + */ + options: setOptions, + + /** + * Creates an order + * @param {string} side - BUY or SELL + * @param {string} symbol - the symbol to buy + * @param {numeric} quantity - the quantity required + * @param {numeric} price - the price to pay for each unit + * @param {object} flags - aadditionalbuy order flags + * @param {function} callback - the callback function + * @return {promise or undefined} - omitting the callback returns a promise + */ + order: function (side, symbol, quantity, price, flags = {}, callback = false) { + if (!callback) { + return new Promise((resolve, reject) => { + callback = (error, response) => { + if (error) { + reject(error); + } else { + resolve(response); + } + } + order(side, symbol, quantity, price, flags, callback); + }) + } else { + order(side, symbol, quantity, price, flags, callback); + } + }, + + /** + * Creates a buy order + * @param {string} symbol - the symbol to buy + * @param {numeric} quantity - the quantity required + * @param {numeric} price - the price to pay for each unit + * @param {object} flags - additional buy order flags + * @param {function} callback - the callback function + * @return {promise or undefined} - omitting the callback returns a promise + */ + buy: function (symbol, quantity, price, flags = {}, callback = false) { + if (!callback) { + return new Promise((resolve, reject) => { + callback = (error, response) => { + if (error) { + reject(error); + } else { + resolve(response); + } + } + order('BUY', symbol, quantity, price, flags, callback); + }) + } else { + order('BUY', symbol, quantity, price, flags, callback); + } + }, + + /** + * Creates a sell order + * @param {string} symbol - the symbol to sell + * @param {numeric} quantity - the quantity required + * @param {numeric} price - the price to sell each unit for + * @param {object} flags - additional order flags + * @param {function} callback - the callback function + * @return {promise or undefined} - omitting the callback returns a promise + */ + sell: function (symbol, quantity, price, flags = {}, callback = false) { + if (!callback) { + return new Promise((resolve, reject) => { + callback = (error, response) => { + if (error) { + reject(error); + } else { + resolve(response); + } + } + order('SELL', symbol, quantity, price, flags, callback); + }) + } else { + order('SELL', symbol, quantity, price, flags, callback); + } + + }, + + /** + * Creates a market buy order + * @param {string} symbol - the symbol to buy + * @param {numeric} quantity - the quantity required + * @param {object} flags - additional buy order flags + * @param {function} callback - the callback function + * @return {promise or undefined} - omitting the callback returns a promise + */ + marketBuy: function (symbol, quantity, flags = { type: 'MARKET' }, callback = false) { + if (typeof flags === 'function') { // Accept callback as third parameter + callback = flags; + flags = { type: 'MARKET' }; + } + if (typeof flags.type === 'undefined') flags.type = 'MARKET'; + if (!callback) { + return new Promise((resolve, reject) => { + callback = (error, response) => { + if (error) { + reject(error); + } else { + resolve(response); + } + } + order('BUY', symbol, quantity, 0, flags, callback); + }) + } else { + order('BUY', symbol, quantity, 0, flags, callback); + } + }, + + /** + * Creates a market sell order + * @param {string} symbol - the symbol to sell + * @param {numeric} quantity - the quantity required + * @param {object} flags - additional sell order flags + * @param {function} callback - the callback function + * @return {promise or undefined} - omitting the callback returns a promise + */ + marketSell: function (symbol, quantity, flags = { type: 'MARKET' }, callback = false) { + if (typeof flags === 'function') { // Accept callback as third parameter + callback = flags; + flags = { type: 'MARKET' }; + } + if (typeof flags.type === 'undefined') flags.type = 'MARKET'; + if (!callback) { + return new Promise((resolve, reject) => { + callback = (error, response) => { + if (error) { + reject(error); + } else { + resolve(response); + } + } + order('SELL', symbol, quantity, 0, flags, callback); + }) + } else { + order('SELL', symbol, quantity, 0, flags, callback); + } + }, + + /** + * Cancels an order + * @param {string} symbol - the symbol to cancel + * @param {string} orderid - the orderid to cancel + * @param {function} callback - the callback function + * @return {promise or undefined} - omitting the callback returns a promise + */ + cancel: function (symbol, orderid, callback = false) { + if (!callback) { + return new Promise((resolve, reject) => { + callback = (error, response) => { + if (error) { + reject(error); + } else { + resolve(response); + } + } + signedRequest(getSpotUrl() + 'v3/order', { symbol: symbol, orderId: orderid }, function (error, data) { + return callback.call(this, error, data, symbol); + }, 'DELETE'); + }) + } else { + signedRequest(getSpotUrl() + 'v3/order', { symbol: symbol, orderId: orderid }, function (error, data) { + return callback.call(this, error, data, symbol); + }, 'DELETE'); + } + }, + + /** + * Gets the status of an order + * @param {string} symbol - the symbol to check + * @param {string} orderid - the orderid to check if !orderid then use flags to search + * @param {function} callback - the callback function + * @param {object} flags - any additional flags + * @return {promise or undefined} - omitting the callback returns a promise + */ + orderStatus: function (symbol, orderid, callback, flags = {}) { + let parameters = Object.assign({ symbol: symbol }, flags); + if (orderid) { + parameters = Object.assign({ orderId: orderid }, parameters) + } + + if (!callback) { + return new Promise((resolve, reject) => { + callback = (error, response) => { + if (error) { + reject(error); + } else { + resolve(response); + } + } + signedRequest(getSpotUrl() + 'v3/order', parameters, function (error, data) { + return callback.call(this, error, data, symbol); + }); + }) + } else { + signedRequest(getSpotUrl() + 'v3/order', parameters, function (error, data) { + return callback.call(this, error, data, symbol); + }); + } + }, + + /** + * Gets open orders + * @param {string} symbol - the symbol to get + * @param {function} callback - the callback function + * @return {promise or undefined} - omitting the callback returns a promise + */ + openOrders: function (symbol, callback) { + let parameters = symbol ? { symbol: symbol } : {}; + if (!callback) { + return new Promise((resolve, reject) => { + callback = (error, response) => { + if (error) { + reject(error); + } else { + resolve(response); + } + } + signedRequest(getSpotUrl() + 'v3/openOrders', parameters, function (error, data) { + return callback.call(this, error, data, symbol); + }); + }) + } else { + signedRequest(getSpotUrl() + 'v3/openOrders', parameters, function (error, data) { + return callback.call(this, error, data, symbol); + }); + } + }, + + /** + * Cancels all orders of a given symbol + * @param {string} symbol - the symbol to cancel all orders for + * @param {function} callback - the callback function + * @return {promise or undefined} - omitting the callback returns a promise + */ + cancelAll: function (symbol, callback = false) { + if (!callback) { + return new Promise((resolve, reject) => { + callback = (error, response) => { + if (error) { + reject(error); + } else { + resolve(response); + } + } + signedRequest(getSpotUrl() + 'v3/openOrders', { symbol }, callback, 'DELETE'); + }) + } else { + signedRequest(getSpotUrl() + 'v3/openOrders', { symbol }, callback, 'DELETE'); + } + }, + + /** + * Cancels all orders of a given symbol + * @param {string} symbol - the symbol to cancel all orders for + * @param {function} callback - the callback function + * @return {promise or undefined} - omitting the callback returns a promise + */ + cancelOrders: function (symbol, callback = false) { + if (!callback) { + return new Promise((resolve, reject) => { + callback = (error, response) => { + if (error) { + reject(error); + } else { + resolve(response); + } + } + signedRequest(getSpotUrl() + 'v3/openOrders', { symbol }, function (error, json) { + if (json.length === 0) { + return callback.call(this, 'No orders present for this symbol', {}, symbol); + } + for (let obj of json) { + let quantity = obj.origQty - obj.executedQty; + Binance.options.log('cancel order: ' + obj.side + ' ' + symbol + ' ' + quantity + ' @ ' + obj.price + ' #' + obj.orderId); + signedRequest(getSpotUrl() + 'v3/order', { symbol, orderId: obj.orderId }, function (error, data) { + return callback.call(this, error, data, symbol); + }, 'DELETE'); + } + }); + }) + } else { + signedRequest(getSpotUrl() + 'v3/openOrders', { symbol: symbol }, function (error, json) { + if (json.length === 0) { + return callback.call(this, 'No orders present for this symbol', {}, symbol); + } + if (Object.keys(json).length === 0) { + return callback.call(this, 'No orders present for this symbol', {}, symbol); + } + for (let obj of json) { + let quantity = obj.origQty - obj.executedQty; + Binance.options.log('cancel order: ' + obj.side + ' ' + symbol + ' ' + quantity + ' @ ' + obj.price + ' #' + obj.orderId); + signedRequest(getSpotUrl() + 'v3/order', { symbol: symbol, orderId: obj.orderId }, function (error, data) { + return callback.call(this, error, data, symbol); + }, 'DELETE'); + } + }); + } + }, + + /** + * Gets all order of a given symbol + * @param {string} symbol - the symbol + * @param {function} callback - the callback function (can also accept options) + * @param {object} options - additional options + * @return {promise or undefined} - omitting the callback returns a promise + */ + allOrders: function (symbol, callback, options = {}) { + let parameters = Object.assign({ symbol }, options); + if (typeof callback == 'object') { // Allow second parameter to be options + options = callback; + callback = false; + } + if (!callback) { + return new Promise((resolve, reject) => { + callback = (error, response) => { + if (error) { + reject(error); + } else { + resolve(response); + } + } + signedRequest(getSpotUrl() + 'v3/allOrders', parameters, function (error, data) { + return callback.call(this, error, data, symbol); + }); + }) + } else { + signedRequest(getSpotUrl() + 'v3/allOrders', parameters, function (error, data) { + return callback.call(this, error, data, symbol); + }); + } + }, + + /** + * Gets the depth information for a given symbol + * @param {string} symbol - the symbol + * @param {function} callback - the callback function + * @param {int} limit - limit the number of returned orders + * @return {promise or undefined} - omitting the callback returns a promise + */ + depth: function (symbol, callback, limit = 100) { + if (!callback) { + return new Promise((resolve, reject) => { + callback = (error, response) => { + if (error) { + reject(error); + } else { + resolve(response); + } + } + publicRequest(getSpotUrl() + 'v3/depth', { symbol: symbol, limit: limit }, function (error, data) { + return callback.call(this, error, depthData(data), symbol); + }); + }) + } else { + publicRequest(getSpotUrl() + 'v3/depth', { symbol: symbol, limit: limit }, function (error, data) { + return callback.call(this, error, depthData(data), symbol); + }); + } + }, + + /** + * Gets the average prices of a given symbol + * @param {string} symbol - the symbol + * @param {function} callback - the callback function + * @return {promise or undefined} - omitting the callback returns a promise + */ + avgPrice: function (symbol, callback = false) { + let opt = { + url: getSpotUrl() + 'v3/avgPrice?symbol=' + symbol, + timeout: Binance.options.recvWindow + }; + if (!callback) { + return new Promise((resolve, reject) => { + request(addProxy(opt), (error, response, body) => { + if (error) return reject(error); + if (response.statusCode !== 200) return reject(response); + let result = {}; + result[symbol] = JSON.parse(response.body).price; + return resolve(result); + }).on('error', reject); + }); + } + request(addProxy(opt), (error, response, body) => { + if (error) return callback(error); + if (response.statusCode !== 200) return callback(response); + let result = {}; + result[symbol] = JSON.parse(response.body).price; + return callback(null, result); + }).on('error', callback); + }, + + /** + * Gets the prices of a given symbol(s) + * @param {string} symbol - the symbol + * @param {function} callback - the callback function + * @return {promise or undefined} - omitting the callback returns a promise + */ + prices: function (symbol, callback = false) { + const params = typeof symbol === 'string' ? '?symbol=' + symbol : ''; + if (typeof symbol === 'function') callback = symbol; // backwards compatibility + + let opt = { + url: getSpotUrl() + 'v3/ticker/price' + params, + timeout: Binance.options.recvWindow + }; + if (!callback) { + return new Promise((resolve, reject) => { + request(addProxy(opt), (error, response, body) => { + if (error) return reject(error); + if (response.statusCode !== 200) return reject(response); + return resolve(priceData(JSON.parse(body))); + }).on('error', reject); + }); + } + request(addProxy(opt), (error, response, body) => { + if (error) return callback(error); + if (response.statusCode !== 200) return callback(response); + return callback(null, priceData(JSON.parse(body))); + }).on('error', callback); + }, + + /** + * Gets the book tickers of given symbol(s) + * @param {string} symbol - the symbol + * @param {function} callback - the callback function + * @return {promise or undefined} - omitting the callback returns a promise + */ + bookTickers: function (symbol, callback) { + const params = typeof symbol === 'string' ? '?symbol=' + symbol : ''; + if (typeof symbol === 'function') callback = symbol; // backwards compatibility + let opt = { + url: getSpotUrl() + 'v3/ticker/bookTicker' + params, + timeout: Binance.options.recvWindow + }; + if (!callback) { + return new Promise((resolve, reject) => { + request(addProxy(opt), function (error, response, body) { + if (error) return reject(error); + if (response.statusCode !== 200) return reject(response); + const result = symbol ? JSON.parse(body) : bookPriceData(JSON.parse(body)); + return resolve(result); + }).on('error', reject); + }); + } + request(addProxy(opt), (error, response, body) => { + if (error) return callback(error); + if (response.statusCode !== 200) return callback(response); + const result = symbol ? JSON.parse(body) : bookPriceData(JSON.parse(body)); + return callback(null, result); + }).on('error', callback); + }, + + /** + * Gets the prevday percentage change + * @param {string} symbol - the symbol or symbols + * @param {function} callback - the callback function + * @return {promise or undefined} - omitting the callback returns a promise + */ + prevDay: function (symbol, callback) { + let input = symbol ? { symbol: symbol } : {}; + if (!callback) { + return new Promise((resolve, reject) => { + callback = (error, response) => { + if (error) { + reject(error); + } else { + resolve(response); + } + } + publicRequest(getSpotUrl() + 'v3/ticker/24hr', input, (error, data) => { + return callback.call(this, error, data, symbol); + }); + }) + } else { + publicRequest(getSpotUrl() + 'v3/ticker/24hr', input, (error, data) => { + return callback.call(this, error, data, symbol); + }); + } + }, + + /** + * Gets the the exchange info + * @param {function} callback - the callback function + * @return {promise or undefined} - omitting the callback returns a promise + */ + exchangeInfo: function (callback) { + if (!callback) { + return new Promise((resolve, reject) => { + callback = (error, response) => { + if (error) { + reject(error); + } else { + resolve(response); + } + } + publicRequest(getSpotUrl() + 'v3/exchangeInfo', {}, callback); + }) + } else { + publicRequest(getSpotUrl() + 'v3/exchangeInfo', {}, callback); + } + }, + + /** + * Gets the dust log for user + * @param {function} callback - the callback function + * @return {promise or undefined} - omitting the callback returns a promise + */ + dustLog: function (callback) { + if (!callback) { + return new Promise((resolve, reject) => { + callback = (error, response) => { + if (error) { + reject(error); + } else { + resolve(response); + } + } + signedRequest(sapi + 'v1/asset/dribblet', {}, callback); + }) + } else { + signedRequest(sapi + 'v1/asset/dribblet', {}, callback); + } + }, + + dustTransfer: function (assets, callback) { + signedRequest(sapi + 'v1/asset/dust', { asset: assets }, callback, 'POST'); + }, + + assetDividendRecord: function (callback, params = {}) { + signedRequest(sapi + 'v1/asset/assetDividend', params, callback); + }, + + /** + * Gets the the system status + * @param {function} callback - the callback function + * @see https://developers.binance.com/docs/wallet/others/system-status + * @return {promise or undefined} - omitting the callback returns a promise + */ + systemStatus: function (callback) { + if (!callback) { + return new Promise((resolve, reject) => { + callback = (error, response) => { + if (error) { + reject(error); + } else { + resolve(response); + } + } + publicRequest(sapi + 'v1/system/status', {}, callback); + }) + } else { + publicRequest(sapi + 'v1/system/status', {}, callback); + } + }, + + /** + * Withdraws asset to given wallet id + * @param {string} asset - the asset symbol + * @param {string} address - the wallet to transfer it to + * @param {number} amount - the amount to transfer + * @param {string} addressTag - and addtional address tag + * @param {function} callback - the callback function + * @param {string} name - the name to save the address as. Set falsy to prevent Binance saving to address book + * @return {promise or undefined} - omitting the callback returns a promise + */ + withdraw: function (asset, address, amount, addressTag = false, callback = false, name = false) { + let params = { asset, address, amount }; + if (name) params.name = name; + if (addressTag) params.addressTag = addressTag; + if (!callback) { + return new Promise((resolve, reject) => { + callback = (error, response) => { + if (error) { + reject(error); + } else { + resolve(response); + } + } + signedRequest(wapi + 'v3/withdraw.html', params, callback, 'POST'); + }) + } else { + signedRequest(wapi + 'v3/withdraw.html', params, callback, 'POST'); + } + }, + + /** + * Get the Withdraws history for a given asset + * @param {function} callback - the callback function + * @param {object} params - supports limit and fromId parameters + * @return {promise or undefined} - omitting the callback returns a promise + */ + withdrawHistory: function (callback, params = {}) { + if (typeof params === 'string') params = { asset: params }; + if (!callback) { + return new Promise((resolve, reject) => { + callback = (error, response) => { + if (error) { + reject(error); + } else { + resolve(response); + } + } + signedRequest(sapi + 'v1/capital/withdraw/history', params, callback); + }) + } else { + signedRequest(sapi + 'v1/capital/withdraw/history', params, callback); + } + }, + + /** + * Get the deposit history + * @param {function} callback - the callback function + * @param {object} params - additional params + * @return {promise or undefined} - omitting the callback returns a promise + */ + depositHistory: function (callback, params = {}) { + if (typeof params === 'string') params = { asset: params }; // Support 'asset' (string) or optional parameters (object) + if (!callback) { + return new Promise((resolve, reject) => { + callback = (error, response) => { + if (error) { + reject(error); + } else { + resolve(response); + } + } + signedRequest(sapi + 'v1/capital/deposit/hisrec', params, callback); + }) + } else { + signedRequest(sapi + 'v1/capital/deposit/hisrec', params, callback); + } + }, + + /** + * Get the deposit address for given asset + * @see https://developers.binance.com/docs/wallet/capital/deposite-address + * @param {string} coin - the asset + * @param {function} callback - the callback function + * @return {promise or undefined} - omitting the callback returns a promise + */ + depositAddress: function (asset, callback) { + if (!callback) { + return new Promise((resolve, reject) => { + callback = (error, response) => { + if (error) { + reject(error); + } else { + resolve(response); + } + } + signedRequest(sapi + 'v1/capital/deposit/address', { coin: asset }, callback); + }) + } else { + signedRequest(sapi + 'v1/capital/deposit/address', { coin: asset }, callback); + } + }, + + /** + * Get the deposit address list for given asset + * @see https://developers.binance.com/docs/wallet/capital/fetch-deposit-address-list-with-network + * @param {string} coin - the asset + * @param {function} callback - the callback function + * @return {promise or undefined} - omitting the callback returns a promise + */ + depositAddressList: function (asset, callback) { + if (!callback) { + return new Promise((resolve, reject) => { + callback = (error, response) => { + if (error) { + reject(error); + } else { + resolve(response); + } + } + signedRequest(sapi + 'v1/capital/deposit/address/list', { coin: asset }, callback); + }) + } else { + signedRequest(sapi + 'v1/capital/deposit/address/list', { coin: asset }, callback); + } + }, + + /** + * Get the account status + * @param {function} callback - the callback function + * @return {promise or undefined} - omitting the callback returns a promise + */ + accountStatus: function (callback) { + if (!callback) { + return new Promise((resolve, reject) => { + callback = (error, response) => { + if (error) { + reject(error); + } else { + resolve(response); + } + } + signedRequest(wapi + 'v3/accountStatus.html', {}, callback); + }) + } else { + signedRequest(wapi + 'v3/accountStatus.html', {}, callback); + } + }, + + /** + * Get the trade fee + * @param {function} callback - the callback function + * @see https://developers.binance.com/docs/wallet/asset/trade-fee + * @param {string} symbol (optional) + * @return {promise or undefined} - omitting the callback returns a promise + */ + tradeFee: function (callback, symbol = false) { + let params = symbol ? { symbol: symbol } : {}; + if (!callback) { + return new Promise((resolve, reject) => { + callback = (error, response) => { + if (error) { + reject(error); + } else { + resolve(response); + } + } + signedRequest(sapi + 'v1/asset/tradeFee', params, callback); + }) + } else { + signedRequest(sapi + 'v1/asset/tradeFee', params, callback); + } + }, + + /** + * Fetch asset detail (minWithdrawAmount, depositStatus, withdrawFee, withdrawStatus, depositTip) + * @param {function} callback - the callback function + * @see https://developers.binance.com/docs/wallet/asset + * @return {promise or undefined} - omitting the callback returns a promise + */ + assetDetail: function (callback) { + if (!callback) { + return new Promise((resolve, reject) => { + callback = (error, response) => { + if (error) { + reject(error); + } else { + resolve(response); + } + } + signedRequest(sapi + 'asset/assetDetail', {}, callback); + }) + } else { + signedRequest(sapi + 'asset/assetDetail', {}, callback); + } + }, + + /** + * Get the account + * @param {function} callback - the callback function + * @return {promise or undefined} - omitting the callback returns a promise + */ + account: function (callback) { + if (!callback) { + return new Promise((resolve, reject) => { + callback = (error, response) => { + if (error) { + reject(error); + } else { + resolve(response); + } + } + signedRequest(getSpotUrl() + 'v3/account', {}, callback); + }) + } else { + signedRequest(getSpotUrl() + 'v3/account', {}, callback); + } + }, + + /** + * Get the balance data + * @param {function} callback - the callback function + * @return {promise or undefined} - omitting the callback returns a promise + */ + balance: function (callback) { + if (!callback) { + return new Promise((resolve, reject) => { + callback = (error, response) => { + if (error) { + reject(error); + } else { + resolve(response); + } + } + signedRequest(getSpotUrl() + 'v3/account', {}, function (error, data) { + callback(error, balanceData(data)); + }); + }) + } else { + signedRequest(getSpotUrl() + 'v3/account', {}, function (error, data) { + callback(error, balanceData(data)); + }); + } + }, + + /** + * Get trades for a given symbol + * @param {string} symbol - the symbol + * @param {function} callback - the callback function + * @param {object} options - additional options + * @return {promise or undefined} - omitting the callback returns a promise + */ + trades: (symbol, callback, options = {}) => { + let parameters = Object.assign({ symbol: symbol }, options); + if (!callback) { + return new Promise((resolve, reject) => { + callback = (error, response) => { + if (error) { + reject(error); + } else { + resolve(response); + } + } + signedRequest(getSpotUrl() + 'v3/myTrades', parameters, function (error, data) { + return callback.call(this, error, data, symbol); + }); + }) + } else { + signedRequest(getSpotUrl() + 'v3/myTrades', parameters, function (error, data) { + return callback.call(this, error, data, symbol); + }); + } + }, + + /** + * Tell api to use the server time to offset time indexes + * @param {function} callback - the callback function + * @return {promise or undefined} - omitting the callback returns a promise + */ + useServerTime: (callback = false) => { + if (!callback) { + return new Promise((resolve, reject) => { + callback = (error, response) => { + if (error) { + reject(error); + } else { + resolve(response); + } + } + publicRequest(getSpotUrl() + 'v3/time', {}, function (error, response) { + if (!error) { + Binance.info.timeOffset = response.serverTime - new Date().getTime(); + //Binance.options.log("server time set: ", response.serverTime, Binance.info.timeOffset); + } + callback(error, response); + }); + }) + } else { + publicRequest(getSpotUrl() + 'v3/time', {}, function (error, response) { + if (!error) { + Binance.info.timeOffset = response.serverTime - new Date().getTime(); + //Binance.options.log("server time set: ", response.serverTime, Binance.info.timeOffset); + } + callback(error, response); + }); + } + }, + + /** + * Get Binance server time + * @param {function} callback - the callback function + * @return {promise or undefined} - omitting the callback returns a promise + */ + time: function (callback) { + if (!callback) { + return new Promise((resolve, reject) => { + callback = (error, response) => { + if (error) { + reject(error); + } else { + resolve(response); + } + } + publicRequest(getSpotUrl() + 'v3/time', {}, callback); + }) + } else { + publicRequest(getSpotUrl() + 'v3/time', {}, callback); + } + }, + + /** + * Ping binance + * @param {function} callback - the callback function + * @return {promise or undefined} - omitting the callback returns a promise + */ + ping: function (callback) { + if (!callback) { + return new Promise((resolve, reject) => { + callback = (error, response) => { + if (error) { + reject(error); + } else { + resolve(response); + } + } + publicRequest(getSpotUrl() + 'v3/ping', {}, callback); + }) + } else { + publicRequest(getSpotUrl() + 'v3/ping', {}, callback); + } + }, + + /** + * Get agg trades for given symbol + * @param {string} symbol - the symbol + * @param {object} options - additional optoins + * @param {function} callback - the callback function + * @return {promise or undefined} - omitting the callback returns a promise + */ + aggTrades: function (symbol, options = {}, callback = false) { //fromId startTime endTime limit + let parameters = Object.assign({ symbol }, options); + if (!callback) { + return new Promise((resolve, reject) => { + callback = (error, response) => { + if (error) { + reject(error); + } else { + resolve(response); + } + } + publicRequest(getSpotUrl() + 'v3/aggTrades', parameters, callback); + }) + } else { + publicRequest(getSpotUrl() + 'v3/aggTrades', parameters, callback); + } + }, + + /** + * Get the recent trades + * @param {string} symbol - the symbol + * @param {function} callback - the callback function + * @param {int} limit - limit the number of items returned + * @return {promise or undefined} - omitting the callback returns a promise + */ + recentTrades: function (symbol, callback, limit = 500) { + if (!callback) { + return new Promise((resolve, reject) => { + callback = (error, response) => { + if (error) { + reject(error); + } else { + resolve(response); + } + } + marketRequest(getSpotUrl() + 'v1/trades', { symbol: symbol, limit: limit }, callback); + }) + } else { + marketRequest(getSpotUrl() + 'v1/trades', { symbol: symbol, limit: limit }, callback); + } + }, + + /** + * Get the historical trade info + * @param {string} symbol - the symbol + * @param {function} callback - the callback function + * @param {int} limit - limit the number of items returned + * @param {int} fromId - from this id + * @return {promise or undefined} - omitting the callback returns a promise + */ + historicalTrades: function (symbol, callback, limit = 500, fromId = false) { + let parameters = { symbol: symbol, limit: limit }; + if (fromId) parameters.fromId = fromId; + if (!callback) { + return new Promise((resolve, reject) => { + callback = (error, response) => { + if (error) { + reject(error); + } else { + resolve(response); + } + } + marketRequest(getSpotUrl() + 'v3/historicalTrades', parameters, callback); + }) + } else { + marketRequest(getSpotUrl() + 'v3/historicalTrades', parameters, callback); + } + }, + + /** + * Convert chart data to highstock array [timestamp,open,high,low,close] + * @param {object} chart - the chart + * @param {boolean} include_volume - to include the volume or not + * @return {array} - an array + */ + highstock: function (chart, include_volume = false) { + let array = []; + for (let timestamp in chart) { + let obj = chart[timestamp]; + let line = [ + Number(timestamp), + parseFloat(obj.open), + parseFloat(obj.high), + parseFloat(obj.low), + parseFloat(obj.close) + ]; + if (include_volume) line.push(parseFloat(obj.volume)); + array.push(line); + } + return array; + }, + + /** + * Populates OHLC information + * @param {object} chart - the chart + * @return {object} - object with candle information + */ + ohlc: function (chart) { + let open = [], high = [], low = [], close = [], volume = []; + for (let timestamp in chart) { //Binance.ohlc[symbol][interval] + let obj = chart[timestamp]; + open.push(parseFloat(obj.open)); + high.push(parseFloat(obj.high)); + low.push(parseFloat(obj.low)); + close.push(parseFloat(obj.close)); + volume.push(parseFloat(obj.volume)); + } + return { open: open, high: high, low: low, close: close, volume: volume }; + }, + + /** + * Gets the candles information for a given symbol + * intervals: 1m,3m,5m,15m,30m,1h,2h,4h,6h,8h,12h,1d,3d,1w,1M + * @param {string} symbol - the symbol + * @param {function} interval - the callback function + * @param {function} callback - the callback function + * @param {object} options - additional options + * @return {promise or undefined} - omitting the callback returns a promise + */ + candlesticks: function (symbol, interval = '5m', callback = false, options = { limit: 500 }) { + let params = Object.assign({ symbol: symbol, interval: interval }, options); + if (!callback) { + return new Promise((resolve, reject) => { + callback = (error, response) => { + if (error) { + reject(error); + } else { + resolve(response); + } + } + publicRequest(getSpotUrl() + 'v3/klines', params, function (error, data) { + return callback.call(this, error, data, symbol); + }); + }) + } else { + publicRequest(getSpotUrl() + 'v3/klines', params, function (error, data) { + return callback.call(this, error, data, symbol); + }); + } + }, + + /** + * Queries the public api + * @param {string} url - the public api endpoint + * @param {object} data - the data to send + * @param {function} callback - the callback function + * @param {string} method - the http method + * @return {promise or undefined} - omitting the callback returns a promise + */ + publicRequest: function (url, data, callback, method = 'GET') { + if (!callback) { + return new Promise((resolve, reject) => { + callback = (error, response) => { + if (error) { + reject(error); + } else { + resolve(response); + } + } + publicRequest(url, data, callback, method); + }) + } else { + publicRequest(url, data, callback, method); + } + }, + + /** + * Queries the futures API by default + * @param {string} url - the signed api endpoint + * @param {object} data - the data to send + * @param {object} flags - type of request, authentication method and endpoint url + */ + promiseRequest: function (url, data = {}, flags = {}) { + return promiseRequest(url, data, flags); + }, + + /** + * Queries the signed api + * @param {string} url - the signed api endpoint + * @param {object} data - the data to send + * @param {function} callback - the callback function + * @param {string} method - the http method + * @param {boolean} noDataInSignature - Prevents data from being added to signature + * @return {promise or undefined} - omitting the callback returns a promise + */ + signedRequest: function (url, data, callback, method = 'GET', noDataInSignature = false) { + if (!callback) { + return new Promise((resolve, reject) => { + callback = (error, response) => { + if (error) { + reject(error); + } else { + resolve(response); + } + } + signedRequest(url, data, callback, method, noDataInSignature); + }) + } else { + signedRequest(url, data, callback, method, noDataInSignature); + } + }, + + /** + * Gets the market asset of given symbol + * @param {string} symbol - the public api endpoint + * @return {undefined} + */ + getMarket: function (symbol) { + if (symbol.endsWith('BTC')) return 'BTC'; + else if (symbol.endsWith('ETH')) return 'ETH'; + else if (symbol.endsWith('BNB')) return 'BNB'; + else if (symbol.endsWith('XRP')) return 'XRP'; + else if (symbol.endsWith('PAX')) return 'PAX'; + else if (symbol.endsWith('USDT')) return 'USDT'; + else if (symbol.endsWith('USDC')) return 'USDC'; + else if (symbol.endsWith('USDS')) return 'USDS'; + else if (symbol.endsWith('TUSD')) return 'TUSD'; + }, + + /** + * Get the account binance lending information + * @param {function} callback - the callback function + * @return {promise or undefined} - omitting the callback returns a promise + */ + lending: async (params = {}) => { + return promiseRequest('v1/lending/union/account', params, { base: sapi, type: 'SIGNED' }); + }, + + //** Futures methods */ + futuresPing: async (params = {}) => { + return promiseRequest('v1/ping', params, { base: fapi }); + }, + + futuresTime: async (params = {}) => { + return promiseRequest('v1/time', params, { base: fapi }).then(r => r.serverTime); + }, + + futuresExchangeInfo: async () => { + return promiseRequest('v1/exchangeInfo', {}, { base: fapi }); + }, + + futuresPrices: async (params = {}) => { + let data = await promiseRequest('v2/ticker/price', params, { base: fapi }); + return Array.isArray(data) ? data.reduce((out, i) => ((out[i.symbol] = i.price), out), {}) : data; + }, + + futuresDaily: async (symbol = false, params = {}) => { + if (symbol) params.symbol = symbol; + let data = await promiseRequest('v1/ticker/24hr', params, { base: fapi }); + return symbol ? data : data.reduce((out, i) => ((out[i.symbol] = i), out), {}); + }, + + futuresOpenInterest: async (symbol) => { + return promiseRequest('v1/openInterest', { symbol }, { base: fapi }).then(r => r.openInterest); + }, + + futuresCandles: async (symbol, interval = "30m", params = {}) => { + params.symbol = symbol; + params.interval = interval; + return promiseRequest('v1/klines', params, { base: fapi }); + }, + + futuresMarkPrice: async (symbol = false) => { + return promiseRequest('v1/premiumIndex', symbol ? { symbol } : {}, { base: fapi }); + }, + + futuresTrades: async (symbol, params = {}) => { + params.symbol = symbol; + return promiseRequest('v1/trades', params, { base: fapi }); + }, + + futuresHistoricalTrades: async (symbol, params = {}) => { + params.symbol = symbol; + return promiseRequest('v1/historicalTrades', params, { base: fapi, type: 'MARKET_DATA' }); + }, + + futuresAggTrades: async (symbol, params = {}) => { + params.symbol = symbol; + return promiseRequest('v1/aggTrades', params, { base: fapi }); + }, + + futuresForceOrders: async (params = {}) => { + return promiseRequest('v1/forceOrders', params, { base: fapi, type: 'SIGNED' }); + }, + + futuresDeleverageQuantile: async (params = {}) => { + return promiseRequest('v1/adlQuantile', params, { base: fapi, type: 'SIGNED' }); + }, + + futuresUserTrades: async (symbol, params = {}) => { + params.symbol = symbol; + return promiseRequest('v1/userTrades', params, { base: fapi, type: 'SIGNED' }); + }, + + futuresGetDataStream: async (params = {}) => { + //A User Data Stream listenKey is valid for 60 minutes after creation. setInterval + return promiseRequest('v1/listenKey', params, { base: fapi, type: 'SIGNED', method: 'POST' }); + }, + + futuresKeepDataStream: async (params = {}) => { + return promiseRequest('v1/listenKey', params, { base: fapi, type: 'SIGNED', method: 'PUT' }); + }, + + futuresCloseDataStream: async (params = {}) => { + return promiseRequest('v1/listenKey', params, { base: fapi, type: 'SIGNED', method: 'DELETE' }); + }, + + futuresLiquidationOrders: async (symbol = false, params = {}) => { + if (symbol) params.symbol = symbol; + return promiseRequest('v1/allForceOrders', params, { base: fapi }); + }, + + /** + * Get the account binance lending information + * @param {function} callback - the callback function + * @param {string} symbol - position symbol, optional + * @see https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/Position-Information-V3 + * @see https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/Position-Information-V2 + * @return {promise or undefined} - omitting the callback returns a promise + */ + futuresPositionRisk: async (params = {}, useV2 = false) => { + const endpoint = useV2 ? 'v2/positionRisk' : 'v3/positionRisk' + return promiseRequest(endpoint, params, { base: fapi, type: 'SIGNED' }); + }, + + futuresPositionRiskV2: async (params = {}) => { + return futuresPositionRisk(params, true) + }, + + futuresFundingRate: async (symbol, params = {}) => { + params.symbol = symbol; + return promiseRequest('v1/fundingRate', params, { base: fapi }); + }, + + futuresLeverageBracket: async (symbol = false, params = {}) => { + if (symbol) params.symbol = symbol; + return promiseRequest('v1/leverageBracket', params, { base: fapi, type: 'USER_DATA' }); + }, + + futuresTradingStatus: async (symbol = false, params = {}) => { + if (symbol) params.symbol = symbol; + return promiseRequest('v1/apiTradingStatus', params, { base: fapi, type: 'USER_DATA' }); + }, + + futuresCommissionRate: async (symbol = false, params = {}) => { + if (symbol) params.symbol = symbol; + return promiseRequest('v1/commissionRate', params, { base: fapi, type: 'USER_DATA' }); + }, + + // leverage 1 to 125 + futuresLeverage: async (symbol, leverage, params = {}) => { + params.symbol = symbol; + params.leverage = leverage; + return promiseRequest('v1/leverage', params, { base: fapi, method: 'POST', type: 'SIGNED' }); + }, + + // ISOLATED, CROSSED + futuresMarginType: async (symbol, marginType, params = {}) => { + params.symbol = symbol; + params.marginType = marginType; + return promiseRequest('v1/marginType', params, { base: fapi, method: 'POST', type: 'SIGNED' }); + }, + + // type: 1: Add postion margin,2: Reduce postion margin + futuresPositionMargin: async (symbol, amount, type = 1, params = {}) => { + params.symbol = symbol; + params.amount = amount; + params.type = type; + return promiseRequest('v1/positionMargin', params, { base: fapi, method: 'POST', type: 'SIGNED' }); + }, + + futuresPositionMarginHistory: async (symbol, params = {}) => { + params.symbol = symbol; + return promiseRequest('v1/positionMargin/history', params, { base: fapi, type: 'SIGNED' }); + }, + + futuresIncome: async (params = {}) => { + return promiseRequest('v1/income', params, { base: fapi, type: 'SIGNED' }); + }, + + futuresBalance: async (params = {}) => { + return promiseRequest('v2/balance', params, { base: fapi, type: 'SIGNED' }); + }, + + futuresAccount: async (params = {}) => { + return promiseRequest('v3/account', params, { base: fapi, type: 'SIGNED' }); + }, + + futuresDepth: async (symbol, params = {}) => { + params.symbol = symbol; + return promiseRequest('v1/depth', params, { base: fapi }); + }, + + futuresQuote: async (symbol = false, params = {}) => { + if (symbol) params.symbol = symbol; + //let data = await promiseRequest( 'v1/ticker/bookTicker', params, {base:fapi} ); + //return data.reduce((out, i) => ((out[i.symbol] = i), out), {}), + let data = await promiseRequest('v1/ticker/bookTicker', params, { base: fapi }); + return symbol ? data : data.reduce((out, i) => ((out[i.symbol] = i), out), {}); + }, + + futuresBuy: async (symbol, quantity, price, params = {}) => { + return futuresOrder('BUY', symbol, quantity, price, params); + }, + + futuresSell: async (symbol, quantity, price, params = {}) => { + return futuresOrder('SELL', symbol, quantity, price, params); + }, + + futuresMarketBuy: async (symbol, quantity, params = {}) => { + return futuresOrder('BUY', symbol, quantity, false, params); + }, + + futuresMarketSell: async (symbol, quantity, params = {}) => { + return futuresOrder('SELL', symbol, quantity, false, params); + }, + + futuresMultipleOrders: async (orders = [{}]) => { + for (let i = 0; i < orders.length; i++) { + if (!orders[i].newClientOrderId) { + orders[i].newClientOrderId = CONTRACT_PREFIX + uuid22(); + } + } + let params = { batchOrders: JSON.stringify(orders) }; + return promiseRequest('v1/batchOrders', params, { base: fapi, type: 'TRADE', method: 'POST' }); + }, + + futuresOrder, // side symbol quantity [price] [params] + + futuresOrderStatus: async (symbol, params = {}) => { // Either orderId or origClientOrderId must be sent + params.symbol = symbol; + return promiseRequest('v1/order', params, { base: fapi, type: 'SIGNED' }); + }, + + futuresCancel: async (symbol, params = {}) => { // Either orderId or origClientOrderId must be sent + params.symbol = symbol; + return promiseRequest('v1/order', params, { base: fapi, type: 'SIGNED', method: 'DELETE' }); + }, + + futuresCancelAll: async (symbol, params = {}) => { + params.symbol = symbol; + return promiseRequest('v1/allOpenOrders', params, { base: fapi, type: 'SIGNED', method: 'DELETE' }); + }, + + futuresCountdownCancelAll: async (symbol, countdownTime = 0, params = {}) => { + params.symbol = symbol; + params.countdownTime = countdownTime; + return promiseRequest('v1/countdownCancelAll', params, { base: fapi, type: 'SIGNED', method: 'POST' }); + }, + + futuresOpenOrders: async (symbol = false, params = {}) => { + if (symbol) params.symbol = symbol; + return promiseRequest('v1/openOrders', params, { base: fapi, type: 'SIGNED' }); + }, + + futuresAllOrders: async (symbol = false, params = {}) => { // Get all account orders; active, canceled, or filled. + if (symbol) params.symbol = symbol; + return promiseRequest('v1/allOrders', params, { base: fapi, type: 'SIGNED' }); + }, + + futuresPositionSideDual: async (params = {}) => { + return promiseRequest('v1/positionSide/dual', params, { base: fapi, type: 'SIGNED' }); + }, + + futuresChangePositionSideDual: async (dualSidePosition, params = {}) => { + params.dualSidePosition = dualSidePosition; + return promiseRequest('v1/positionSide/dual', params, { base: fapi, type: 'SIGNED', method: 'POST' }); + }, + futuresTransferAsset: async (asset, amount, type) => { + let params = Object.assign({ asset, amount, type }); + return promiseRequest('v1/futures/transfer', params, { base: sapi, type: 'SIGNED', method: 'POST' }); + }, + + futuresHistDataId: async (symbol = false, params = {}) => { + if (symbol) params.symbol = symbol; + return promiseRequest('v1/futuresHistDataId', params, { base: sapi, type: 'SIGNED', method: 'POST' }) + }, + + futuresDownloadLink: async (downloadId) => { + return promiseRequest('v1/downloadLink', { downloadId }, { base: sapi, type: 'SIGNED' }) + }, + + // futures websockets support: ticker bookTicker miniTicker aggTrade markPrice + /* TODO: https://binance-docs.github.io/apidocs/futures/en/#change-log + Cancel multiple orders DELETE /fapi/v1/batchOrders + New Future Account Transfer POST https://api.binance.com/sapi/v1/futures/transfer + Get Postion Margin Change History (TRADE) + + wss://fstream.binance.com/ws/ + Diff. Book Depth Streams (250ms, 100ms, or realtime): @depth OR @depth@100ms OR @depth@0ms + Partial Book Depth Streams (5, 10, 20): @depth OR @depth@100ms + All Market Liquidation Order Streams: !forceOrder@arr + Liquidation Order Streams for specific symbol: @forceOrder + Chart data (250ms): @kline_ + SUBSCRIBE, UNSUBSCRIBE, LIST_SUBSCRIPTIONS, SET_PROPERTY, GET_PROPERTY + Live Subscribing/Unsubscribing to streams: requires sending futures subscription id when connecting + futuresSubscriptions { "method": "LIST_SUBSCRIPTIONS", "id": 1 } + futuresUnsubscribe { "method": "UNSUBSCRIBE", "params": [ "btcusdt@depth" ], "id": 1 } + futures depthCache + */ + + /* + const futuresOrder = (side, symbol, quantity, price = 0, flags = {}, callback = false) => { + let opt = { + symbol: symbol, + side: side, + type: 'LIMIT', + quantity: quantity + }; + if (typeof flags.type !== 'undefined') opt.type = flags.type; + if (opt.type.includes('LIMIT')) { + opt.price = price; + opt.timeInForce = 'GTC'; + } + if (typeof flags.timeInForce !== 'undefined') opt.timeInForce = flags.timeInForce; + signedRequest(`${fapi}v1/order`, opt, function (error, response) { + if (!response) { + if (callback) return callback(error, response); + else return Binance.options.log('futuresOrder error:', error); + } + if (callback) return callback(error, response); + else return Binance.options.log(`futuresOrder ${side} (${symbol},${quantity},${price})`, response); + }, 'POST'); + };*/ + + //** Delivery methods */ + deliveryPing: async (params = {}) => { + return promiseRequest('v1/ping', params, { base: dapi }); + }, + + deliveryTime: async (params = {}) => { + return promiseRequest('v1/time', params, { base: dapi }).then(r => r.serverTime); + }, + + deliveryExchangeInfo: async () => { + return promiseRequest('v1/exchangeInfo', {}, { base: dapi }); + }, + + deliveryPrices: async (params = {}) => { + let data = await promiseRequest('v1/ticker/price', params, { base: dapi }); + return data.reduce((out, i) => ((out[i.symbol] = i.price), out), {}); + }, + + deliveryDaily: async (symbol = false, params = {}) => { + if (symbol) params.symbol = symbol; + let data = await promiseRequest('v1/ticker/24hr', params, { base: dapi }); + return symbol ? data : data.reduce((out, i) => ((out[i.symbol] = i), out), {}); + }, + + deliveryOpenInterest: async (symbol) => { + return promiseRequest('v1/openInterest', { symbol }, { base: dapi }).then(r => r.openInterest); + }, + + deliveryCandles: async (symbol, interval = "30m", params = {}) => { + params.symbol = symbol; + params.interval = interval; + return promiseRequest('v1/klines', params, { base: dapi }); + }, + + deliveryContinuousKlines: async (pair, contractType = "CURRENT_QUARTER", interval = "30m", params = {}) => { + params.pair = pair; + params.interval = interval; + params.contractType = contractType; + return promiseRequest('v1/continuousKlines', params, { base: dapi }); + }, + + deliveryIndexKlines: async (pair, interval = "30m", params = {}) => { + params.pair = pair; + params.interval = interval; + return promiseRequest('v1/indexPriceKlines', params, { base: dapi }); + }, + + deliveryMarkPriceKlines: async (symbol, interval = "30m", params = {}) => { + params.symbol = symbol; + params.interval = interval; + return promiseRequest('v1/markPriceKlines', params, { base: dapi }); + }, + + deliveryMarkPrice: async (symbol = false) => { + return promiseRequest('v1/premiumIndex', symbol ? { symbol } : {}, { base: dapi }); + }, + + deliveryTrades: async (symbol, params = {}) => { + params.symbol = symbol; + return promiseRequest('v1/trades', params, { base: dapi }); + }, + + deliveryHistoricalTrades: async (symbol, params = {}) => { + params.symbol = symbol; + return promiseRequest('v1/historicalTrades', params, { base: dapi, type: 'MARKET_DATA' }); + }, + + deliveryAggTrades: async (symbol, params = {}) => { + params.symbol = symbol; + return promiseRequest('v1/aggTrades', params, { base: dapi }); + }, + + deliveryUserTrades: async (symbol, params = {}) => { + params.symbol = symbol; + return promiseRequest('v1/userTrades', params, { base: dapi, type: 'SIGNED' }); + }, + + deliveryCommissionRate: async (symbol, params = {}) => { + if (symbol) params.symbol = symbol; + return promiseRequest('v1/commissionRate', params, { base: dapi, type: 'SIGNED' }); + }, + + deliveryGetDataStream: async (params = {}) => { + //A User Data Stream listenKey is valid for 60 minutes after creation. setInterval + return promiseRequest('v1/listenKey', params, { base: dapi, type: 'SIGNED', method: 'POST' }); + }, + + deliveryKeepDataStream: async (params = {}) => { + return promiseRequest('v1/listenKey', params, { base: dapi, type: 'SIGNED', method: 'PUT' }); + }, + + deliveryCloseDataStream: async (params = {}) => { + return promiseRequest('v1/listenKey', params, { base: dapi, type: 'SIGNED', method: 'DELETE' }); + }, + + deliveryLiquidationOrders: async (symbol = false, params = {}) => { + if (symbol) params.symbol = symbol; + return promiseRequest('v1/allForceOrders', params, { base: dapi }); + }, + + deliveryPositionRisk: async (params = {}) => { + return promiseRequest('v1/positionRisk', params, { base: dapi, type: 'SIGNED' }); + }, + + deliveryLeverageBracket: async (symbol = false, params = {}) => { + if (symbol) params.symbol = symbol; + return promiseRequest('v1/leverageBracket', params, { base: dapi, type: 'USER_DATA' }); + }, + + deliveryLeverageBracketSymbols: async (symbol = false, params = {}) => { + if (symbol) params.symbol = symbol; + return promiseRequest('v2/leverageBracket', params, { base: dapi, type: 'USER_DATA' }); + }, + + // leverage 1 to 125 + deliveryLeverage: async (symbol, leverage, params = {}) => { + params.symbol = symbol; + params.leverage = leverage; + return promiseRequest('v1/leverage', params, { base: dapi, method: 'POST', type: 'SIGNED' }); + }, + + // ISOLATED, CROSSED + deliveryMarginType: async (symbol, marginType, params = {}) => { + params.symbol = symbol; + params.marginType = marginType; + return promiseRequest('v1/marginType', params, { base: dapi, method: 'POST', type: 'SIGNED' }); + }, + + // type: 1: Add postion margin,2: Reduce postion margin + deliveryPositionMargin: async (symbol, amount, type = 1, params = {}) => { + params.symbol = symbol; + params.amount = amount; + params.type = type; + return promiseRequest('v1/positionMargin', params, { base: dapi, method: 'POST', type: 'SIGNED' }); + }, + + deliveryPositionMarginHistory: async (symbol, params = {}) => { + params.symbol = symbol; + return promiseRequest('v1/positionMargin/history', params, { base: dapi, type: 'SIGNED' }); + }, + + deliveryIncome: async (params = {}) => { + return promiseRequest('v1/income', params, { base: dapi, type: 'SIGNED' }); + }, + + deliveryBalance: async (params = {}) => { + return promiseRequest('v1/balance', params, { base: dapi, type: 'SIGNED' }); + }, + + deliveryAccount: async (params = {}) => { + return promiseRequest('v1/account', params, { base: dapi, type: 'SIGNED' }); + }, + + deliveryDepth: async (symbol, params = {}) => { + params.symbol = symbol; + return promiseRequest('v1/depth', params, { base: dapi }); + }, + + deliveryQuote: async (symbol = false, params = {}) => { + if (symbol) params.symbol = symbol; + //let data = await promiseRequest( 'v1/ticker/bookTicker', params, {base:dapi} ); + //return data.reduce((out, i) => ((out[i.symbol] = i), out), {}), + let data = await promiseRequest('v1/ticker/bookTicker', params, { base: dapi }); + return symbol ? data : data.reduce((out, i) => ((out[i.symbol] = i), out), {}); + }, + + deliveryBuy: async (symbol, quantity, price, params = {}) => { + return deliveryOrder('BUY', symbol, quantity, price, params); + }, + + deliverySell: async (symbol, quantity, price, params = {}) => { + return deliveryOrder('SELL', symbol, quantity, price, params); + }, + + deliveryMarketBuy: async (symbol, quantity, params = {}) => { + return deliveryOrder('BUY', symbol, quantity, false, params); + }, + + deliveryMarketSell: async (symbol, quantity, params = {}) => { + return deliveryOrder('SELL', symbol, quantity, false, params); + }, + + deliveryOrder, // side symbol quantity [price] [params] + + deliveryOrderStatus: async (symbol, params = {}) => { // Either orderId or origClientOrderId must be sent + params.symbol = symbol; + return promiseRequest('v1/order', params, { base: dapi, type: 'SIGNED' }); + }, + + deliveryCancel: async (symbol, params = {}) => { // Either orderId or origClientOrderId must be sent + params.symbol = symbol; + return promiseRequest('v1/order', params, { base: dapi, type: 'SIGNED', method: 'DELETE' }); + }, + + deliveryCancelAll: async (symbol, params = {}) => { + params.symbol = symbol; + return promiseRequest('v1/allOpenOrders', params, { base: dapi, type: 'SIGNED', method: 'DELETE' }); + }, + + deliveryCountdownCancelAll: async (symbol, countdownTime = 0, params = {}) => { + params.symbol = symbol; + params.countdownTime = countdownTime; + return promiseRequest('v1/countdownCancelAll', params, { base: dapi, type: 'SIGNED', method: 'POST' }); + }, + + deliveryOpenOrders: async (symbol = false, params = {}) => { + if (symbol) params.symbol = symbol; + return promiseRequest('v1/openOrders', params, { base: dapi, type: 'SIGNED' }); + }, + + deliveryAllOrders: async (symbol = false, params = {}) => { // Get all account orders; active, canceled, or filled. + if (symbol) params.symbol = symbol; + return promiseRequest('v1/allOrders', params, { base: dapi, type: 'SIGNED' }); + }, + + deliveryPositionSideDual: async (params = {}) => { + return promiseRequest('v1/positionSide/dual', params, { base: dapi, type: 'SIGNED' }); + }, + + deliveryChangePositionSideDual: async (dualSidePosition, params = {}) => { + params.dualSidePosition = dualSidePosition; + return promiseRequest('v1/positionSide/dual', params, { base: dapi, type: 'SIGNED', method: 'POST' }); + }, + + //** Margin methods */ + /** + * Creates an order + * @param {string} side - BUY or SELL + * @param {string} symbol - the symbol to buy + * @param {numeric} quantity - the quantity required + * @param {numeric} price - the price to pay for each unit + * @param {object} flags - additional buy order flags + * @param {function} callback - the callback function + * @param {string} isIsolated - the isolate margin option + * @return {undefined} + */ + mgOrder: function (side, symbol, quantity, price, flags = {}, callback = false, isIsolated = 'FALSE') { + marginOrder(side, symbol, quantity, price, { ...flags, isIsolated }, callback); + }, + + /** + * Creates a buy order + * @param {string} symbol - the symbol to buy + * @param {numeric} quantity - the quantity required + * @param {numeric} price - the price to pay for each unit + * @param {object} flags - additional buy order flags + * @param {function} callback - the callback function + * @param {string} isIsolated - the isolate margin option + * @return {undefined} + */ + mgBuy: function (symbol, quantity, price, flags = {}, callback = false, isIsolated = 'FALSE') { + marginOrder('BUY', symbol, quantity, price, { ...flags, isIsolated }, callback); + }, + + /** + * Creates a sell order + * @param {string} symbol - the symbol to sell + * @param {numeric} quantity - the quantity required + * @param {numeric} price - the price to sell each unit for + * @param {object} flags - additional order flags + * @param {function} callback - the callback function + * @param {string} isIsolated - the isolate margin option + * @return {undefined} + */ + mgSell: function (symbol, quantity, price, flags = {}, callback = false, isIsolated = 'FALSE') { + marginOrder('SELL', symbol, quantity, price, { ...flags, isIsolated }, callback); + }, + + /** + * Creates a market buy order + * @param {string} symbol - the symbol to buy + * @param {numeric} quantity - the quantity required + * @param {object} flags - additional buy order flags + * @param {function} callback - the callback function + * @param {string} isIsolated - the isolate margin option + * @return {undefined} + */ + mgMarketBuy: function (symbol, quantity, flags = { type: 'MARKET' }, callback = false, isIsolated = 'FALSE') { + if (typeof flags === 'function') { // Accept callback as third parameter + callback = flags; + flags = { type: 'MARKET' }; + } + if (typeof flags.type === 'undefined') flags.type = 'MARKET'; + marginOrder('BUY', symbol, quantity, 0, { ...flags, isIsolated }, callback); + }, + + /** + * Creates a market sell order + * @param {string} symbol - the symbol to sell + * @param {numeric} quantity - the quantity required + * @param {object} flags - additional sell order flags + * @param {function} callback - the callback function + * @param {string} isIsolated - the isolate margin option + * @return {undefined} + */ + mgMarketSell: function (symbol, quantity, flags = { type: 'MARKET' }, callback = false, isIsolated = 'FALSE') { + if (typeof flags === 'function') { // Accept callback as third parameter + callback = flags; + flags = { type: 'MARKET' }; + } + if (typeof flags.type === 'undefined') flags.type = 'MARKET'; + marginOrder('SELL', symbol, quantity, 0, { ...flags, isIsolated }, callback); + }, + + /** + * Cancels an order + * @param {string} symbol - the symbol to cancel + * @param {string} orderid - the orderid to cancel + * @param {function} callback - the callback function + * @return {undefined} + */ + mgCancel: function (symbol, orderid, callback = false, isIsolated = 'FALSE') { + signedRequest(sapi + 'v1/margin/order', { symbol: symbol, orderId: orderid, isIsolated }, function (error, data) { + if (callback) return callback.call(this, error, data, symbol); + }, 'DELETE'); + }, + + /** + * Gets all order of a given symbol + * @param {string} symbol - the symbol + * @param {function} callback - the callback function + * @param {object} options - additional options + * @return {promise or undefined} - omitting the callback returns a promise + */ + mgAllOrders: function (symbol, callback, options = {}) { + let parameters = Object.assign({ symbol: symbol }, options); + if (!callback) { + return new Promise((resolve, reject) => { + callback = (error, response) => { + if (error) { + reject(error); + } else { + resolve(response); + } + } + signedRequest(sapi + 'v1/margin/allOrders', parameters, function (error, data) { + return callback.call(this, error, data, symbol); + }); + }) + } else { + signedRequest(sapi + 'v1/margin/allOrders', parameters, function (error, data) { + return callback.call(this, error, data, symbol); + }); + } + }, + + /** + * Gets the status of an order + * @param {string} symbol - the symbol to check + * @param {string} orderid - the orderid to check + * @param {function} callback - the callback function + * @param {object} flags - any additional flags + * @return {undefined} + */ + mgOrderStatus: function (symbol, orderid, callback, flags = {}) { + let parameters = Object.assign({ symbol: symbol, orderId: orderid }, flags); + signedRequest(sapi + 'v1/margin/order', parameters, function (error, data) { + if (callback) return callback.call(this, error, data, symbol); + }); + }, + + /** + * Gets open orders + * @param {string} symbol - the symbol to get + * @param {function} callback - the callback function + * @return {undefined} + */ + mgOpenOrders: function (symbol, callback) { + let parameters = symbol ? { symbol: symbol } : {}; + signedRequest(sapi + 'v1/margin/openOrders', parameters, function (error, data) { + return callback.call(this, error, data, symbol); + }); + }, + + /** + * Cancels all order of a given symbol + * @param {string} symbol - the symbol to cancel all orders for + * @param {function} callback - the callback function + * @return {undefined} + */ + mgCancelOrders: function (symbol, callback = false) { + signedRequest(sapi + 'v1/margin/openOrders', { symbol: symbol }, function (error, json) { + if (json.length === 0) { + if (callback) return callback.call(this, 'No orders present for this symbol', {}, symbol); + } + for (let obj of json) { + let quantity = obj.origQty - obj.executedQty; + Binance.options.log('cancel order: ' + obj.side + ' ' + symbol + ' ' + quantity + ' @ ' + obj.price + ' #' + obj.orderId); + signedRequest(sapi + 'v1/margin/order', { symbol: symbol, orderId: obj.orderId }, function (error, data) { + if (callback) return callback.call(this, error, data, symbol); + }, 'DELETE'); + } + }); + }, + + /** + * Transfer from main account to margin account + * @param {string} asset - the asset + * @param {number} amount - the asset + * @param {function} callback - the callback function + * @param {object} options - additional options + * @return {undefined} + */ + mgTransferMainToMargin: function (asset, amount, callback) { + let parameters = Object.assign({ asset: asset, amount: amount, type: 1 }); + signedRequest(sapi + 'v1/margin/transfer', parameters, function (error, data) { + if (callback) return callback(error, data); + }, 'POST'); + }, + + /** + * Transfer from margin account to main account + * @param {string} asset - the asset + * @param {number} amount - the asset + * @param {function} callback - the callback function + * @return {undefined} + */ + mgTransferMarginToMain: function (asset, amount, callback) { + let parameters = Object.assign({ asset: asset, amount: amount, type: 2 }); + signedRequest(sapi + 'v1/margin/transfer', parameters, function (error, data) { + if (callback) return callback(error, data); + }, 'POST'); + }, + /** + * Universal Transfer requires API permissions enabled + * @param {string} type - ENUM , example MAIN_UMFUTURE for SPOT to USDT futures, see https://binance-docs.github.io/apidocs/spot/en/#user-universal-transfer + * @param {string} asset - the asset - example :USDT + * @param {number} amount - the callback function + * @param {function} callback - the callback function (optionnal) + * @return {promise} + */ + universalTransfer: (type, asset, amount, callback) => + universalTransfer(type, asset, amount, callback), + + /** + * Get trades for a given symbol - margin account + * @param {string} symbol - the symbol + * @param {function} callback - the callback function + * @param {object} options - additional options + * @return {promise or undefined} - omitting the callback returns a promise + */ + mgTrades: (symbol, callback, options = {}) => { + let parameters = Object.assign({ symbol: symbol }, options); + if (!callback) { + return new Promise((resolve, reject) => { + callback = (error, response) => { + if (error) { + reject(error); + } else { + resolve(response); + } + } + signedRequest(sapi + 'v1/margin/myTrades', parameters, function (error, data) { + return callback.call(this, error, data, symbol); + }); + }) + } else { + signedRequest(sapi + 'v1/margin/myTrades', parameters, function (error, data) { + return callback.call(this, error, data, symbol); + }); + } + }, + + /** + * Transfer from main account to delivery account + * @param {string} asset - the asset + * @param {number} amount - the asset + * @param {function} callback - the callback function (optionnal) + * @param {object} options - additional options + * @return {undefined} + */ + transferMainToFutures: (asset, amount, callback) => + transferBetweenMainAndFutures(asset, amount, 1, callback), + + /** + * Transfer from delivery account to main account + * @param {string} asset - the asset + * @param {number} amount - the asset + * @param {function} callback - the callback function (optionnal) + * @return {undefined} + */ + transferFuturesToMain: (asset, amount, callback) => + transferBetweenMainAndFutures(asset, amount, 2, callback), + + /** + * Transfer from main account to delivery account + * @param {string} asset - the asset + * @param {number} amount - the asset + * @param {function} callback - the callback function (optionnal) + * @param {object} options - additional options + * @return {undefined} + */ + transferMainToDelivery: (asset, amount, callback) => + transferBetweenMainAndFutures(asset, amount, 3, callback), + + /** + * Transfer from delivery account to main account + * @param {string} asset - the asset + * @param {number} amount - the asset + * @param {function} callback - the callback function (optionnal) + * @return {undefined} + */ + transferDeliveryToMain: (asset, amount, callback) => + transferBetweenMainAndFutures(asset, amount, 4, callback), + + /** + * Get maximum transfer-out amount of an asset + * @param {string} asset - the asset + * @param {function} callback - the callback function + * @return {undefined} + */ + maxTransferable: function (asset, callback) { + signedRequest(sapi + 'v1/margin/maxTransferable', { asset: asset }, function (error, data) { + if (callback) return callback(error, data); + }); + }, + + /** + * Margin account borrow/loan + * @param {string} asset - the asset + * @param {number} amount - the asset + * @param {function} callback - the callback function + * @param {string} isIsolated - the isolated option + * @param {string} symbol - symbol for isolated margin + * @return {undefined} + */ + mgBorrow: function (asset, amount, callback, isIsolated = 'FALSE', symbol = null) { + let parameters = Object.assign({ asset: asset, amount: amount }); + if (isIsolated === 'TRUE' && !symbol) throw new Error('If "isIsolated" = "TRUE", "symbol" must be sent') + const isolatedObj = isIsolated === 'TRUE' ? { + isIsolated, + symbol + } : {} + signedRequest(sapi + 'v1/margin/loan', { ...parameters, ...isolatedObj }, function (error, data) { + if (callback) return callback(error, data); + }, 'POST'); + }, + + /** + * Margin account borrow/loan + * @param {string} asset - the asset + * @param {object} options - additional options + * @param {function} callback - the callback function + * @return {undefined} + */ + mgQueryLoan: function (asset, options, callback) { + let parameters = Object.assign({ asset: asset }, options); + signedRequest(sapi + 'v1/margin/loan', { ...parameters }, function (error, data) { + if (callback) return callback(error, data); + }, 'GET'); + }, + + /** + * Margin account repay + * @param {string} asset - the asset + * @param {object} options - additional options + * @param {function} callback - the callback function + * @return {undefined} + */ + mgQueryRepay: function (asset, options, callback) { + let parameters = Object.assign({ asset: asset }, options); + signedRequest(sapi + 'v1/margin/repay', { ...parameters }, function (error, data) { + if (callback) return callback(error, data); + }, 'GET'); + }, + + /** + * Margin account repay + * @param {string} asset - the asset + * @param {number} amount - the asset + * @param {function} callback - the callback function + * @param {string} isIsolated - the isolated option + * @param {string} symbol - symbol for isolated margin + * @return {undefined} + */ + mgRepay: function (asset, amount, callback, isIsolated = 'FALSE', symbol = null) { + let parameters = Object.assign({ asset: asset, amount: amount }); + if (isIsolated === 'TRUE' && !symbol) throw new Error('If "isIsolated" = "TRUE", "symbol" must be sent') + const isolatedObj = isIsolated === 'TRUE' ? { + isIsolated, + symbol + } : {} + signedRequest(sapi + 'v1/margin/repay', { ...parameters, ...isolatedObj }, function (error, data) { + if (callback) return callback(error, data); + }, 'POST'); + }, + + /** + * Margin account details + * @param {function} callback - the callback function + * @param {boolean} isIsolated - the callback function + * @return {undefined} + */ + mgAccount: function (callback, isIsolated = false) { + let endpoint = 'v1/margin'; + endpoint += (isIsolated) ? '/isolated' : '' + '/account'; + signedRequest(sapi + endpoint, {}, function (error, data) { + if (callback) return callback(error, data); + }); + }, + /** + * Get maximum borrow amount of an asset + * @param {string} asset - the asset + * @param {function} callback - the callback function + * @return {undefined} + */ + maxBorrowable: function (asset, callback) { + signedRequest(sapi + 'v1/margin/maxBorrowable', { asset: asset }, function (error, data) { + if (callback) return callback(error, data); + }); + }, + + // Futures WebSocket Functions: + /** + * Subscribe to a single futures websocket + * @param {string} url - the futures websocket endpoint + * @param {function} callback - optional execution callback + * @param {object} params - Optional reconnect {boolean} (whether to reconnect on disconnect), openCallback {function}, id {string} + * @return {WebSocket} the websocket reference + */ + futuresSubscribeSingle: function (url, callback, params = {}) { + return futuresSubscribeSingle(url, callback, params); + }, + + /** + * Subscribe to a combined futures websocket + * @param {string} streams - the list of websocket endpoints to connect to + * @param {function} callback - optional execution callback + * @param {object} params - Optional reconnect {boolean} (whether to reconnect on disconnect), openCallback {function}, id {string} + * @return {WebSocket} the websocket reference + */ + futuresSubscribe: function (streams, callback, params = {}) { + return futuresSubscribe(streams, callback, params); + }, + + /** + * Returns the known futures websockets subscriptions + * @return {array} array of futures websocket subscriptions + */ + futuresSubscriptions: function () { + return Binance.futuresSubscriptions; + }, + + /** + * Terminates a futures websocket + * @param {string} endpoint - the string associated with the endpoint + * @return {undefined} + */ + futuresTerminate: function (endpoint) { + if (Binance.options.verbose) Binance.options.log('Futures WebSocket terminating:', endpoint); + return futuresTerminate(endpoint); + }, + + /** + * Futures WebSocket aggregated trades + * @param {array/string} symbols - an array or string of symbols to query + * @param {function} callback - callback function + * @return {string} the websocket endpoint + */ + futuresAggTradeStream: function futuresAggTradeStream(symbols, callback) { + let reconnect = () => { + if (Binance.options.reconnect) futuresAggTradeStream(symbols, callback); + }; + let subscription, cleanCallback = data => callback(fAggTradeConvertData(data)); + if (Array.isArray(symbols)) { + if (!isArrayUnique(symbols)) throw Error('futuresAggTradeStream: "symbols" cannot contain duplicate elements.'); + let streams = symbols.map(symbol => symbol.toLowerCase() + '@aggTrade'); + subscription = futuresSubscribe(streams, cleanCallback, { reconnect }); + } else { + let symbol = symbols; + subscription = futuresSubscribeSingle(symbol.toLowerCase() + '@aggTrade', cleanCallback, { reconnect }); + } + return subscription.endpoint; + }, + + /** + * Futures WebSocket mark price + * @param {symbol} symbol name or false. can also be a callback + * @param {function} callback - callback function + * @param {string} speed - 1 second updates. leave blank for default 3 seconds + * @return {string} the websocket endpoint + */ + futuresMarkPriceStream: function fMarkPriceStream(symbol = false, callback = console.log, speed = '@1s') { + if (typeof symbol == 'function') { + callback = symbol; + symbol = false; + } + let reconnect = () => { + if (Binance.options.reconnect) fMarkPriceStream(symbol, callback, speed); + }; + const endpoint = symbol ? `${symbol.toLowerCase()}@markPrice` : '!markPrice@arr' + let subscription = futuresSubscribeSingle(endpoint + speed, data => callback(fMarkPriceConvertData(data)), { reconnect }); + return subscription.endpoint; + }, + + /** + * Futures WebSocket liquidations stream + * @param {symbol} symbol name or false. can also be a callback + * @param {function} callback - callback function + * @return {string} the websocket endpoint + */ + futuresLiquidationStream: function fLiquidationStream(symbol = false, callback = console.log) { + if (typeof symbol == 'function') { + callback = symbol; + symbol = false; + } + let reconnect = () => { + if (Binance.options.reconnect) fLiquidationStream(symbol, callback); + }; + const endpoint = symbol ? `${symbol.toLowerCase()}@forceOrder` : '!forceOrder@arr' + let subscription = futuresSubscribeSingle(endpoint, data => callback(fLiquidationConvertData(data)), { reconnect }); + return subscription.endpoint; + }, + + /** + * Futures WebSocket prevDay ticker + * @param {symbol} symbol name or false. can also be a callback + * @param {function} callback - callback function + * @return {string} the websocket endpoint + */ + futuresTickerStream: function fTickerStream(symbol = false, callback = console.log) { + if (typeof symbol == 'function') { + callback = symbol; + symbol = false; + } + let reconnect = () => { + if (Binance.options.reconnect) fTickerStream(symbol, callback); + }; + const endpoint = symbol ? `${symbol.toLowerCase()}@ticker` : '!ticker@arr' + let subscription = futuresSubscribeSingle(endpoint, data => callback(fTickerConvertData(data)), { reconnect }); + return subscription.endpoint; + }, + + /** + * Futures WebSocket miniTicker + * @param {symbol} symbol name or false. can also be a callback + * @param {function} callback - callback function + * @return {string} the websocket endpoint + */ + futuresMiniTickerStream: function fMiniTickerStream(symbol = false, callback = console.log) { + if (typeof symbol == 'function') { + callback = symbol; + symbol = false; + } + let reconnect = () => { + if (Binance.options.reconnect) fMiniTickerStream(symbol, callback); + }; + const endpoint = symbol ? `${symbol.toLowerCase()}@miniTicker` : '!miniTicker@arr' + let subscription = futuresSubscribeSingle(endpoint, data => callback(fMiniTickerConvertData(data)), { reconnect }); + return subscription.endpoint; + }, + + /** + * Futures WebSocket bookTicker + * @param {symbol} symbol name or false. can also be a callback + * @param {function} callback - callback function + * @return {string} the websocket endpoint + */ + futuresBookTickerStream: function fBookTickerStream(symbol = false, callback = console.log) { + if (typeof symbol == 'function') { + callback = symbol; + symbol = false; + } + let reconnect = () => { + if (Binance.options.reconnect) fBookTickerStream(symbol, callback); + }; + const endpoint = symbol ? `${symbol.toLowerCase()}@bookTicker` : '!bookTicker' + let subscription = futuresSubscribeSingle(endpoint, data => callback(fBookTickerConvertData(data)), { reconnect }); + return subscription.endpoint; + }, + + /** + * Websocket futures klines + * @param {array/string} symbols - an array or string of symbols to query + * @param {string} interval - the time interval + * @param {function} callback - callback function + * @param {int} limit - maximum results, no more than 1000 + * @return {string} the websocket endpoint + */ + futuresChart: async function futuresChart(symbols, interval, callback, limit = 500) { + let reconnect = () => { + if (Binance.options.reconnect) futuresChart(symbols, interval, callback, limit); + }; + + let futuresChartInit = symbol => { + if (typeof Binance.futuresMeta[symbol] === 'undefined') Binance.futuresMeta[symbol] = {}; + if (typeof Binance.futuresMeta[symbol][interval] === 'undefined') Binance.futuresMeta[symbol][interval] = {}; + if (typeof Binance.futuresTicks[symbol] === 'undefined') Binance.futuresTicks[symbol] = {}; + if (typeof Binance.futuresTicks[symbol][interval] === 'undefined') Binance.futuresTicks[symbol][interval] = {}; + if (typeof Binance.futuresRealtime[symbol] === 'undefined') Binance.futuresRealtime[symbol] = {}; + if (typeof Binance.futuresRealtime[symbol][interval] === 'undefined') Binance.futuresRealtime[symbol][interval] = {}; + if (typeof Binance.futuresKlineQueue[symbol] === 'undefined') Binance.futuresKlineQueue[symbol] = {}; + if (typeof Binance.futuresKlineQueue[symbol][interval] === 'undefined') Binance.futuresKlineQueue[symbol][interval] = []; + Binance.futuresMeta[symbol][interval].timestamp = 0; + } + + let handleFuturesKlineStream = kline => { + let symbol = kline.s, interval = kline.k.i; + if (!Binance.futuresMeta[symbol][interval].timestamp) { + if (typeof (Binance.futuresKlineQueue[symbol][interval]) !== 'undefined' && kline !== null) { + Binance.futuresKlineQueue[symbol][interval].push(kline); + } + } else { + //Binance.options.log('futures klines at ' + kline.k.t); + futuresKlineHandler(symbol, kline); + if (callback) callback(symbol, interval, futuresKlineConcat(symbol, interval)); + } + }; + + let getFuturesKlineSnapshot = async (symbol, limit = 500) => { + let data = await promiseRequest('v1/klines', { symbol, interval, limit }, { base: fapi }); + futuresKlineData(symbol, interval, data); + //Binance.options.log('/futures klines at ' + Binance.futuresMeta[symbol][interval].timestamp); + if (typeof Binance.futuresKlineQueue[symbol][interval] !== 'undefined') { + for (let kline of Binance.futuresKlineQueue[symbol][interval]) futuresKlineHandler(symbol, kline, Binance.futuresMeta[symbol][interval].timestamp); + delete Binance.futuresKlineQueue[symbol][interval]; + } + if (callback) callback(symbol, interval, futuresKlineConcat(symbol, interval)); + }; + + let subscription; + if (Array.isArray(symbols)) { + if (!isArrayUnique(symbols)) throw Error('futuresChart: "symbols" array cannot contain duplicate elements.'); + symbols.forEach(futuresChartInit); + let streams = symbols.map(symbol => `${symbol.toLowerCase()}@kline_${interval}`); + subscription = futuresSubscribe(streams, handleFuturesKlineStream, reconnect); + symbols.forEach(element => getFuturesKlineSnapshot(element, limit)); + } else { + let symbol = symbols; + futuresChartInit(symbol); + subscription = futuresSubscribeSingle(symbol.toLowerCase() + '@kline_' + interval, handleFuturesKlineStream, { reconnect }); + getFuturesKlineSnapshot(symbol, limit); + } + return subscription.endpoint; + }, + + /** + * Websocket futures candlesticks + * @param {array/string} symbols - an array or string of symbols to query + * @param {string} interval - the time interval + * @param {function} callback - callback function + * @return {string} the websocket endpoint + */ + futuresCandlesticks: function futuresCandlesticks(symbols, interval, callback) { + let reconnect = () => { + if (Binance.options.reconnect) futuresCandlesticks(symbols, interval, callback); + }; + let subscription; + if (Array.isArray(symbols)) { + if (!isArrayUnique(symbols)) throw Error('futuresCandlesticks: "symbols" array cannot contain duplicate elements.'); + let streams = symbols.map(symbol => symbol.toLowerCase() + '@kline_' + interval); + subscription = futuresSubscribe(streams, callback, { reconnect }); + } else { + let symbol = symbols.toLowerCase(); + subscription = futuresSubscribeSingle(symbol + '@kline_' + interval, callback, { reconnect }); + } + return subscription.endpoint; + }, + + // Delivery WebSocket Functions: + /** + * Subscribe to a single delivery websocket + * @param {string} url - the delivery websocket endpoint + * @param {function} callback - optional execution callback + * @param {object} params - Optional reconnect {boolean} (whether to reconnect on disconnect), openCallback {function}, id {string} + * @return {WebSocket} the websocket reference + */ + deliverySubscribeSingle: function (url, callback, params = {}) { + return deliverySubscribeSingle(url, callback, params); + }, + + /** + * Subscribe to a combined delivery websocket + * @param {string} streams - the list of websocket endpoints to connect to + * @param {function} callback - optional execution callback + * @param {object} params - Optional reconnect {boolean} (whether to reconnect on disconnect), openCallback {function}, id {string} + * @return {WebSocket} the websocket reference + */ + deliverySubscribe: function (streams, callback, params = {}) { + return deliverySubscribe(streams, callback, params); + }, + + /** + * Returns the known delivery websockets subscriptions + * @return {array} array of delivery websocket subscriptions + */ + deliverySubscriptions: function () { + return Binance.deliverySubscriptions; + }, + + /** + * Terminates a delivery websocket + * @param {string} endpoint - the string associated with the endpoint + * @return {undefined} + */ + deliveryTerminate: function (endpoint) { + if (Binance.options.verbose) Binance.options.log('Delivery WebSocket terminating:', endpoint); + return deliveryTerminate(endpoint); + }, + + /** + * Delivery WebSocket aggregated trades + * @param {array/string} symbols - an array or string of symbols to query + * @param {function} callback - callback function + * @return {string} the websocket endpoint + */ + deliveryAggTradeStream: function deliveryAggTradeStream(symbols, callback) { + let reconnect = () => { + if (Binance.options.reconnect) deliveryAggTradeStream(symbols, callback); + }; + let subscription, cleanCallback = data => callback(dAggTradeConvertData(data)); + if (Array.isArray(symbols)) { + if (!isArrayUnique(symbols)) throw Error('deliveryAggTradeStream: "symbols" cannot contain duplicate elements.'); + let streams = symbols.map(symbol => symbol.toLowerCase() + '@aggTrade'); + subscription = deliverySubscribe(streams, cleanCallback, { reconnect }); + } else { + let symbol = symbols; + subscription = deliverySubscribeSingle(symbol.toLowerCase() + '@aggTrade', cleanCallback, { reconnect }); + } + return subscription.endpoint; + }, + + /** + * Delivery WebSocket mark price + * @param {symbol} symbol name or false. can also be a callback + * @param {function} callback - callback function + * @param {string} speed - 1 second updates. leave blank for default 3 seconds + * @return {string} the websocket endpoint + */ + deliveryMarkPriceStream: function dMarkPriceStream(symbol = false, callback = console.log, speed = '@1s') { + if (typeof symbol == 'function') { + callback = symbol; + symbol = false; + } + let reconnect = () => { + if (Binance.options.reconnect) dMarkPriceStream(symbol, callback); + }; + const endpoint = symbol ? `${symbol.toLowerCase()}@markPrice` : '!markPrice@arr' + let subscription = deliverySubscribeSingle(endpoint + speed, data => callback(dMarkPriceConvertData(data)), { reconnect }); + return subscription.endpoint; + }, + + /** + * Delivery WebSocket liquidations stream + * @param {symbol} symbol name or false. can also be a callback + * @param {function} callback - callback function + * @return {string} the websocket endpoint + */ + deliveryLiquidationStream: function dLiquidationStream(symbol = false, callback = console.log) { + if (typeof symbol == 'function') { + callback = symbol; + symbol = false; + } + let reconnect = () => { + if (Binance.options.reconnect) dLiquidationStream(symbol, callback); + }; + const endpoint = symbol ? `${symbol.toLowerCase()}@forceOrder` : '!forceOrder@arr' + let subscription = deliverySubscribeSingle(endpoint, data => callback(dLiquidationConvertData(data)), { reconnect }); + return subscription.endpoint; + }, + + /** + * Delivery WebSocket prevDay ticker + * @param {symbol} symbol name or false. can also be a callback + * @param {function} callback - callback function + * @return {string} the websocket endpoint + */ + deliveryTickerStream: function dTickerStream(symbol = false, callback = console.log) { + if (typeof symbol == 'function') { + callback = symbol; + symbol = false; + } + let reconnect = () => { + if (Binance.options.reconnect) dTickerStream(symbol, callback); + }; + const endpoint = symbol ? `${symbol.toLowerCase()}@ticker` : '!ticker@arr' + let subscription = deliverySubscribeSingle(endpoint, data => callback(dTickerConvertData(data)), { reconnect }); + return subscription.endpoint; + }, + + /** + * Delivery WebSocket miniTicker + * @param {symbol} symbol name or false. can also be a callback + * @param {function} callback - callback function + * @return {string} the websocket endpoint + */ + deliveryMiniTickerStream: function dMiniTickerStream(symbol = false, callback = console.log) { + if (typeof symbol == 'function') { + callback = symbol; + symbol = false; + } + let reconnect = () => { + if (Binance.options.reconnect) dMiniTickerStream(symbol, callback); + }; + const endpoint = symbol ? `${symbol.toLowerCase()}@miniTicker` : '!miniTicker@arr' + let subscription = deliverySubscribeSingle(endpoint, data => callback(dMiniTickerConvertData(data)), { reconnect }); + return subscription.endpoint; + }, + + /** + * Delivery WebSocket bookTicker + * @param {symbol} symbol name or false. can also be a callback + * @param {function} callback - callback function + * @return {string} the websocket endpoint + */ + deliveryBookTickerStream: function dBookTickerStream(symbol = false, callback = console.log) { + if (typeof symbol == 'function') { + callback = symbol; + symbol = false; + } + let reconnect = () => { + if (Binance.options.reconnect) dBookTickerStream(symbol, callback); + }; + const endpoint = symbol ? `${symbol.toLowerCase()}@bookTicker` : '!bookTicker' + let subscription = deliverySubscribeSingle(endpoint, data => callback(dBookTickerConvertData(data)), { reconnect }); + return subscription.endpoint; + }, + + /** + * Websocket delivery klines + * @param {array/string} symbols - an array or string of symbols to query + * @param {string} interval - the time interval + * @param {function} callback - callback function + * @param {int} limit - maximum results, no more than 1000 + * @return {string} the websocket endpoint + */ + deliveryChart: async function deliveryChart(symbols, interval, callback, limit = 500) { + let reconnect = () => { + if (Binance.options.reconnect) deliveryChart(symbols, interval, callback, limit); + }; + + let deliveryChartInit = symbol => { + if (typeof Binance.deliveryMeta[symbol] === 'undefined') Binance.deliveryMeta[symbol] = {}; + if (typeof Binance.deliveryMeta[symbol][interval] === 'undefined') Binance.deliveryMeta[symbol][interval] = {}; + if (typeof Binance.deliveryTicks[symbol] === 'undefined') Binance.deliveryTicks[symbol] = {}; + if (typeof Binance.deliveryTicks[symbol][interval] === 'undefined') Binance.deliveryTicks[symbol][interval] = {}; + if (typeof Binance.deliveryRealtime[symbol] === 'undefined') Binance.deliveryRealtime[symbol] = {}; + if (typeof Binance.deliveryRealtime[symbol][interval] === 'undefined') Binance.deliveryRealtime[symbol][interval] = {}; + if (typeof Binance.deliveryKlineQueue[symbol] === 'undefined') Binance.deliveryKlineQueue[symbol] = {}; + if (typeof Binance.deliveryKlineQueue[symbol][interval] === 'undefined') Binance.deliveryKlineQueue[symbol][interval] = []; + Binance.deliveryMeta[symbol][interval].timestamp = 0; + } + + let handleDeliveryKlineStream = kline => { + let symbol = kline.s, interval = kline.k.i; + if (!Binance.deliveryMeta[symbol][interval].timestamp) { + if (typeof (Binance.deliveryKlineQueue[symbol][interval]) !== 'undefined' && kline !== null) { + Binance.deliveryKlineQueue[symbol][interval].push(kline); + } + } else { + //Binance.options.log('futures klines at ' + kline.k.t); + deliveryKlineHandler(symbol, kline); + if (callback) callback(symbol, interval, deliveryKlineConcat(symbol, interval)); + } + }; + + let getDeliveryKlineSnapshot = async (symbol, limit = 500) => { + let data = await promiseRequest('v1/klines', { symbol, interval, limit }, { base: fapi }); + deliveryKlineData(symbol, interval, data); + //Binance.options.log('/delivery klines at ' + Binance.deliveryMeta[symbol][interval].timestamp); + if (typeof Binance.deliveryKlineQueue[symbol][interval] !== 'undefined') { + for (let kline of Binance.deliveryKlineQueue[symbol][interval]) deliveryKlineHandler(symbol, kline, Binance.deliveryMeta[symbol][interval].timestamp); + delete Binance.deliveryKlineQueue[symbol][interval]; + } + if (callback) callback(symbol, interval, deliveryKlineConcat(symbol, interval)); + }; + + let subscription; + if (Array.isArray(symbols)) { + if (!isArrayUnique(symbols)) throw Error('deliveryChart: "symbols" array cannot contain duplicate elements.'); + symbols.forEach(deliveryChartInit); + let streams = symbols.map(symbol => `${symbol.toLowerCase()}@kline_${interval}`); + subscription = deliverySubscribe(streams, handleDeliveryKlineStream, reconnect); + symbols.forEach(element => getDeliveryKlineSnapshot(element, limit)); + } else { + let symbol = symbols; + deliveryChartInit(symbol); + subscription = deliverySubscribeSingle(symbol.toLowerCase() + '@kline_' + interval, handleDeliveryKlineStream, reconnect); + getDeliveryKlineSnapshot(symbol, limit); + } + return subscription.endpoint; + }, + + /** + * Websocket delivery candlesticks + * @param {array/string} symbols - an array or string of symbols to query + * @param {string} interval - the time interval + * @param {function} callback - callback function + * @return {string} the websocket endpoint + */ + deliveryCandlesticks: function deliveryCandlesticks(symbols, interval, callback) { + let reconnect = () => { + if (Binance.options.reconnect) deliveryCandlesticks(symbols, interval, callback); + }; + let subscription; + if (Array.isArray(symbols)) { + if (!isArrayUnique(symbols)) throw Error('deliveryCandlesticks: "symbols" array cannot contain duplicate elements.'); + let streams = symbols.map(symbol => symbol.toLowerCase() + '@kline_' + interval); + subscription = deliverySubscribe(streams, callback, { reconnect }); + } else { + let symbol = symbols.toLowerCase(); + subscription = deliverySubscribeSingle(symbol + '@kline_' + interval, callback, { reconnect }); + } + return subscription.endpoint; + }, + + websockets: { + /** + * Userdata websockets function + * @param {function} callback - the callback function + * @param {function} execution_callback - optional execution callback + * @param {function} subscribed_callback - subscription callback + * @param {function} list_status_callback - status callback + * @return {undefined} + */ + userData: function userData(callback, execution_callback = false, subscribed_callback = false, list_status_callback = false) { + let reconnect = () => { + if (Binance.options.reconnect) userData(callback, execution_callback, subscribed_callback); + }; + apiRequest(getSpotUrl() + 'v3/userDataStream', {}, function (error, response) { + Binance.options.listenKey = response.listenKey; + setTimeout(function userDataKeepAlive() { // keepalive + try { + apiRequest(getSpotUrl() + 'v3/userDataStream?listenKey=' + Binance.options.listenKey, {}, function (err) { + if (err) setTimeout(userDataKeepAlive, 60000); // retry in 1 minute + else setTimeout(userDataKeepAlive, 60 * 30 * 1000); // 30 minute keepalive + }, 'PUT'); + } catch (error) { + setTimeout(userDataKeepAlive, 60000); // retry in 1 minute + } + }, 60 * 30 * 1000); // 30 minute keepalive + Binance.options.balance_callback = callback; + Binance.options.execution_callback = execution_callback ? execution_callback : callback;//This change is required to listen for Orders + Binance.options.list_status_callback = list_status_callback; + const subscription = subscribe(Binance.options.listenKey, userDataHandler, reconnect); + if (subscribed_callback) subscribed_callback(subscription.endpoint); + }, 'POST'); + }, + + /** + * Margin Userdata websockets function + * @param {function} callback - the callback function + * @param {function} execution_callback - optional execution callback + * @param {function} subscribed_callback - subscription callback + * @param {function} list_status_callback - status callback + * @return {undefined} + */ + userMarginData: function userMarginData(callback, execution_callback = false, subscribed_callback = false, list_status_callback = false) { + let reconnect = () => { + if (Binance.options.reconnect) userMarginData(callback, execution_callback, subscribed_callback); + }; + apiRequest(sapi + 'v1/userDataStream', {}, function (error, response) { + Binance.options.listenMarginKey = response.listenKey; + setTimeout(function userDataKeepAlive() { // keepalive + try { + apiRequest(sapi + 'v1/userDataStream?listenKey=' + Binance.options.listenMarginKey, {}, function (err) { + if (err) setTimeout(userDataKeepAlive, 60000); // retry in 1 minute + else setTimeout(userDataKeepAlive, 60 * 30 * 1000); // 30 minute keepalive + }, 'PUT'); + } catch (error) { + setTimeout(userDataKeepAlive, 60000); // retry in 1 minute + } + }, 60 * 30 * 1000); // 30 minute keepalive + Binance.options.margin_balance_callback = callback; + Binance.options.margin_execution_callback = execution_callback; + Binance.options.margin_list_status_callback = list_status_callback; + const subscription = subscribe(Binance.options.listenMarginKey, userMarginDataHandler, reconnect); + if (subscribed_callback) subscribed_callback(subscription.endpoint); + }, 'POST'); + }, + + /** + * Future Userdata websockets function + * @param {function} margin_call_callback + * @param {function} account_update_callback + * @param {function} order_update_callback + * @param {Function} subscribed_callback - subscription callback + */ + userFutureData: function userFutureData(margin_call_callback, account_update_callback = undefined, order_update_callback = undefined, subscribed_callback = undefined, account_config_update_callback = undefined) { + const url = (Binance.options.test) ? fapiTest : fapi; + + let reconnect = () => { + if (Binance.options.reconnect) userFutureData(margin_call_callback, account_update_callback, order_update_callback, subscribed_callback) + } + + apiRequest(url + 'v1/listenKey', {}, function (error, response) { + Binance.options.listenFutureKey = response.listenKey; + setTimeout(function userDataKeepAlive() { // keepalive + try { + apiRequest(url + 'v1/listenKey?listenKey=' + Binance.options.listenFutureKey, {}, function (err) { + if (err) setTimeout(userDataKeepAlive, 60000); // retry in 1 minute + else setTimeout(userDataKeepAlive, 60 * 30 * 1000); // 30 minute keepalive + }, 'PUT'); + } catch (error) { + setTimeout(userDataKeepAlive, 60000); // retry in 1 minute + } + }, 60 * 30 * 1000); // 30 minute keepalive + Binance.options.future_margin_call_callback = margin_call_callback; + Binance.options.future_account_update_callback = account_update_callback; + Binance.options.future_account_config_update_callback = account_config_update_callback; + Binance.options.future_order_update_callback = order_update_callback; + const subscription = futuresSubscribe(Binance.options.listenFutureKey, userFutureDataHandler, { reconnect }); + if (subscribed_callback) subscribed_callback(subscription.endpoint); + }, 'POST'); + }, + + /** + * Delivery Userdata websockets function + * @param {function} margin_call_callback + * @param {function} account_update_callback + * @param {function} order_update_callback + * @param {Function} subscribed_callback - subscription callback + */ + userDeliveryData: function userDeliveryData( + margin_call_callback, + account_update_callback = undefined, + order_update_callback = undefined, + subscribed_callback = undefined + ) { + const url = Binance.options.test ? dapiTest : dapi; + + let reconnect = () => { + if (Binance.options.reconnect) + userDeliveryData( + margin_call_callback, + account_update_callback, + order_update_callback, + subscribed_callback + ); + }; + + apiRequest( + url + "v1/listenKey", + {}, + function (error, response) { + Binance.options.listenDeliveryKey = response.listenKey; + setTimeout(function userDataKeepAlive() { + // keepalive + try { + apiRequest( + url + + "v1/listenKey?listenKey=" + + Binance.options.listenDeliveryKey, + {}, + function (err) { + if (err) setTimeout(userDataKeepAlive, 60000); + // retry in 1 minute + else setTimeout(userDataKeepAlive, 60 * 30 * 1000); // 30 minute keepalive + }, + "PUT" + ); + } catch (error) { + setTimeout(userDataKeepAlive, 60000); // retry in 1 minute + } + }, 60 * 30 * 1000); // 30 minute keepalive + Binance.options.delivery_margin_call_callback = margin_call_callback; + Binance.options.delivery_account_update_callback = account_update_callback; + Binance.options.delivery_order_update_callback = order_update_callback; + const subscription = deliverySubscribe( + Binance.options.listenDeliveryKey, + userDeliveryDataHandler, + { reconnect } + ); + if (subscribed_callback) subscribed_callback(subscription.endpoint); + }, + "POST" + ); + }, + + /** + * Subscribe to a generic websocket + * @param {string} url - the websocket endpoint + * @param {function} callback - optional execution callback + * @param {boolean} reconnect - subscription callback + * @return {WebSocket} the websocket reference + */ + subscribe: function (url, callback, reconnect = false) { + return subscribe(url, callback, reconnect); + }, + + /** + * Subscribe to a generic combined websocket + * @param {string} url - the websocket endpoint + * @param {function} callback - optional execution callback + * @param {boolean} reconnect - subscription callback + * @return {WebSocket} the websocket reference + */ + subscribeCombined: function (url, callback, reconnect = false) { + return subscribeCombined(url, callback, reconnect); + }, + + /** + * Returns the known websockets subscriptions + * @return {array} array of web socket subscriptions + */ + subscriptions: function () { + return Binance.subscriptions; + }, + + /** + * Terminates a web socket + * @param {string} endpoint - the string associated with the endpoint + * @return {undefined} + */ + terminate: function (endpoint) { + if (Binance.options.verbose) Binance.options.log('WebSocket terminating:', endpoint); + return terminate(endpoint); + }, + + /** + * Websocket depth chart + * @param {array/string} symbols - an array or string of symbols to query + * @param {function} callback - callback function + * @return {string} the websocket endpoint + */ + depth: function depth(symbols, callback) { + let reconnect = () => { + if (Binance.options.reconnect) depth(symbols, callback); + }; + let subscription; + if (Array.isArray(symbols)) { + if (!isArrayUnique(symbols)) throw Error('depth: "symbols" cannot contain duplicate elements.'); + let streams = symbols.map(function (symbol) { + return symbol.toLowerCase() + '@depth@100ms'; + }); + subscription = subscribeCombined(streams, callback, reconnect); + } else { + let symbol = symbols; + subscription = subscribe(symbol.toLowerCase() + '@depth@100ms', callback, reconnect); + } + return subscription.endpoint; + }, + + /** + * Websocket depth cache + * @param {array/string} symbols - an array or string of symbols to query + * @param {function} callback - callback function + * @param {int} limit - the number of entries + * @return {string} the websocket endpoint + */ + depthCache: function depthCacheFunction(symbols, callback, limit = 500) { + let reconnect = () => { + if (Binance.options.reconnect) depthCacheFunction(symbols, callback, limit); + }; + + let symbolDepthInit = symbol => { + if (typeof Binance.depthCacheContext[symbol] === 'undefined') Binance.depthCacheContext[symbol] = {}; + let context = Binance.depthCacheContext[symbol]; + context.snapshotUpdateId = null; + context.lastEventUpdateId = null; + context.messageQueue = []; + Binance.depthCache[symbol] = { bids: {}, asks: {} }; + }; + + let assignEndpointIdToContext = (symbol, endpointId) => { + if (Binance.depthCacheContext[symbol]) { + let context = Binance.depthCacheContext[symbol]; + context.endpointId = endpointId; + } + }; + + let handleDepthStreamData = depth => { + let symbol = depth.s; + let context = Binance.depthCacheContext[symbol]; + if (context.messageQueue && !context.snapshotUpdateId) { + context.messageQueue.push(depth); + } else { + try { + depthHandler(depth); + } catch (err) { + return terminate(context.endpointId, true); + } + if (callback) callback(symbol, Binance.depthCache[symbol], context); + } + }; + + let getSymbolDepthSnapshot = (symbol, cb) => { + publicRequest(getSpotUrl() + 'v3/depth', { symbol: symbol, limit: limit }, function (error, json) { + if (error) { + return cb(error, null); + } + // Store symbol next use + json.symb = symbol; + cb(null, json) + }); + }; + + let updateSymbolDepthCache = json => { + // Get previous store symbol + let symbol = json.symb; + // Initialize depth cache from snapshot + Binance.depthCache[symbol] = depthData(json); + // Prepare depth cache context + let context = Binance.depthCacheContext[symbol]; + context.snapshotUpdateId = json.lastUpdateId; + context.messageQueue = context.messageQueue.filter(depth => depth.u > context.snapshotUpdateId); + // Process any pending depth messages + for (let depth of context.messageQueue) { + /* Although sync errors shouldn't ever happen here, we catch and swallow them anyway + just in case. The stream handler function above will deal with broken caches. */ + try { + depthHandler(depth); + } catch (err) { + // Do nothing + } + } + delete context.messageQueue; + if (callback) callback(symbol, Binance.depthCache[symbol]); + }; + + /* If an array of symbols are sent we use a combined stream connection rather. + This is transparent to the developer, and results in a single socket connection. + This essentially eliminates "unexpected response" errors when subscribing to a lot of data. */ + let subscription; + if (Array.isArray(symbols)) { + if (!isArrayUnique(symbols)) throw Error('depthCache: "symbols" cannot contain duplicate elements.'); + symbols.forEach(symbolDepthInit); + let streams = symbols.map(function (symbol) { + return symbol.toLowerCase() + `@depth@100ms`; + }); + subscription = subscribeCombined(streams, handleDepthStreamData, reconnect, function () { + async.mapLimit(symbols, 50, getSymbolDepthSnapshot, (err, results) => { + if (err) throw err; + results.forEach(updateSymbolDepthCache); + }); + }); + symbols.forEach(s => assignEndpointIdToContext(s, subscription.endpoint)); + } else { + let symbol = symbols; + symbolDepthInit(symbol); + subscription = subscribe(symbol.toLowerCase() + `@depth@100ms`, handleDepthStreamData, reconnect, function () { + async.mapLimit([symbol], 1, getSymbolDepthSnapshot, (err, results) => { + if (err) throw err; + results.forEach(updateSymbolDepthCache); + }); + }); + assignEndpointIdToContext(symbol, subscription.endpoint); + } + return subscription.endpoint; + }, + + /** + * Clear Websocket depth cache + * @param {String|Array} symbols - a single symbol, or an array of symbols, to clear the cache of + * @returns {void} + */ + clearDepthCache(symbols) { + const symbolsArr = Array.isArray(symbols) ? symbols : [symbols]; + symbolsArr.forEach(thisSymbol => { + delete Binance.depthCache[thisSymbol]; + }); + }, + + /** + * Websocket staggered depth cache + * @param {array/string} symbols - an array of symbols to query + * @param {function} callback - callback function + * @param {int} limit - the number of entries + * @param {int} stagger - ms between each depth cache + * @return {Promise} the websocket endpoint + */ + depthCacheStaggered: function (symbols, callback, limit = 100, stagger = 200) { + if (!Array.isArray(symbols)) symbols = [symbols]; + let chain = null; + + symbols.forEach(symbol => { + let promise = () => new Promise(resolve => { + this.depthCache(symbol, callback, limit); + setTimeout(resolve, stagger); + }); + chain = chain ? chain.then(promise) : promise(); + }); + + return chain; + }, + + /** + * Websocket aggregated trades + * @param {array/string} symbols - an array or string of symbols to query + * @param {function} callback - callback function + * @return {string} the websocket endpoint + */ + aggTrades: function trades(symbols, callback) { + let reconnect = () => { + if (Binance.options.reconnect) trades(symbols, callback); + }; + let subscription; + if (Array.isArray(symbols)) { + if (!isArrayUnique(symbols)) throw Error('trades: "symbols" cannot contain duplicate elements.'); + let streams = symbols.map(function (symbol) { + return symbol.toLowerCase() + '@aggTrade'; + }); + subscription = subscribeCombined(streams, callback, reconnect); + } else { + let symbol = symbols; + subscription = subscribe(symbol.toLowerCase() + '@aggTrade', callback, reconnect); + } + return subscription.endpoint; + }, + + /** + * Websocket raw trades + * @param {array/string} symbols - an array or string of symbols to query + * @param {function} callback - callback function + * @return {string} the websocket endpoint + */ + trades: function trades(symbols, callback) { + let reconnect = () => { + if (Binance.options.reconnect) trades(symbols, callback); + }; + + let subscription; + if (Array.isArray(symbols)) { + if (!isArrayUnique(symbols)) throw Error('trades: "symbols" cannot contain duplicate elements.'); + let streams = symbols.map(function (symbol) { + return symbol.toLowerCase() + '@trade'; + }); + subscription = subscribeCombined(streams, callback, reconnect); + } else { + let symbol = symbols; + subscription = subscribe(symbol.toLowerCase() + '@trade', callback, reconnect); + } + return subscription.endpoint; + }, + + /** + * Websocket klines + * @param {array/string} symbols - an array or string of symbols to query + * @param {string} interval - the time interval + * @param {function} callback - callback function + * @param {int} limit - maximum results, no more than 1000 + * @return {string} the websocket endpoint + */ + chart: function chart(symbols, interval, callback, limit = 500) { + let reconnect = () => { + if (Binance.options.reconnect) chart(symbols, interval, callback, limit); + }; + + let symbolChartInit = symbol => { + if (typeof Binance.info[symbol] === 'undefined') Binance.info[symbol] = {}; + if (typeof Binance.info[symbol][interval] === 'undefined') Binance.info[symbol][interval] = {}; + if (typeof Binance.ohlc[symbol] === 'undefined') Binance.ohlc[symbol] = {}; + if (typeof Binance.ohlc[symbol][interval] === 'undefined') Binance.ohlc[symbol][interval] = {}; + if (typeof Binance.ohlcLatest[symbol] === 'undefined') Binance.ohlcLatest[symbol] = {}; + if (typeof Binance.ohlcLatest[symbol][interval] === 'undefined') Binance.ohlcLatest[symbol][interval] = {}; + if (typeof Binance.klineQueue[symbol] === 'undefined') Binance.klineQueue[symbol] = {}; + if (typeof Binance.klineQueue[symbol][interval] === 'undefined') Binance.klineQueue[symbol][interval] = []; + Binance.info[symbol][interval].timestamp = 0; + } + + let handleKlineStreamData = kline => { + let symbol = kline.s, interval = kline.k.i; + if (!Binance.info[symbol][interval].timestamp) { + if (typeof (Binance.klineQueue[symbol][interval]) !== 'undefined' && kline !== null) { + Binance.klineQueue[symbol][interval].push(kline); + } + } else { + //Binance.options.log('@klines at ' + kline.k.t); + klineHandler(symbol, kline); + if (callback) callback(symbol, interval, klineConcat(symbol, interval)); + } + }; + + let getSymbolKlineSnapshot = (symbol, limit = 500) => { + publicRequest(getSpotUrl() + 'v3/klines', { symbol: symbol, interval: interval, limit: limit }, function (error, data) { + klineData(symbol, interval, data); + //Binance.options.log('/klines at ' + Binance.info[symbol][interval].timestamp); + if (typeof Binance.klineQueue[symbol][interval] !== 'undefined') { + for (let kline of Binance.klineQueue[symbol][interval]) klineHandler(symbol, kline, Binance.info[symbol][interval].timestamp); + delete Binance.klineQueue[symbol][interval]; + } + if (callback) callback(symbol, interval, klineConcat(symbol, interval)); + }); + }; + + let subscription; + if (Array.isArray(symbols)) { + if (!isArrayUnique(symbols)) throw Error('chart: "symbols" cannot contain duplicate elements.'); + symbols.forEach(symbolChartInit); + let streams = symbols.map(function (symbol) { + return symbol.toLowerCase() + '@kline_' + interval; + }); + subscription = subscribeCombined(streams, handleKlineStreamData, reconnect); + symbols.forEach(element => getSymbolKlineSnapshot(element, limit)); + } else { + let symbol = symbols; + symbolChartInit(symbol); + subscription = subscribe(symbol.toLowerCase() + '@kline_' + interval, handleKlineStreamData, reconnect); + getSymbolKlineSnapshot(symbol, limit); + } + return subscription.endpoint; + }, + + /** + * Websocket candle sticks + * @param {array/string} symbols - an array or string of symbols to query + * @param {string} interval - the time interval + * @param {function} callback - callback function + * @return {string} the websocket endpoint + */ + candlesticks: function candlesticks(symbols, interval, callback) { + let reconnect = () => { + if (Binance.options.reconnect) candlesticks(symbols, interval, callback); + }; + + /* If an array of symbols are sent we use a combined stream connection rather. + This is transparent to the developer, and results in a single socket connection. + This essentially eliminates "unexpected response" errors when subscribing to a lot of data. */ + let subscription; + if (Array.isArray(symbols)) { + if (!isArrayUnique(symbols)) throw Error('candlesticks: "symbols" cannot contain duplicate elements.'); + let streams = symbols.map(function (symbol) { + return symbol.toLowerCase() + '@kline_' + interval; + }); + subscription = subscribeCombined(streams, callback, reconnect); + } else { + let symbol = symbols.toLowerCase(); + subscription = subscribe(symbol + '@kline_' + interval, callback, reconnect); + } + return subscription.endpoint; + }, + + /** + * Websocket mini ticker + * @param {function} callback - callback function + * @return {string} the websocket endpoint + */ + miniTicker: function miniTicker(callback) { + let reconnect = () => { + if (Binance.options.reconnect) miniTicker(callback); + }; + let subscription = subscribe('!miniTicker@arr', function (data) { + let markets = {}; + for (let obj of data) { + markets[obj.s] = { + close: obj.c, + open: obj.o, + high: obj.h, + low: obj.l, + volume: obj.v, + quoteVolume: obj.q, + eventTime: obj.E + }; + } + callback(markets); + }, reconnect); + return subscription.endpoint; + }, + + /** + * Spot WebSocket bookTicker (bid/ask quotes including price & amount) + * @param {symbol} symbol name or false. can also be a callback + * @param {function} callback - callback function + * @return {string} the websocket endpoint + */ + bookTickers: function bookTickerStream(symbol = false, callback = console.log) { + if (typeof symbol == 'function') { + callback = symbol; + symbol = false; + } + let reconnect = () => { + if (Binance.options.reconnect) bookTickerStream(symbol, callback); + }; + const endpoint = symbol ? `${symbol.toLowerCase()}@bookTicker` : '!bookTicker' + let subscription = subscribe(endpoint, data => callback(fBookTickerConvertData(data)), reconnect); + return subscription.endpoint; + }, + + /** + * Websocket prevday percentage + * @param {array/string} symbols - an array or string of symbols to query + * @param {function} callback - callback function + * @param {boolean} singleCallback - avoid call one callback for each symbol in data array + * @return {string} the websocket endpoint + */ + prevDay: function prevDay(symbols, callback, singleCallback) { + let reconnect = () => { + if (Binance.options.reconnect) prevDay(symbols, callback, singleCallback); + }; + + let subscription; + // Combine stream for array of symbols + if (Array.isArray(symbols)) { + if (!isArrayUnique(symbols)) throw Error('prevDay: "symbols" cannot contain duplicate elements.'); + let streams = symbols.map(function (symbol) { + return symbol.toLowerCase() + '@ticker'; + }); + subscription = subscribeCombined(streams, function (data) { + prevDayStreamHandler(data, callback); + }, reconnect); + // Raw stream for a single symbol + } else if (symbols) { + let symbol = symbols; + subscription = subscribe(symbol.toLowerCase() + '@ticker', function (data) { + prevDayStreamHandler(data, callback); + }, reconnect); + // Raw stream of all listed symbols + } else { + subscription = subscribe('!ticker@arr', function (data) { + if (singleCallback) { + prevDayStreamHandler(data, callback); + } else { + for (let line of data) { + prevDayStreamHandler(line, callback); + } + } + }, reconnect); + } + return subscription.endpoint; + } + } + }; +} +module.exports = api; +//https://github.com/binance-exchange/binance-official-api-docs diff --git a/examples/advanced.md b/examples/advanced.md index 0600ccf6..fac0a218 100644 --- a/examples/advanced.md +++ b/examples/advanced.md @@ -1,4 +1,4 @@ -![Downloads](https://img.shields.io/npm/dt/node-binance-api.svg?style=for-the-badge&maxAge=86400) ![Stars](https://img.shields.io/github/stars/jaggedsoft/node-binance-api.svg?style=for-the-badge&label=Stars) ![Contributors](https://img.shields.io/github/contributors/jaggedsoft/node-binance-api.svg?style=for-the-badge&maxAge=86400) ![Issues](https://img.shields.io/github/issues/jaggedsoft/node-binance-api.svg?style=for-the-badge&maxAge=86400) ![Issue Closure](https://img.shields.io/issuestats/i/github/jaggedsoft/node-binance-api.svg?style=for-the-badge&maxAge=86400) +![Downloads](https://img.shields.io/npm/dt/node-binance-api.svg?style=for-the-badge&maxAge=86400) ![Stars](https://img.shields.io/github/stars/jaggedsoft/node-binance-api.svg?style=for-the-badge&label=Stars) ![Contributors](https://img.shields.io/github/contributors/jaggedsoft/node-binance-api.svg?style=for-the-badge&maxAge=86400) ![Issues](https://img.shields.io/github/issues/jaggedsoft/node-binance-api.svg?style=for-the-badge&maxAge=86400) ## Advanced Examples #### exchangeInfo(): Pull minimum order size, quantity, etc. @@ -49,6 +49,29 @@ if ( price * amount < minNotional ) { // Round to stepSize amount = binance.roundStep(amount, stepSize); ``` +#### Show Weight Used From Last Request +> Note: Weight is not shared between Spot & Futures API +```js +const responseInfo = () => `[${ binance.statusCode() }] usedWeight:${ binance.usedWeight() } ${ binance.lastURL() }`; +await binance.time(); +console.info( responseInfo() ); +await binance.exchangeInfo(); +console.info( responseInfo() ); +await binance.futuresExchangeInfo(); +console.info( responseInfo() ); +console.info( `Futures ping time: ${ binance.futuresLatency() } latency` ); + +// Output: +// [200] usedWeight:1 url: https://api.binance.com/api/v3/time +// [200] usedWeight:2 url: https://api.binance.com/api/v3/exchangeInfo +// [200] usedWeight:1 url: https://fapi.binance.com/fapi/v1/exchangeInfo +// Futures ping time: 0ms latency + +// Note: binance.getInfo() includes orderCount1s, orderCount1m, orderCount1h, orderCount1d and more. +// binance.usedWeight() defaults to X-MBX-USED-WEIGHT-1m +// binance.orderCount() defaults to X-MBX-ORDER-COUNT-1m +// binance.statusCode() usedWeight() and lastURL() are shortcuts. lastRequest is the timestamp of the last response. +``` #### Show API Rate limits ```js @@ -163,6 +186,15 @@ SNM available: 0.76352833 (0.00000000 on order) ```
+ + + +#### Margin User Data: Account Balance Updates, Trade Updates, New Orders, Filled Orders, Cancelled Orders via WebSocket +This is exactly the same as the ws User Data but calling: +```javascript +binance.websockets.userMarginData(margin_balance_update, margin_execution_update); +``` + #### newOrderRespType example when placing orders ```js // Returns additional information, such as filled orders @@ -178,7 +210,7 @@ binance.marketBuy("BNBBTC", quantity, flags, function(error, response) { ``` ![image](https://user-images.githubusercontent.com/4283360/36094574-acb15ae6-0fa3-11e8-9209-e6f528e09e84.png) > First price: 0.00106140 - + #### Recent Trades (historicalTrades, recentTrades, aggTrades functions) ```js @@ -198,3 +230,75 @@ binance.historicalTrades("BNBBTC", (error, response)=>{ console.log("historicalTrades", response); }); ``` + +#### Withdraw with custom name +```js +// let name = false // Falsy value won't save address to address book +let name = 'My Withdrawal Address' +binance.withdraw("BTC", "1C5gqLRs96Xq4V2ZZAR1347yUCpHie7sa", 0.2, undefined, name) +``` + +#### Withdraw with Callback +```js +binance.withdraw("ETH", "0x1d2034348c851ea29c7d03731c7968a5bcc91564", 1, false, (error, response) => { + console.log(response); +}); +``` + +### Proxy Support +For the standard REST API the https_proxy or socks_proxy variable is honoured +*NOTE* proxy package has no dns name support, please use proxy IP address + +**Linux** +```bash +export https_proxy=http://ip:port +#export socks_proxy=socks://ip:port +# run your app +``` + +**Windows** +```bash +set https_proxy=http://ip:port +#set socks_proxy=socks://ip:port +# run your app +``` + +For web sockets currently only the socks method is functional at this time + +**linux** +```bash +export socks_proxy=socks://ip:port +# run your app +``` + +**windows** +```bash +set socks_proxy=socks://ip:port +# run your app +``` + +#### Asynchronous Syntax Options +> The examples below show three most common syntaxes for asynchronous API calls and their respective methods of error handling. If you do not pass a callback function as an argument, the API call returns a promise instead. + +```js +const callback = binance.prices("NEOBTC", (error, response) => { + if (error) { + console.error(error) + } else { + console.log(response) + } +}) + +const classicPromise = binance.prices("NEOBTC") + .then(response => console.log(response)) + .catch(error => console.error(error)) + +const asyncAwait = (async _ => { + try { + const response = await binance.prices("NEOBTC") + console.log(response) + } catch (error) { + console.error(error) + } +})() +``` diff --git a/examples/binanceStreams.js b/examples/binanceStreams.js new file mode 100644 index 00000000..d4021f81 --- /dev/null +++ b/examples/binanceStreams.js @@ -0,0 +1,16 @@ +import Binance from "../src/node-binance-api.js" + + +async function main() { + const binance = new Binance({ + APIKEY: '', + APISECRET: '', + test: true + }); + const logger = (message) => { + console.log(message); + } + binance.tradesStream("BTCUSDT", logger); +} + +main() \ No newline at end of file diff --git a/examples/class-example.mts b/examples/class-example.mts new file mode 100644 index 00000000..32fb831c --- /dev/null +++ b/examples/class-example.mts @@ -0,0 +1,15 @@ +import Binance from "../src/node-binance-api.js" + + +async function main() { + const binance = new Binance({ + APIKEY: 'X4BHNSimXOK6RKs2FcKqExquJtHjMxz5hWqF0BBeVnfa5bKFMk7X0wtkfEz0cPrJ', + APISECRET: 'x8gLihunpNq0d46F2q0TWJmeCDahX5LMXSlv3lSFNbMI3rujSOpTDKdhbcmPSf2i', + test: true + }); + const order = await binance.marketBuy("LTCUSDT", 0.1); + console.log( order ); + console.log( 'Hello, World!' ); +} + +main() \ No newline at end of file diff --git a/examples/experiments.js b/examples/experiments.js index 059cee36..a2bc892d 100644 --- a/examples/experiments.js +++ b/examples/experiments.js @@ -4,7 +4,7 @@ const WebSocket = require('ws'); let streams = []; let markets = []; - let prevDay = await axios.get('https://api.binance.com/api/v1/ticker/24hr'); + let prevDay = await axios.get('https://api.binance.com/api/v3/ticker/24hr'); for ( let obj of prevDay.data ) { markets.push(obj.symbol); streams.push(obj.symbol.toLowerCase()+'@aggTrade'); diff --git a/examples/oco.md b/examples/oco.md new file mode 100644 index 00000000..c143987d --- /dev/null +++ b/examples/oco.md @@ -0,0 +1,14 @@ +( async () => { + const Binance = require('node-binance-api'); + const binance = new Binance({ + APIKEY: '', + APISECRET: '' + }); + + // OCO Order + binance.order('SELL', 'BNBBTC', 1, 0.0029, { type:'OCO' , stopLimitPrice: 0.001, stopPrice: 0.001 }, (error, response) => {}) + + // Alternative Syntax + binance.sell('BNBBTC', 1, 0.0029, { type:'OCO' , stopLimitPrice: 0.001, stopPrice: 0.001 }, (error, response) => {}) + +} )(); diff --git a/examples/proxy.mts b/examples/proxy.mts new file mode 100644 index 00000000..f6f49043 --- /dev/null +++ b/examples/proxy.mts @@ -0,0 +1,19 @@ +// import Binance from "node-binance-api" +import Binance from "../src/node-binance-api.js" + + +async function main () { + const exchange = new Binance().options({}); + exchange.httpsProxy = 'http://188.34.194.190:8911'; + // + // ### socksProxy ### + // exchange.socksProxy = 'socks5://127.0.0.1:1080'; + // + // ### cors/redirection proxy ### + // exchange.urlProxy = 'https://example.com/?getUrl='; + // + const res = await exchange.futuresTime(); + console.log( res ); +} + +main (); \ No newline at end of file diff --git a/examples/standalone.js b/examples/standalone.js deleted file mode 100644 index 530ee17a..00000000 --- a/examples/standalone.js +++ /dev/null @@ -1,16 +0,0 @@ -// Standalone async functions not requiring the library -const axios = require( 'axios' ); -async function bookTicker( symbol = false ) { - return new Promise( ( resolve, reject ) => { - params = symbol ? `?symbol=${symbol}` : ''; - axios.get( 'https://api.binance.com/api/v3/ticker/bookTicker' + params ) - .then( function ( response ) { - resolve( response.data ); - } ) - .catch( function ( error ) { - throw error; - } ); - } ); -} -//console.log(await bookTicker()); -//console.log(await bookTicker("BTCUSDT")); diff --git a/launch.json b/launch.json deleted file mode 100644 index 417fd3f1..00000000 --- a/launch.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "version": "0.1.0", - "configurations": [ - { - "name": "Debug Mocha Test", - "type": "node", - "address": "localhost", - "port": 9229, - "sourceMaps": false - } - ] -} \ No newline at end of file diff --git a/node-binance-api.js b/node-binance-api.js deleted file mode 100644 index 3b55d818..00000000 --- a/node-binance-api.js +++ /dev/null @@ -1,2058 +0,0 @@ -/* ============================================================ - * node-binance-api - * https://github.com/jaggedsoft/node-binance-api - * ============================================================ - * Copyright 2017-, Jon Eyrick - * Released under the MIT License - * ============================================================ */ - -/** - * Node Binance API - * @module jaggedsoft/node-binance-api - * @return {object} instance to class object - */ -let api = function Binance() { - let Binance = this; // eslint-disable-line consistent-this - 'use strict'; // eslint-disable-line no-unused-expressions - - const WebSocket = require('ws'); - const request = require('request'); - const crypto = require('crypto'); - const file = require('fs'); - const url = require('url'); - const HttpsProxyAgent = require('https-proxy-agent'); - const SocksProxyAgent = require('socks-proxy-agent'); - const stringHash = require('string-hash'); - const async = require('async'); - const base = 'https://api.binance.com/api/'; - const wapi = 'https://api.binance.com/wapi/'; - const stream = 'wss://stream.binance.com:9443/ws/'; - const combineStream = 'wss://stream.binance.com:9443/stream?streams='; - const userAgent = 'Mozilla/4.0 (compatible; Node Binance API)'; - const contentType = 'application/x-www-form-urlencoded'; - Binance.subscriptions = {}; - Binance.depthCache = {}; - Binance.depthCacheContext = {}; - Binance.ohlcLatest = {}; - Binance.klineQueue = {}; - Binance.ohlc = {}; - const default_options = { - recvWindow: 5000, - useServerTime: false, - reconnect: true, - verbose: false, - test: false, - log: function (...args) { - console.log(Array.prototype.slice.call(args)); - } - }; - Binance.options = default_options; - Binance.info = { timeOffset: 0 }; - Binance.socketHeartbeatInterval = null; - - /** - * Replaces socks connection uri hostname with IP address - * @param {string} connString - socks connection string - * @return {string} modified string with ip address - */ - const proxyReplacewithIp = function (connString) { - return connString; - } - - /** - * Returns an array in the form of [host, port] - * @param {string} connString - connection string - * @return {array} array of host and port - */ - const parseProxy = function (connString) { - let arr = connString.split('/'); - let host = arr[2].split(':')[0]; - let port = arr[2].split(':')[1]; - return [arr[0], host, port]; - } - - /** - * Checks to see of the object is iterable - * @param {object} obj - The object check - * @return {boolean} true or false is iterable - */ - const isIterable = function (obj) { - // checks for null and undefined - if (obj === null) { - return false; - } - return typeof obj[Symbol.iterator] === 'function'; - } - - const addProxy = opt => { - let proxy = Binance.options.proxy - ? `http://${ - Binance.options.proxy.auth - ? Binance.options.proxy.auth.username + - ':' + - Binance.options.proxy.auth.password + - '@' - : '' - }${Binance.options.proxy.host}:${Binance.options.proxy.port}` - : ''; - if (proxy) { - opt.proxy = proxy; - } - return opt; - } - - const reqHandler = cb => (error, response, body) => { - if (!cb) return; - - if (error) return cb(error, {}); - - if (response && response.statusCode !== 200) return cb(response, {}); - - return cb(null, JSON.parse(body)); - } - - const proxyRequest = (opt, cb) => request(addProxy(opt), reqHandler(cb)); - - const reqObj = (url, data = {}, method = 'GET', key) => ({ - url: url, - qs: data, - method: method, - timeout: Binance.options.recvWindow, - headers: { - 'User-Agent': userAgent, - 'Content-type': contentType, - 'X-MBX-APIKEY': key || '' - } - }) - const reqObjPOST = (url, data = {}, method = 'POST', key) => ({ - url: url, - form: data, - method: method, - timeout: Binance.options.recvWindow, - headers: { - 'User-Agent': userAgent, - 'Content-type': contentType, - 'X-MBX-APIKEY': key || '' - } - }) - /** - * Create a http request to the public API - * @param {string} url - The http endpoint - * @param {object} data - The data to send - * @param {function} callback - The callback method to call - * @param {string} method - the http method - * @return {undefined} - */ - const publicRequest = function (url, data = {}, callback, method = 'GET') { - let opt = reqObj(url, data, method); - proxyRequest(opt, callback); - }; - - /** - * Create a http request to the public API - * @param {string} url - The http endpoint - * @param {object} data - The data to send - * @param {function} callback - The callback method to call - * @param {string} method - the http method - * @return {undefined} - */ - const apiRequest = function (url, data = {}, callback, method = 'GET') { - if (!Binance.options.APIKEY) throw Error('apiRequest: Invalid API Key'); - let opt = reqObj( - url, - data, - method, - Binance.options.APIKEY - ); - proxyRequest(opt, callback); - }; - - /** - * Make market request - * @param {string} url - The http endpoint - * @param {object} data - The data to send - * @param {function} callback - The callback method to call - * @param {string} method - the http method - * @return {undefined} - */ - const marketRequest = function (url, data = {}, callback, method = 'GET') { - if (!Binance.options.APIKEY) throw Error('apiRequest: Invalid API Key'); - let query = Object.keys(data).reduce(function (a, k) { - a.push(k + '=' + encodeURIComponent(data[k])); - return a; - }, []).join('&'); - - let opt = reqObj( - url + (query ? '?' + query : ''), - data, - method, - Binance.options.APIKEY - ); - proxyRequest(opt, callback); - }; - - /** - * Create a signed http request to the signed API - * @param {string} url - The http endpoint - * @param {object} data - The data to send - * @param {function} callback - The callback method to call - * @param {string} method - the http method - * @return {undefined} - */ - const signedRequest = function (url, data = {}, callback, method = 'GET') { - if (!Binance.options.APIKEY) throw Error('apiRequest: Invalid API Key'); - if (!Binance.options.APISECRET) throw Error('signedRequest: Invalid API Secret'); - data.timestamp = new Date().getTime() + Binance.info.timeOffset; - if (typeof data.recvWindow === 'undefined') data.recvWindow = Binance.options.recvWindow; - let query = method === 'POST' && data.name === 'API Withdraw' ? '' : Object.keys(data).reduce(function (a, k) { - a.push(k + '=' + encodeURIComponent(data[k])); - return a; - }, []).join('&'); - let signature = crypto.createHmac('sha256', Binance.options.APISECRET).update(query).digest('hex'); // set the HMAC hash header - if (method === 'POST') { - let opt = reqObjPOST( - url + '?signature=' + signature, - data, - method, - Binance.options.APIKEY - ); - proxyRequest(opt, callback); - } else { - let opt = reqObj( - url + '?' + query + '&signature=' + signature, - data, - method, - Binance.options.APIKEY - ); - proxyRequest(opt, callback); - } - }; - - /** - * Create a signed http request to the signed API - * @param {string} side - BUY or SELL - * @param {string} symbol - The symbol to buy or sell - * @param {string} quantity - The quantity to buy or sell - * @param {string} price - The price per unit to transact each unit at - * @param {object} flags - additional order settings - * @param {function} callback - the callback function - * @return {undefined} - */ - const order = function (side, symbol, quantity, price, flags = {}, callback = false) { - let endpoint = 'v3/order'; - if (Binance.options.test) endpoint += '/test'; - let opt = { - symbol: symbol, - side: side, - type: 'LIMIT', - quantity: quantity - }; - if (typeof flags.type !== 'undefined') opt.type = flags.type; - if (opt.type.includes('LIMIT')) { - opt.price = price; - opt.timeInForce = 'GTC'; - } - if (typeof flags.timeInForce !== 'undefined') opt.timeInForce = flags.timeInForce; - if (typeof flags.newOrderRespType !== 'undefined') opt.newOrderRespType = flags.newOrderRespType; - if (typeof flags.newClientOrderId !== 'undefined') opt.newClientOrderId = flags.newClientOrderId; - - /* - * STOP_LOSS - * STOP_LOSS_LIMIT - * TAKE_PROFIT - * TAKE_PROFIT_LIMIT - * LIMIT_MAKER - */ - if (typeof flags.icebergQty !== 'undefined') opt.icebergQty = flags.icebergQty; - if (typeof flags.stopPrice !== 'undefined') { - opt.stopPrice = flags.stopPrice; - if (opt.type === 'LIMIT') throw Error('stopPrice: Must set "type" to one of the following: STOP_LOSS, STOP_LOSS_LIMIT, TAKE_PROFIT, TAKE_PROFIT_LIMIT'); - } - signedRequest(base + endpoint, opt, function (error, response) { - if (!response) { - if (callback) callback(error, response); - else Binance.options.log('Order() error:', error); - return; - } - if (typeof response.msg !== 'undefined' && response.msg === 'Filter failure: MIN_NOTIONAL') { - Binance.options.log('Order quantity too small. See exchangeInfo() for minimum amounts'); - } - if (callback) callback(error, response); - else Binance.options.log(side + '(' + symbol + ',' + quantity + ',' + price + ') ', response); - }, 'POST'); - }; - - /** - * No-operation function - * @return {undefined} - */ - const noop = function () { - // do nothing - }; - - /** - * Reworked Tuitio's heartbeat code into a shared single interval tick - * @return {undefined} - */ - const socketHeartbeat = function () { - - /* sockets removed from `subscriptions` during a manual terminate() - will no longer be at risk of having functions called on them */ - for (let endpointId in Binance.subscriptions) { - const ws = Binance.subscriptions[endpointId]; - if (ws.isAlive) { - ws.isAlive = false; - if (ws.readyState === WebSocket.OPEN) ws.ping(noop); - } else { - if (Binance.options.verbose) Binance.options.log('Terminating inactive/broken WebSocket: ' + ws.endpoint); - if (ws.readyState === WebSocket.OPEN) ws.terminate(); - } - } - }; - - /** - * Called when socket is opened, subscriptions are registered for later reference - * @param {function} opened_callback - a callback function - * @return {undefined} - */ - const handleSocketOpen = function (opened_callback) { - this.isAlive = true; - if (Object.keys(Binance.subscriptions).length === 0) { - Binance.socketHeartbeatInterval = setInterval(socketHeartbeat, 30000); - } - Binance.subscriptions[this.endpoint] = this; - if (typeof opened_callback === 'function') opened_callback(this.endpoint); - }; - - /** - * Called when socket is closed, subscriptions are de-registered for later reference - * @param {boolean} reconnect - true or false to reconnect the socket - * @param {string} code - code associated with the socket - * @param {string} reason - string with the response - * @return {undefined} - */ - const handleSocketClose = function (reconnect, code, reason) { - delete Binance.subscriptions[this.endpoint]; - if (Binance.subscriptions && Object.keys(Binance.subscriptions).length === 0) { - clearInterval(Binance.socketHeartbeatInterval); - } - Binance.options.log('WebSocket closed: ' + this.endpoint + - (code ? ' (' + code + ')' : '') + - (reason ? ' ' + reason : '')); - if (Binance.options.reconnect && this.reconnect && reconnect) { - if (this.endpoint && parseInt(this.endpoint.length, 10) === 60) Binance.options.log('Account data WebSocket reconnecting...'); - else Binance.options.log('WebSocket reconnecting: ' + this.endpoint + '...'); - try { - reconnect(); - } catch (error) { - Binance.options.log('WebSocket reconnect error: ' + error.message); - } - } - }; - - /** - * Called when socket errors - * @param {object} error - error object message - * @return {undefined} - */ - const handleSocketError = function (error) { - /* Errors ultimately result in a `close` event. - see: https://github.com/websockets/ws/blob/828194044bf247af852b31c49e2800d557fedeff/lib/websocket.js#L126 */ - Binance.options.log('WebSocket error: ' + this.endpoint + - (error.code ? ' (' + error.code + ')' : '') + - (error.message ? ' ' + error.message : '')); - }; - - /** - * Called on each socket heartbeat - * @return {undefined} - */ - const handleSocketHeartbeat = function () { - this.isAlive = true; - }; - - /** - * Used to subscribe to a single websocket endpoint - * @param {string} endpoint - endpoint to connect to - * @param {function} callback - the function to called when information is received - * @param {boolean} reconnect - whether to reconnect on disconnect - * @param {object} opened_callback - the function to called when opened - * @return {WebSocket} - websocket reference - */ - const subscribe = function (endpoint, callback, reconnect = false, opened_callback = false) { - - let httpsproxy = process.env.https_proxy || false; - let socksproxy = process.env.socks_proxy || false; - let ws = false; - - if (socksproxy !== false) { - socksproxy = proxyReplacewithIp(socksproxy); - if (Binance.options.verbose) Binance.options.log('using socks proxy server ' + socksproxy); - let agent = new SocksProxyAgent({ - protocol: parseProxy(socksproxy)[0], - host: parseProxy(socksproxy)[1], - port: parseProxy(socksproxy)[2] - }); - ws = new WebSocket(stream + endpoint, { agent: agent }); - } else if (httpsproxy !== false) { - if (Binance.options.verbose) Binance.options.log('using proxy server ' + agent); - let config = url.parse(httpsproxy); - let agent = new HttpsProxyAgent(config); - ws = new WebSocket(stream + endpoint, { agent: agent }); - } else { - ws = new WebSocket(stream + endpoint); - } - - if (Binance.options.verbose) Binance.options.log('Subscribed to ' + endpoint); - ws.reconnect = Binance.options.reconnect; - ws.endpoint = endpoint; - ws.isAlive = false; - ws.on('open', handleSocketOpen.bind(ws, opened_callback)); - ws.on('pong', handleSocketHeartbeat); - ws.on('error', handleSocketError); - ws.on('close', handleSocketClose.bind(ws, reconnect)); - ws.on('message', function (data) { - try { - callback(JSON.parse(data)); - } catch (error) { - Binance.options.log('Parse error: ' + error.message); - } - }); - return ws; - }; - - /** - * Used to subscribe to a combined websocket endpoint - * @param {string} streams - streams to connect to - * @param {function} callback - the function to called when information is received - * @param {boolean} reconnect - whether to reconnect on disconnect - * @param {object} opened_callback - the function to called when opened - * @return {WebSocket} - websocket reference - */ - const subscribeCombined = function (streams, callback, reconnect = false, opened_callback = false) { - - let httpsproxy = process.env.https_proxy || false; - let socksproxy = process.env.socks_proxy || false; - const queryParams = streams.join('/'); - let ws = false; - - if (socksproxy !== false) { - socksproxy = proxyReplacewithIp(socksproxy); - if (Binance.options.verbose) Binance.options.log('using socks proxy server ' + socksproxy); - let agent = new SocksProxyAgent({ - protocol: parseProxy(socksproxy)[0], - host: parseProxy(socksproxy)[1], - port: parseProxy(socksproxy)[2] - }); - ws = new WebSocket(combineStream + queryParams, { agent: agent }); - } else if (httpsproxy !== false) { - if (Binance.options.verbose) Binance.options.log('using proxy server ' + httpsproxy); - let config = url.parse(httpsproxy); - let agent = new HttpsProxyAgent(config); - ws = new WebSocket(combineStream + queryParams, { agent: agent }); - } else { - ws = new WebSocket(combineStream + queryParams); - } - - ws.reconnect = Binance.options.reconnect; - ws.endpoint = stringHash(queryParams); - ws.isAlive = false; - if (Binance.options.verbose) { - Binance.options.log('CombinedStream: Subscribed to [' + ws.endpoint + '] ' + queryParams); - } - ws.on('open', handleSocketOpen.bind(ws, opened_callback)); - ws.on('pong', handleSocketHeartbeat); - ws.on('error', handleSocketError); - ws.on('close', handleSocketClose.bind(ws, reconnect)); - ws.on('message', function (data) { - try { - callback(JSON.parse(data).data); - } catch (error) { - Binance.options.log('CombinedStream: Parse error: ' + error.message); - } - }); - return ws; - }; - - /** - * Used to terminate a web socket - * @param {string} endpoint - endpoint identifier associated with the web socket - * @param {boolean} reconnect - auto reconnect after termination - * @return {undefined} - */ - const terminate = function (endpoint, reconnect = false) { - let ws = Binance.subscriptions[endpoint]; - if (!ws) return; - ws.removeAllListeners('message'); - ws.reconnect = reconnect; - ws.terminate(); - } - - /** - * Used as part of the user data websockets callback - * @param {object} data - user data callback data type - * @return {undefined} - */ - const userDataHandler = function (data) { - let type = data.e; - if (type === 'outboundAccountInfo') { - Binance.options.balance_callback(data); - } else if (type === 'executionReport') { - if (Binance.options.execution_callback) Binance.options.execution_callback(data); - } else { - Binance.options.log('Unexpected userData: ' + type); - } - }; - - /** - * Parses the previous day stream and calls the user callback with friendly object - * @param {object} data - user data callback data type - * @param {function} callback - user data callback data type - * @return {undefined} - */ - const prevDayStreamHandler = function (data, callback) { - let { - e: eventType, - E: eventTime, - s: symbol, - p: priceChange, - P: percentChange, - w: averagePrice, - x: prevClose, - c: close, - Q: closeQty, - b: bestBid, - B: bestBidQty, - a: bestAsk, - A: bestAskQty, - o: open, - h: high, - l: low, - v: volume, - q: quoteVolume, - O: openTime, - C: closeTime, - F: firstTradeId, - L: lastTradeId, - n: numTrades - } = data; - callback(null, { - eventType, - eventTime, - symbol, - priceChange, - percentChange, - averagePrice, - prevClose, - close, - closeQty, - bestBid, - bestBidQty, - bestAsk, - bestAskQty, - open, - high, - low, - volume, - quoteVolume, - openTime, - closeTime, - firstTradeId, - lastTradeId, - numTrades - }); - }; - - /** - * Gets the price of a given symbol or symbols - * @param {array} data - array of symbols - * @return {array} - symbols with their current prices - */ - const priceData = function (data) { - const prices = {}; - if (Array.isArray(data)) { - for (let obj of data) { - prices[obj.symbol] = obj.price; - } - } else { // Single price returned - prices[data.symbol] = data.price; - } - return prices; - }; - - /** - * Used by bookTickers to format the bids and asks given given symbols - * @param {array} data - array of symbols - * @return {object} - symbols with their bids and asks data - */ - const bookPriceData = function (data) { - let prices = {}; - for (let obj of data) { - prices[obj.symbol] = { - bid: obj.bidPrice, - bids: obj.bidQty, - ask: obj.askPrice, - asks: obj.askQty - }; - } - return prices; - }; - - /** - * Used by balance to get the balance data - * @param {array} data - account info object - * @return {object} - balances hel with available, onorder amounts - */ - const balanceData = function (data) { - let balances = {}; - if (typeof data === 'undefined') return {}; - if (typeof data.balances === 'undefined') { - Binance.options.log('balanceData error', data); - return {}; - } - for (let obj of data.balances) { - balances[obj.asset] = { available: obj.free, onOrder: obj.locked }; - } - return balances; - }; - - /** - * Used by web sockets depth and populates OHLC and info - * @param {string} symbol - symbol to get candlestick info - * @param {string} interval - time interval, 1m, 3m, 5m .... - * @param {array} ticks - tick array - * @return {undefined} - */ - const klineData = function (symbol, interval, ticks) { // Used for /depth - let last_time = 0; - if (isIterable(ticks)) { - for (let tick of ticks) { - // eslint-disable-next-line no-unused-vars - let [time, open, high, low, close, volume, closeTime, assetVolume, trades, buyBaseVolume, buyAssetVolume, ignored] = tick; - Binance.ohlc[symbol][interval][time] = { open: open, high: high, low: low, close: close, volume: volume }; - last_time = time; - } - - Binance.info[symbol][interval].timestamp = last_time; - } - }; - - /** - * Combines all OHLC data with latest update - * @param {string} symbol - the symbol - * @param {string} interval - time interval, 1m, 3m, 5m .... - * @return {array} - interval data for given symbol - */ - const klineConcat = function (symbol, interval) { - let output = Binance.ohlc[symbol][interval]; - if (typeof Binance.ohlcLatest[symbol][interval].time === 'undefined') return output; - const time = Binance.ohlcLatest[symbol][interval].time; - const last_updated = Object.keys(Binance.ohlc[symbol][interval]).pop(); - if (time >= last_updated) { - output[time] = Binance.ohlcLatest[symbol][interval]; - delete output[time].time; - output[time].isFinal = false; - } - return output; - }; - - /** - * Used for websocket @kline - * @param {string} symbol - the symbol - * @param {object} kline - object with kline info - * @param {string} firstTime - time filter - * @return {undefined} - */ - const klineHandler = function (symbol, kline, firstTime = 0) { - // TODO: add Taker buy base asset volume - // eslint-disable-next-line no-unused-vars - let { e: eventType, E: eventTime, k: ticks } = kline; - // eslint-disable-next-line no-unused-vars - let { o: open, h: high, l: low, c: close, v: volume, i: interval, x: isFinal, q: quoteVolume, t: time } = ticks; //n:trades, V:buyVolume, Q:quoteBuyVolume - if (time <= firstTime) return; - if (!isFinal) { - if (typeof Binance.ohlcLatest[symbol][interval].time !== 'undefined') { - if (Binance.ohlcLatest[symbol][interval].time > time) return; - } - Binance.ohlcLatest[symbol][interval] = { open: open, high: high, low: low, close: close, volume: volume, time: time }; - return; - } - // Delete an element from the beginning so we don't run out of memory - const first_updated = Object.keys(Binance.ohlc[symbol][interval]).shift(); - if (first_updated) delete Binance.ohlc[symbol][interval][first_updated]; - Binance.ohlc[symbol][interval][time] = { open: open, high: high, low: low, close: close, volume: volume }; - }; - - /** - * Used for /depth endpoint - * @param {object} data - containing the bids and asks - * @return {undefined} - */ - const depthData = function (data) { - if (!data) return { bids: [], asks: [] }; - let bids = {}, asks = {}, obj; - if (typeof data.bids !== 'undefined') { - for (obj of data.bids) { - bids[obj[0]] = parseFloat(obj[1]); - } - } - if (typeof data.asks !== 'undefined') { - for (obj of data.asks) { - asks[obj[0]] = parseFloat(obj[1]); - } - } - return { lastUpdateId: data.lastUpdateId, bids: bids, asks: asks }; - } - - /** - * Used for /depth endpoint - * @param {object} depth - information - * @return {undefined} - */ - const depthHandler = function (depth) { - let symbol = depth.s, obj; - let context = Binance.depthCacheContext[symbol]; - - let updateDepthCache = function () { - Binance.depthCache[symbol].eventTime = depth.E; - for (obj of depth.b) { //bids - Binance.depthCache[symbol].bids[obj[0]] = parseFloat(obj[1]); - if (obj[1] === '0.00000000') { - delete Binance.depthCache[symbol].bids[obj[0]]; - } - } - for (obj of depth.a) { //asks - Binance.depthCache[symbol].asks[obj[0]] = parseFloat(obj[1]); - if (obj[1] === '0.00000000') { - delete Binance.depthCache[symbol].asks[obj[0]]; - } - } - context.skipCount = 0; - context.lastEventUpdateId = depth.u; - context.lastEventUpdateTime = depth.E; - } - - // This now conforms 100% to the Binance docs constraints on managing a local order book - if (context.lastEventUpdateId) { - const expectedUpdateId = context.lastEventUpdateId + 1; - if (depth.U <= expectedUpdateId) { - updateDepthCache(); - } else { - let msg = 'depthHandler: [' + symbol + '] The depth cache is out of sync.'; - msg += ' Symptom: Unexpected Update ID. Expected "' + expectedUpdateId + '", got "' + depth.U + '"'; - if (Binance.options.verbose) Binance.options.log(msg); - throw new Error(msg); - } - } else if (depth.U > context.snapshotUpdateId + 1) { - /* In this case we have a gap between the data of the stream and the snapshot. - This is an out of sync error, and the connection must be torn down and reconnected. */ - let msg = 'depthHandler: [' + symbol + '] The depth cache is out of sync.'; - msg += ' Symptom: Gap between snapshot and first stream data.'; - if (Binance.options.verbose) Binance.options.log(msg); - throw new Error(msg); - } else if (depth.u < context.snapshotUpdateId + 1) { - /* In this case we've received data that we've already had since the snapshot. - This isn't really an issue, and we can just update the cache again, or ignore it entirely. */ - - // do nothing - } else { - // This is our first legal update from the stream data - updateDepthCache(); - } - }; - - /** - * Gets depth cache for given symbol - * @param {string} symbol - the symbol to fetch - * @return {object} - the depth cache object - */ - const getDepthCache = function (symbol) { - if (typeof Binance.depthCache[symbol] === 'undefined') return { bids: {}, asks: {} }; - return Binance.depthCache[symbol]; - }; - - /** - * Calculate Buy/Sell volume from DepthCache - * @param {string} symbol - the symbol to fetch - * @return {object} - the depth volume cache object - */ - const depthVolume = function (symbol) { - let cache = getDepthCache(symbol), quantity, price; - let bidbase = 0, askbase = 0, bidqty = 0, askqty = 0; - for (price in cache.bids) { - quantity = cache.bids[price]; - bidbase += parseFloat((quantity * parseFloat(price)).toFixed(8)); - bidqty += quantity; - } - for (price in cache.asks) { - quantity = cache.asks[price]; - askbase += parseFloat((quantity * parseFloat(price)).toFixed(8)); - askqty += quantity; - } - return { bids: bidbase, asks: askbase, bidQty: bidqty, askQty: askqty }; - }; - - /** - * Checks whether or not an array contains any duplicate elements - * Note(keith1024): at the moment this only works for primitive types, - * will require modification to work with objects - * @param {array} array - the array to check - * @return {boolean} - true or false - */ - const isArrayUnique = function (array) { - let s = new Set(array); - return s.size === array.length; - }; - return { - - /** - * Gets depth cache for given symbol - * @param {symbol} symbol - get depch cache for this symbol - * @return {object} - object - */ - depthCache: function (symbol) { - return getDepthCache(symbol); - }, - - /** - * Gets depth volume for given symbol - * @param {symbol} symbol - get depch volume for this symbol - * @return {object} - object - */ - depthVolume: function (symbol) { - return depthVolume(symbol); - }, - - /** - * Count decimal places - * @param {float} float - get the price precision point - * @return {int} - number of place - */ - getPrecision: function (float) { - if ( !float || Number.isInteger( float ) ) return 0; - return float.toString().split('.')[1].length || 0; - }, - - /** - * rounds number with given step - * @param {float} qty - quantity to round - * @param {float} stepSize - stepSize as specified by exchangeInfo - * @return {float} - number - */ - roundStep: function (qty, stepSize) { - // Integers do not require rounding - if (Number.isInteger(qty)) return qty; - const qtyString = qty.toFixed(16); - const desiredDecimals = Math.max(stepSize.indexOf('1') - 1, 0); - const decimalIndex = qtyString.indexOf('.'); - return parseFloat(qtyString.slice(0, decimalIndex + desiredDecimals + 1)); - }, - - /** - * rounds price to required precision - * @param {float} price - price to round - * @param {float} tickSize - tickSize as specified by exchangeInfo - * @return {float} - number - */ - roundTicks: function (price, tickSize) { - const formatter = new Intl.NumberFormat('en-US', { style: 'decimal', minimumFractionDigits: 0, maximumFractionDigits: 8 }); - const precision = formatter.format(tickSize).split('.')[1].length || 0; - if (typeof price === 'string') price = parseFloat(price); - return price.toFixed(precision); - }, - - /** - * Gets percentage of given numbers - * @param {float} min - the smaller number - * @param {float} max - the bigger number - * @param {int} width - percentage width - * @return {float} - percentage - */ - percent: function (min, max, width = 100) { - return (min * 0.01) / (max * 0.01) * width; - }, - - /** - * Gets the sum of an array of numbers - * @param {array} array - the number to add - * @return {float} - sum - */ - sum: function (array) { - return array.reduce((a, b) => a + b, 0); - }, - - /** - * Reverses the keys of an object - * @param {object} object - the object - * @return {object} - the object - */ - reverse: function (object) { - let range = Object.keys(object).reverse(), output = {}; - for (let price of range) { - output[price] = object[price]; - } - return output; - }, - - /** - * Converts an object to an array - * @param {object} obj - the object - * @return {array} - the array - */ - array: function (obj) { - return Object.keys(obj).map(function (key) { - return [Number(key), obj[key]]; - }); - }, - - /** - * Sorts bids - * @param {string} symbol - the object - * @param {int} max - the max number of bids - * @param {string} baseValue - the object - * @return {object} - the object - */ - sortBids: function (symbol, max = Infinity, baseValue = false) { - let object = {}, count = 0, cache; - if (typeof symbol === 'object') cache = symbol; - else cache = getDepthCache(symbol).bids; - let sorted = Object.keys(cache).sort(function (a, b) { - return parseFloat(b) - parseFloat(a) - }); - let cumulative = 0; - for (let price of sorted) { - if (baseValue === 'cumulative') { - cumulative += parseFloat(cache[price]); - object[price] = cumulative; - } else if (!baseValue) object[price] = parseFloat(cache[price]); - else object[price] = parseFloat((cache[price] * parseFloat(price)).toFixed(8)); - if (++count >= max) break; - } - return object; - }, - - /** - * Sorts asks - * @param {string} symbol - the object - * @param {int} max - the max number of bids - * @param {string} baseValue - the object - * @return {object} - the object - */ - sortAsks: function (symbol, max = Infinity, baseValue = false) { - let object = {}, count = 0, cache; - if (typeof symbol === 'object') cache = symbol; - else cache = getDepthCache(symbol).asks; - let sorted = Object.keys(cache).sort(function (a, b) { - return parseFloat(a) - parseFloat(b); - }); - let cumulative = 0; - for (let price of sorted) { - if (baseValue === 'cumulative') { - cumulative += parseFloat(cache[price]); - object[price] = cumulative; - } else if (!baseValue) object[price] = parseFloat(cache[price]); - else object[price] = parseFloat((cache[price] * parseFloat(price)).toFixed(8)); - if (++count >= max) break; - } - return object; - }, - - /** - * Returns the first property of an object - * @param {object} object - the object to get the first member - * @return {string} - the object key - */ - first: function (object) { - return Object.keys(object).shift(); - }, - - /** - * Returns the last property of an object - * @param {object} object - the object to get the first member - * @return {string} - the object key - */ - last: function (object) { - return Object.keys(object).pop(); - }, - - /** - * Returns an array of properties starting at start - * @param {object} object - the object to get the properties form - * @param {int} start - the starting index - * @return {array} - the array of entires - */ - slice: function (object, start = 0) { - return Object.entries(object).slice(start).map(entry => entry[0]); - }, - - /** - * Gets the minimum key form object - * @param {object} object - the object to get the properties form - * @return {string} - the minimum key - */ - min: function (object) { - return Math.min.apply(Math, Object.keys(object)); - }, - - /** - * Gets the maximum key form object - * @param {object} object - the object to get the properties form - * @return {string} - the minimum key - */ - max: function (object) { - return Math.max.apply(Math, Object.keys(object)); - }, - - /** - * Sets an option given a key and value - * @param {string} key - the key to set - * @param {object} value - the value of the key - * @return {undefined} - */ - setOption: function (key, value) { - Binance.options[key] = value; - }, - - /** - * Gets an option given a key - * @param {string} key - the key to set - * @return {undefined} - */ - getOption: function (key) { - return Binance.options[key]; - }, - - /** - * Returns the entire info object - * @return {object} - the info object - */ - getInfo: function () { - return Binance.info; - }, - - /** - * Returns the entire options object - * @return {object} - the options object - */ - getOptions: function () { - return Binance.options; - }, - - /** - * Gets an option given a key - * @param {object} opt - the object with the class configuration - * @param {function} callback - the callback function - * @return {undefined} - */ - options: function (opt, callback = false) { - if (typeof opt === 'string') { // Pass json config filename - Binance.options = JSON.parse(file.readFileSync(opt)); - } else Binance.options = opt; - if (typeof Binance.options.recvWindow === 'undefined') Binance.options.recvWindow = default_options.recvWindow; - if (typeof Binance.options.useServerTime === 'undefined') Binance.options.useServerTime = default_options.useServerTime; - if (typeof Binance.options.reconnect === 'undefined') Binance.options.reconnect = default_options.reconnect; - if (typeof Binance.options.test === 'undefined') Binance.options.test = default_options.test; - if (typeof Binance.options.log === 'undefined') Binance.options.log = default_options.log; - if (typeof Binance.options.verbose === 'undefined') Binance.options.verbose = default_options.verbose; - if (Binance.options.useServerTime) { - apiRequest(base + 'v1/time', {}, function (error, response) { - Binance.info.timeOffset = response.serverTime - new Date().getTime(); - //Binance.options.log("server time set: ", response.serverTime, Binance.info.timeOffset); - if (callback) callback(); - }); - } else if (callback) callback(); - return this; - }, - - - /** - * Creates an order - * @param {string} side - BUY or SELL - * @param {string} symbol - the symbol to buy - * @param {numeric} quantity - the quantity required - * @param {numeric} price - the price to pay for each unit - * @param {object} flags - aadditionalbuy order flags - * @param {function} callback - the callback function - * @return {undefined} - */ - order: function (side, symbol, quantity, price, flags = {}, callback = false) { - order(side, symbol, quantity, price, flags, callback); - }, - - /** - * Creates a buy order - * @param {string} symbol - the symbol to buy - * @param {numeric} quantity - the quantity required - * @param {numeric} price - the price to pay for each unit - * @param {object} flags - additional buy order flags - * @param {function} callback - the callback function - * @return {undefined} - */ - buy: function (symbol, quantity, price, flags = {}, callback = false) { - order('BUY', symbol, quantity, price, flags, callback); - }, - - /** - * Creates a sell order - * @param {string} symbol - the symbol to sell - * @param {numeric} quantity - the quantity required - * @param {numeric} price - the price to sell each unit for - * @param {object} flags - additional order flags - * @param {function} callback - the callback function - * @return {undefined} - */ - sell: function (symbol, quantity, price, flags = {}, callback = false) { - order('SELL', symbol, quantity, price, flags, callback); - }, - - /** - * Creates a market buy order - * @param {string} symbol - the symbol to buy - * @param {numeric} quantity - the quantity required - * @param {object} flags - additional buy order flags - * @param {function} callback - the callback function - * @return {undefined} - */ - marketBuy: function (symbol, quantity, flags = { type: 'MARKET' }, callback = false) { - if (typeof flags === 'function') { // Accept callback as third parameter - callback = flags; - flags = { type: 'MARKET' }; - } - if (typeof flags.type === 'undefined') flags.type = 'MARKET'; - order('BUY', symbol, quantity, 0, flags, callback); - }, - - /** - * Creates a market sell order - * @param {string} symbol - the symbol to sell - * @param {numeric} quantity - the quantity required - * @param {object} flags - additional sell order flags - * @param {function} callback - the callback function - * @return {undefined} - */ - marketSell: function (symbol, quantity, flags = { type: 'MARKET' }, callback = false) { - if (typeof flags === 'function') { // Accept callback as third parameter - callback = flags; - flags = { type: 'MARKET' }; - } - if (typeof flags.type === 'undefined') flags.type = 'MARKET'; - order('SELL', symbol, quantity, 0, flags, callback); - }, - - /** - * Cancels an order - * @param {string} symbol - the symbol to cancel - * @param {string} orderid - the orderid to cancel - * @param {function} callback - the callback function - * @return {undefined} - */ - cancel: function (symbol, orderid, callback = false) { - signedRequest(base + 'v3/order', { symbol: symbol, orderId: orderid }, function (error, data) { - if (callback) return callback.call(this, error, data, symbol); - }, 'DELETE'); - }, - - /** - * Gets the status of an order - * @param {string} symbol - the symbol to check - * @param {string} orderid - the orderid to check - * @param {function} callback - the callback function - * @param {object} flags - any additional flags - * @return {undefined} - */ - orderStatus: function (symbol, orderid, callback, flags = {}) { - let parameters = Object.assign({ symbol: symbol, orderId: orderid }, flags); - signedRequest(base + 'v3/order', parameters, function (error, data) { - if (callback) return callback.call(this, error, data, symbol); - }); - }, - - /** - * Gets open orders - * @param {string} symbol - the symbol to get - * @param {function} callback - the callback function - * @return {undefined} - */ - openOrders: function (symbol, callback) { - let parameters = symbol ? { symbol: symbol } : {}; - signedRequest(base + 'v3/openOrders', parameters, function (error, data) { - return callback.call(this, error, data, symbol); - }); - }, - - /** - * Cancels all order of a given symbol - * @param {string} symbol - the symbol to cancel all orders for - * @param {function} callback - the callback function - * @return {undefined} - */ - cancelOrders: function (symbol, callback = false) { - signedRequest(base + 'v3/openOrders', { symbol: symbol }, function (error, json) { - if (json.length === 0) { - if (callback) return callback.call(this, 'No orders present for this symbol', {}, symbol); - } - for (let obj of json) { - let quantity = obj.origQty - obj.executedQty; - Binance.options.log('cancel order: ' + obj.side + ' ' + symbol + ' ' + quantity + ' @ ' + obj.price + ' #' + obj.orderId); - signedRequest(base + 'v3/order', { symbol: symbol, orderId: obj.orderId }, function (error, data) { - if (callback) return callback.call(this, error, data, symbol); - }, 'DELETE'); - } - }); - }, - - /** - * Gets all order of a given symbol - * @param {string} symbol - the symbol - * @param {function} callback - the callback function - * @param {object} options - additional options - * @return {undefined} - */ - allOrders: function (symbol, callback, options = {}) { - let parameters = Object.assign({ symbol: symbol }, options); - signedRequest(base + 'v3/allOrders', parameters, function (error, data) { - if (callback) return callback.call(this, error, data, symbol); - }); - }, - - /** - * Gets the depth information for a given symbol - * @param {string} symbol - the symbol - * @param {function} callback - the callback function - * @param {int} limit - limit the number of returned orders - * @return {undefined} - */ - depth: function (symbol, callback, limit = 100) { - publicRequest(base + 'v1/depth', { symbol: symbol, limit: limit }, function (error, data) { - return callback.call(this, error, depthData(data), symbol); - }); - }, - - /** - * Gets the average prices of a given symbol - * @param {string} symbol - the symbol - * @param {function} callback - the callback function - * @return {undefined} - */ - avgPrice: function (symbol, callback = false) { - let opt = { - url: base + 'v3/avgPrice?symbol=' + symbol, - timeout: Binance.options.recvWindow - }; - - request(addProxy(opt), function (error, response, body) { - if (!callback) return; - - if (error) return callback(error); - - if (response && response.statusCode !== 200) return callback(response); - - if (callback) return callback(null, priceData(JSON.parse(body))); - }); - }, - - /** - * Gets the prices of a given symbol(s) - * @param {string} symbol - the symbol - * @param {function} callback - the callback function - * @return {undefined} - */ - prices: function (symbol, callback = false) { - const params = typeof symbol === 'string' ? '?symbol=' + symbol : ''; - if (typeof symbol === 'function') callback = symbol; // backwards compatibility - - let opt = { - url: base + 'v3/ticker/price' + params, - timeout: Binance.options.recvWindow - }; - - request(addProxy(opt), function (error, response, body) { - if (!callback) return; - - if (error) return callback(error); - - if (response && response.statusCode !== 200) return callback(response); - - if (callback) return callback(null, priceData(JSON.parse(body))); - }); - }, - - /** - * Gets the book tickers of given symbol(s) - * @param {string} symbol - the symbol - * @param {function} callback - the callback function - * @return {undefined} - */ - bookTickers: function (symbol, callback) { - const params = typeof symbol === 'string' ? '?symbol=' + symbol : ''; - if (typeof symbol === 'function') callback = symbol; // backwards compatibility - - let opt = { - url: base + 'v3/ticker/bookTicker' + params, - timeout: Binance.options.recvWindow - }; - - request(addProxy(opt), function (error, response, body) { - if (!callback) return; - - if (error) return callback(error); - - if (response && response.statusCode !== 200) return callback(response); - - if (callback) { - const result = symbol ? JSON.parse(body) : bookPriceData(JSON.parse(body)); - return callback(null, result); - } - }); - }, - - /** - * Gets the prevday percentage change - * @param {string} symbol - the symbol or symbols - * @param {function} callback - the callback function - * @return {undefined} - */ - prevDay: function (symbol, callback) { - let input = symbol ? { symbol: symbol } : {}; - publicRequest(base + 'v1/ticker/24hr', input, function (error, data) { - if (callback) return callback.call(this, error, data, symbol); - }); - }, - - /** - * Gets the the exchange info - * @param {function} callback - the callback function - * @return {undefined} - */ - exchangeInfo: function (callback) { - publicRequest(base + 'v1/exchangeInfo', {}, callback); - }, - /** - * Gets the dust log for user - * @param {function} callback - the callback function - * @return {undefined} - */ - dustLog: function (callback) { - signedRequest(wapi + '/v3/userAssetDribbletLog.html', {}, callback); - }, - /** - * Gets the the system status - * @param {function} callback - the callback function - * @return {undefined} - */ - systemStatus: function (callback) { - publicRequest(wapi + 'v3/systemStatus.html', {}, callback); - }, - - /** - * Withdraws asset to given wallet id - * @param {string} asset - the asset symbol - * @param {string} address - the wallet to transfer it to - * @param {number} amount - the amount to transfer - * @param {string} addressTag - and addtional address tag - * @param {function} callback - the callback function - * @return {undefined} - */ - withdraw: function (asset, address, amount, addressTag = false, callback = false) { - let params = { asset, address, amount }; - params.name = 'API Withdraw'; - if (addressTag) params.addressTag = addressTag; - signedRequest(wapi + 'v3/withdraw.html', params, callback, 'POST'); - }, - - /** - * Get the Withdraws history for a given asset - * @param {function} callback - the callback function - * @param {object} params - supports limit and fromId parameters - * @return {undefined} - */ - withdrawHistory: function (callback, params = {}) { - if (typeof params === 'string') params = { asset: params }; - signedRequest(wapi + 'v3/withdrawHistory.html', params, callback); - }, - - /** - * Get the deposit history - * @param {function} callback - the callback function - * @param {object} params - additional params - * @return {undefined} - */ - depositHistory: function (callback, params = {}) { - if (typeof params === 'string') params = { asset: params }; // Support 'asset' (string) or optional parameters (object) - signedRequest(wapi + 'v3/depositHistory.html', params, callback); - }, - - /** - * Get the deposit history for given asset - * @param {string} asset - the asset - * @param {function} callback - the callback function - * @return {undefined} - */ - depositAddress: function (asset, callback) { - signedRequest(wapi + 'v3/depositAddress.html', { asset: asset }, callback); - }, - - /** - * Get the account status - * @param {function} callback - the callback function - * @return {undefined} - */ - accountStatus: function (callback) { - signedRequest(wapi + 'v3/accountStatus.html', {}, callback); - }, - - /** - * Get the trade fee - * @param {function} callback - the callback function - * @param {string} symbol (optional) - * @return {undefined} - */ - tradeFee: function (callback, symbol = false) { - let params = symbol ? { symbol: symbol } : {}; - signedRequest(wapi + 'v3/tradeFee.html', params, callback); - }, - - /** - * Fetch asset detail (minWithdrawAmount, depositStatus, withdrawFee, withdrawStatus, depositTip) - * @param {function} callback - the callback function - * @return {undefined} - */ - assetDetail: function (callback) { - signedRequest(wapi + 'v3/assetDetail.html', {}, callback); - }, - - /** - * Get the account - * @param {function} callback - the callback function - * @return {undefined} - */ - account: function (callback) { - signedRequest(base + 'v3/account', {}, callback); - }, - - /** - * Get the balance data - * @param {function} callback - the callback function - * @return {undefined} - */ - balance: function (callback) { - signedRequest(base + 'v3/account', {}, function (error, data) { - if (callback) callback(error, balanceData(data)); - }); - }, - - /** - * Get trades for a given symbol - * @param {string} symbol - the symbol - * @param {function} callback - the callback function - * @param {object} options - additional options - * @return {undefined} - */ - trades: function (symbol, callback, options = {}) { - let parameters = Object.assign({ symbol: symbol }, options); - signedRequest(base + 'v3/myTrades', parameters, function (error, data) { - if (callback) return callback.call(this, error, data, symbol); - }); - }, - - /** - * Tell api to use the server time to offset time indexes - * @param {function} callback - the callback function - * @return {undefined} - */ - useServerTime: function (callback = false) { - apiRequest(base + 'v1/time', {}, function (error, response) { - Binance.info.timeOffset = response.serverTime - new Date().getTime(); - //Binance.options.log("server time set: ", response.serverTime, Binance.info.timeOffset); - if (callback) callback(); - }); - }, - - /** - * Gets the time - * @param {function} callback - the callback function - * @return {undefined} - */ - time: function (callback) { - apiRequest(base + 'v1/time', {}, callback); - }, - - /** - * Get agg trades for given symbol - * @param {string} symbol - the symbol - * @param {object} options - addtional optoins - * @param {function} callback - the callback function - * @return {undefined} - */ - aggTrades: function (symbol, options = {}, callback = false) { //fromId startTime endTime limit - let parameters = Object.assign({ symbol }, options); - marketRequest(base + 'v1/aggTrades', parameters, callback); - }, - - /** - * Get the recent trades - * @param {string} symbol - the symbol - * @param {function} callback - the callback function - * @param {int} limit - limit the number of items returned - * @return {undefined} - */ - recentTrades: function (symbol, callback, limit = 500) { - marketRequest(base + 'v1/trades', { symbol: symbol, limit: limit }, callback); - }, - - /** - * Get the historical trade info - * @param {string} symbol - the symbol - * @param {function} callback - the callback function - * @param {int} limit - limit the number of items returned - * @param {int} fromId - from this id - * @return {undefined} - */ - historicalTrades: function (symbol, callback, limit = 500, fromId = false) { - let parameters = { symbol: symbol, limit: limit }; - if (fromId) parameters.fromId = fromId; - marketRequest(base + 'v1/historicalTrades', parameters, callback); - }, - - /** - * Convert chart data to highstock array [timestamp,open,high,low,close] - * @param {object} chart - the chart - * @param {boolean} include_volume - to include the volume or not - * @return {array} - an array - */ - highstock: function (chart, include_volume = false) { - let array = []; - for (let timestamp in chart) { - let obj = chart[timestamp]; - let line = [ - Number(timestamp), - parseFloat(obj.open), - parseFloat(obj.high), - parseFloat(obj.low), - parseFloat(obj.close) - ]; - if (include_volume) line.push(parseFloat(obj.volume)); - array.push(line); - } - return array; - }, - - /** - * Populates hte OHLC information - * @param {object} chart - the chart - * @return {object} - object with candle information - */ - ohlc: function (chart) { - let open = [], high = [], low = [], close = [], volume = []; - for (let timestamp in chart) { //Binance.ohlc[symbol][interval] - let obj = chart[timestamp]; - open.push(parseFloat(obj.open)); - high.push(parseFloat(obj.high)); - low.push(parseFloat(obj.low)); - close.push(parseFloat(obj.close)); - volume.push(parseFloat(obj.volume)); - } - return { open: open, high: high, low: low, close: close, volume: volume }; - }, - - /** - * Gets the candles information for a given symbol - * intervals: 1m,3m,5m,15m,30m,1h,2h,4h,6h,8h,12h,1d,3d,1w,1M - * @param {string} symbol - the symbol - * @param {function} interval - the callback function - * @param {function} callback - the callback function - * @param {object} options - additional options - * @return {undefined} - */ - candlesticks: function (symbol, interval = '5m', callback = false, options = { limit: 500 }) { - if (!callback) return; - let params = Object.assign({ symbol: symbol, interval: interval }, options); - publicRequest(base + 'v1/klines', params, function (error, data) { - return callback.call(this, error, data, symbol); - }); - }, - - /** - * Queries the public api - * @param {string} url - the public api endpoint - * @param {object} data - the data to send - * @param {function} callback - the callback function - * @param {string} method - the http method - * @return {undefined} - */ - publicRequest: function (url, data, callback, method = 'GET') { - publicRequest(url, data, callback, method) - }, - - /** - * Queries the signed api - * @param {string} url - the signed api endpoint - * @param {object} data - the data to send - * @param {function} callback - the callback function - * @param {string} method - the http method - * @return {undefined} - */ - signedRequest: function (url, data, callback, method = 'GET') { - signedRequest(url, data, callback, method); - }, - - /** - * Gets the market asset of given symbol - * @param {string} symbol - the public api endpoint - * @return {undefined} - */ - getMarket: function (symbol) { - const substring = symbol.substr(-3); - if (substring === 'BTC') return 'BTC'; - else if (substring === 'ETH') return 'ETH'; - else if (substring === 'BNB') return 'BNB'; - else if (symbol.substr(-4) === 'USDT') return 'USDT'; - }, - websockets: { - /** - * Userdata websockets function - * @param {function} callback - the callback function - * @param {function} execution_callback - optional execution callback - * @param {function} subscribed_callback - subscription callback - * @return {undefined} - */ - userData: function userData(callback, execution_callback = false, subscribed_callback = false) { - let reconnect = function () { - if (Binance.options.reconnect) userData(callback, execution_callback, subscribed_callback); - }; - apiRequest(base + 'v1/userDataStream', {}, function (error, response) { - Binance.options.listenKey = response.listenKey; - setTimeout(function userDataKeepAlive() { // keepalive - try { - apiRequest(base + 'v1/userDataStream?listenKey=' + Binance.options.listenKey, {}, function (err) { - if (err) setTimeout(userDataKeepAlive, 60000); // retry in 1 minute - else setTimeout(userDataKeepAlive, 60 * 30 * 1000); // 30 minute keepalive - }, 'PUT'); - } catch (error) { - setTimeout(userDataKeepAlive, 60000); // retry in 1 minute - } - }, 60 * 30 * 1000); // 30 minute keepalive - Binance.options.balance_callback = callback; - Binance.options.execution_callback = execution_callback; - const subscription = subscribe(Binance.options.listenKey, userDataHandler, reconnect); - if (subscribed_callback) subscribed_callback(subscription.endpoint); - }, 'POST'); - }, - - /** - * Subscribe to a generic websocket - * @param {string} url - the websocket endpoint - * @param {function} callback - optional execution callback - * @param {boolean} reconnect - subscription callback - * @return {WebSocket} the websocket reference - */ - subscribe: function (url, callback, reconnect = false) { - return subscribe(url, callback, reconnect); - }, - - /** - * Subscribe to a generic combined websocket - * @param {string} url - the websocket endpoint - * @param {function} callback - optional execution callback - * @param {boolean} reconnect - subscription callback - * @return {WebSocket} the websocket reference - */ - subscribeCombined: function (url, callback, reconnect = false) { - return subscribeCombined(url, callback, reconnect); - }, - - /** - * Returns the known websockets subscriptions - * @return {array} array of web socket subscriptions - */ - subscriptions: function () { - return Binance.subscriptions; - }, - - /** - * Terminates a web socket - * @param {string} endpoint - the string associated with the endpoint - * @return {undefined} - */ - terminate: function (endpoint) { - if (Binance.options.verbose) Binance.options.log('WebSocket terminating:', endpoint); - return terminate(endpoint); - }, - - /** - * Websocket depth chart - * @param {array/string} symbols - an array or string of symbols to query - * @param {function} callback - callback function - * @return {string} the websocket endpoint - */ - depth: function depth(symbols, callback) { - let reconnect = function () { - if (Binance.options.reconnect) depth(symbols, callback); - }; - - let subscription; - if (Array.isArray(symbols)) { - if (!isArrayUnique(symbols)) throw Error('depth: "symbols" cannot contain duplicate elements.'); - let streams = symbols.map(function (symbol) { - return symbol.toLowerCase() + '@depth'; - }); - subscription = subscribeCombined(streams, callback, reconnect); - } else { - let symbol = symbols; - subscription = subscribe(symbol.toLowerCase() + '@depth', callback, reconnect); - } - return subscription.endpoint; - }, - - /** - * Websocket depth cache - * @param {array/string} symbols - an array or string of symbols to query - * @param {function} callback - callback function - * @param {int} limit - the number of entries - * @return {string} the websocket endpoint - */ - depthCache: function depthCacheFunction(symbols, callback, limit = 500) { - let reconnect = function () { - if (Binance.options.reconnect) depthCacheFunction(symbols, callback, limit); - }; - - let symbolDepthInit = function (symbol) { - if (typeof Binance.depthCacheContext[symbol] === 'undefined') Binance.depthCacheContext[symbol] = {}; - - let context = Binance.depthCacheContext[symbol]; - context.snapshotUpdateId = null; - context.lastEventUpdateId = null; - context.messageQueue = []; - - Binance.depthCache[symbol] = { bids: {}, asks: {} }; - }; - - let assignEndpointIdToContext = function (symbol, endpointId) { - if (Binance.depthCacheContext[symbol]) { - let context = Binance.depthCacheContext[symbol]; - context.endpointId = endpointId; - } - }; - - let handleDepthStreamData = function (depth) { - let symbol = depth.s; - let context = Binance.depthCacheContext[symbol]; - if (context.messageQueue && !context.snapshotUpdateId) { - context.messageQueue.push(depth); - } else { - try { - depthHandler(depth); - } catch (err) { - return terminate(context.endpointId, true); - } - if (callback) callback(symbol, Binance.depthCache[symbol], context); - } - }; - - let getSymbolDepthSnapshot = function (symbol, cb) { - - publicRequest(base + 'v1/depth', { symbol: symbol, limit: limit }, function (error, json) { - if (error) { - return cb(error, null); - } - // Store symbol next use - json.symb = symbol; - cb(null, json) - }); - }; - - let updateSymbolDepthCache = function (json) { - // Get previous store symbol - let symbol = json.symb; - // Initialize depth cache from snapshot - Binance.depthCache[symbol] = depthData(json); - // Prepare depth cache context - let context = Binance.depthCacheContext[symbol]; - context.snapshotUpdateId = json.lastUpdateId; - context.messageQueue = context.messageQueue.filter(depth => depth.u > context.snapshotUpdateId); - // Process any pending depth messages - for (let depth of context.messageQueue) { - - /* Although sync errors shouldn't ever happen here, we catch and swallow them anyway - just in case. The stream handler function above will deal with broken caches. */ - try { - depthHandler(depth); - } catch (err) { - // do nothing - } - } - delete context.messageQueue; - if (callback) callback(symbol, Binance.depthCache[symbol]); - }; - - /* If an array of symbols are sent we use a combined stream connection rather. - This is transparent to the developer, and results in a single socket connection. - This essentially eliminates "unexpected response" errors when subscribing to a lot of data. */ - let subscription; - if (Array.isArray(symbols)) { - if (!isArrayUnique(symbols)) throw Error('depthCache: "symbols" cannot contain duplicate elements.'); - - symbols.forEach(symbolDepthInit); - let streams = symbols.map(function (symbol) { - return symbol.toLowerCase() + '@depth'; - }); - subscription = subscribeCombined(streams, handleDepthStreamData, reconnect, function () { - async.mapLimit(symbols, 50, getSymbolDepthSnapshot, (err, results) => { - if (err) throw err; - results.forEach(updateSymbolDepthCache); - }); - }); - symbols.forEach(s => assignEndpointIdToContext(s, subscription.endpoint)); - } else { - let symbol = symbols; - symbolDepthInit(symbol); - subscription = subscribe(symbol.toLowerCase() + '@depth', handleDepthStreamData, reconnect, function () { - async.mapLimit([symbol], 1, getSymbolDepthSnapshot, (err, results) => { - if (err) throw err; - results.forEach(updateSymbolDepthCache); - }); - }); - assignEndpointIdToContext(symbol, subscription.endpoint); - } - return subscription.endpoint; - }, - - /** - * Websocket staggered depth cache - * @param {array/string} symbols - an array of symbols to query - * @param {function} callback - callback function - * @param {int} limit - the number of entries - * @param {int} stagger - ms between each depth cache - * @return {Promise} the websocket endpoint - */ - depthCacheStaggered: function (symbols, callback, limit = 100, stagger = 200) { - if (!Array.isArray(symbols)) symbols = [symbols]; - let chain = null; - - symbols.forEach(symbol => { - let promise = () => new Promise(resolve => { - this.depthCache(symbol, callback, limit); - setTimeout(resolve, stagger); - }); - chain = chain ? chain.then(promise) : promise(); - }); - - return chain; - }, - - /** - * Websocket aggregated trades - * @param {array/string} symbols - an array or string of symbols to query - * @param {function} callback - callback function - * @return {string} the websocket endpoint - */ - aggTrades: function trades(symbols, callback) { - let reconnect = function () { - if (Binance.options.reconnect) trades(symbols, callback); - }; - - let subscription; - if (Array.isArray(symbols)) { - if (!isArrayUnique(symbols)) throw Error('trades: "symbols" cannot contain duplicate elements.'); - let streams = symbols.map(function (symbol) { - return symbol.toLowerCase() + '@aggTrade'; - }); - subscription = subscribeCombined(streams, callback, reconnect); - } else { - let symbol = symbols; - subscription = subscribe(symbol.toLowerCase() + '@aggTrade', callback, reconnect); - } - return subscription.endpoint; - }, - - /** - * Websocket raw trades - * @param {array/string} symbols - an array or string of symbols to query - * @param {function} callback - callback function - * @return {string} the websocket endpoint - */ - trades: function trades(symbols, callback) { - let reconnect = function () { - if (Binance.options.reconnect) trades(symbols, callback); - }; - - let subscription; - if (Array.isArray(symbols)) { - if (!isArrayUnique(symbols)) throw Error('trades: "symbols" cannot contain duplicate elements.'); - let streams = symbols.map(function (symbol) { - return symbol.toLowerCase() + '@trade'; - }); - subscription = subscribeCombined(streams, callback, reconnect); - } else { - let symbol = symbols; - subscription = subscribe(symbol.toLowerCase() + '@trade', callback, reconnect); - } - return subscription.endpoint; - }, - - /** - * Websocket klines - * @param {array/string} symbols - an array or string of symbols to query - * @param {string} interval - the time interval - * @param {function} callback - callback function - * @param {int} limit - maximum results, no more than 1000 - * @return {string} the websocket endpoint - */ - chart: function chart(symbols, interval, callback, limit = 500) { - let reconnect = function () { - if (Binance.options.reconnect) chart(symbols, interval, callback, limit); - }; - - let symbolChartInit = function (symbol) { - if (typeof Binance.info[symbol] === 'undefined') Binance.info[symbol] = {}; - if (typeof Binance.info[symbol][interval] === 'undefined') Binance.info[symbol][interval] = {}; - if (typeof Binance.ohlc[symbol] === 'undefined') Binance.ohlc[symbol] = {}; - if (typeof Binance.ohlc[symbol][interval] === 'undefined') Binance.ohlc[symbol][interval] = {}; - if (typeof Binance.ohlcLatest[symbol] === 'undefined') Binance.ohlcLatest[symbol] = {}; - if (typeof Binance.ohlcLatest[symbol][interval] === 'undefined') Binance.ohlcLatest[symbol][interval] = {}; - if (typeof Binance.klineQueue[symbol] === 'undefined') Binance.klineQueue[symbol] = {}; - if (typeof Binance.klineQueue[symbol][interval] === 'undefined') Binance.klineQueue[symbol][interval] = []; - Binance.info[symbol][interval].timestamp = 0; - } - - let handleKlineStreamData = function (kline) { - let symbol = kline.s; - if (!Binance.info[symbol][interval].timestamp) { - if (typeof (Binance.klineQueue[symbol][interval]) !== 'undefined' && kline !== null) { - Binance.klineQueue[symbol][interval].push(kline); - } - } else { - //Binance.options.log('@klines at ' + kline.k.t); - klineHandler(symbol, kline); - if (callback) callback(symbol, interval, klineConcat(symbol, interval)); - } - }; - - let getSymbolKlineSnapshot = function (symbol, limit = 500) { - publicRequest(base + 'v1/klines', { symbol: symbol, interval: interval, limit: limit }, function (error, data) { - klineData(symbol, interval, data); - //Binance.options.log('/klines at ' + Binance.info[symbol][interval].timestamp); - if (typeof Binance.klineQueue[symbol][interval] !== 'undefined') { - for (let kline of Binance.klineQueue[symbol][interval]) klineHandler(symbol, kline, Binance.info[symbol][interval].timestamp); - delete Binance.klineQueue[symbol][interval]; - } - if (callback) callback(symbol, interval, klineConcat(symbol, interval)); - }); - }; - - let subscription; - if (Array.isArray(symbols)) { - if (!isArrayUnique(symbols)) throw Error('chart: "symbols" cannot contain duplicate elements.'); - symbols.forEach(symbolChartInit); - let streams = symbols.map(function (symbol) { - return symbol.toLowerCase() + '@kline_' + interval; - }); - subscription = subscribeCombined(streams, handleKlineStreamData, reconnect); - symbols.forEach(element => getSymbolKlineSnapshot(element, limit)); - } else { - let symbol = symbols; - symbolChartInit(symbol); - subscription = subscribe(symbol.toLowerCase() + '@kline_' + interval, handleKlineStreamData, reconnect); - getSymbolKlineSnapshot(symbol, limit); - } - return subscription.endpoint; - }, - - /** - * Websocket candle sticks - * @param {array/string} symbols - an array or string of symbols to query - * @param {string} interval - the time interval - * @param {function} callback - callback function - * @return {string} the websocket endpoint - */ - candlesticks: function candlesticks(symbols, interval, callback) { - let reconnect = function () { - if (Binance.options.reconnect) candlesticks(symbols, interval, callback); - }; - - /* If an array of symbols are sent we use a combined stream connection rather. - This is transparent to the developer, and results in a single socket connection. - This essentially eliminates "unexpected response" errors when subscribing to a lot of data. */ - let subscription; - if (Array.isArray(symbols)) { - if (!isArrayUnique(symbols)) throw Error('candlesticks: "symbols" cannot contain duplicate elements.'); - let streams = symbols.map(function (symbol) { - return symbol.toLowerCase() + '@kline_' + interval; - }); - subscription = subscribeCombined(streams, callback, reconnect); - } else { - let symbol = symbols.toLowerCase(); - subscription = subscribe(symbol + '@kline_' + interval, callback, reconnect); - } - return subscription.endpoint; - }, - - /** - * Websocket mini ticker - * @param {function} callback - callback function - * @return {string} the websocket endpoint - */ - miniTicker: function miniTicker(callback) { - let reconnect = function () { - if (Binance.options.reconnect) miniTicker(callback); - }; - let subscription = subscribe('!miniTicker@arr', function (data) { - let markets = {}; - for (let obj of data) { - markets[obj.s] = { - close: obj.c, - open: obj.o, - high: obj.h, - low: obj.l, - volume: obj.v, - quoteVolume: obj.q, - eventTime: obj.E - }; - } - callback(markets); - }, reconnect); - return subscription.endpoint; - }, - - /** - * Websocket prevday percentage - * @param {array/string} symbols - an array or string of symbols to query - * @param {function} callback - callback function - * @return {string} the websocket endpoint - */ - prevDay: function prevDay(symbols, callback) { - let reconnect = function () { - if (Binance.options.reconnect) prevDay(symbols, callback); - }; - - let subscription; - // Combine stream for array of symbols - if (Array.isArray(symbols)) { - if (!isArrayUnique(symbols)) throw Error('prevDay: "symbols" cannot contain duplicate elements.'); - let streams = symbols.map(function (symbol) { - return symbol.toLowerCase() + '@ticker'; - }); - subscription = subscribeCombined(streams, function (data) { - prevDayStreamHandler(data, callback); - }, reconnect); - // Raw stream for a single symbol - } else if (symbols) { - let symbol = symbols; - subscription = subscribe(symbol.toLowerCase() + '@ticker', function (data) { - prevDayStreamHandler(data, callback); - }, reconnect); - // Raw stream of all listed symbols - } else { - subscription = subscribe('!ticker@arr', function (data) { - for (let line of data) { - prevDayStreamHandler(line, callback); - } - }, reconnect); - } - return subscription.endpoint; - } - } - }; -} -module.exports = api; -//https://github.com/binance-exchange/binance-official-api-docs diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 00000000..81e944f4 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,10041 @@ +{ + "name": "node-binance-api", + "version": "0.13.5", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "node-binance-api", + "version": "0.13.5", + "license": "MIT", + "dependencies": { + "https-proxy-agent": "^7.0.0", + "json-bigint": "^1.0.0", + "lodash.zipobject": "^4.1.3", + "node-fetch": "^3.3.2", + "socks-proxy-agent": "^8.0.5", + "string-hash": "^1.1.3", + "url": "^0.11.0", + "ws": "^7.2.0" + }, + "devDependencies": { + "@rollup/plugin-commonjs": "^28.0.3", + "@rollup/plugin-json": "^6.1.0", + "@rollup/plugin-node-resolve": "^16.0.1", + "@rollup/plugin-typescript": "^12.1.2", + "@types/async": "^3.2.24", + "@types/chai": "^5.0.1", + "@types/json-bigint": "^1.0.4", + "@types/mocha": "^10.0.10", + "@types/request": "^2.48.12", + "@types/string-hash": "^1.1.3", + "@types/ws": "^8.18.0", + "@typescript-eslint/eslint-plugin": "^8.28.0", + "chai": "^4.5.0", + "chai-counter": "^1.0.0", + "codacy-coverage": "^2.0.3", + "codecov": "^3.6.1", + "coveralls": "^3.0.7", + "eslint": "^8.57.0", + "eslint-plugin-jsdoc": "^50.6.9", + "esm-to-cjs": "^1.2.1", + "istanbul": "^0.4.5", + "jest": "^29.7.0", + "jsdoc": "^4.0.4", + "mocha": "^11.1.0", + "mocha-lcov-reporter": "^1.3.0", + "nock": "^14.0.1", + "nyc": "^17.1.0", + "rollup": "^4.37.0", + "rollup-plugin-execute": "^1.1.1", + "ts-jest": "^29.3.0", + "tslib": "^2.8.1", + "tsx": "^4.19.3", + "typescript": "^5.8.2" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", + "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.25.9", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.26.8", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.8.tgz", + "integrity": "sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.26.10", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.10.tgz", + "integrity": "sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.26.2", + "@babel/generator": "^7.26.10", + "@babel/helper-compilation-targets": "^7.26.5", + "@babel/helper-module-transforms": "^7.26.0", + "@babel/helpers": "^7.26.10", + "@babel/parser": "^7.26.10", + "@babel/template": "^7.26.9", + "@babel/traverse": "^7.26.10", + "@babel/types": "^7.26.10", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.0.tgz", + "integrity": "sha512-VybsKvpiN1gU1sdMZIp7FcqphVVKEwcuj02x73uvcHE0PTihx1nlBcowYWhDwjpoAXRv43+gDzyggGnn1XZhVw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.27.0", + "@babel/types": "^7.27.0", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.0.tgz", + "integrity": "sha512-LVk7fbXml0H2xH34dFzKQ7TDZ2G4/rVTOrq9V+icbbadjbVxxeFeDsNHv2SrZeWoA+6ZiTyWYWtScEIW07EAcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.26.8", + "@babel/helper-validator-option": "^7.25.9", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", + "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz", + "integrity": "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9", + "@babel/traverse": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.26.5.tgz", + "integrity": "sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", + "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", + "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz", + "integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.0.tgz", + "integrity": "sha512-U5eyP/CTFPuNE3qk+WZMxFkp/4zUzdceQlfzf7DdGdhp+Fezd7HD+i8Y24ZuTMKX3wQBld449jijbGq6OdGNQg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.27.0", + "@babel/types": "^7.27.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.0.tgz", + "integrity": "sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.27.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.26.0.tgz", + "integrity": "sha512-e2dttdsJ1ZTpi3B9UYGLw41hifAubg19AtCu/2I/F1QNVclOBr1dYpTdmdyZ84Xiz43BS/tCUkMAZNLv12Pi+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.25.9.tgz", + "integrity": "sha512-ld6oezHQMZsZfp6pWtbjaNDF2tiiCYYDqQszHt5VV437lewP9aSi2Of99CK0D0XB21k7FLgnLcmQKyKzynfeAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.25.9.tgz", + "integrity": "sha512-hjMgRy5hb8uJJjUcdWunWVcoi9bGpJp8p5Ol1229PoN6aytsLwNMgmdftO23wnCLMfVmTwZDWMPNq/D1SY60JQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.0.tgz", + "integrity": "sha512-2ncevenBqXI6qRMukPlXwHKHchC7RyMuu4xv5JBXRfOGVcTy1mXCD12qrp7Jsoxll1EV3+9sE4GugBVRjT2jFA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.26.2", + "@babel/parser": "^7.27.0", + "@babel/types": "^7.27.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.0.tgz", + "integrity": "sha512-19lYZFzYVQkkHkl4Cy4WrAVcqBkgvV2YM2TU3xG6DIwO7O3ecbDPfW3yM3bjAGcqcQHi+CCtjMR3dIEHxsd6bA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.26.2", + "@babel/generator": "^7.27.0", + "@babel/parser": "^7.27.0", + "@babel/template": "^7.27.0", + "@babel/types": "^7.27.0", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/types": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.0.tgz", + "integrity": "sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@es-joy/jsdoccomment": { + "version": "0.49.0", + "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.49.0.tgz", + "integrity": "sha512-xjZTSFgECpb9Ohuk5yMX5RhUEbfeQcuOp8IF60e+wyzWEF0M5xeSgqsfLtvPEX8BIyOX9saZqzuGPmZ8oWc+5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "comment-parser": "1.4.1", + "esquery": "^1.6.0", + "jsdoc-type-pratt-parser": "~4.1.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.1.tgz", + "integrity": "sha512-kfYGy8IdzTGy+z0vFGvExZtxkFlA4zAxgKEahG9KE1ScBjpQnFsNOX8KTU5ojNru5ed5CVoJYXFtoxaq5nFbjQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.1.tgz", + "integrity": "sha512-dp+MshLYux6j/JjdqVLnMglQlFu+MuVeNrmT5nk6q07wNhCdSnB7QZj+7G8VMUGh1q+vj2Bq8kRsuyA00I/k+Q==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.1.tgz", + "integrity": "sha512-50tM0zCJW5kGqgG7fQ7IHvQOcAn9TKiVRuQ/lN0xR+T2lzEFvAi1ZcS8DiksFcEpf1t/GYOeOfCAgDHFpkiSmA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.1.tgz", + "integrity": "sha512-GCj6WfUtNldqUzYkN/ITtlhwQqGWu9S45vUXs7EIYf+7rCiiqH9bCloatO9VhxsL0Pji+PF4Lz2XXCES+Q8hDw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.1.tgz", + "integrity": "sha512-5hEZKPf+nQjYoSr/elb62U19/l1mZDdqidGfmFutVUjjUZrOazAtwK+Kr+3y0C/oeJfLlxo9fXb1w7L+P7E4FQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.1.tgz", + "integrity": "sha512-hxVnwL2Dqs3fM1IWq8Iezh0cX7ZGdVhbTfnOy5uURtao5OIVCEyj9xIzemDi7sRvKsuSdtCAhMKarxqtlyVyfA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.1.tgz", + "integrity": "sha512-1MrCZs0fZa2g8E+FUo2ipw6jw5qqQiH+tERoS5fAfKnRx6NXH31tXBKI3VpmLijLH6yriMZsxJtaXUyFt/8Y4A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.1.tgz", + "integrity": "sha512-0IZWLiTyz7nm0xuIs0q1Y3QWJC52R8aSXxe40VUxm6BB1RNmkODtW6LHvWRrGiICulcX7ZvyH6h5fqdLu4gkww==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.1.tgz", + "integrity": "sha512-NdKOhS4u7JhDKw9G3cY6sWqFcnLITn6SqivVArbzIaf3cemShqfLGHYMx8Xlm/lBit3/5d7kXvriTUGa5YViuQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.1.tgz", + "integrity": "sha512-jaN3dHi0/DDPelk0nLcXRm1q7DNJpjXy7yWaWvbfkPvI+7XNSc/lDOnCLN7gzsyzgu6qSAmgSvP9oXAhP973uQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.1.tgz", + "integrity": "sha512-OJykPaF4v8JidKNGz8c/q1lBO44sQNUQtq1KktJXdBLn1hPod5rE/Hko5ugKKZd+D2+o1a9MFGUEIUwO2YfgkQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.1.tgz", + "integrity": "sha512-nGfornQj4dzcq5Vp835oM/o21UMlXzn79KobKlcs3Wz9smwiifknLy4xDCLUU0BWp7b/houtdrgUz7nOGnfIYg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.1.tgz", + "integrity": "sha512-1osBbPEFYwIE5IVB/0g2X6i1qInZa1aIoj1TdL4AaAb55xIIgbg8Doq6a5BzYWgr+tEcDzYH67XVnTmUzL+nXg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.1.tgz", + "integrity": "sha512-/6VBJOwUf3TdTvJZ82qF3tbLuWsscd7/1w+D9LH0W/SqUgM5/JJD0lrJ1fVIfZsqB6RFmLCe0Xz3fmZc3WtyVg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.1.tgz", + "integrity": "sha512-nSut/Mx5gnilhcq2yIMLMe3Wl4FK5wx/o0QuuCLMtmJn+WeWYoEGDN1ipcN72g1WHsnIbxGXd4i/MF0gTcuAjQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.1.tgz", + "integrity": "sha512-cEECeLlJNfT8kZHqLarDBQso9a27o2Zd2AQ8USAEoGtejOrCYHNtKP8XQhMDJMtthdF4GBmjR2au3x1udADQQQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.1.tgz", + "integrity": "sha512-xbfUhu/gnvSEg+EGovRc+kjBAkrvtk38RlerAzQxvMzlB4fXpCFCeUAYzJvrnhFtdeyVCDANSjJvOvGYoeKzFA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.1.tgz", + "integrity": "sha512-O96poM2XGhLtpTh+s4+nP7YCCAfb4tJNRVZHfIE7dgmax+yMP2WgMd2OecBuaATHKTHsLWHQeuaxMRnCsH8+5g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.1.tgz", + "integrity": "sha512-X53z6uXip6KFXBQ+Krbx25XHV/NCbzryM6ehOAeAil7X7oa4XIq+394PWGnwaSQ2WRA0KI6PUO6hTO5zeF5ijA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.1.tgz", + "integrity": "sha512-Na9T3szbXezdzM/Kfs3GcRQNjHzM6GzFBeU1/6IV/npKP5ORtp9zbQjvkDJ47s6BCgaAZnnnu/cY1x342+MvZg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.1.tgz", + "integrity": "sha512-T3H78X2h1tszfRSf+txbt5aOp/e7TAz3ptVKu9Oyir3IAOFPGV6O9c2naym5TOriy1l0nNf6a4X5UXRZSGX/dw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.1.tgz", + "integrity": "sha512-2H3RUvcmULO7dIE5EWJH8eubZAI4xw54H1ilJnRNZdeo8dTADEZ21w6J22XBkXqGJbe0+wnNJtw3UXRoLJnFEg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.1.tgz", + "integrity": "sha512-GE7XvrdOzrb+yVKB9KsRMq+7a2U/K5Cf/8grVFRAGJmfADr/e/ODQ134RK2/eeHqYV5eQRFxb1hY7Nr15fv1NQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.1.tgz", + "integrity": "sha512-uOxSJCIcavSiT6UnBhBzE8wy3n0hOkJsBOzy7HDAuTDE++1DJMRRVCPGisULScHL+a/ZwdXPpXD3IyFKjA7K8A==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.1.tgz", + "integrity": "sha512-Y1EQdcfwMSeQN/ujR5VayLOJ1BHaK+ssyk0AEzPjC+t1lITgsnccPqFjb6V+LsTp/9Iov4ysfjxLaGJ9RPtkVg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.5.1.tgz", + "integrity": "sha512-soEIOALTfTK6EjmKMMoLugwaP0rzkad90iIWd1hMO9ARkSAyjfMfkRRhLvD5qH7vvM0Cg72pieUfR6yh6XxC4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/@eslint/eslintrc/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@eslint/js": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", + "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@hapi/address": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@hapi/address/-/address-1.0.1.tgz", + "integrity": "sha512-Z7nz/NjPN7nqVe9plLg7yjmKTfde3jf/6ytcNIXPVrWzzm3H/QnIHYbVQEoMtqWcxmfblOkAxF9TPpTRaCim8g==", + "deprecated": "Please update your dependencies as this version is no longer maintained and may contain bugs and security issues.", + "dev": true, + "license": "SEE LICENSE IN LICENSE.md", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", + "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", + "deprecated": "Use @eslint/config-array instead", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.3", + "debug": "^4.3.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "deprecated": "Use @eslint/object-schema instead", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/console": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", + "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/core": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", + "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/reporters": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-changed-files": "^29.7.0", + "jest-config": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-resolve-dependencies": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "jest-watcher": "^29.7.0", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/environment": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", + "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "expect": "^29.7.0", + "jest-snapshot": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", + "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "jest-get-type": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/fake-timers": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", + "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@sinonjs/fake-timers": "^10.0.2", + "@types/node": "*", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/globals": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", + "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/types": "^29.6.3", + "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/reporters": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", + "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^6.0.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "slash": "^3.0.0", + "string-length": "^4.0.1", + "strip-ansi": "^6.0.0", + "v8-to-istanbul": "^9.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/reporters/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/source-map": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", + "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.18", + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-result": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", + "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-sequencer": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", + "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/test-result": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/transform": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", + "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.2" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", + "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@jsdoc/salty": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/@jsdoc/salty/-/salty-0.2.9.tgz", + "integrity": "sha512-yYxMVH7Dqw6nO0d5NIV8OQWnitU8k6vXH8NtgqAfIa/IUqRMxRv/NUJJ08VEKbAakwxlgBl5PJdrU0dMPStsnw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "lodash": "^4.17.21" + }, + "engines": { + "node": ">=v12.0.0" + } + }, + "node_modules/@mswjs/interceptors": { + "version": "0.37.6", + "resolved": "https://registry.npmjs.org/@mswjs/interceptors/-/interceptors-0.37.6.tgz", + "integrity": "sha512-wK+5pLK5XFmgtH3aQ2YVvA3HohS3xqV/OxuVOdNx9Wpnz7VE/fnC+e1A7ln6LFYeck7gOJ/dsZV6OLplOtAJ2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@open-draft/deferred-promise": "^2.2.0", + "@open-draft/logger": "^0.3.0", + "@open-draft/until": "^2.0.0", + "is-node-process": "^1.2.0", + "outvariant": "^1.4.3", + "strict-event-emitter": "^0.5.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@open-draft/deferred-promise": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@open-draft/deferred-promise/-/deferred-promise-2.2.0.tgz", + "integrity": "sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@open-draft/logger": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@open-draft/logger/-/logger-0.3.0.tgz", + "integrity": "sha512-X2g45fzhxH238HKO4xbSr7+wBS8Fvw6ixhTDuvLd5mqh6bJJCFAPwU9mPDxbcrRtfxv4u5IHCEH77BmxvXmmxQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-node-process": "^1.2.0", + "outvariant": "^1.4.0" + } + }, + "node_modules/@open-draft/until": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@open-draft/until/-/until-2.1.0.tgz", + "integrity": "sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@pkgr/core": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.2.tgz", + "integrity": "sha512-fdDH1LSGfZdTH2sxdpVMw31BanV28K/Gry0cVFxaNP77neJSkd82mM8ErPNYs9e+0O7SdHBLTDzDgwUuy18RnQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, + "node_modules/@rollup/plugin-commonjs": { + "version": "28.0.3", + "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-28.0.3.tgz", + "integrity": "sha512-pyltgilam1QPdn+Zd9gaCfOLcnjMEJ9gV+bTw6/r73INdvzf1ah9zLIJBm+kW7R6IUFIQ1YO+VqZtYxZNWFPEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^5.0.1", + "commondir": "^1.0.1", + "estree-walker": "^2.0.2", + "fdir": "^6.2.0", + "is-reference": "1.2.1", + "magic-string": "^0.30.3", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=16.0.0 || 14 >= 14.17" + }, + "peerDependencies": { + "rollup": "^2.68.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-json": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@rollup/plugin-json/-/plugin-json-6.1.0.tgz", + "integrity": "sha512-EGI2te5ENk1coGeADSIwZ7G2Q8CJS2sF120T7jLw4xFw9n7wIOXHo+kIYRAoVpJAN+kmqZSoO3Fp4JtoNF4ReA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^5.1.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-node-resolve": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-16.0.1.tgz", + "integrity": "sha512-tk5YCxJWIG81umIvNkSod2qK5KyQW19qcBF/B78n1bjtOON6gzKoVeSzAE8yHCZEDmqkHKkxplExA8KzdJLJpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^5.0.1", + "@types/resolve": "1.20.2", + "deepmerge": "^4.2.2", + "is-module": "^1.0.0", + "resolve": "^1.22.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^2.78.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-typescript": { + "version": "12.1.2", + "resolved": "https://registry.npmjs.org/@rollup/plugin-typescript/-/plugin-typescript-12.1.2.tgz", + "integrity": "sha512-cdtSp154H5sv637uMr1a8OTWB0L1SWDSm1rDGiyfcGcvQ6cuTs4MDk2BVEBGysUWago4OJN4EQZqOTl/QY3Jgg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^5.1.0", + "resolve": "^1.22.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^2.14.0||^3.0.0||^4.0.0", + "tslib": "*", + "typescript": ">=3.7.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + }, + "tslib": { + "optional": true + } + } + }, + "node_modules/@rollup/pluginutils": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.4.tgz", + "integrity": "sha512-USm05zrsFxYLPdWWq+K3STlWiT/3ELn3RcV5hJMghpeAIhxfsUIg6mt12CBJBInWMV4VneoV7SfGv8xIwo2qNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.37.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.37.0.tgz", + "integrity": "sha512-l7StVw6WAa8l3vA1ov80jyetOAEo1FtHvZDbzXDO/02Sq/QVvqlHkYoFwDJPIMj0GKiistsBudfx5tGFnwYWDQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.37.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.37.0.tgz", + "integrity": "sha512-6U3SlVyMxezt8Y+/iEBcbp945uZjJwjZimu76xoG7tO1av9VO691z8PkhzQ85ith2I8R2RddEPeSfcbyPfD4hA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.37.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.37.0.tgz", + "integrity": "sha512-+iTQ5YHuGmPt10NTzEyMPbayiNTcOZDWsbxZYR1ZnmLnZxG17ivrPSWFO9j6GalY0+gV3Jtwrrs12DBscxnlYA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.37.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.37.0.tgz", + "integrity": "sha512-m8W2UbxLDcmRKVjgl5J/k4B8d7qX2EcJve3Sut7YGrQoPtCIQGPH5AMzuFvYRWZi0FVS0zEY4c8uttPfX6bwYQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.37.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.37.0.tgz", + "integrity": "sha512-FOMXGmH15OmtQWEt174v9P1JqqhlgYge/bUjIbiVD1nI1NeJ30HYT9SJlZMqdo1uQFyt9cz748F1BHghWaDnVA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.37.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.37.0.tgz", + "integrity": "sha512-SZMxNttjPKvV14Hjck5t70xS3l63sbVwl98g3FlVVx2YIDmfUIy29jQrsw06ewEYQ8lQSuY9mpAPlmgRD2iSsA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.37.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.37.0.tgz", + "integrity": "sha512-hhAALKJPidCwZcj+g+iN+38SIOkhK2a9bqtJR+EtyxrKKSt1ynCBeqrQy31z0oWU6thRZzdx53hVgEbRkuI19w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.37.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.37.0.tgz", + "integrity": "sha512-jUb/kmn/Gd8epbHKEqkRAxq5c2EwRt0DqhSGWjPFxLeFvldFdHQs/n8lQ9x85oAeVb6bHcS8irhTJX2FCOd8Ag==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.37.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.37.0.tgz", + "integrity": "sha512-oNrJxcQT9IcbcmKlkF+Yz2tmOxZgG9D9GRq+1OE6XCQwCVwxixYAa38Z8qqPzQvzt1FCfmrHX03E0pWoXm1DqA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.37.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.37.0.tgz", + "integrity": "sha512-pfxLBMls+28Ey2enpX3JvjEjaJMBX5XlPCZNGxj4kdJyHduPBXtxYeb8alo0a7bqOoWZW2uKynhHxF/MWoHaGQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loongarch64-gnu": { + "version": "4.37.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.37.0.tgz", + "integrity": "sha512-yCE0NnutTC/7IGUq/PUHmoeZbIwq3KRh02e9SfFh7Vmc1Z7atuJRYWhRME5fKgT8aS20mwi1RyChA23qSyRGpA==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.37.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.37.0.tgz", + "integrity": "sha512-NxcICptHk06E2Lh3a4Pu+2PEdZ6ahNHuK7o6Np9zcWkrBMuv21j10SQDJW3C9Yf/A/P7cutWoC/DptNLVsZ0VQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.37.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.37.0.tgz", + "integrity": "sha512-PpWwHMPCVpFZLTfLq7EWJWvrmEuLdGn1GMYcm5MV7PaRgwCEYJAwiN94uBuZev0/J/hFIIJCsYw4nLmXA9J7Pw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.37.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.37.0.tgz", + "integrity": "sha512-DTNwl6a3CfhGTAOYZ4KtYbdS8b+275LSLqJVJIrPa5/JuIufWWZ/QFvkxp52gpmguN95eujrM68ZG+zVxa8zHA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.37.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.37.0.tgz", + "integrity": "sha512-hZDDU5fgWvDdHFuExN1gBOhCuzo/8TMpidfOR+1cPZJflcEzXdCy1LjnklQdW8/Et9sryOPJAKAQRw8Jq7Tg+A==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.37.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.37.0.tgz", + "integrity": "sha512-pKivGpgJM5g8dwj0ywBwe/HeVAUSuVVJhUTa/URXjxvoyTT/AxsLTAbkHkDHG7qQxLoW2s3apEIl26uUe08LVQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.37.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.37.0.tgz", + "integrity": "sha512-E2lPrLKE8sQbY/2bEkVTGDEk4/49UYRVWgj90MY8yPjpnGBQ+Xi1Qnr7b7UIWw1NOggdFQFOLZ8+5CzCiz143w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.37.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.37.0.tgz", + "integrity": "sha512-Jm7biMazjNzTU4PrQtr7VS8ibeys9Pn29/1bm4ph7CP2kf21950LgN+BaE2mJ1QujnvOc6p54eWWiVvn05SOBg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.37.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.37.0.tgz", + "integrity": "sha512-e3/1SFm1OjefWICB2Ucstg2dxYDkDTZGDYgwufcbsxTHyqQps1UQf33dFEChBNmeSsTOyrjw2JJq0zbG5GF6RA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.37.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.37.0.tgz", + "integrity": "sha512-LWbXUBwn/bcLx2sSsqy7pK5o+Nr+VCoRoAohfJ5C/aBio9nfJmGQqHAhU6pwxV/RmyTk5AqdySma7uwWGlmeuA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/commons/node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", + "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.0" + } + }, + "node_modules/@tootallnate/once": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/@types/async": { + "version": "3.2.24", + "resolved": "https://registry.npmjs.org/@types/async/-/async-3.2.24.tgz", + "integrity": "sha512-8iHVLHsCCOBKjCF2KwFe0p9Z3rfM9mL+sSP8btyR5vTjJRAqpBYD28/ZLgXPf0pjG1VxOvtCV/BgXkQbpSe8Hw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.6.8", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", + "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.20.6", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.6.tgz", + "integrity": "sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.20.7" + } + }, + "node_modules/@types/caseless": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.5.tgz", + "integrity": "sha512-hWtVTC2q7hc7xZ/RLbxapMvDMgUnDvKvMOpKal4DrMyfGBUfB1oKaZlIRr6mJL+If3bAP6sV/QneGzF6tJjZDg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/chai": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.1.tgz", + "integrity": "sha512-iu1JLYmGmITRzUgNiLMZD3WCoFzpYtueuyAgHTXqgwSRAMIlFTnZqG6/xenkpUGRJEzSfklUTI4GNSzks/dc0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/deep-eql": "*" + } + }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", + "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/graceful-fs": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", + "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/json-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@types/json-bigint/-/json-bigint-1.0.4.tgz", + "integrity": "sha512-ydHooXLbOmxBbubnA7Eh+RpBzuaIiQjh8WGJYQB50JFGFrdxW7JzVlyEV7fAXw0T2sqJ1ysTneJbiyNLqZRAag==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/markdown-it": { + "version": "14.1.2", + "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.2.tgz", + "integrity": "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/linkify-it": "^5", + "@types/mdurl": "^2" + } + }, + "node_modules/@types/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/mocha": { + "version": "10.0.10", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.10.tgz", + "integrity": "sha512-xPyYSz1cMPnJQhl0CLMH68j3gprKZaTjG3s5Vi+fDgx+uhG9NOXwbVt52eFS8ECyXhyKcjDLCBEqBExKuiZb7Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "22.13.13", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.13.tgz", + "integrity": "sha512-ClsL5nMwKaBRwPcCvH8E7+nU4GxHVx1axNvMZTFHMEfNI7oahimt26P5zjVCRrjiIWj6YFXfE1v3dEp94wLcGQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.20.0" + } + }, + "node_modules/@types/request": { + "version": "2.48.12", + "resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.12.tgz", + "integrity": "sha512-G3sY+NpsA9jnwm0ixhAFQSJ3Q9JkpLZpJbI3GMv0mIAT0y3mRabYeINzal5WOChIiaTEGQYlHOKgkaM9EisWHw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/caseless": "*", + "@types/node": "*", + "@types/tough-cookie": "*", + "form-data": "^2.5.0" + } + }, + "node_modules/@types/resolve": { + "version": "1.20.2", + "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz", + "integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/stack-utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/string-hash": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@types/string-hash/-/string-hash-1.1.3.tgz", + "integrity": "sha512-p6skq756fJWiA59g2Uss+cMl6tpoDGuCBuxG0SI1t0NwJmYOU66LAMS6QiCgu7cUh3/hYCaMl5phcCW1JP5wOA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/tough-cookie": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", + "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/ws": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8svvI3hMyvN0kKCJMvTJP/x6Y/EoQbepff882wL+Sn5QsXb3etnamgrJq4isrBxSJj5L2AuXcI0+bgkoAXGUJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/yargs": { + "version": "17.0.33", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", + "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.28.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.28.0.tgz", + "integrity": "sha512-lvFK3TCGAHsItNdWZ/1FkvpzCxTHUVuFrdnOGLMa0GGCFIbCgQWVk3CzCGdA7kM3qGVc+dfW9tr0Z/sHnGDFyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.28.0", + "@typescript-eslint/type-utils": "8.28.0", + "@typescript-eslint/utils": "8.28.0", + "@typescript-eslint/visitor-keys": "8.28.0", + "graphemer": "^1.4.0", + "ignore": "^5.3.1", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.0.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.28.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.28.0.tgz", + "integrity": "sha512-LPcw1yHD3ToaDEoljFEfQ9j2xShY367h7FZ1sq5NJT9I3yj4LHer1Xd1yRSOdYy9BpsrxU7R+eoDokChYM53lQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@typescript-eslint/scope-manager": "8.28.0", + "@typescript-eslint/types": "8.28.0", + "@typescript-eslint/typescript-estree": "8.28.0", + "@typescript-eslint/visitor-keys": "8.28.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.28.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.28.0.tgz", + "integrity": "sha512-u2oITX3BJwzWCapoZ/pXw6BCOl8rJP4Ij/3wPoGvY8XwvXflOzd1kLrDUUUAIEdJSFh+ASwdTHqtan9xSg8buw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.28.0", + "@typescript-eslint/visitor-keys": "8.28.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.28.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.28.0.tgz", + "integrity": "sha512-oRoXu2v0Rsy/VoOGhtWrOKDiIehvI+YNrDk5Oqj40Mwm0Yt01FC/Q7nFqg088d3yAsR1ZcZFVfPCTTFCe/KPwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/typescript-estree": "8.28.0", + "@typescript-eslint/utils": "8.28.0", + "debug": "^4.3.4", + "ts-api-utils": "^2.0.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.28.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.28.0.tgz", + "integrity": "sha512-bn4WS1bkKEjx7HqiwG2JNB3YJdC1q6Ue7GyGlwPHyt0TnVq6TtD/hiOdTZt71sq0s7UzqBFXD8t8o2e63tXgwA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.28.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.28.0.tgz", + "integrity": "sha512-H74nHEeBGeklctAVUvmDkxB1mk+PAZ9FiOMPFncdqeRBXxk1lWSYraHw8V12b7aa6Sg9HOBNbGdSHobBPuQSuA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.28.0", + "@typescript-eslint/visitor-keys": "8.28.0", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^2.0.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.28.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.28.0.tgz", + "integrity": "sha512-OELa9hbTYciYITqgurT1u/SzpQVtDLmQMFzy/N8pQE+tefOyCWT79jHsav294aTqV1q1u+VzqDGbuujvRYaeSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "8.28.0", + "@typescript-eslint/types": "8.28.0", + "@typescript-eslint/typescript-estree": "8.28.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.28.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.28.0.tgz", + "integrity": "sha512-hbn8SZ8w4u2pRwgQ1GlUrPKE+t2XvcCW5tTRF7j6SMYIuYG37XuzIW44JCZPa36evi0Oy2SnM664BlIaAuQcvg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.28.0", + "eslint-visitor-keys": "^4.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", + "dev": true, + "license": "ISC" + }, + "node_modules/abbrev": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.9.tgz", + "integrity": "sha512-LEyx4aLEC3x6T0UguF6YILf+ntvmOaWsVfENmIW0E9H09vKlLDGelMjjSm0jkDHALj8A8quZ/HapKNigzwge+Q==", + "dev": true, + "license": "ISC" + }, + "node_modules/acorn": { + "version": "8.14.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", + "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/agent-base": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", + "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/amdefine": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", + "integrity": "sha512-S2Hw0TtNkMJhIabBwIojKL9YHO5T0n5eNqWJ7Lrlel/zDbftQpxpapi8tZs3X1HWa+u+QeydGmzzNU0m09+Rcg==", + "dev": true, + "license": "BSD-3-Clause OR MIT", + "optional": true, + "engines": { + "node": ">=0.4.2" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-escapes/node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/anymatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/append-transform": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-2.0.0.tgz", + "integrity": "sha512-7yeyCEurROLQJFv5Xj4lEGTy0borxepjFv1g22oAdqFu//SrAlDl1O1Nxx15SH1RoliUml6p8dwJW9jvZughhg==", + "dev": true, + "license": "MIT", + "dependencies": { + "default-require-extensions": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/archy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", + "integrity": "sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==", + "dev": true, + "license": "MIT" + }, + "node_modules/are-docs-informative": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/are-docs-informative/-/are-docs-informative-0.0.2.tgz", + "integrity": "sha512-ixiS0nLNNG5jNQzgZJNoUpBKdo9yTYZMGJ+QgT2jmjR7G7+QHRCc4v6LQ3NgE7EBJq+o0ams3waJwkrlBom8Ig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/argv": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/argv/-/argv-0.0.2.tgz", + "integrity": "sha512-dEamhpPEwRUBpLNHeuCm/v+g0anFByHahxodVO/BbAarHVBBg2MccCwf9K+o1Pof+2btdnkJelYVUWjW/VrATw==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", + "dev": true, + "engines": { + "node": ">=0.6.10" + } + }, + "node_modules/asn1": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", + "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": "~2.1.0" + } + }, + "node_modules/assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/async": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha512-nSVgobk4rv61R9PUSDtYt7mPVB2olxNR5RWJcAsH676/ef11bUZwvu7+RGYrYauVdDPcO519v68wRhXQtxsV9w==", + "dev": true, + "license": "MIT" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "*" + } + }, + "node_modules/aws4": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.13.2.tgz", + "integrity": "sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw==", + "dev": true, + "license": "MIT" + }, + "node_modules/babel-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", + "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/transform": "^29.7.0", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^29.6.3", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.8.0" + } + }, + "node_modules/babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-jest-hoist": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", + "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.1.14", + "@types/babel__traverse": "^7.0.6" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/babel-preset-current-node-syntax": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.1.0.tgz", + "integrity": "sha512-ldYss8SbBlWva1bs28q78Ju5Zq1F+8BrqBZZ0VFhLBvhh6lCpC2o3gDJi/5DRLs9FgYZCnmPYIVFU4lRXCkyUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-import-attributes": "^7.24.7", + "@babel/plugin-syntax-import-meta": "^7.10.4", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/babel-preset-jest": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", + "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", + "dev": true, + "license": "MIT", + "dependencies": { + "babel-plugin-jest-hoist": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tweetnacl": "^0.14.3" + } + }, + "node_modules/bignumber.js": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.2.tgz", + "integrity": "sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", + "dev": true, + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true, + "license": "ISC" + }, + "node_modules/browserslist": { + "version": "4.24.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", + "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "caniuse-lite": "^1.0.30001688", + "electron-to-chromium": "^1.5.73", + "node-releases": "^2.0.19", + "update-browserslist-db": "^1.1.1" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/bs-logger": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", + "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-json-stable-stringify": "2.x" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "node-int64": "^0.4.0" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/caching-transform": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz", + "integrity": "sha512-kpqOvwXnjjN44D89K5ccQC+RUrsy7jB/XLlRrx0D7/2HNcTPqzsb6XgYoErwko6QsV184CA2YgS1fxDiiDZMWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasha": "^5.0.0", + "make-dir": "^3.0.0", + "package-hash": "^4.0.0", + "write-file-atomic": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/caching-transform/node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/caching-transform/node_modules/write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "dev": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001707", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001707.tgz", + "integrity": "sha512-3qtRjw/HQSMlDWf+X79N206fepf4SOOU6SQLMaq/0KkZLmSjPxAkBOQQ+FxbHKfHmYLZFfdWsO3KA90ceHPSnw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/catharsis": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.9.0.tgz", + "integrity": "sha512-prMTQVpcns/tzFgFVkVp6ak6RykZyWb3gu8ckUpd6YkTlacOd3DXGJjIpD4Q6zJirizvaiAjSSHlOsA+6sNh2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash": "^4.17.15" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/chai": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.5.0.tgz", + "integrity": "sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.3", + "deep-eql": "^4.1.3", + "get-func-name": "^2.0.2", + "loupe": "^2.3.6", + "pathval": "^1.1.1", + "type-detect": "^4.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/chai-counter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/chai-counter/-/chai-counter-1.0.0.tgz", + "integrity": "sha512-GW85A8lFoy0p/WPI9hFLyPqsSn7gPE6Ir7gD1/IXBPjVI1gud4b2owZziGDF1MQepDPMD2oDktmC21ea30lZfQ==", + "dev": true, + "dependencies": { + "chai": "^1.9.1" + }, + "engines": { + "node": ">= 0.10.x" + } + }, + "node_modules/chai-counter/node_modules/assertion-error": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.0.0.tgz", + "integrity": "sha512-g/gZV+G476cnmtYI+Ko9d5khxSoCSoom/EaNmmCfwpOvBXEJ18qwFrxfP1/CsIqk2no1sAKKwxndV0tP7ROOFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/chai-counter/node_modules/chai": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-1.10.0.tgz", + "integrity": "sha512-E3L9M2SeQU1XagJkE9KJyTAXXHKJkJ1EsKkFp0Rl53lYa3mro2PVgYHNiCb2YRa2nUeyg7aqmI1EIcSBayNd5w==", + "dev": true, + "license": "MIT", + "dependencies": { + "assertion-error": "1.0.0", + "deep-eql": "0.1.3" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/chai-counter/node_modules/deep-eql": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-0.1.3.tgz", + "integrity": "sha512-6sEotTRGBFiNcqVoeHwnfopbSpi5NbH1VWJmYCVkmxMmaVTT0bUTrNaGyBwhgP4MZL012W/mkzIn3Da+iDYweg==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-detect": "0.1.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/chai-counter/node_modules/type-detect": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-0.1.1.tgz", + "integrity": "sha512-5rqszGVwYgBoDkIm2oUtvkfZMQ0vk29iDMU0W2qCa3rG0vPDNczCMT4hV/bLBgLg8k8ri6+u3Zbt+S/14eMzlA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/check-error": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", + "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-func-name": "^2.0.2" + }, + "engines": { + "node": "*" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cjs-module-lexer": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz", + "integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/codacy-coverage": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/codacy-coverage/-/codacy-coverage-2.1.1.tgz", + "integrity": "sha512-MGMkPS5d9AqQEXTZ4grn/syl/7VvOehgWTeU2B41E22q767QolclfdfadKAndL287cIPEOEdwh9JBqCwQJLtFw==", + "deprecated": "Package being deprecated in favor of https://github.com/codacy/codacy-coverage-reporter", + "dev": true, + "license": "MIT", + "dependencies": { + "bluebird": "^3.5.x", + "commander": "^2.x", + "joi": "^12.x", + "lcov-parse": "^1.x", + "lodash": "^4.17.4", + "log-driver": "^1.x", + "request": "^2.83.0", + "request-promise": "^4.x" + }, + "bin": { + "codacy-coverage": "bin/codacy-coverage.js" + }, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/codecov": { + "version": "3.8.2", + "resolved": "https://registry.npmjs.org/codecov/-/codecov-3.8.2.tgz", + "integrity": "sha512-6w/kt/xvmPsWMfDFPE/T054txA9RTgcJEw36PNa6MYX+YV29jCHCRFXwbQ3QZBTOgnex1J2WP8bo2AT8TWWz9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "argv": "0.0.2", + "ignore-walk": "3.0.3", + "js-yaml": "3.14.1", + "teeny-request": "7.0.1", + "urlgrey": "0.4.4" + }, + "bin": { + "codecov": "bin/codecov" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/collect-v8-coverage": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", + "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/comment-parser": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.4.1.tgz", + "integrity": "sha512-buhp5kePrmda3vhc5B9t7pUQXAb2Tnd0qgpkIhPhkHXxJpiPJ11H0ZEU0oBpJ2QztSbzG/ZxMj/CHsYJqRHmyg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", + "dev": true, + "license": "MIT" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/coveralls": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/coveralls/-/coveralls-3.1.1.tgz", + "integrity": "sha512-+dxnG2NHncSD1NrqbSM3dn/lE57O6Qf/koe9+I7c+wzkqRmEvcp0kgJdxKInzYzkICKkFMZsX3Vct3++tsF9ww==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "js-yaml": "^3.13.1", + "lcov-parse": "^1.0.0", + "log-driver": "^1.2.7", + "minimist": "^1.2.5", + "request": "^2.88.2" + }, + "bin": { + "coveralls": "bin/coveralls.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/create-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", + "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "prompts": "^2.0.1" + }, + "bin": { + "create-jest": "bin/create-jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", + "dev": true, + "license": "MIT", + "dependencies": { + "assert-plus": "^1.0.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/dedent": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.3.tgz", + "integrity": "sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" + }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/deep-eql": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.4.tgz", + "integrity": "sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-detect": "^4.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/default-require-extensions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.1.tgz", + "integrity": "sha512-eXTJmRbm2TIt9MgWTsOH1wEuhew6XGZcMeGKCtLedIg/NCsg1iBePXkceTdK4Fii7pzmN9tGsZhKzZ4h7O/fxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "strip-bom": "^4.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/diff": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", + "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/diff-sequences": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, + "license": "MIT" + }, + "node_modules/ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "node_modules/ecc-jsbn/node_modules/jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", + "dev": true, + "license": "MIT" + }, + "node_modules/ejs": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", + "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "jake": "^10.8.5" + }, + "bin": { + "ejs": "bin/cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.123", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.123.tgz", + "integrity": "sha512-refir3NlutEZqlKaBLK0tzlVLe5P2wDKS7UQt/3SpibizgsRAPOsqQC3ffw1nlv3ze5gjRQZYHoPymgVZkplFA==", + "dev": true, + "license": "ISC" + }, + "node_modules/emittery": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", + "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-module-lexer": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.6.0.tgz", + "integrity": "sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es6-error": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", + "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", + "dev": true, + "license": "MIT" + }, + "node_modules/esbuild": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.1.tgz", + "integrity": "sha512-BGO5LtrGC7vxnqucAe/rmvKdJllfGaYWdyABvyMoXQlfYMb2bbRuReWR5tEGE//4LcNJj9XrkovTqNYRFZHAMQ==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.1", + "@esbuild/android-arm": "0.25.1", + "@esbuild/android-arm64": "0.25.1", + "@esbuild/android-x64": "0.25.1", + "@esbuild/darwin-arm64": "0.25.1", + "@esbuild/darwin-x64": "0.25.1", + "@esbuild/freebsd-arm64": "0.25.1", + "@esbuild/freebsd-x64": "0.25.1", + "@esbuild/linux-arm": "0.25.1", + "@esbuild/linux-arm64": "0.25.1", + "@esbuild/linux-ia32": "0.25.1", + "@esbuild/linux-loong64": "0.25.1", + "@esbuild/linux-mips64el": "0.25.1", + "@esbuild/linux-ppc64": "0.25.1", + "@esbuild/linux-riscv64": "0.25.1", + "@esbuild/linux-s390x": "0.25.1", + "@esbuild/linux-x64": "0.25.1", + "@esbuild/netbsd-arm64": "0.25.1", + "@esbuild/netbsd-x64": "0.25.1", + "@esbuild/openbsd-arm64": "0.25.1", + "@esbuild/openbsd-x64": "0.25.1", + "@esbuild/sunos-x64": "0.25.1", + "@esbuild/win32-arm64": "0.25.1", + "@esbuild/win32-ia32": "0.25.1", + "@esbuild/win32-x64": "0.25.1" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/escodegen": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.8.1.tgz", + "integrity": "sha512-yhi5S+mNTOuRvyW4gWlg5W1byMaQGWWSYHXsuFZ7GBo7tpyOwi2EdzMP/QWxh9hwkD2m+wDVHJsxhRIj+v/b/A==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esprima": "^2.7.1", + "estraverse": "^1.9.1", + "esutils": "^2.0.2", + "optionator": "^0.8.1" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=0.12.0" + }, + "optionalDependencies": { + "source-map": "~0.2.0" + } + }, + "node_modules/escodegen/node_modules/estraverse": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.9.3.tgz", + "integrity": "sha512-25w1fMXQrGdoquWnScXZGckOv+Wes+JDnuN/+7ex3SauFRS72r2lFDec0EKPt2YD1wUJ/IrfEex+9yp4hfSOJA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/escodegen/node_modules/levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/escodegen/node_modules/optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/escodegen/node_modules/prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/escodegen/node_modules/type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/eslint": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", + "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", + "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.1", + "@humanwhocodes/config-array": "^0.13.0", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-plugin-jsdoc": { + "version": "50.6.9", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-50.6.9.tgz", + "integrity": "sha512-7/nHu3FWD4QRG8tCVqcv+BfFtctUtEDWc29oeDXB4bwmDM2/r1ndl14AG/2DUntdqH7qmpvdemJKwb3R97/QEw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@es-joy/jsdoccomment": "~0.49.0", + "are-docs-informative": "^0.0.2", + "comment-parser": "1.4.1", + "debug": "^4.3.6", + "escape-string-regexp": "^4.0.0", + "espree": "^10.1.0", + "esquery": "^1.6.0", + "parse-imports": "^2.1.1", + "semver": "^7.6.3", + "spdx-expression-parse": "^4.0.0", + "synckit": "^0.9.1" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0 || ^9.0.0" + } + }, + "node_modules/eslint-plugin-jsdoc/node_modules/eslint-visitor-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-plugin-jsdoc/node_modules/espree": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", + "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.14.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-plugin-jsdoc/node_modules/semver": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/eslint/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/esm-to-cjs": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/esm-to-cjs/-/esm-to-cjs-1.2.1.tgz", + "integrity": "sha512-5zzepH0eV7F3LOz+p3Hn8iTaszfqVu9ueGcLltcuCz2kNe7Dbu9p2hePzi3xN+0S/m/d0WCC1IjIEP2UoHbmuQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esprima": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", + "integrity": "sha512-OarPfz0lFCiW4/AV2Oy1Rp9qu0iusTKqykwTspGCZtPxmF81JR4MmIebvF1F9+UOKth2ZubLQ4XGGaU+hSn99A==", + "dev": true, + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/expect-utils": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true, + "license": "MIT" + }, + "node_modules/extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==", + "dev": true, + "engines": [ + "node >=0.6.0" + ], + "license": "MIT" + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fastq": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "bser": "2.1.1" + } + }, + "node_modules/fdir": { + "version": "6.4.3", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.3.tgz", + "integrity": "sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "dependencies": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + }, + "engines": { + "node": "^12.20 || >= 14.13" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/filelist": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", + "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "minimatch": "^5.0.1" + } + }, + "node_modules/filelist/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/filelist/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-cache-dir": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", + "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", + "dev": true, + "license": "MIT", + "dependencies": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/avajs/find-cache-dir?sponsor=1" + } + }, + "node_modules/find-cache-dir/node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, + "license": "BSD-3-Clause", + "bin": { + "flat": "cli.js" + } + }, + "node_modules/flat-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "dev": true, + "license": "ISC" + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/foreground-child/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "*" + } + }, + "node_modules/form-data": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.3.tgz", + "integrity": "sha512-XHIrMD0NpDrNM/Ckf7XJiBbLl57KEhT3+i3yY+eWm+cqYZJQTZrKo8Y8AWKnuV5GT4scfuUGt9LzNoIx3dU1nQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "mime-types": "^2.1.35", + "safe-buffer": "^5.2.1" + }, + "engines": { + "node": ">= 0.12" + } + }, + "node_modules/formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "license": "MIT", + "dependencies": { + "fetch-blob": "^3.1.2" + }, + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/fromentries": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.3.2.tgz", + "integrity": "sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-func-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", + "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-tsconfig": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.0.tgz", + "integrity": "sha512-kGzZ3LWWQcGIAmg6iWvXn0ei6WDtV26wzHRMwDSzmAbcXrTEXxHy6IehI6/4eT6VRKyMP1eF1VqwrVUmE/LR7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", + "dev": true, + "license": "MIT", + "dependencies": { + "assert-plus": "^1.0.0" + } + }, + "node_modules/glob": { + "version": "5.0.15", + "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", + "integrity": "sha512-c9IPMazfRITpmAAKi22dK1VKxGDX9ehhqfABDriL/lzO92xcUKEJPQHrVA/2YHSNFB4iFlykVmWvwo48nr3OxA==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "2 || 3", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true, + "license": "MIT" + }, + "node_modules/handlebars": { + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", + "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.2", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, + "node_modules/handlebars/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=4" + } + }, + "node_modules/har-validator": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", + "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", + "deprecated": "this library is no longer supported", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.3", + "har-schema": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasha": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.2.tgz", + "integrity": "sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-stream": "^2.0.0", + "type-fest": "^0.8.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/hasha/node_modules/type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=8" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "license": "MIT", + "bin": { + "he": "bin/he" + } + }, + "node_modules/hoek": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-4.3.1.tgz", + "integrity": "sha512-v7E+yIjcHECn973i0xHm4kJkEpv3C8sbYS4344WXbzYqRyiDD7rjnnKo4hsJkejQBAFdRMUGNHySeSPKSH9Rqw==", + "deprecated": "This module has moved and is now available at @hapi/hoek. Please update your dependencies as this version is no longer maintained an may contain bugs and security issues.", + "dev": true, + "license": "SEE LICENSE IN LICENSE.md", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true, + "license": "MIT" + }, + "node_modules/http-proxy-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/http-proxy-agent/node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + }, + "engines": { + "node": ">=0.8", + "npm": ">=1.3.7" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/ignore-walk": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.3.tgz", + "integrity": "sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw==", + "dev": true, + "license": "ISC", + "dependencies": { + "minimatch": "^3.0.4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-local": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", + "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/ip-address": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", + "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", + "license": "MIT", + "dependencies": { + "jsbn": "1.1.0", + "sprintf-js": "^1.1.3" + }, + "engines": { + "node": ">= 12" + } + }, + "node_modules/ip-address/node_modules/sprintf-js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", + "license": "BSD-3-Clause" + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", + "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-node-process": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-node-process/-/is-node-process-1.2.0.tgz", + "integrity": "sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-reference": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz", + "integrity": "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "*" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==", + "dev": true, + "license": "MIT" + }, + "node_modules/istanbul": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/istanbul/-/istanbul-0.4.5.tgz", + "integrity": "sha512-nMtdn4hvK0HjUlzr1DrKSUY8ychprt8dzHOgY2KXsIhHu5PuQQEOTM27gV9Xblyon7aUH/TSFIjRHEODF/FRPg==", + "deprecated": "This module is no longer maintained, try this instead:\n npm i nyc\nVisit https://istanbul.js.org/integrations for other alternatives.", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "abbrev": "1.0.x", + "async": "1.x", + "escodegen": "1.8.x", + "esprima": "2.7.x", + "glob": "^5.0.15", + "handlebars": "^4.0.1", + "js-yaml": "3.x", + "mkdirp": "0.5.x", + "nopt": "3.x", + "once": "1.x", + "resolve": "1.1.x", + "supports-color": "^3.1.0", + "which": "^1.1.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "istanbul": "lib/cli.js" + } + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-hook": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-3.0.0.tgz", + "integrity": "sha512-Pt/uge1Q9s+5VAZ+pCo16TYMWPBIl+oaNIjgLQxcX0itS6ueeaA+pEfThZpH8WxhFgCiEb8sAJY6MdUKgiIWaQ==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "append-transform": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", + "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/core": "^7.23.9", + "@babel/parser": "^7.23.9", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-instrument/node_modules/semver": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-processinfo": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.3.tgz", + "integrity": "sha512-NkwHbo3E00oybX6NGJi6ar0B29vxyvNwoC7eJ4G4Yq28UfY758Hgn/heV8VRFhevPED4LXfFz0DQ8z/0kw9zMg==", + "dev": true, + "license": "ISC", + "dependencies": { + "archy": "^1.0.0", + "cross-spawn": "^7.0.3", + "istanbul-lib-coverage": "^3.2.0", + "p-map": "^3.0.0", + "rimraf": "^3.0.0", + "uuid": "^8.3.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/istanbul-reports": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", + "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul/node_modules/has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha512-DyYHfIYwAJmjAjSSPKANxI8bFY9YtFrgkAfinBojQ8YJTOuOuav64tMUJv584SES4xl74PmuaevIyaLESHdTAA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/istanbul/node_modules/resolve": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", + "integrity": "sha512-9znBF0vBcaSN3W2j7wKvdERPwqTxSpCq+if5C0WoTCyV9n24rua28jeuQ2pL/HOf+yUe/Mef+H/5p60K0Id3bg==", + "dev": true, + "license": "MIT" + }, + "node_modules/istanbul/node_modules/supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha512-Jds2VIYDrlp5ui7t8abHN2bjAu4LV/q4N2KivFPpGH0lrka0BMq/33AmECUXlKPcHigkNaqfXRENFju+rlcy+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^1.0.0" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/istanbul/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jake": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.2.tgz", + "integrity": "sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "async": "^3.2.3", + "chalk": "^4.0.2", + "filelist": "^1.0.4", + "minimatch": "^3.1.2" + }, + "bin": { + "jake": "bin/cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jake/node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "dev": true, + "license": "MIT" + }, + "node_modules/jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", + "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/core": "^29.7.0", + "@jest/types": "^29.6.3", + "import-local": "^3.0.2", + "jest-cli": "^29.7.0" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-changed-files": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", + "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "execa": "^5.0.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-circus": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", + "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^1.0.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^29.7.0", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0", + "pretty-format": "^29.7.0", + "pure-rand": "^6.0.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-cli": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", + "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/core": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "create-jest": "^29.7.0", + "exit": "^0.1.2", + "import-local": "^3.0.2", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "yargs": "^17.3.1" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-config": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", + "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/test-sequencer": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-jest": "^29.7.0", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-circus": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@types/node": "*", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/jest-config/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/jest-diff": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", + "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-docblock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", + "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "detect-newline": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-each": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", + "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "jest-util": "^29.7.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-environment-node": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", + "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-get-type": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-haste-map": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", + "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + } + }, + "node_modules/jest-leak-detector": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", + "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", + "dev": true, + "license": "MIT", + "dependencies": { + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-matcher-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", + "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-message-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", + "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.6.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-mock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", + "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-pnp-resolver": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "jest-resolve": "*" + }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } + } + }, + "node_modules/jest-regex-util": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", + "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "resolve": "^1.20.0", + "resolve.exports": "^2.0.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve-dependencies": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", + "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", + "dev": true, + "license": "MIT", + "dependencies": { + "jest-regex-util": "^29.6.3", + "jest-snapshot": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runner": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", + "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/environment": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "graceful-fs": "^4.2.9", + "jest-docblock": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-leak-detector": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-resolve": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-util": "^29.7.0", + "jest-watcher": "^29.7.0", + "jest-worker": "^29.7.0", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runtime": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", + "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/globals": "^29.7.0", + "@jest/source-map": "^29.6.3", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runtime/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/jest-snapshot": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", + "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.11.6", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-jsx": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/types": "^7.3.3", + "@jest/expect-utils": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "natural-compare": "^1.4.0", + "pretty-format": "^29.7.0", + "semver": "^7.5.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-snapshot/node_modules/semver": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-util/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/jest-validate": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", + "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "leven": "^3.1.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-watcher": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", + "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "jest-util": "^29.7.0", + "string-length": "^4.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "jest-util": "^29.7.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/joi": { + "version": "12.1.1", + "resolved": "https://registry.npmjs.org/joi/-/joi-12.1.1.tgz", + "integrity": "sha512-Gh3iTjGLqGmQKTDuMFfsV7zT4uHtckqtrp88VFOc89V/sNtshOlAAtEkMM8TxJqRt1Cei00Hlh6/Bp7WjcqEEg==", + "deprecated": "Please update your dependencies as this version is no longer maintained and may contain bugs and security issues.", + "dev": true, + "license": "SEE LICENSE IN LICENSE.md", + "dependencies": { + "@hapi/address": "1.x.x", + "hoek": "4.x.x", + "topo": "2.x.x" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/js-yaml/node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/js2xmlparser": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/js2xmlparser/-/js2xmlparser-4.0.2.tgz", + "integrity": "sha512-6n4D8gLlLf1n5mNLQPRfViYzu9RATblzPEtm1SthMX1Pjao0r9YI9nw7ZIfRxQMERS87mcswrg+r/OYrPRX6jA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "xmlcreate": "^2.0.4" + } + }, + "node_modules/jsbn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", + "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==", + "license": "MIT" + }, + "node_modules/jsdoc": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-4.0.4.tgz", + "integrity": "sha512-zeFezwyXeG4syyYHbvh1A967IAqq/67yXtXvuL5wnqCkFZe8I0vKfm+EO+YEvLguo6w9CDUbrAXVtJSHh2E8rw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@babel/parser": "^7.20.15", + "@jsdoc/salty": "^0.2.1", + "@types/markdown-it": "^14.1.1", + "bluebird": "^3.7.2", + "catharsis": "^0.9.0", + "escape-string-regexp": "^2.0.0", + "js2xmlparser": "^4.0.2", + "klaw": "^3.0.0", + "markdown-it": "^14.1.0", + "markdown-it-anchor": "^8.6.7", + "marked": "^4.0.10", + "mkdirp": "^1.0.4", + "requizzle": "^0.2.3", + "strip-json-comments": "^3.1.0", + "underscore": "~1.13.2" + }, + "bin": { + "jsdoc": "jsdoc.js" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/jsdoc-type-pratt-parser": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-4.1.0.tgz", + "integrity": "sha512-Hicd6JK5Njt2QB6XYFS7ok9e37O8AYk3jTcppG4YVQnYjOemymvTcmc7OWsmq/Qqj5TdRFO5/x/tIPmBeRtGHg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/jsdoc/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/jsdoc/node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, + "license": "MIT", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-bigint": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", + "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", + "license": "MIT", + "dependencies": { + "bignumber.js": "^9.0.0" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", + "dev": true, + "license": "(AFL-2.1 OR BSD-3-Clause)" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", + "dev": true, + "license": "ISC" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsprim": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", + "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.4.0", + "verror": "1.10.0" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/klaw": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/klaw/-/klaw-3.0.0.tgz", + "integrity": "sha512-0Fo5oir+O9jnXu5EefYbVK+mHMBeEVEy2cmctR1O1NECcCkPRreJKrS6Qt/j3KC2C148Dfo9i3pCmCMsdqGr0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.9" + } + }, + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/lcov-parse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lcov-parse/-/lcov-parse-1.0.0.tgz", + "integrity": "sha512-aprLII/vPzuQvYZnDRU78Fns9I2Ag3gi4Ipga/hxnVMCZC8DnR2nI7XBqrPoywGfxqIx/DgarGvDJZAD3YBTgQ==", + "dev": true, + "license": "BSD-3-Clause", + "bin": { + "lcov-parse": "bin/cli.js" + } + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "uc.micro": "^2.0.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.flattendeep": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", + "integrity": "sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.zipobject": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/lodash.zipobject/-/lodash.zipobject-4.1.3.tgz", + "integrity": "sha512-A9SzX4hMKWS25MyalwcOnNoplyHbkNVsjidhTp8ru0Sj23wY9GWBKS8gAIGDSAqeWjIjvE4KBEl24XXAs+v4wQ==", + "license": "MIT" + }, + "node_modules/log-driver": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/log-driver/-/log-driver-1.2.7.tgz", + "integrity": "sha512-U7KCmLdqsGHBLeWqYlFA0V0Sl6P08EE1ZrmA9cxjUE0WVqT9qnyVDPz1kzpFEP0jdJuFnasWIfSd7fsaNXkpbg==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=0.8.6" + } + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/loupe": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", + "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-func-name": "^2.0.1" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/magic-string": { + "version": "0.30.17", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", + "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0" + } + }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true, + "license": "ISC" + }, + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tmpl": "1.0.5" + } + }, + "node_modules/markdown-it": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz", + "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1", + "entities": "^4.4.0", + "linkify-it": "^5.0.0", + "mdurl": "^2.0.0", + "punycode.js": "^2.3.1", + "uc.micro": "^2.1.0" + }, + "bin": { + "markdown-it": "bin/markdown-it.mjs" + } + }, + "node_modules/markdown-it-anchor": { + "version": "8.6.7", + "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-8.6.7.tgz", + "integrity": "sha512-FlCHFwNnutLgVTflOYHPW2pPcl2AACqVzExlkGQNsi4CJgqOHN7YTgDd4LuhgN1BFO3TS0vLAruV1Td6dwWPJA==", + "dev": true, + "license": "Unlicense", + "peerDependencies": { + "@types/markdown-it": "*", + "markdown-it": "*" + } + }, + "node_modules/markdown-it/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/marked": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz", + "integrity": "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==", + "dev": true, + "license": "MIT", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 12" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==", + "dev": true, + "license": "MIT" + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true, + "license": "MIT" + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/mocha": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.1.0.tgz", + "integrity": "sha512-8uJR5RTC2NgpY3GrYcgpZrsEd9zKbPDpob1RezyR2upGHRQtHWofmzTMzTMSV6dru3tj5Ukt0+Vnq1qhFEEwAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-colors": "^4.1.3", + "browser-stdout": "^1.3.1", + "chokidar": "^3.5.3", + "debug": "^4.3.5", + "diff": "^5.2.0", + "escape-string-regexp": "^4.0.0", + "find-up": "^5.0.0", + "glob": "^10.4.5", + "he": "^1.2.0", + "js-yaml": "^4.1.0", + "log-symbols": "^4.1.0", + "minimatch": "^5.1.6", + "ms": "^2.1.3", + "serialize-javascript": "^6.0.2", + "strip-json-comments": "^3.1.1", + "supports-color": "^8.1.1", + "workerpool": "^6.5.1", + "yargs": "^17.7.2", + "yargs-parser": "^21.1.1", + "yargs-unparser": "^2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/mocha-lcov-reporter": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/mocha-lcov-reporter/-/mocha-lcov-reporter-1.3.0.tgz", + "integrity": "sha512-/5zI2tW4lq/ft8MGpYQ1nIH6yePPtIzdGeUEwFMKfMRdLfAQ1QW2c68eEJop32tNdN5srHa/E2TzB+erm3YMYA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/mocha/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/mocha/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/mocha/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mocha/node_modules/glob/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mocha/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/mocha/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mocha/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true, + "license": "MIT" + }, + "node_modules/nock": { + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/nock/-/nock-14.0.1.tgz", + "integrity": "sha512-IJN4O9pturuRdn60NjQ7YkFt6Rwei7ZKaOwb1tvUIIqTgeD0SDDAX3vrqZD4wcXczeEy/AsUXxpGpP/yHqV7xg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@mswjs/interceptors": "^0.37.3", + "json-stringify-safe": "^5.0.1", + "propagate": "^2.0.0" + }, + "engines": { + "node": ">=18.20.0 <20 || >=20.12.1" + } + }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "engines": { + "node": ">=10.5.0" + } + }, + "node_modules/node-fetch": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "license": "MIT", + "dependencies": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" + } + }, + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-preload": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz", + "integrity": "sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "process-on-spawn": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/node-releases": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", + "dev": true, + "license": "MIT" + }, + "node_modules/nopt": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", + "integrity": "sha512-4GUt3kSEYmk4ITxzB/b9vaIDfUVWN/Ml1Fwl11IlnIG2iaJ9O6WXZ9SrYM9NLI8OCBieN2Y8SWC2oJV0RQ7qYg==", + "dev": true, + "license": "ISC", + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc": { + "version": "17.1.0", + "resolved": "https://registry.npmjs.org/nyc/-/nyc-17.1.0.tgz", + "integrity": "sha512-U42vQ4czpKa0QdI1hu950XuNhYqgoM+ZF1HT+VuUHL9hPfDPVvNQyltmMqdE9bUHMVa+8yNbc3QKTj8zQhlVxQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "caching-transform": "^4.0.0", + "convert-source-map": "^1.7.0", + "decamelize": "^1.2.0", + "find-cache-dir": "^3.2.0", + "find-up": "^4.1.0", + "foreground-child": "^3.3.0", + "get-package-type": "^0.1.0", + "glob": "^7.1.6", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-hook": "^3.0.0", + "istanbul-lib-instrument": "^6.0.2", + "istanbul-lib-processinfo": "^2.0.2", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.0.2", + "make-dir": "^3.0.0", + "node-preload": "^0.2.1", + "p-map": "^3.0.0", + "process-on-spawn": "^1.0.0", + "resolve-from": "^5.0.0", + "rimraf": "^3.0.0", + "signal-exit": "^3.0.2", + "spawn-wrap": "^2.0.0", + "test-exclude": "^6.0.0", + "yargs": "^15.0.2" + }, + "bin": { + "nyc": "bin/nyc.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/nyc/node_modules/cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "node_modules/nyc/node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "dev": true, + "license": "MIT" + }, + "node_modules/nyc/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/nyc/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/nyc/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/nyc/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/nyc/node_modules/yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "*" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/outvariant": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/outvariant/-/outvariant-1.4.3.tgz", + "integrity": "sha512-+Sl2UErvtsoajRDKCE5/dBz4DIvHXQQnAxtQTF04OJxY0+DyZXSo5P5Bb7XYWOh81syohlYL24hbDwxedPUJCA==", + "dev": true, + "license": "MIT" + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-map": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", + "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/package-hash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-4.0.0.tgz", + "integrity": "sha512-whdkPIooSu/bASggZ96BWVvZTRMOFxnyUG5PnTSGKoJE2gd5mbVNmR2Nj20QFzxYYgAXpoqC+AiXzl+UMRh7zQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "graceful-fs": "^4.1.15", + "hasha": "^5.0.0", + "lodash.flattendeep": "^4.4.0", + "release-zalgo": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-imports": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/parse-imports/-/parse-imports-2.2.1.tgz", + "integrity": "sha512-OL/zLggRp8mFhKL0rNORUTR4yBYujK/uU+xZL+/0Rgm2QE4nLO9v8PzEweSJEbMGKmDRjJE4R3IMJlL2di4JeQ==", + "dev": true, + "license": "Apache-2.0 AND MIT", + "dependencies": { + "es-module-lexer": "^1.5.3", + "slashes": "^3.0.12" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/pathval": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pirates": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", + "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pkg-dir/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/process-on-spawn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/process-on-spawn/-/process-on-spawn-1.1.0.tgz", + "integrity": "sha512-JOnOPQ/8TZgjs1JIH/m9ni7FfimjNa/PRx7y/Wb5qdItsnhO0jE4AT7fC0HjC28DUQWDr50dwSYZLdRMlqDq3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "fromentries": "^1.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/propagate": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/propagate/-/propagate-2.0.1.tgz", + "integrity": "sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/psl": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz", + "integrity": "sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "funding": { + "url": "https://github.com/sponsors/lupomontero" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/punycode.js": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", + "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/pure-rand": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", + "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "license": "MIT" + }, + "node_modules/qs": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", + "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true, + "license": "MIT" + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/readdirp/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/release-zalgo": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", + "integrity": "sha512-gUAyHVHPPC5wdqX/LG4LWtRYtgjxyX78oanFNTMMyFEfOqdC54s3eE82imuWKbOeqYht2CrNf64Qb8vgmmtZGA==", + "dev": true, + "license": "ISC", + "dependencies": { + "es6-error": "^4.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/request": { + "version": "2.88.2", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", + "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", + "deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.5.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/request-promise": { + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/request-promise/-/request-promise-4.2.6.tgz", + "integrity": "sha512-HCHI3DJJUakkOr8fNoCc73E5nU5bqITjOYFMDrKHYOXWXrgD/SBaC7LjwuPymUprRyuF06UK7hd/lMHkmUXglQ==", + "deprecated": "request-promise has been deprecated because it extends the now deprecated request package, see https://github.com/request/request/issues/3142", + "dev": true, + "license": "ISC", + "dependencies": { + "bluebird": "^3.5.0", + "request-promise-core": "1.1.4", + "stealthy-require": "^1.1.1", + "tough-cookie": "^2.3.3" + }, + "engines": { + "node": ">=0.10.0" + }, + "peerDependencies": { + "request": "^2.34" + } + }, + "node_modules/request-promise-core": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.4.tgz", + "integrity": "sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw==", + "dev": true, + "license": "ISC", + "dependencies": { + "lodash": "^4.17.19" + }, + "engines": { + "node": ">=0.10.0" + }, + "peerDependencies": { + "request": "^2.34" + } + }, + "node_modules/request/node_modules/form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 0.12" + } + }, + "node_modules/request/node_modules/uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", + "dev": true, + "license": "MIT", + "bin": { + "uuid": "bin/uuid" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true, + "license": "ISC" + }, + "node_modules/requizzle": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.4.tgz", + "integrity": "sha512-JRrFk1D4OQ4SqovXOgdav+K8EAhSB/LJZqCz8tbX0KObcdeM15Ss59ozWMBWmmINMagCwmqn4ZNryUGpBsl6Jw==", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash": "^4.17.21" + } + }, + "node_modules/resolve": { + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-cwd/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/resolve.exports": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.3.tgz", + "integrity": "sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rollup": { + "version": "4.37.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.37.0.tgz", + "integrity": "sha512-iAtQy/L4QFU+rTJ1YUjXqJOJzuwEghqWzCEYD2FEghT7Gsy1VdABntrO4CLopA5IkflTyqNiLNwPcOJ3S7UKLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.6" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.37.0", + "@rollup/rollup-android-arm64": "4.37.0", + "@rollup/rollup-darwin-arm64": "4.37.0", + "@rollup/rollup-darwin-x64": "4.37.0", + "@rollup/rollup-freebsd-arm64": "4.37.0", + "@rollup/rollup-freebsd-x64": "4.37.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.37.0", + "@rollup/rollup-linux-arm-musleabihf": "4.37.0", + "@rollup/rollup-linux-arm64-gnu": "4.37.0", + "@rollup/rollup-linux-arm64-musl": "4.37.0", + "@rollup/rollup-linux-loongarch64-gnu": "4.37.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.37.0", + "@rollup/rollup-linux-riscv64-gnu": "4.37.0", + "@rollup/rollup-linux-riscv64-musl": "4.37.0", + "@rollup/rollup-linux-s390x-gnu": "4.37.0", + "@rollup/rollup-linux-x64-gnu": "4.37.0", + "@rollup/rollup-linux-x64-musl": "4.37.0", + "@rollup/rollup-win32-arm64-msvc": "4.37.0", + "@rollup/rollup-win32-ia32-msvc": "4.37.0", + "@rollup/rollup-win32-x64-msvc": "4.37.0", + "fsevents": "~2.3.2" + } + }, + "node_modules/rollup-plugin-execute": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/rollup-plugin-execute/-/rollup-plugin-execute-1.1.1.tgz", + "integrity": "sha512-isCNR/VrwlEfWJMwsnmt5TBRod8dW1IjVRxcXCBrxDmVTeA1IXjzeLSS3inFBmRD7KDPlo38KSb2mh5v5BoWgA==", + "dev": true, + "license": "MIT" + }, + "node_modules/rollup/node_modules/@types/estree": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true, + "license": "MIT" + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/serialize-javascript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "dev": true, + "license": "ISC" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true, + "license": "MIT" + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/slashes": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/slashes/-/slashes-3.0.12.tgz", + "integrity": "sha512-Q9VME8WyGkc7pJf6QEkj3wE+2CnvZMI+XJhwdTPR8Z/kWQRXi7boAWLDibRPyHRTUTPx5FaU7MsyrjI3yLB4HA==", + "dev": true, + "license": "ISC" + }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "license": "MIT", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks": { + "version": "2.8.4", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.4.tgz", + "integrity": "sha512-D3YaD0aRxR3mEcqnidIs7ReYJFVzWdd6fXJYUM8ixcQcJRGTka/b3saV0KflYhyVJXKhb947GndU35SxYNResQ==", + "license": "MIT", + "dependencies": { + "ip-address": "^9.0.5", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks-proxy-agent": { + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz", + "integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "^4.3.4", + "socks": "^2.8.3" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/source-map": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.2.0.tgz", + "integrity": "sha512-CBdZ2oa/BHhS4xj5DlhjWNHcan57/5YuvfdLf17iVmIpd9KRm+DFLmC6nBNj+6Ua7Kt3TmOjDpQT1aTYOQtoUA==", + "dev": true, + "optional": true, + "dependencies": { + "amdefine": ">=0.0.4" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/source-map-support/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/spawn-wrap": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-2.0.0.tgz", + "integrity": "sha512-EeajNjfN9zMnULLwhZZQU3GWBoFNkbngTUPfaawT4RkMiviTxcX0qfhVbGey39mfctfDHkWtuecgQ8NJcyQWHg==", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^2.0.0", + "is-windows": "^1.0.2", + "make-dir": "^3.0.0", + "rimraf": "^3.0.0", + "signal-exit": "^3.0.2", + "which": "^2.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/spawn-wrap/node_modules/foreground-child": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", + "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/spawn-wrap/node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", + "dev": true, + "license": "CC-BY-3.0" + }, + "node_modules/spdx-expression-parse": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-4.0.0.tgz", + "integrity": "sha512-Clya5JIij/7C6bRR22+tnGXbc4VKlibKSVj2iHvVeX5iMW7s1SIQlqu699JkODJJIhh/pUu8L0/VLh8xflD+LQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.21", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.21.tgz", + "integrity": "sha512-Bvg/8F5XephndSK3JffaRqdT+gyhfqIPwDHpX80tJrF8QQRYMo8sNMeaZ2Dp5+jhwKnUmIOyFFQfHRkjJm5nXg==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/sshpk": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.18.0.tgz", + "integrity": "sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + }, + "bin": { + "sshpk-conv": "bin/sshpk-conv", + "sshpk-sign": "bin/sshpk-sign", + "sshpk-verify": "bin/sshpk-verify" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sshpk/node_modules/jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", + "dev": true, + "license": "MIT" + }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/stack-utils/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/stealthy-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", + "integrity": "sha512-ZnWpYnYugiOVEY5GkcuJK1io5V8QmNYChG62gSit9pQVGErXtrKuPC55ITaVSukmMta5qpMU7vqLt2Lnni4f/g==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stream-events": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/stream-events/-/stream-events-1.0.5.tgz", + "integrity": "sha512-E1GUzBSgvct8Jsb3v2X15pjzN1tYebtbLaMg+eBOUOAxgbLoSbT2NS91ckc5lJD1KfLjId+jXJRgo0qnV5Nerg==", + "dev": true, + "license": "MIT", + "dependencies": { + "stubs": "^3.0.0" + } + }, + "node_modules/strict-event-emitter": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/strict-event-emitter/-/strict-event-emitter-0.5.1.tgz", + "integrity": "sha512-vMgjE/GGEPEFnhFub6pa4FmJBRBVOLpIII2hvCZ8Kzb7K0hlHo7mQv6xYrBvCL2LtAIBwFUK8wvuJgTVSQ5MFQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/string-hash": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/string-hash/-/string-hash-1.1.3.tgz", + "integrity": "sha512-kJUvRUFK49aub+a7T1nNE66EJbZBMnBgoC1UbCZ5n6bsZKBRga4KgBRTMn/pFkeCZSYtNeSyMxPDM0AXWELk2A==", + "license": "CC0-1.0" + }, + "node_modules/string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/stubs": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/stubs/-/stubs-3.0.0.tgz", + "integrity": "sha512-PdHt7hHUJKxvTCgbKX9C1V/ftOcjJQgz8BZwNfV5c4B6dcGqlpelTbJ999jBGZ2jYiPAwcX5dP6oBwVlBlUbxw==", + "dev": true, + "license": "MIT" + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/synckit": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.9.2.tgz", + "integrity": "sha512-vrozgXDQwYO72vHjUb/HnFbQx1exDjoKzqx23aXEg2a9VIg2TSFZ8FmeZpTjUCFMYw7mpX4BE2SFu8wI7asYsw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@pkgr/core": "^0.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, + "node_modules/teeny-request": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-7.0.1.tgz", + "integrity": "sha512-sasJmQ37klOlplL4Ia/786M5YlOcoLGQyq2TE4WHSRupbAuDaQW0PfVxV4MtdBtRJ4ngzS+1qim8zP6Zp35qCw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "http-proxy-agent": "^4.0.0", + "https-proxy-agent": "^5.0.0", + "node-fetch": "^2.6.1", + "stream-events": "^1.0.5", + "uuid": "^8.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/teeny-request/node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/teeny-request/node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/teeny-request/node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "license": "ISC", + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/test-exclude/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true, + "license": "MIT" + }, + "node_modules/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/topo": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/topo/-/topo-2.1.1.tgz", + "integrity": "sha512-ZPrPP5nwzZy1fw9abHQH2k+YarTgp9UMAztcB3MmlcZSif63Eg+az05p6wTDaZmnqpS3Mk7K+2W60iHarlz8Ug==", + "deprecated": "This module has moved and is now available at @hapi/topo. Please update your dependencies as this version is no longer maintained and may contain bugs and security issues.", + "dev": true, + "license": "SEE LICENSE IN LICENSE.md", + "dependencies": { + "hoek": "4.x.x" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "dev": true, + "license": "MIT" + }, + "node_modules/ts-api-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", + "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "node_modules/ts-jest": { + "version": "29.3.0", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.3.0.tgz", + "integrity": "sha512-4bfGBX7Gd1Aqz3SyeDS9O276wEU/BInZxskPrbhZLyv+c1wskDCqDFMJQJLWrIr/fKoAH4GE5dKUlrdyvo+39A==", + "dev": true, + "license": "MIT", + "dependencies": { + "bs-logger": "^0.2.6", + "ejs": "^3.1.10", + "fast-json-stable-stringify": "^2.1.0", + "jest-util": "^29.0.0", + "json5": "^2.2.3", + "lodash.memoize": "^4.1.2", + "make-error": "^1.3.6", + "semver": "^7.7.1", + "type-fest": "^4.37.0", + "yargs-parser": "^21.1.1" + }, + "bin": { + "ts-jest": "cli.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.0 <8", + "@jest/transform": "^29.0.0", + "@jest/types": "^29.0.0", + "babel-jest": "^29.0.0", + "jest": "^29.0.0", + "typescript": ">=4.3 <6" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "@jest/transform": { + "optional": true + }, + "@jest/types": { + "optional": true + }, + "babel-jest": { + "optional": true + }, + "esbuild": { + "optional": true + } + } + }, + "node_modules/ts-jest/node_modules/semver": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ts-jest/node_modules/type-fest": { + "version": "4.38.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.38.0.tgz", + "integrity": "sha512-2dBz5D5ycHIoliLYLi0Q2V7KRaDlH0uWIvmk7TYlAg5slqwiPv1ezJdZm1QEM0xgk29oYWMCbIG7E6gHpvChlg==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/tsx": { + "version": "4.19.3", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.19.3.tgz", + "integrity": "sha512-4H8vUNGNjQ4V2EOoGw005+c+dGuPSnhpPBPHBtsZdGZBk/iJb4kguGlPWaZTZ3q5nMtFOEsY0nRDlh9PJyd6SQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "~0.25.0", + "get-tsconfig": "^4.7.5" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", + "dev": true, + "license": "Unlicense" + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-detect": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.1.0.tgz", + "integrity": "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-typedarray": "^1.0.0" + } + }, + "node_modules/typescript": { + "version": "5.8.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz", + "integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/uc.micro": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", + "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", + "dev": true, + "license": "MIT" + }, + "node_modules/uglify-js": { + "version": "3.19.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", + "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", + "dev": true, + "license": "BSD-2-Clause", + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/underscore": { + "version": "1.13.7", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.7.tgz", + "integrity": "sha512-GMXzWtsc57XAtguZgaQViUOzs0KTkk8ojr3/xAxXLITqf/3EMwxC0inyETfDFjH/Krbhuep0HNbbjI9i/q3F3g==", + "dev": true, + "license": "MIT" + }, + "node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "dev": true, + "license": "MIT" + }, + "node_modules/update-browserslist-db": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/url": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.4.tgz", + "integrity": "sha512-oCwdVC7mTuWiPyjLUz/COz5TLk6wgp0RCsN+wHZ2Ekneac9w8uuV0njcbbie2ME+Vs+d6duwmYuR3HgQXs1fOg==", + "license": "MIT", + "dependencies": { + "punycode": "^1.4.1", + "qs": "^6.12.3" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/url/node_modules/punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==", + "license": "MIT" + }, + "node_modules/url/node_modules/qs": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/urlgrey": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/urlgrey/-/urlgrey-0.4.4.tgz", + "integrity": "sha512-vfQzI+JDPBrBRw374pgWi6bFPfc+6BonRsazCj3weBIWe8moRcvfgy0lpaiGkMGnExs4Z/Dws8lp5mc9IegURw==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/v8-to-istanbul": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", + "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", + "dev": true, + "license": "ISC", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", + "dev": true, + "engines": [ + "node >=0.6.0" + ], + "license": "MIT", + "dependencies": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "node_modules/walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "makeerror": "1.0.12" + } + }, + "node_modules/web-streams-polyfill": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", + "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-module": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", + "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/workerpool": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.5.1.tgz", + "integrity": "sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/write-file-atomic": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", + "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", + "dev": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/ws": { + "version": "7.5.10", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", + "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", + "license": "MIT", + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xmlcreate": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-2.0.4.tgz", + "integrity": "sha512-nquOebG4sngPmGPICTS5EnxqhKbCmz5Ox5hsszI2T6U5qdrJizBc+0ilYSEjTSzU0yZcmvppztXe/5Al5fUwdg==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-unparser/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yargs-unparser/node_modules/decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/package.json b/package.json index 82249300..760c4481 100644 --- a/package.json +++ b/package.json @@ -1,51 +1,96 @@ { "name": "node-binance-api", - "version": "0.9.2", - "description": "Binance API for node https://github.com/jaggedsoft/node-binance-api", - "main": "node-binance-api.js", + "version": "1.0.1", + "description": "Binance API for node https://github.com/ccxt/node-binance-api", + "typings": "./dist/node-binance-api.d.ts", + "type": "module", + "exports": { + ".": { + "import": "./dist/node-binance-api.js", + "require": "./dist/cjs/node-binance-api.cjs" + } + }, "dependencies": { - "async": "^2.6.1", - "https-proxy-agent": "^2.2.1", - "request": "^2.85.0", - "socks-proxy-agent": "^4.0.1", + "https-proxy-agent": "^7.0.0", + "json-bigint": "^1.0.0", + "lodash.zipobject": "^4.1.3", + "node-fetch": "^3.3.2", + "socks-proxy-agent": "^8.0.5", "string-hash": "^1.1.3", "url": "^0.11.0", - "ws": "^4.1.0" + "ws": "^7.2.0" }, "scripts": { - "test": "mocha", + "build": "tsc && npm run bundle-cjs", + "test": "mocha ./tests/live-tests.cjs", + "static-test": "mocha ./tests/static-tests.mjs", + "ts-test-live": "mocha ./tests/binance-class-live.test.ts", + "ts-test-static": "mocha ./tests/binance-class-static.test.ts", + "test-cjs": "node ./tests/cjs-test.cjs", + "crypto-tests": "mocha ./tests/crypto.test.ts", + "ws-tests": "mocha ./tests/binance-class-ws.test.ts", + "ws-tests-spot": "mocha ./tests/binance-ws-spot.test.ts --exit", + "ws-tests-futures": "mocha ./tests/binance-ws-futures.test.ts --exit", "test-debug": "mocha --inspect-brk", - "lint": "eslint -c .eslintrc.js node-binance-api.js test.js", + "lint": "eslint src/", "cover": "istanbul cover _mocha --report lcovonly", "coveralls": "cat ./coverage/lcov.info | coveralls", "codacy": "cat ./coverage/lcov.info | codacy-coverage -v", - "codecov": "codecov" + "codecov": "codecov", + "bundle-cjs": "mkdir -p dist/cjs && rollup -c rollup.config.js", + "package-test": "./tests/package.sh", + "publishPackage": "sh publish.sh && git push && git push --tags && npm publish", + "rollup-plugin-execute": "^1.1.1" }, "repository": { "type": "git", - "url": "git://github.com/jaggedsoft/node-binance-api.git" + "url": "https://github.com/ccxt/node-binance-api.git" }, "keywords": [ "binance", - "api" + "api", + "trading", + "crypto" ], - "author": "Jon Eyrick (https://nextlocal.net)", + "author": "Jon Eyrick (https://darkpool.ventures)", "license": "MIT", "bugs": { - "url": "https://github.com/jaggedsoft/node-binance-api/issues" + "url": "https://github.com/ccxt/node-binance-api/issues" }, - "homepage": "https://github.com/jaggedsoft/node-binance-api#readme", + "homepage": "https://github.com/ccxt/node-binance-api", "devDependencies": { - "chai": "^4.1.2", + "@rollup/plugin-commonjs": "^28.0.3", + "@rollup/plugin-json": "^6.1.0", + "@rollup/plugin-node-resolve": "^16.0.1", + "@rollup/plugin-typescript": "^12.1.2", + "@types/async": "^3.2.24", + "@types/chai": "^5.0.1", + "@types/json-bigint": "^1.0.4", + "@types/mocha": "^10.0.10", + "@types/request": "^2.48.12", + "@types/string-hash": "^1.1.3", + "@types/ws": "^8.18.0", + "@typescript-eslint/eslint-plugin": "^8.28.0", + "chai": "^4.5.0", "chai-counter": "^1.0.0", - "codacy-coverage": "^2.1.1", - "codecov": "^3.0.4", - "coveralls": "^3.0.2", - "eslint": "^4.19.1", + "codacy-coverage": "^2.0.3", + "codecov": "^3.6.1", + "coveralls": "^3.0.7", + "eslint": "^8.57.0", + "eslint-plugin-jsdoc": "^50.6.9", + "esm-to-cjs": "^1.2.1", "istanbul": "^0.4.5", - "jsdoc": "^3.5.5", - "mocha": "^5.2.0", + "jest": "^29.7.0", + "jsdoc": "^4.0.4", + "mocha": "^11.1.0", "mocha-lcov-reporter": "^1.3.0", - "nyc": "^13.0.0" + "nock": "^14.0.1", + "nyc": "^17.1.0", + "rollup": "^4.37.0", + "rollup-plugin-execute": "^1.1.1", + "ts-jest": "^29.3.0", + "tslib": "^2.8.1", + "tsx": "^4.19.3", + "typescript": "^5.8.2" } } diff --git a/publish.sh b/publish.sh new file mode 100755 index 00000000..59170a7f --- /dev/null +++ b/publish.sh @@ -0,0 +1,21 @@ + +#!/bin/bash +set -e + +# release defaults to patch (last number in semver) +RELEASE="patch" && [ -n "$1" ] && RELEASE=$1 + +# cut the release +VERSION=$(npm --no-git-tag-version version $RELEASE | sed 's/v//') + +git add package.json +git commit -m "release: cut the $VERSION release" + +# tag the release +git tag $VERSION +git tag -l + +echo -e "\033[1;92m You are ready to publish!" +echo -e "\033[1;95m git push" +echo -e "\033[1;95m git push --tags" +echo -e "\033[1;95m npm publish" diff --git a/rollup.config.js b/rollup.config.js new file mode 100644 index 00000000..d831207b --- /dev/null +++ b/rollup.config.js @@ -0,0 +1,40 @@ +import commonjs from "@rollup/plugin-commonjs"; +import json from "@rollup/plugin-json"; +import execute from 'rollup-plugin-execute'; +import nodeResolve from '@rollup/plugin-node-resolve' +// import typescript from '@rollup/plugin-typescript'; + +export default [ + { + // preserveModules: true, + input: "./dist/node-binance-api.js", + output: [ + { + file: "./dist/cjs/node-binance-api.cjs", + format: "cjs", + } + ], + plugins: [ + nodeResolve({ + preferBuiltins: true, + // node resolve generate dist/cjs/js directory + jail: '/src' + }), + json(), + commonjs({ + transformMixedEsModules: true, + // dynamicRequireTargets: ["**/js/src/static_dependencies/**/*.cjs"], + }), + execute("echo '{ \"type\": \"commonjs\" }' > ./dist/cjs/package.json") // this is needed to make node treat files inside dist/cjs as CJS modules + ], + onwarn: ( warning, next ) => { + if ( warning.message.indexOf('is implicitly using "default" export mode') > -1 ) return; + next( warning ); + }, + external: [ + 'socks-proxy-agent', + // node resolve generate dist/cjs/js directory, treat ws, debug as external + 'ws', 'debug' + ] + } +]; \ No newline at end of file diff --git a/src/.eslintrc.json b/src/.eslintrc.json new file mode 100644 index 00000000..a3766fdb --- /dev/null +++ b/src/.eslintrc.json @@ -0,0 +1,54 @@ +{ + "env": { + "browser": true, + "es6": true + }, + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/eslint-recommended" + ], + "globals": { + "Atomics": "readonly", + "SharedArrayBuffer": "readonly" + }, + "parser": "@typescript-eslint/parser", + "parserOptions": { + "ecmaVersion": 2018, + "sourceType": "module" + }, + "plugins": [ + "@typescript-eslint", + "jsdoc" + ], + "rules": { + "no-multiple-empty-lines": ["error", { "max": 1 }], + "import/named": "off", + "strict": "off", + "semi": "error", + "indent": ["error", 4], + // "func-call-spacing": ["error", "always"], + "block-spacing": ["error", "always"], + "keyword-spacing": ["error", { "before": true, "after": true }], + "object-curly-spacing": ["error", "always", { "objectsInObjects": false }], + "object-curly-newline": ["error", { "consistent": true }], + "space-infix-ops": "error", + "space-unary-ops": "error", + "space-in-parens": "error", + "jsdoc/require-description": "off" + }, + "settings": { + "import/resolver": { + "node": { + "extensions": [".js", ".ts"] + } + }, + "import/parsers": { + "@typescript-eslint/parser": [".ts", ".js"] + }, + "jsdoc": { + "tagNamePreference": { + "function": "method" + } + } + } +} \ No newline at end of file diff --git a/src/node-binance-api.ts b/src/node-binance-api.ts new file mode 100644 index 00000000..e68b6743 --- /dev/null +++ b/src/node-binance-api.ts @@ -0,0 +1,6061 @@ + +import WebSocket from 'ws'; +// import request from 'request'; +import crypto from 'crypto'; +import file from 'fs'; +import url from 'url'; +import JSONbig from 'json-bigint'; +// @ts-ignore +import { HttpsProxyAgent } from 'https-proxy-agent'; +// @ts-ignore +import { SocksProxyAgent } from 'socks-proxy-agent'; +// @ts-ignore +import nodeFetch from 'node-fetch'; + +// @ts-ignore +import zip from 'lodash.zipobject'; +import stringHash from 'string-hash'; +import async from 'async'; +// eslint-disable-next-line +import { Interval, PositionRisk, Order, FuturesOrder, PositionSide, WorkingType, OrderType, OrderStatus, TimeInForce, Callback, IConstructorArgs, OrderSide, FundingRate, CancelOrder, AggregatedTrade, Trade, MyTrade, WithdrawHistoryResponse, DepositHistoryResponse, DepositAddress, WithdrawResponse, Candle, FuturesCancelAllOpenOrder, OrderBook, Ticker, FuturesUserTrade, Account, FuturesAccountInfo, FuturesBalance, QueryOrder, HttpMethod, BookTicker, DailyStats, PremiumIndex, OpenInterest, IWebsocketsMethods } from './types'; +// export { Interval, PositionRisk, Order, FuturesOrder, PositionSide, WorkingType, OrderType, OrderStatus, TimeInForce, Callback, IConstructorArgs, OrderSide, FundingRate, CancelOrder, AggregatedTrade, Trade, MyTrade, WithdrawHistoryResponse, DepositHistoryResponse, DepositAddress, WithdrawResponse, Candle, FuturesCancelAllOpenOrder, OrderBook, Ticker, FuturesUserTrade, FuturesAccountInfo, FuturesBalance, QueryOrder } from './types'; + +export interface Dictionary { + [key: string]: T; +} + +export type Dict = Dictionary; + +export default class Binance { + + domain = 'com'; + base = `https://api.binance.${this.domain}/api/`; + baseTest = `https://testnet.binance.vision/api/`; + wapi = `https://api.binance.${this.domain}/wapi/`; + sapi = `https://api.binance.${this.domain}/sapi/`; + fapi = `https://fapi.binance.${this.domain}/fapi/`; + dapi = `https://dapi.binance.${this.domain}/dapi/`; + fapiTest = `https://testnet.binancefuture.com/fapi/`; + dapiTest = `https://testnet.binancefuture.com/dapi/`; + fstream = `wss://fstream.binance.${this.domain}/stream?streams=`; + fstreamSingle = `wss://fstream.binance.${this.domain}/ws/`; + fstreamSingleTest = `wss://stream.binancefuture.${this.domain}/ws/`; + fstreamTest = `wss://stream.binancefuture.${this.domain}/stream?streams=`; + dstream = `wss://dstream.binance.${this.domain}/stream?streams=`; + dstreamSingle = `wss://dstream.binance.${this.domain}/ws/`; + dstreamSingleTest = `wss://dstream.binancefuture.${this.domain}/ws/`; + dstreamTest = `wss://dstream.binancefuture.${this.domain}/stream?streams=`; + stream = `wss://stream.binance.${this.domain}:9443/ws/`; + streamTest = `wss://testnet.binance.vision/ws/`; + combineStream = `wss://stream.binance.${this.domain}:9443/stream?streams=`; + combineStreamTest = `wss://testnet.binance.vision/stream?streams=`; + + verbose = false; + + futuresListenKeyKeepAlive: number = 60 * 30 * 1000; // 30 minutes + spotListenKeyKeepAlive: number = 60 * 30 * 1000; // 30 minutes + heartBeatInterval: number = 30000; // 30 seconds + + // proxy variables + urlProxy: string = undefined; + httpsProxy: string = undefined; + socksProxy: string = undefined; + nodeFetch: any = undefined; + + APIKEY: string = undefined; + APISECRET: string = undefined; + PRIVATEKEY: string = undefined; + PRIVATEKEYPASSWORD: string = undefined; + test = false; + + timeOffset: number = 0; + + userAgent = 'Mozilla/4.0 (compatible; Node Binance API)'; + contentType = 'application/x-www-form-urlencoded'; + SPOT_PREFIX = "x-HNA2TXFJ"; + CONTRACT_PREFIX = "x-Cb7ytekJ"; + + // Websockets Options + isAlive = false; + socketHeartbeatInterval: any = null; + // endpoint: string = ""; // endpoint for WS? + reconnect = true; + + headers: Dict = {}; + subscriptions: Dict = {}; + futuresSubscriptions: Dict = {}; + futuresInfo: Dict = {}; + futuresMeta: Dict = {}; + futuresTicks: Dict = {}; + futuresRealtime: Dict = {}; + futuresKlineQueue: Dict = {}; + deliverySubscriptions: Dict = {}; + deliveryInfo: Dict = {}; + deliveryMeta: Dict = {}; + deliveryTicks: Dict = {}; + deliveryRealtime: Dict = {}; + deliveryKlineQueue: Dict = {}; + depthCache: Dict = {}; + depthCacheContext: Dict = {}; + ohlcLatest: Dict = {}; + klineQueue: Dict = {}; + ohlc: Dict = {}; + info: Dict = {}; + + websockets: IWebsocketsMethods = { // deprecated structure, keeping it for backwards compatibility + userData: this.userData.bind(this), + userMarginData: this.userMarginData.bind(this), + depthCacheStaggered: this.depthCacheStaggered.bind(this), + userFutureData: this.userFutureData.bind(this), + userDeliveryData: this.userDeliveryData.bind(this), + subscribeCombined: this.subscribeCombined.bind(this), + subscribe: this.subscribe.bind(this), + subscriptions: () => this.getSubscriptions.bind(this), + terminate: this.terminate.bind(this), + depth: this.depthStream.bind(this), + depthCache: this.depthCacheStream.bind(this), + clearDepthCache: this.clearDepthCache.bind(this), + aggTrades: this.aggTradesStream.bind(this), + trades: this.tradesStream.bind(this), + chart: this.chart.bind(this), + candlesticks: this.candlesticksStream.bind(this), + miniTicker: this.miniTicker.bind(this), + bookTickers: this.bookTickersStream.bind(this), + prevDay: this.prevDayStream.bind(this), + futuresCandlesticks: this.futuresCandlesticksStream.bind(this), + futuresTicker: this.futuresTickerStream.bind(this), + futuresMiniTicker: this.futuresMiniTickerStream.bind(this), + futuresAggTrades: this.futuresAggTradeStream.bind(this), + futuresMarkPrice: this.futuresMarkPriceStream.bind(this), + futuresLiquidation: this.futuresLiquidationStream.bind(this), + futuresBookTicker: this.futuresBookTickerStream.bind(this), + futuresChart: this.futuresChart.bind(this), + deliveryAggTrade: this.deliveryAggTradeStream.bind(this), + deliveryCandlesticks: this.deliveryCandlesticks.bind(this), + deliveryTicker: this.deliveryTickerStream.bind(this), + deliveryMiniTicker: this.deliveryMiniTickerStream.bind(this), + deliveryMarkPrice: this.deliveryMarkPriceStream.bind(this), + deliveryBookTicker: this.deliveryBookTickerStream.bind(this), + deliveryChart: this.deliveryChart.bind(this), + deliveryLiquidation: this.deliveryLiquidationStream.bind(this), + futuresSubcriptions: () => this.getFuturesSubscriptions.bind(this), + deliverySubcriptions: () => this.getDeliverySubscriptions.bind(this), + futuresTerminate: this.futuresTerminate.bind(this), + deliveryTerminate: this.deliveryTerminate.bind(this), + }; + + default_options = { + recvWindow: 5000, + useServerTime: false, + reconnect: true, + keepAlive: true, + verbose: false, + test: false, + hedgeMode: false, + localAddress: false, + family: 4, + log(...args) { + console.log(Array.prototype.slice.call(args)); + } + }; + + Options: any = { + }; + + constructor(userOptions: Partial | string = {}) { + + if (userOptions) { + this.setOptions(userOptions); + } + + } + + options(opt = {}): Binance { + // // return await this.setOptions(opt, callback); // keep this method for backwards compatibility + // this.assignOptions(opt, callback); + this.setOptions(opt); + return this; + } + + assignOptions(opt = {}) { + if (typeof opt === 'string') { // Pass json config filename + this.Options = JSON.parse(file.readFileSync(opt) as any); + } else this.Options = opt; + if (!this.Options.recvWindow) this.Options.recvWindow = this.default_options.recvWindow; + if (!this.Options.useServerTime) this.Options.useServerTime = this.default_options.useServerTime; + if (!this.Options.reconnect) this.Options.reconnect = this.default_options.reconnect; + if (!this.Options.test) this.Options.test = this.default_options.test; + if (!this.Options.hedgeMode) this.Options.hedgeMode = this.default_options.hedgeMode; + if (!this.Options.log) this.Options.log = this.default_options.log; + if (!this.Options.verbose) this.Options.verbose = this.default_options.verbose; + if (!this.Options.keepAlive) this.Options.keepAlive = this.default_options.keepAlive; + if (!this.Options.localAddress) this.Options.localAddress = this.default_options.localAddress; + if (!this.Options.family) this.Options.family = this.default_options.family; + if (this.Options.urls !== undefined) { + const { urls } = this.Options; + if (urls.base) this.base = urls.base; + if (urls.wapi) this.wapi = urls.wapi; + if (urls.sapi) this.sapi = urls.sapi; + if (urls.fapi) this.fapi = urls.fapi; + if (urls.fapiTest) this.fapiTest = urls.fapiTest; + if (urls.stream) this.stream = urls.stream; + if (urls.combineStream) this.combineStream = urls.combineStream; + if (urls.fstream) this.fstream = urls.fstream; + if (urls.fstreamSingle) this.fstreamSingle = urls.fstreamSingle; + if (urls.fstreamTest) this.fstreamTest = urls.fstreamTest; + if (urls.fstreamSingleTest) this.fstreamSingleTest = urls.fstreamSingleTest; + if (urls.dstream) this.dstream = urls.dstream; + if (urls.dstreamSingle) this.dstreamSingle = urls.dstreamSingle; + if (urls.dstreamTest) this.dstreamTest = urls.dstreamTest; + if (urls.dstreamSingleTest) this.dstreamSingleTest = urls.dstreamSingleTest; + } + + if (this.Options.APIKEY) this.APIKEY = this.Options.APIKEY; + if (this.Options.APISECRET) this.APISECRET = this.Options.APISECRET; + if (this.Options.PRIVATEKEY) this.PRIVATEKEY = this.Options.PRIVATEKEY; + if (this.Options.PRIVATEKEYPASSWORD) this.PRIVATEKEYPASSWORD = this.Options.PRIVATEKEYPASSWORD; + if (this.Options.test) this.test = true; + if (this.Options.headers) this.headers = this.Options.Headers; + if (this.Options.domain) this.domain = this.Options.domain; + } + + async setOptions(opt = {}): Promise { + + this.assignOptions(opt); + if (this.Options.useServerTime) { + const res = await this.publicSpotRequest('v3/time'); + this.timeOffset = res.serverTime - new Date().getTime(); + } + return this; + } + + // ---- HELPER FUNCTIONS ---- // + + extend = (...args: any[]) => Object.assign({}, ...args); + + getSpotUrl() { + if (this.Options.test) return this.baseTest; + return this.base; + } + + getFapiUrl() { + if (this.Options.test) return this.fapiTest; + return this.fapi; + } + + getDapiUrl() { + if (this.Options.test) return this.dapiTest; + return this.dapi; + } + + getCombineStreamUrl() { + if (this.Options.test) return this.combineStreamTest; + return this.combineStream; + } + + getStreamUrl() { + if (this.Options.test) return this.streamTest; + return this.stream; + } + + uuid22(a?: any) { + return a ? (a ^ Math.random() * 16 >> a / 4).toString(16) : (([1e7] as any) + 1e3 + 4e3 + 8e5).replace(/[018]/g, this.uuid22); + } + + getUrlProxy() { + if (this.urlProxy) { + return this.urlProxy; + } + return undefined; + } + + getHttpsProxy() { + if (this.httpsProxy) { + return this.httpsProxy; + } + if (process.env.https_proxy) { + return process.env.https_proxy; + } + return undefined; + } + + getSocksProxy() { + if (this.socksProxy) { + return this.socksProxy; + } + if (process.env.socks_proxy) { + return process.env.socks_proxy; + } + return undefined; + } + + // ------ Request Related Functions ------ // + + /** + * Replaces socks connection uri hostname with IP address + * @param {string} connString - socks connection string + * @return {string} modified string with ip address + */ + proxyReplacewithIp(connString: string) { + return connString; + } + + /** + * Returns an array in the form of [host, port] + * @param {string} connString - connection string + * @return {array} array of host and port + */ + parseProxy(connString: string) { + const arr = connString.split('/'); + const host = arr[2].split(':')[0]; + const port = arr[2].split(':')[1]; + return [arr[0], host, port]; + } + + /** + * Checks to see of the object is iterable + * @param {object} obj - The object check + * @return {boolean} true or false is iterable + */ + isIterable(obj) { + if (obj === null) return false; + return typeof obj[Symbol.iterator] === 'function'; + } + + addProxy(opt) { + if (this.Options.proxy) { + const proxyauth = this.Options.proxy.auth ? `${this.Options.proxy.auth.username}:${this.Options.proxy.auth.password}@` : ''; + opt.proxy = `http://${proxyauth}${this.Options.proxy.host}:${this.Options.proxy.port}`; + } + return opt; + } + + async reqHandler(response) { + this.info.lastRequest = new Date().getTime(); + if (response) { + this.info.statusCode = response.status || 0; + if (response.request) this.info.lastURL = response.request.uri.href; + if (response.headers) { + this.info.usedWeight = response.headers['x-mbx-used-weight-1m'] || 0; + this.info.orderCount1s = response.headers['x-mbx-order-count-1s'] || 0; + this.info.orderCount1m = response.headers['x-mbx-order-count-1m'] || 0; + this.info.orderCount1h = response.headers['x-mbx-order-count-1h'] || 0; + this.info.orderCount1d = response.headers['x-mbx-order-count-1d'] || 0; + } + } + + if (response && response.status !== 200) { + // let parsedResponse = ''; + // try { + // parsedResponse = await response.json(); + // } catch (e) { + // parsedResponse = await response.text(); + // } + const error = new Error(await response.text()); + // error.code = response.status; + // error.url = response.url; + throw error; + } + } + + async proxyRequest(opt: any) { + // const req = request(this.addProxy(opt), this.reqHandler(cb)).on('error', (err) => { cb(err, {}) }); + // family: opt.family, + // timeout: opt.timeout, + + const urlBody = new URLSearchParams(opt.form); + const reqOptions: Dict = { + method: opt.method, + headers: opt.headers, + // body: urlBody + // body: (opt.form) + }; + if (opt.method !== 'GET') { + reqOptions.body = urlBody; + } else { + if (opt.qs) { + // opt.url += '?' + this.makeQueryString(opt.qs); + } + } + if (this.Options.verbose) { + this.Options.log('HTTP Request:', opt.method, opt.url, reqOptions); + } + + // https-proxy + const httpsproxy = this.getHttpsProxy(); + const socksproxy = this.getSocksProxy(); + const urlProxy = this.getUrlProxy(); + if (httpsproxy) { + if (this.Options.verbose) this.Options.log('using https proxy: ' + httpsproxy); + reqOptions.agent = new HttpsProxyAgent(httpsproxy); + } else if (socksproxy) { + if (this.Options.verbose) this.Options.log('using socks proxy: ' + socksproxy); + reqOptions.agent = new SocksProxyAgent(socksproxy); + } + + if (urlProxy) { + opt.url = urlProxy + opt.url; + } + + let fetchImplementation = fetch; + // require node-fetch + if (reqOptions.agent) { + fetchImplementation = nodeFetch; + } + + const response = await fetchImplementation(opt.url, reqOptions); + + await this.reqHandler(response); + const json = await response.json(); + + if (this.Options.verbose) { + this.Options.log('HTTP Response:', json); + } + return json; + } + + reqObj(url: string, data: Dict = {}, method: HttpMethod = 'GET', key?: string) { + return { + url: url, + qs: data, + method: method, + family: this.Options.family, + localAddress: this.Options.localAddress, + timeout: this.Options.recvWindow, + forever: this.Options.keepAlive, + headers: { + 'User-Agent': this.userAgent, + 'Content-type': this.contentType, + 'X-MBX-APIKEY': key || '' + } + }; + } + + reqObjPOST(url: string, data: Dict = {}, method = 'POST', key: string) { + return { + url: url, + form: data, + method: method, + family: this.Options.family, + localAddress: this.Options.localAddress, + timeout: this.Options.recvWindow, + forever: this.Options.keepAlive, + qsStringifyOptions: { + arrayFormat: 'repeat' + }, + headers: { + 'User-Agent': this.userAgent, + 'Content-type': this.contentType, + 'X-MBX-APIKEY': key || '' + } + }; + } + + async publicRequest(url: string, data: Dict = {}, method: HttpMethod = 'GET') { + const query = this.makeQueryString(data); + const opt = this.reqObj(url + (query ? '?' + query : ''), data, method); + const res = await this.proxyRequest(opt); + return res; + } + + /** + * Used to make public requests to the futures (FAPI) API + * @param path + * @param data + * @param method + * @returns + */ + async publicFuturesRequest(path: string, data: Dict = {}, method: HttpMethod = 'GET') { + return await this.publicRequest(this.getFapiUrl() + path, data, method); + } + + /** + * Used to make public requests to the delivery (DAPI) API + * @param path + * @param data + * @param method + * @returns + */ + async publicDeliveryRequest(path: string, data: Dict = {}, method: HttpMethod = 'GET') { + return await this.publicRequest(this.getDapiUrl() + path, data, method); + } + + /** + * Used to make private requests to the futures (FAPI) API + * @param path + * @param data + * @param method + * @returns + */ + async privateFuturesRequest(path: string, data: Dict = {}, method: HttpMethod = 'GET'): Promise { + return await this.futuresRequest(this.getFapiUrl() + path, data, method, true); + } + + /** + * Used to make private requests to the delivery (DAPI) API + * @param path + * @param data + * @param method + * @returns + */ + async privateDeliveryRequest(path: string, data: Dict = {}, method: HttpMethod = 'GET'): Promise { + return await this.futuresRequest(this.getDapiUrl() + path, data, method, true); + } + + /** + * Used to make a request to the futures API, this is a generic function that can be used to make any request to the futures API + * @param url + * @param data + * @param method + * @param isPrivate + * @returns + */ + async futuresRequest(url: string, data: Dict = {}, method: HttpMethod = 'GET', isPrivate = false) { + let query = ''; + const headers = { + 'User-Agent': this.userAgent, + 'Content-type': 'application/x-www-form-urlencoded' + } as Dict; + + if (isPrivate) { + if (!data.recvWindow) data.recvWindow = this.Options.recvWindow; + this.requireApiKey('promiseRequest'); + headers['X-MBX-APIKEY'] = this.APIKEY; + } + + const opt = { + headers: this.extend(headers, this.headers), + url: url, + method: method, + timeout: this.Options.recvWindow, + followAllRedirects: true + }; + query = this.makeQueryString(data); + if (method === 'GET') { + opt.url = `${url}?${query}`; + } + if (isPrivate) { + data.timestamp = new Date().getTime(); + if (this.timeOffset) { + data.timestamp += this.timeOffset; + } + query = this.makeQueryString(data); + data.signature = this.generateSignature(query); + opt.url = `${url}?${query}&signature=${data.signature}`; + } + (opt as any).qs = data; + const response = await this.proxyRequest(opt); + return response; + + } + + // ------ Request Related Functions ------ // + + // XXX: This one works with array (e.g. for dust.transfer) + // XXX: I _guess_ we could use replace this function with the `qs` module + makeQueryString(q) { + + const res = Object.keys(q) + .reduce((a, k) => { + if (Array.isArray(q[k])) { + q[k].forEach(v => { + a.push(k + "=" + encodeURIComponent(v)); + }); + } else if (q[k] !== undefined) { + a.push(k + "=" + encodeURIComponent(q[k])); + } + return a; + }, []) + .join("&"); + return res; + } + + /** + * Create a http request to the public API + * @param {string} url - The http endpoint + * @param {object} data - The data to send + * @param {function} callback - The callback method to call + * @param {string} method - the http method + * @return {undefined} + */ + async apiRequest(url: string, data: Dict = {}, method: HttpMethod = 'GET') { + this.requireApiKey('apiRequest'); + const opt = this.reqObj( + url, + data, + method, + this.APIKEY + ); + const res = await this.proxyRequest(opt); + return res; + } + + requireApiKey(source = 'requireApiKey', fatalError = true) { + if (!this.APIKEY) { + if (fatalError) throw Error(`${source}: Invalid API Key!`); + return false; + } + return true; + } + + // Check if API secret is present + requireApiSecret(source = 'requireApiSecret', fatalError = true) { + if (!this.APIKEY) { + if (fatalError) throw Error(`${source}: Invalid API Key!`); + return false; + } + if (!this.APISECRET) { + if (fatalError) throw Error(`${source}: Invalid API Secret!`); + return false; + } + return true; + } + + /** + * Create a public spot/margin request + * @param {string} path - url path + * @param {object} data - The data to send + * @param {string} method - the http method + * @param {boolean} noDataInSignature - Prevents data from being added to signature + * @return {undefined} + */ + async publicSpotRequest(path: string, data: Dict = {}, method: HttpMethod = 'GET') { + return await this.publicRequest/**/(this.getSpotUrl() + path, data, method); + } + + /** + * Create a signed spot/margin request + * @param {string} path - url path + * @param {object} data - The data to send + * @param {string} method - the http method + * @param {boolean} noDataInSignature - Prevents data from being added to signature + * @return {undefined} + */ + async privateSpotRequest(path: string, data: Dict = {}, method: HttpMethod = 'GET', noDataInSignature = false) { + return await this.signedRequest/**/(this.getSpotUrl() + path, data, method, noDataInSignature); + } + + /** + * Create a signed http request + * @param {string} url - The http endpoint + * @param {object} data - The data to send + * @param {function} callback - The callback method to call + * @param {string} method - the http method + * @param {boolean} noDataInSignature - Prevents data from being added to signature + * @return {undefined} + */ + async signedRequest(url: string, data: Dict = {}, method: HttpMethod = 'GET', noDataInSignature = false) { + this.requireApiSecret('signedRequest'); + + data.timestamp = new Date().getTime(); + if (this.timeOffset) data.timestamp += this.timeOffset; + + if (!data.recvWindow) data.recvWindow = this.Options.recvWindow; + const query = method === 'POST' && noDataInSignature ? '' : this.makeQueryString(data); + + const signature = this.generateSignature(query); + + if (method === 'POST') { + const opt = this.reqObjPOST( + url, + data, + method, + this.APIKEY + ); + opt.form.signature = signature; + const reqPost = await this.proxyRequest(opt); + return reqPost; + } else { + const opt = this.reqObj( + url + '?' + query + '&signature=' + signature, + data, + method, + this.APIKEY + ); + const reqGet = await this.proxyRequest(opt); + return reqGet; + } + } + + generateSignature(query: string, encode = true) { + const secret = this.APISECRET || this.PRIVATEKEY; + let signature = ''; + if (secret.includes ('PRIVATE KEY')) { + // if less than the below length, then it can't be RSA key + let keyObject: crypto.KeyObject; + try { + const privateKeyObj: crypto.PrivateKeyInput = { key: secret }; + + if (this.PRIVATEKEYPASSWORD) { + privateKeyObj.passphrase = this.PRIVATEKEYPASSWORD; + } + + keyObject = crypto.createPrivateKey(privateKeyObj); + + } catch (e){ + throw new Error( + 'Invalid private key. Please provide a valid RSA or ED25519 private key. ' + e.toString() + ); + } + + if (secret.length > 120) { + // RSA key + signature = crypto + .sign('RSA-SHA256', Buffer.from(query), keyObject) + .toString('base64'); + if (encode) signature = encodeURIComponent (signature); + return signature; + } else { + // Ed25519 key + signature = crypto.sign(null, Buffer.from(query), keyObject).toString('base64'); + } + } else { + signature = crypto.createHmac('sha256', this.Options.APISECRET).update(query).digest('hex'); // set the HMAC hash header + } + return signature; + } + + // --- ENDPOINTS --- // + + /** + * Create a signed spot order + * @see https://developers.binance.com/docs/binance-spot-api-docs/rest-api/trading-endpoints#new-order-trade + * @see https://developers.binance.com/docs/binance-spot-api-docs/rest-api/public-api-endpoints#test-new-order-trade + * @see https://developers.binance.com/docs/binance-spot-api-docs/rest-api/trading-endpoints#new-order-list---oco-trade + * @param {OrderType} type - LIMIT, MARKET, STOP_LOSS, STOP_LOSS_LIMIT, TAKE_PROFIT, TAKE_PROFIT_LIMIT, LIMIT_MAKER + * @param {OrderSide} side - BUY or SELL + * @param {string} symbol - The symbol to buy or sell + * @param {string} quantity - The quantity to buy or sell + * @param {string} price - The price per unit to transact each unit at + * @param {object} params - additional order settings + * @return {undefined} + */ + async order(type: OrderType, side: OrderSide, symbol: string, quantity: number, price?: number, params: Dict = {}): Promise { + let endpoint = params.type === 'OCO' ? 'v3/orderList/oco' : 'v3/order'; + if (params.test) { + delete params.test; + endpoint += '/test'; + } + const request = { + symbol: symbol, + side: side, + type: type + } as Dict; + if (params.quoteOrderQty && params.quoteOrderQty > 0) + request.quoteOrderQty = params.quoteOrderQty; + else + request.quantity = quantity; + + if (request.type.includes('LIMIT')) { + request.price = price; + if (request.type !== 'LIMIT_MAKER') { + request.timeInForce = 'GTC'; + } + } + if (request.type == 'MARKET' && typeof params.quoteOrderQty !== 'undefined') { + request.quoteOrderQty = params.quoteOrderQty; + delete request.quantity; + } + if (request.type === 'OCO') { + request.price = price; + request.stopLimitPrice = params.stopLimitPrice; + request.stopLimitTimeInForce = 'GTC'; + delete request.type; + // if (typeof params.listClientOrderId !== 'undefined') opt.listClientOrderId = params.listClientOrderId; + // if (typeof params.limitClientOrderId !== 'undefined') opt.limitClientOrderId = params.limitClientOrderId; + // if (typeof params.stopClientOrderId !== 'undefined') opt.stopClientOrderId = params.stopClientOrderId; + } + // if (typeof params.timeInForce !== 'undefined') opt.timeInForce = params.timeInForce; + // if (typeof params.newOrderRespType !== 'undefined') opt.newOrderRespType = params.newOrderRespType; + if (!params.newClientOrderId) { + request.newClientOrderId = this.SPOT_PREFIX + this.uuid22(); + } + + /* + * STOP_LOSS + * STOP_LOSS_LIMIT + * TAKE_PROFIT + * TAKE_PROFIT_LIMIT + * LIMIT_MAKER + */ + // if (typeof params.icebergQty !== 'undefined') request.icebergQty = params.icebergQty; + if (params.stopPrice) { + request.stopPrice = params.stopPrice; + if (request.type === 'LIMIT') throw Error('stopPrice: Must set "type" to one of the following: STOP_LOSS, STOP_LOSS_LIMIT, TAKE_PROFIT, TAKE_PROFIT_LIMIT'); + } + const response = await this.privateSpotRequest(endpoint, this.extend(request, params), 'POST'); + // to do error handling + // if ( !response ) { + // if ( callback ) callback( error, response ); + // else this.options.log( 'Order() error:', error ); + // return; + // } + // if ( typeof response.msg !== 'undefined' && response.msg === 'Filter failure: MIN_NOTIONAL' ) { + // this.options.log( 'Order quantity too small. See exchangeInfo() for minimum amounts' ); + // } + // if ( callback ) callback( error, response ); + // else this.options.log( side + '(' + symbol + ',' + quantity + ',' + price + ') ', response ); + return response; + } + + /** + * Creates a buy order + * @param {string} symbol - the symbol to buy + * @param {numeric} quantity - the quantity required + * @param {numeric} price - the price to pay for each unit + * @param {object} flags - additional buy order flags + * @return {promise or undefined} - omitting the callback returns a promise + */ + async buy(symbol: string, quantity: number, price: number, flags = {}) { + return await this.order('LIMIT', 'BUY', symbol, quantity, price, flags); + } + + /** +* Creates a sell order +* @param {string} symbol - the symbol to sell +* @param {numeric} quantity - the quantity required +* @param {numeric} price - the price to pay for each unit +* @param {object} flags - additional buy order flags +* @param {function} callback - the callback function +* @return {promise or undefined} - omitting the callback returns a promise +*/ + async sell(symbol: string, quantity: number, price: number, flags = {}) { + return await this.order('LIMIT', 'SELL', symbol, quantity, price, flags); + } + + /** +* Creates a market buy order +* @see https://developers.binance.com/docs/binance-spot-api-docs/rest-api/trading-endpoints#new-order-trade +* @see https://developers.binance.com/docs/binance-spot-api-docs/rest-api/public-api-endpoints#test-new-order-trade +* @see https://developers.binance.com/docs/binance-spot-api-docs/rest-api/trading-endpoints#new-order-list---oco-trade +* @param {string} symbol - the symbol to buy +* @param {numeric} quantity - the quantity required +* @param {object} params - additional buy order flags +* @return {promise or undefined} - omitting the callback returns a promise +*/ + async marketBuy(symbol: string, quantity: number, params: Dict = {}) { + return await this.order('MARKET', 'BUY', symbol, quantity, 0, params); + } + + /** +* Creates a spot limit order +* @see https://developers.binance.com/docs/binance-spot-api-docs/rest-api/trading-endpoints#new-order-trade +* @see https://developers.binance.com/docs/binance-spot-api-docs/rest-api/public-api-endpoints#test-new-order-trade +* @see https://developers.binance.com/docs/binance-spot-api-docs/rest-api/trading-endpoints#new-order-list---oco-trade +* @param {string} side - the side of the order (BUY or SELL) +* @param {string} symbol - the symbol to buy +* @param {numeric} quantity - the quantity required +* @param {numeric} price - the price to pay for each unit +* @param {object} params - additional buy order flags +* @return {promise or undefined} - omitting the callback returns a promise +*/ + async limitOrder(side: OrderSide, symbol: string, quantity: number, price: number, params: Dict = {}) { + return await this.order('LIMIT', side, symbol, quantity, price, params); + } + + /** +* Creates a market buy order using the cost instead of the quantity (eg: 100usd instead of 0.01btc) +* @see https://developers.binance.com/docs/binance-spot-api-docs/rest-api/trading-endpoints#new-order-trade +* @see https://developers.binance.com/docs/binance-spot-api-docs/rest-api/public-api-endpoints#test-new-order-trade +* @see https://developers.binance.com/docs/binance-spot-api-docs/rest-api/trading-endpoints#new-order-list---oco-trade +* @param {string} symbol - the symbol to buy +* @param {numeric} quantity - the quantity required +* @param {object} params - additional buy order flags +* @return {promise or undefined} - omitting the callback returns a promise +*/ + async marketBuyWithCost(symbol: string, cost: number, params: Dict = {}) { + params.quoteOrderQty = cost; + return await this.order('MARKET', 'BUY', symbol, 0, 0, params); + } + + /** + * Creates a market sell order + * @see https://developers.binance.com/docs/binance-spot-api-docs/rest-api/trading-endpoints#new-order-trade + * @see https://developers.binance.com/docs/binance-spot-api-docs/rest-api/public-api-endpoints#test-new-order-trade + * @see https://developers.binance.com/docs/binance-spot-api-docs/rest-api/trading-endpoints#new-order-list---oco-trade + * @param {string} symbol - the symbol to sell + * @param {numeric} quantity - the quantity required + * @param {object} flags - additional buy order flags + * @return {promise or undefined} - omitting the callback returns a promise + */ + async marketSell(symbol: string, quantity: number, params: Dict = {}) { + return await this.order('MARKET', 'SELL', symbol, quantity, 0, params); + } + + /** + * Creates a market sell order using the cost instead of the quantity (eg: 100usd instead of 0.01btc) + * @see https://developers.binance.com/docs/binance-spot-api-docs/rest-api/trading-endpoints#new-order-trade + * @see https://developers.binance.com/docs/binance-spot-api-docs/rest-api/public-api-endpoints#test-new-order-trade + * @see https://developers.binance.com/docs/binance-spot-api-docs/rest-api/trading-endpoints#new-order-list---oco-trade + * @param {string} symbol - the symbol to sell + * @param {numeric} quantity - the quantity required + * @param {object} flags - additional buy order flags + * @return {promise or undefined} - omitting the callback returns a promise + */ + async marketSellWithCost(symbol: string, cost: number, params: Dict = {}) { + params.quoteOrderQty = cost; + return await this.order('MARKET', 'SELL', symbol, 0, 0, params); + } + + /** + * Cancels an order + * @see https://developers.binance.com/docs/binance-spot-api-docs/rest-api/trading-endpoints#cancel-order-trade + * @param {string} symbol - the symbol to cancel + * @param {string} orderid - the orderid to cancel + * @return {promise or undefined} - omitting the callback returns a promise + */ + async cancel(symbol: string, orderid: number | string, params: Dict = {}): Promise { + return await this.privateSpotRequest('v3/order', this.extend({ symbol: symbol, orderId: orderid }, params), 'DELETE'); + } + + /** +* Gets the status of an order +* @see https://developers.binance.com/docs/binance-spot-api-docs/rest-api/trading-endpoints#query-order-user_data +* @param {string} symbol - the symbol to check +* @param {string} orderid - the orderid to check if !orderid then use flags to search +* @param {object} flags - any additional flags +* @return {promise or undefined} - omitting the callback returns a promise +*/ + async orderStatus(symbol: string, orderid?: number | string, flags = {}) { + let parameters = Object.assign({ symbol: symbol }, flags); + if (orderid) { + parameters = Object.assign({ orderId: orderid }, parameters); + } + return await this.privateSpotRequest('v3/order', parameters); + } + + /** +* Gets open orders +* @see https://developers.binance.com/docs/binance-spot-api-docs/rest-api/trading-endpoints#current-open-orders-user_data +* @param {string} symbol - the symbol to get +* @return {promise or undefined} - omitting the callback returns a promise +*/ + async openOrders(symbol?: string, params: Dict = {}): Promise { + const parameters = symbol ? { symbol: symbol } : {}; + return await this.privateSpotRequest('v3/openOrders', this.extend(parameters, params)); + } + + /** + * Cancels all orders of a given symbol + * @see https://developers.binance.com/docs/binance-spot-api-docs/rest-api/trading-endpoints#cancel-all-open-orders-on-a-symbol-trade + * @param {string} symbol - the symbol to cancel all orders for + * @return {promise or undefined} - omitting the callback returns a promise + */ + async cancelAllOrders(symbol: string, params: Dict = {}) { + return await this.privateSpotRequest('v3/openOrders', this.extend({ symbol }, params), 'DELETE'); + } + + // /** + // * Cancels all orders of a given symbol + // * @param {string} symbol - the symbol to cancel all orders for + // * @return {promise or undefined} - omitting the callback returns a promise + // */ + // async cancelOrders(symbol: string, params: Dict = {}) { + // const json = await this.privateSpotRequest('v3/openOrders', this.extend({ symbol: symbol }, params), 'DELETE'); + // // if (json.length === 0) { + // // return callback.call(this, 'No orders present for this symbol', {}, symbol); + // // } + // // if (Object.keys(json).length === 0) { + // // return callback.call(this, 'No orders present for this symbol', {}, symbol); + // // } + // // for (let obj of json) { + // // let quantity = obj.origQty - obj.executedQty; + // // this.options.log('cancel order: ' + obj.side + ' ' + symbol + ' ' + quantity + ' @ ' + obj.price + ' #' + obj.orderId); + // // signedRequest(this.getSpotUrl() + 'v3/order', { symbol: symbol, orderId: obj.orderId }, function (error, data) { + // // return callback.call(this, error, data, symbol); + // // }, 'DELETE'); + // // } + // return json; // to do: check this logic of cancelling remaining orders manually + + // } + + /** + * Gets all order of a given symbol + * @see https://developers.binance.com/docs/binance-spot-api-docs/rest-api/trading-endpoints#all-orders-user_data + * @param {string} symbol - the symbol + * @param {object} options - additional options + * @return {promise or undefined} - omitting the callback returns a promise + */ + async allOrders(symbol: string, params: Dict = {}): Promise { + const parameters = this.extend({ symbol }, params); + return await this.privateSpotRequest('v3/allOrders', parameters); + } + + /** + * Create a signed margin order + * @see https://developers.binance.com/docs/margin_trading/trade/Margin-Account-New-Order + * @param {string} side - BUY or SELL + * @param {string} symbol - The symbol to buy or sell + * @param {string} quantity - The quantity to buy or sell + * @param {string} price - The price per unit to transact each unit at + * @param {object} params - additional order settings + * @return {undefined} + */ + async marginOrder(type: OrderType, side: string, symbol: string, quantity: number, price?: number, params: Dict = {}) { + let endpoint = 'v1/margin/order'; + if (this.Options.test || params.test) endpoint += '/test'; + const request = { + symbol: symbol, + side: side, + type: type, + quantity: quantity + } as Dict; + if (typeof params.type !== 'undefined') request.type = params.type; + if ('isIsolated' in params) request.isIsolated = params.isIsolated; + if (request.type.includes('LIMIT')) { + request.price = price; + if (request.type !== 'LIMIT_MAKER') { + request.timeInForce = 'GTC'; + } + } + + if (typeof params.timeInForce !== 'undefined') request.timeInForce = params.timeInForce; + if (typeof params.newOrderRespType !== 'undefined') request.newOrderRespType = params.newOrderRespType; + // if ( typeof flags.newClientOrderId !== 'undefined' ) opt.newClientOrderId = flags.newClientOrderId; + if (typeof params.newClientOrderId !== 'undefined') { + request.newClientOrderId = params.newClientOrderId; + } else { + request.newClientOrderId = this.SPOT_PREFIX + this.uuid22(); + } + if (typeof params.sideEffectType !== 'undefined') request.sideEffectType = params.sideEffectType; + + /* + * STOP_LOSS + * STOP_LOSS_LIMIT + * TAKE_PROFIT + * TAKE_PROFIT_LIMIT + */ + if (typeof params.icebergQty !== 'undefined') request.icebergQty = params.icebergQty; + if (typeof params.stopPrice !== 'undefined') { + request.stopPrice = params.stopPrice; + if (request.type === 'LIMIT') throw Error('stopPrice: Must set "type" to one of the following: STOP_LOSS, STOP_LOSS_LIMIT, TAKE_PROFIT, TAKE_PROFIT_LIMIT'); + } + return await this.privateSpotRequest(endpoint, this.extend(request, params), 'POST'); + } + + // Futures internal functions + /** + * @see https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/New-Order + * @param type + * @param side + * @param symbol symbol if the market + * @param quantity + * @param price + * @param params extra parameters to be sent in the request + * @returns + */ + async futuresOrder(type: OrderType, side: string, symbol: string, quantity: number, price?: number, params: Dict = {}): Promise { + params.symbol = symbol; + params.side = side; + params.type = type; + if (quantity) params.quantity = quantity; + // if in the binance futures setting Hedged mode is active, positionSide parameter is mandatory + if (!params.positionSide && this.Options.hedgeMode) { + params.positionSide = side === 'BUY' ? 'LONG' : 'SHORT'; + } + // LIMIT STOP MARKET STOP_MARKET TAKE_PROFIT TAKE_PROFIT_MARKET + // reduceOnly stopPrice + if (price) { + params.price = price; + } + if (!params.timeInForce && (params.type.includes('LIMIT') || params.type === 'STOP' || params.type === 'TAKE_PROFIT')) { + params.timeInForce = 'GTX'; // Post only by default. Use GTC for limit orders. + } + + if (!params.newClientOrderId) { + params.newClientOrderId = this.CONTRACT_PREFIX + this.uuid22(); + } + return await this.privateFuturesRequest('v1/order', params, 'POST'); + } + + async deliveryOrder(type: OrderType, side: string, symbol: string, quantity: number, price?: number, params: Dict = {}): Promise { + params.symbol = symbol; + params.side = side; + params.quantity = quantity; + params.type = type; + // if in the binance futures setting Hedged mode is active, positionSide parameter is mandatory + if (this.Options.hedgeMode) { + params.positionSide = side === 'BUY' ? 'LONG' : 'SHORT'; + } + // LIMIT STOP MARKET STOP_MARKET TAKE_PROFIT TAKE_PROFIT_MARKET + // reduceOnly stopPrice + if (price) { + params.price = price; + if (!params.type) params.type = 'LIMIT'; + } else { + if (!params.type) params.type = 'MARKET'; + } + if (!params.timeInForce && (params.type.includes('LIMIT') || params.type === 'STOP' || params.type === 'TAKE_PROFIT')) { + params.timeInForce = 'GTX'; // Post only by default. Use GTC for limit orders. + } + + if (!params.newClientOrderId) { + params.newClientOrderId = this.CONTRACT_PREFIX + this.uuid22(); + } + return await this.privateDeliveryRequest('v1/order', params, 'POST'); + } + + // ------ WS RELATED FUNCTIONS ------ // + + noop() { + return; + } + + /** + * Reworked Tuitio's heartbeat code into a shared single interval tick + * @return {undefined} + */ + socketHeartbeat() { + /* Sockets removed from `subscriptions` during a manual terminate() + will no longer be at risk of having functions called on them */ + for (const endpointId in this.subscriptions) { + const ws = this.subscriptions[endpointId]; + if (ws.isAlive) { + ws.isAlive = false; + if (ws.readyState === WebSocket.OPEN) ws.ping(this.noop); + } else { + if (this.Options.verbose) this.Options.log('Terminating inactive/broken WebSocket: ' + ws.endpoint); + if (ws.readyState === WebSocket.OPEN) ws.terminate(); + } + } + } + + /** + * Called when socket is opened, subscriptions are registered for later reference + * @param {function} opened_callback - a callback function + * @return {undefined} + */ + handleSocketOpen(wsBind, opened_callback: Callback) { + wsBind.isAlive = true; + if (Object.keys(this.subscriptions).length === 0) { + this.socketHeartbeatInterval = setInterval(this.socketHeartbeat, this.heartBeatInterval); + } + this.subscriptions[wsBind.url] = wsBind; + if (typeof opened_callback === 'function') opened_callback(wsBind.url); + } + + /** + * Called when socket is closed, subscriptions are de-registered for later reference + * @param {Function} reconnect - reconnect callback + * @param {string} code - code associated with the socket + * @param {string} reason - string with the response + * @return {undefined} + */ + handleSocketClose(wsBind, reconnect: Function, code, reason: string) { + delete this.subscriptions[wsBind.url]; + if (this.subscriptions && Object.keys(this.subscriptions).length === 0) { + clearInterval(this.socketHeartbeatInterval); + } + this.Options.log('WebSocket closed: ' + wsBind.url + + (code ? ' (' + code + ')' : '') + + (reason ? ' ' + reason : '')); + if (this.Options.reconnect && wsBind.reconnect && reconnect) { + if (wsBind.url && wsBind.url.length === 60) this.Options.log('Account data WebSocket reconnecting...'); + else this.Options.log('WebSocket reconnecting: ' + wsBind.url + '...'); + try { + reconnect(); + } catch (error) { + this.Options.log('WebSocket reconnect error: ' + error.message); + } + } + } + + /** + * Called when socket errors + * @param {object} error - error object message + * @return {undefined} + */ + handleSocketError(wsBind, error) { + /* Errors ultimately result in a `close` event. + see: https://github.com/websockets/ws/blob/828194044bf247af852b31c49e2800d557fedeff/lib/websocket.js#L126 */ + this.Options.log('WebSocket error: ' + wsBind.url + + (error.code ? ' (' + error.code + ')' : '') + + (error.message ? ' ' + error.message : '')); + } + + /** + * Called on each socket heartbeat + * @return {undefined} + */ + handleSocketHeartbeat(wsBind) { + wsBind.isAlive = true; + } + + // ----- WS ENDPOINTS ----- // + + /** + * Get Binance server time + * @return {promise or undefined} - omitting the callback returns a promise + */ + async time() { + const res = await this.publicSpotRequest('v3/time', {}); + return res; + } + + /** + * Used to subscribe to a single websocket endpoint + * @param {string} endpoint - endpoint to connect to + * @param {function} callback - the function to call when information is received + * @param {boolean} reconnect - whether to reconnect on disconnect + * @param {object} opened_callback - the function to call when opened + * @return {WebSocket} - websocket reference + */ + subscribe(endpoint: string, callback: Callback, reconnect?: Callback, opened_callback?: Callback) { + const httpsproxy = this.getHttpsProxy(); + let socksproxy = this.getSocksProxy(); + let ws: any = undefined; + + if (socksproxy) { + socksproxy = this.proxyReplacewithIp(socksproxy); + if (this.Options.verbose) this.Options.log('using socks proxy server ' + socksproxy); + const agent = new SocksProxyAgent({ + protocol: this.parseProxy(socksproxy)[0], + host: this.parseProxy(socksproxy)[1], + port: this.parseProxy(socksproxy)[2] + }); + ws = new WebSocket(this.getStreamUrl() + endpoint, { agent: agent }); + } else if (httpsproxy) { + const config = url.parse(httpsproxy); + const agent = new HttpsProxyAgent(config); + if (this.Options.verbose) this.Options.log('using proxy server ' + agent); + ws = new WebSocket(this.getStreamUrl() + endpoint, { agent: agent }); + } else { + ws = new WebSocket(this.getStreamUrl() + endpoint); + } + + if (this.Options.verbose) this.Options.log('Subscribed to ' + endpoint); + ws.reconnect = this.Options.reconnect; + ws.endpoint = endpoint; + ws.isAlive = false; + ws.on('open', this.handleSocketOpen.bind(this, ws, opened_callback)); + ws.on('pong', this.handleSocketHeartbeat.bind(this, ws)); + ws.on('error', this.handleSocketError.bind(this, ws)); + ws.on('close', this.handleSocketClose.bind(this, ws, reconnect)); + ws.on('message', data => { + try { + if (this.Options.verbose) this.Options.log('WebSocket data:', data); + callback(JSONbig.parse(data)); + } catch (error) { + this.Options.log('Parse error: ' + error.message); + } + }); + return ws; + } + + /** + * Used to subscribe to a combined websocket endpoint + * @param {string} streams - streams to connect to + * @param {function} callback - the function to call when information is received + * @param {boolean} reconnect - whether to reconnect on disconnect + * @param {object} opened_callback - the function to call when opened + * @return {WebSocket} - websocket reference + */ + subscribeCombined(streams: any, callback: Callback, reconnect?: Callback, opened_callback?: Callback) { + const httpsproxy = this.getHttpsProxy(); + let socksproxy = this.getSocksProxy(); + const queryParams = streams.join('/'); + let ws: any = undefined; + if (socksproxy) { + socksproxy = this.proxyReplacewithIp(socksproxy); + if (this.Options.verbose) this.Options.log('using socks proxy server ' + socksproxy); + const agent = new SocksProxyAgent({ + protocol: this.parseProxy(socksproxy)[0], + host: this.parseProxy(socksproxy)[1], + port: this.parseProxy(socksproxy)[2] + }); + ws = new WebSocket(this.getCombineStreamUrl() + queryParams, { agent: agent }); + } else if (httpsproxy) { + if (this.Options.verbose) this.Options.log('using proxy server ' + httpsproxy); + const config = url.parse(httpsproxy); + const agent = new HttpsProxyAgent(config); + ws = new WebSocket(this.getCombineStreamUrl() + queryParams, { agent: agent }); + } else { + ws = new WebSocket(this.getCombineStreamUrl() + queryParams); + } + + ws.reconnect = this.Options.reconnect; + ws.endpoint = stringHash(queryParams); + ws.isAlive = false; + if (this.Options.verbose) { + this.Options.log('CombinedStream: Subscribed to [' + ws.endpoint + '] ' + queryParams); + } + ws.on('open', this.handleSocketOpen.bind(this, ws, opened_callback)); + ws.on('pong', this.handleSocketHeartbeat.bind(this, ws)); + ws.on('error', this.handleSocketError.bind(this, ws)); + ws.on('close', this.handleSocketClose.bind(this, ws, reconnect)); + ws.on('message', data => { + try { + if (this.Options.verbose) this.Options.log('CombinedStream: WebSocket data:', data + ); + callback(JSONbig.parse(data).data); + } catch (error) { + this.Options.log('CombinedStream: Parse error: ' + error.message); + } + }); + return ws; + } + + /** + * Used to terminate a web socket + * @param {string} endpoint - endpoint identifier associated with the web socket + * @param {boolean} reconnect - auto reconnect after termination + * @return {undefined} + */ + terminate(endpoint: string, reconnect = false) { + if (this.Options.verbose) this.Options.log('WebSocket terminating:', endpoint); + const ws = this.subscriptions[endpoint]; + if (!ws) return; + ws.removeAllListeners('message'); + ws.reconnect = reconnect; + ws.terminate(); + } + + /** + * Futures heartbeat code with a shared single interval tick + * @return {undefined} + */ + futuresSocketHeartbeat() { + /* Sockets removed from subscriptions during a manual terminate() + will no longer be at risk of having functions called on them */ + for (const endpointId in this.futuresSubscriptions) { + const ws = this.futuresSubscriptions[endpointId]; + if (ws.isAlive) { + ws.isAlive = false; + if (ws.readyState === WebSocket.OPEN) ws.ping(this.noop); + } else { + if (this.Options.verbose) this.Options.log(`Terminating zombie futures WebSocket: ${ws.endpoint}`); + if (ws.readyState === WebSocket.OPEN) ws.terminate(); + } + } + } + + /** + * Called when a futures socket is opened, subscriptions are registered for later reference + * @param {function} openCallback - a callback function + * @return {undefined} + */ + handleFuturesSocketOpen(wsBind: any, openCallback: Callback) { + wsBind.isAlive = true; + if (Object.keys(this.futuresSubscriptions).length === 0) { + this.socketHeartbeatInterval = setInterval(this.futuresSocketHeartbeat, this.heartBeatInterval); + } + this.futuresSubscriptions[wsBind.url] = wsBind; + if (typeof openCallback === 'function') openCallback(wsBind.url); + } + + /** + * Called when futures websocket is closed, subscriptions are de-registered for later reference + * @param {boolean} reconnect - true or false to reconnect the socket + * @param {string} code - code associated with the socket + * @param {string} reason - string with the response + * @return {undefined} + */ + handleFuturesSocketClose(wsBind, reconnect, code, reason) { + delete this.futuresSubscriptions[wsBind.url]; + if (this.futuresSubscriptions && Object.keys(this.futuresSubscriptions).length === 0) { + clearInterval(this.socketHeartbeatInterval); + } + this.Options.log('Futures WebSocket closed: ' + wsBind.url + + (code ? ' (' + code + ')' : '') + + (reason ? ' ' + reason : '')); + if (this.Options.reconnect && wsBind.reconnect && reconnect) { + if (wsBind.url && wsBind.url.length === 60) this.Options.log('Futures account data WebSocket reconnecting...'); + else this.Options.log('Futures WebSocket reconnecting: ' + wsBind.url + '...'); + try { + reconnect(); + } catch (error) { + this.Options.log('Futures WebSocket reconnect error: ' + error.message); + } + } + } + + /** + * Called when a futures websocket errors + * @param {object} error - error object message + * @return {undefined} + */ + handleFuturesSocketError(wsBind, error) { + this.Options.log('Futures WebSocket error: ' + wsBind.url + + (error.code ? ' (' + error.code + ')' : '') + + (error.message ? ' ' + error.message : '')); + } + + /** + * Called on each futures socket heartbeat + * @return {undefined} + */ + handleFuturesSocketHeartbeat(wsBind) { + wsBind.isAlive = true; + } + + /** + * Used to subscribe to a single futures websocket endpoint + * @param {string} endpoint - endpoint to connect to + * @param {function} callback - the function to call when information is received + * @param {object} params - Optional reconnect {boolean} (whether to reconnect on disconnect), openCallback {function}, id {string} + * @return {WebSocket} - websocket reference + */ + futuresSubscribeSingle(endpoint: string, callback: Callback, params: Dict = {}) { + if (typeof params === 'boolean') params = { reconnect: params }; + if (!params.reconnect) params.reconnect = false; + if (!params.openCallback) params.openCallback = false; + if (!params.id) params.id = false; + const httpsproxy = this.getHttpsProxy(); + let socksproxy = this.getSocksProxy(); + let ws: any = undefined; + + if (socksproxy) { + socksproxy = this.proxyReplacewithIp(socksproxy); + if (this.Options.verbose) this.Options.log(`futuresSubscribeSingle: using socks proxy server: ${socksproxy}`); + const agent = new SocksProxyAgent({ + protocol: this.parseProxy(socksproxy)[0], + host: this.parseProxy(socksproxy)[1], + port: this.parseProxy(socksproxy)[2] + }); + ws = new WebSocket((this.Options.test ? this.fstreamSingleTest : this.fstreamSingle) + endpoint, { agent }); + } else if (httpsproxy) { + const config = url.parse(httpsproxy); + const agent = new HttpsProxyAgent(config); + if (this.Options.verbose) this.Options.log(`futuresSubscribeSingle: using proxy server: ${agent}`); + ws = new WebSocket((this.Options.test ? this.fstreamSingleTest : this.fstreamSingle) + endpoint, { agent }); + } else { + ws = new WebSocket((this.Options.test ? this.fstreamSingleTest : this.fstreamSingle) + endpoint); + } + + if (this.Options.verbose) this.Options.log('futuresSubscribeSingle: Subscribed to ' + endpoint); + callback = callback.bind(this); + ws.reconnect = this.Options.reconnect; + ws.endpoint = endpoint; + ws.isAlive = false; + ws.on('open', this.handleFuturesSocketOpen.bind(this, ws, params.openCallback)); + ws.on('pong', this.handleFuturesSocketHeartbeat.bind(this, ws)); + ws.on('error', this.handleFuturesSocketError.bind(this, ws)); + ws.on('close', this.handleFuturesSocketClose.bind(this, ws, params.reconnect)); + ws.on('message', data => { + try { + if (this.Options.verbose) this.Options.log('futuresSubscribeSingle: Received data:', data); + callback(JSONbig.parse(data)); + } catch (error) { + this.Options.log('Parse error: ' + error.message); + } + }); + return ws; + } + + /** + * Used to subscribe to a combined futures websocket endpoint + * @param {string} streams - streams to connect to + * @param {function} callback - the function to call when information is received + * @param {object} params - Optional reconnect {boolean} (whether to reconnect on disconnect), openCallback {function}, id {string} + * @return {WebSocket} - websocket reference + */ + futuresSubscribe(streams, callback: Callback, params: Dict = {}) { + if (typeof streams === 'string') return this.futuresSubscribeSingle(streams, callback, params); + if (typeof params === 'boolean') params = { reconnect: params }; + if (!params.reconnect) params.reconnect = false; + if (!params.openCallback) params.openCallback = false; + if (!params.id) params.id = false; + const httpsproxy = this.getHttpsProxy(); + let socksproxy = this.getSocksProxy(); + const queryParams = streams.join('/'); + let ws: any = undefined; + if (socksproxy) { + socksproxy = this.proxyReplacewithIp(socksproxy); + if (this.Options.verbose) this.Options.log(`futuresSubscribe: using socks proxy server ${socksproxy}`); + const agent = new SocksProxyAgent({ + protocol: this.parseProxy(socksproxy)[0], + host: this.parseProxy(socksproxy)[1], + port: this.parseProxy(socksproxy)[2] + }); + ws = new WebSocket((this.Options.test ? this.fstreamTest : this.fstream) + queryParams, { agent }); + } else if (httpsproxy) { + if (this.Options.verbose) this.Options.log(`futuresSubscribe: using proxy server ${httpsproxy}`); + const config = url.parse(httpsproxy); + const agent = new HttpsProxyAgent(config); + ws = new WebSocket((this.Options.test ? this.fstreamTest : this.fstream) + queryParams, { agent }); + } else { + ws = new WebSocket((this.Options.test ? this.fstreamTest : this.fstream) + queryParams); + } + + ws.reconnect = this.Options.reconnect; + ws.endpoint = stringHash(queryParams); + ws.isAlive = false; + if (this.Options.verbose) { + this.Options.log(`futuresSubscribe: Subscribed to [${ws.endpoint}] ${queryParams}`); + } + ws.on('open', this.handleFuturesSocketOpen.bind(this, ws, params.openCallback)); + ws.on('pong', this.handleFuturesSocketHeartbeat.bind(this, ws)); + ws.on('error', this.handleFuturesSocketError.bind(this, ws)); + ws.on('close', this.handleFuturesSocketClose.bind(this, ws, params.reconnect)); + ws.on('message', data => { + try { + if (this.Options.verbose) this.Options.log('futuresSubscribe: Received data:', data); + callback(JSONbig.parse(data).data); + } catch (error) { + this.Options.log(`futuresSubscribe: Parse error: ${error.message}`); + } + }); + return ws; + } + + /** + * Used to terminate a futures websocket + * @param {string} endpoint - endpoint identifier associated with the web socket + * @param {boolean} reconnect - auto reconnect after termination + * @return {undefined} + */ + futuresTerminate(endpoint: string, reconnect = false) { + if (this.Options.verbose) this.Options.log('Futures WebSocket terminating:', endpoint); + const ws = this.futuresSubscriptions[endpoint]; + if (!ws) return; + ws.removeAllListeners('message'); + ws.reconnect = reconnect; + ws.terminate(); + } + + /** + * Combines all futures OHLC data with the latest update + * @param {string} symbol - the symbol + * @param {string} interval - time interval + * @return {array} - interval data for given symbol + */ + futuresKlineConcat(symbol: string, interval: Interval) { + const output = this.futuresTicks[symbol][interval]; + if (!this.futuresRealtime[symbol][interval].time) return output; + const time = this.futuresRealtime[symbol][interval].time; + const last_updated = Object.keys(this.futuresTicks[symbol][interval]).pop(); + if (time >= last_updated) { + output[time] = this.futuresRealtime[symbol][interval]; + //delete output[time].time; + output[last_updated].isFinal = true; + output[time].isFinal = false; + } + return output; + } + + /** + * Used for websocket futures @kline + * @param {string} symbol - the symbol + * @param {object} kline - object with kline info + * @param {string} firstTime - time filter + * @return {undefined} + */ + futuresKlineHandler(symbol: string, kline: any, firstTime = 0) { + // eslint-disable-next-line no-unused-vars + const { e: eventType, E: eventTime, k: ticks } = kline; + // eslint-disable-next-line no-unused-vars + const { o: open, h: high, l: low, c: close, v: volume, i: interval, x: isFinal, q: quoteVolume, V: takerBuyBaseVolume, Q: takerBuyQuoteVolume, n: trades, t: time, T: closeTime } = ticks; + if (time <= firstTime) return; + if (!isFinal) { + // if ( typeof this.futuresRealtime[symbol][interval].time !== 'undefined' ) { + // if ( this.futuresRealtime[symbol][interval].time > time ) return; + // } + this.futuresRealtime[symbol][interval] = { time, closeTime, open, high, low, close, volume, quoteVolume, takerBuyBaseVolume, takerBuyQuoteVolume, trades, isFinal }; + return; + } + const first_updated = Object.keys(this.futuresTicks[symbol][interval]).shift(); + if (first_updated) delete this.futuresTicks[symbol][interval][first_updated]; + this.futuresTicks[symbol][interval][time] = { time, closeTime, open, high, low, close, volume, quoteVolume, takerBuyBaseVolume, takerBuyQuoteVolume, trades, isFinal: false }; + } + + /** + * Converts the futures liquidation stream data into a friendly object + * @param {object} data - liquidation data callback data type + * @return {object} - user friendly data type + */ + fLiquidationConvertData(data: any) { + const eventType = data.e, eventTime = data.E; + const { + s: symbol, + S: side, + o: orderType, + f: timeInForce, + q: origAmount, + p: price, + ap: avgPrice, + X: orderStatus, + l: lastFilledQty, + z: totalFilledQty, + T: tradeTime + } = data.o; + return { symbol, side, orderType, timeInForce, origAmount, price, avgPrice, orderStatus, lastFilledQty, totalFilledQty, eventType, tradeTime, eventTime }; + } + + /** + * Converts the futures ticker stream data into a friendly object + * @param {object} data - user data callback data type + * @return {object} - user friendly data type + */ + fTickerConvertData(data: any) { + const friendlyData = (data: any) => { + const { + e: eventType, + E: eventTime, + s: symbol, + p: priceChange, + P: percentChange, + w: averagePrice, + c: close, + Q: closeQty, + o: open, + h: high, + l: low, + v: volume, + q: quoteVolume, + O: openTime, + C: closeTime, + F: firstTradeId, + L: lastTradeId, + n: numTrades + } = data; + return { + eventType, + eventTime, + symbol, + priceChange, + percentChange, + averagePrice, + close, + closeQty, + open, + high, + low, + volume, + quoteVolume, + openTime, + closeTime, + firstTradeId, + lastTradeId, + numTrades + }; + }; + if (Array.isArray(data)) { + const result = []; + for (const obj of data) { + result.push(friendlyData(obj)); + } + return result; + } + return friendlyData(data); + } + + /** + * Converts the futures miniTicker stream data into a friendly object + * @param {object} data - user data callback data type + * @return {object} - user friendly data type + */ + fMiniTickerConvertData(data: any) { + const friendlyData = (data: any) => { + const { + e: eventType, + E: eventTime, + s: symbol, + c: close, + o: open, + h: high, + l: low, + v: volume, + q: quoteVolume + } = data; + return { + eventType, + eventTime, + symbol, + close, + open, + high, + low, + volume, + quoteVolume + }; + }; + if (Array.isArray(data)) { + const result = []; + for (const obj of data) { + result.push(friendlyData(obj)); + } + return result; + } + return friendlyData(data); + } + + /** + * Converts the futures bookTicker stream data into a friendly object + * @param {object} data - user data callback data type + * @return {object} - user friendly data type + */ + fBookTickerConvertData(data: any) { + const { + u: updateId, + s: symbol, + b: bestBid, + B: bestBidQty, + a: bestAsk, + A: bestAskQty + } = data; + return { + updateId, + symbol, + bestBid, + bestBidQty, + bestAsk, + bestAskQty + }; + } + + /** + * Converts the futures UserData stream MARGIN_CALL data into a friendly object + * @param {object} data - user data callback data type + * @return {object} - user friendly data type + */ + fUserDataMarginConvertData(data: any) { + const { + e: eventType, + E: eventTime, + cw: crossWalletBalance, // only pushed with crossed position margin call + // p: positions + } = data; + let { positions } = data; + const positionConverter = position => { + const { + s: symbol, + ps: positionSide, + pa: positionAmount, + mt: marginType, + iw: isolatedWallet, // if isolated position + mp: markPrice, + up: unrealizedPnL, + mm: maintenanceMargin // maintenance margin required + } = position; + return { + symbol, + positionSide, + positionAmount, + marginType, + isolatedWallet, + markPrice, + unrealizedPnL, + maintenanceMargin + }; + }; + const convertedPositions = []; + for (const position of positions) { + convertedPositions.push(positionConverter(position)); + } + positions = convertedPositions; + return { + eventType, + eventTime, + crossWalletBalance, + positions + }; + } + + /** + * Converts the futures UserData stream ACCOUNT_CONFIG_UPDATE into a friendly object + * @param {object} data - user config callback data type + * @return {object} - user friendly data type + */ + fUserConfigDataAccountUpdateConvertData(data: any) { + return { + eventType: data.e, + eventTime: data.E, + transactionTime: data.T, + ac: { + symbol: data.ac.s, + leverage: data.ac.l + } + }; + } + + /** + * Converts the futures UserData stream ACCOUNT_UPDATE data into a friendly object + * @param {object} data - user data callback data type + * @return {object} - user friendly data type + */ + fUserDataAccountUpdateConvertData(data: any) { + const { + e: eventType, + E: eventTime, + T: transaction, + } = data; + let { a: updateData } = data; + const updateConverter = updateData => { + const { + m: eventReasonType + } = data; + let { + // m: eventReasonType, + B: balances, + P: positions + } = updateData; + const positionConverter = position => { + const { + s: symbol, + pa: positionAmount, + ep: entryPrice, + cr: accumulatedRealized, // (Pre-fee) Accumulated Realized + up: unrealizedPnL, + mt: marginType, + iw: isolatedWallet, // if isolated position + ps: positionSide + } = position; + return { + symbol, + positionAmount, + entryPrice, + accumulatedRealized, + unrealizedPnL, + marginType, + isolatedWallet, + positionSide + }; + }; + const balanceConverter = (balance: any) => { + const { + a: asset, + wb: walletBalance, + cw: crossWalletBalance, + bc: balanceChange + } = balance; + return { + asset, + walletBalance, + crossWalletBalance, + balanceChange + }; + }; + + const balanceResult = []; + const positionResult = []; + + for (const balance of balances) { + balanceResult.push(balanceConverter(balance)); + } + for (const position of positions) { + positionResult.push(positionConverter(position)); + } + + balances = balanceResult; + positions = positionResult; + return { + eventReasonType, + balances, + positions + }; + }; + updateData = updateConverter(updateData); + return { + eventType, + eventTime, + transaction, + updateData + }; + } + + /** + * Converts the futures UserData stream ORDER_TRADE_UPDATE data into a friendly object + * @param {object} data - user data callback data type + * @return {object} - user friendly data type + */ + fUserDataOrderUpdateConvertData(data: any) { + const { + e: eventType, + E: eventTime, + T: transaction, // transaction time + } = data; + + let { o: order } = data; + + const orderConverter = (order: any) => { + const { + s: symbol, + c: clientOrderId, + // special client order id: + // starts with "autoclose-": liquidation order + // "adl_autoclose": ADL auto close order + S: side, + o: orderType, + f: timeInForce, + q: originalQuantity, + p: originalPrice, + ap: averagePrice, + sp: stopPrice, // please ignore with TRAILING_STOP_MARKET order, + x: executionType, + X: orderStatus, + i: orderId, + l: orderLastFilledQuantity, + z: orderFilledAccumulatedQuantity, + L: lastFilledPrice, + N: commissionAsset, // will not push if no commission + n: commission, // will not push if no commission + T: orderTradeTime, + t: tradeId, + b: bidsNotional, + a: askNotional, + m: isMakerSide, // is this trade maker side + R: isReduceOnly, // is this reduce only + wt: stopPriceWorkingType, + ot: originalOrderType, + ps: positionSide, + cp: closeAll, // if close-all, pushed with conditional order + AP: activationPrice, // only pushed with TRAILING_STOP_MARKET order + cr: callbackRate, // only pushed with TRAILING_STOP_MARKET order + rp: realizedProfit + } = order; + return { + symbol, + clientOrderId, + side, + orderType, + timeInForce, + originalQuantity, + originalPrice, + averagePrice, + stopPrice, + executionType, + orderStatus, + orderId, + orderLastFilledQuantity, + orderFilledAccumulatedQuantity, + lastFilledPrice, + commissionAsset, + commission, + orderTradeTime, + tradeId, + bidsNotional, + askNotional, + isMakerSide, + isReduceOnly, + stopPriceWorkingType, + originalOrderType, + positionSide, + closeAll, + activationPrice, + callbackRate, + realizedProfit + }; + }; + order = orderConverter(order); + return { + eventType, + eventTime, + transaction, + order + }; + } + + /** + * Converts the futures markPrice stream data into a friendly object + * @param {object} data - user data callback data type + * @return {object} - user friendly data type + */ + fMarkPriceConvertData(data: any) { + const friendlyData = (data: any) => { + const { + e: eventType, + E: eventTime, + s: symbol, + p: markPrice, + i: indexPrice, + r: fundingRate, + T: fundingTime + } = data; + return { + eventType, + eventTime, + symbol, + markPrice, + indexPrice, + fundingRate, + fundingTime + }; + }; + if (Array.isArray(data)) { + const result = []; + for (const obj of data) { + result.push(friendlyData(obj)); + } + return result; + } + return friendlyData(data); + } + + /** + * Converts the futures aggTrade stream data into a friendly object + * @param {object} data - user data callback data type + * @return {object} - user friendly data type + */ + fAggTradeConvertData(data: any) { + const friendlyData = (data: any) => { + const { + e: eventType, + E: eventTime, + s: symbol, + a: aggTradeId, + p: price, + q: amount, + f: firstTradeId, + l: lastTradeId, + T: timestamp, + m: maker + } = data; + return { + eventType, + eventTime, + symbol, + aggTradeId, + price, + amount, + total: price * amount, + firstTradeId, + lastTradeId, + timestamp, + maker + }; + }; + if (Array.isArray(data)) { + const result = []; + for (const obj of data) { + result.push(friendlyData(obj)); + } + return result; + } + return friendlyData(data); + } + + /** + * Delivery heartbeat code with a shared single interval tick + * @return {undefined} + */ + deliverySocketHeartbeat() { + /* Sockets removed from subscriptions during a manual terminate() + will no longer be at risk of having functions called on them */ + for (const endpointId in this.deliverySubscriptions) { + const ws = this.deliverySubscriptions[endpointId]; + if (ws.isAlive) { + ws.isAlive = false; + if (ws.readyState === WebSocket.OPEN) ws.ping(this.noop); + } else { + if (this.Options.verbose) this.Options.log(`Terminating zombie delivery WebSocket: ${ws.endpoint}`); + if (ws.readyState === WebSocket.OPEN) ws.terminate(); + } + } + } + + /** + * Called when a delivery socket is opened, subscriptions are registered for later reference + * @param {function} openCallback - a callback function + * @return {undefined} + */ + handleDeliverySocketOpen(wsBind, openCallback: Callback) { + this.isAlive = true; + if (Object.keys(this.deliverySubscriptions).length === 0) { + this.socketHeartbeatInterval = setInterval(this.deliverySocketHeartbeat, 30000); + } + this.deliverySubscriptions[wsBind.url] = this; + if (typeof openCallback === 'function') openCallback(wsBind.url); + } + + /** + * Called when delivery websocket is closed, subscriptions are de-registered for later reference + * @param {boolean} reconnect - true or false to reconnect the socket + * @param {string} code - code associated with the socket + * @param {string} reason - string with the response + * @return {undefined} + */ + handleDeliverySocketClose(wsBind, reconnect, code, reason) { + delete this.deliverySubscriptions[wsBind.url]; + if (this.deliverySubscriptions && Object.keys(this.deliverySubscriptions).length === 0) { + clearInterval(this.socketHeartbeatInterval); + } + this.Options.log('Delivery WebSocket closed: ' + wsBind.url + + (code ? ' (' + code + ')' : '') + + (reason ? ' ' + reason : '')); + if (this.Options.reconnect && wsBind.reconnect && reconnect) { + if (wsBind.url && wsBind.url.length === 60) this.Options.log('Delivery account data WebSocket reconnecting...'); + else this.Options.log('Delivery WebSocket reconnecting: ' + wsBind.url + '...'); + try { + reconnect(); + } catch (error) { + this.Options.log('Delivery WebSocket reconnect error: ' + error.message); + } + } + } + + /** + * Called when a delivery websocket errors + * @param {object} error - error object message + * @return {undefined} + */ + handleDeliverySocketError(wsBind, error) { + this.Options.log('Delivery WebSocket error: ' + wsBind.url + + (error.code ? ' (' + error.code + ')' : '') + + (error.message ? ' ' + error.message : '')); + } + + /** + * Called on each delivery socket heartbeat + * @return {undefined} + */ + handleDeliverySocketHeartbeat() { + this.isAlive = true; + } + + /** + * Used to subscribe to a single delivery websocket endpoint + * @param {string} endpoint - endpoint to connect to + * @param {function} callback - the function to call when information is received + * @param {object} params - Optional reconnect {boolean} (whether to reconnect on disconnect), openCallback {function}, id {string} + * @return {WebSocket} - websocket reference + */ + deliverySubscribeSingle(endpoint: string, callback: Callback, params: Dict = {}) { + if (typeof params === 'boolean') params = { reconnect: params }; + if (!params.reconnect) params.reconnect = false; + if (!params.openCallback) params.openCallback = false; + if (!params.id) params.id = false; + const httpsproxy = this.getHttpsProxy(); + let socksproxy = this.getSocksProxy(); + let ws: any = undefined; + if (socksproxy) { + socksproxy = this.proxyReplacewithIp(socksproxy); + if (this.Options.verbose) this.Options.log(`deliverySubscribeSingle: using socks proxy server: ${socksproxy}`); + const agent = new SocksProxyAgent({ + protocol: this.parseProxy(socksproxy)[0], + host: this.parseProxy(socksproxy)[1], + port: this.parseProxy(socksproxy)[2] + }); + ws = new WebSocket((this.Options.test ? this.dstreamSingleTest : this.dstreamSingle) + endpoint, { agent }); + } else if (httpsproxy) { + const config = url.parse(httpsproxy); + const agent = new HttpsProxyAgent(config); + if (this.Options.verbose) this.Options.log(`deliverySubscribeSingle: using proxy server: ${agent}`); + ws = new WebSocket((this.Options.test ? this.dstreamSingleTest : this.dstreamSingle) + endpoint, { agent }); + } else { + ws = new WebSocket((this.Options.test ? this.dstreamSingleTest : this.dstreamSingle) + endpoint); + } + + if (this.Options.verbose) this.Options.log('deliverySubscribeSingle: Subscribed to ' + endpoint); + ws.reconnect = this.Options.reconnect; + ws.endpoint = endpoint; + ws.isAlive = false; + ws.on('open', this.handleDeliverySocketOpen.bind(this, ws, params.openCallback)); + ws.on('pong', this.handleDeliverySocketHeartbeat.bind(this, ws)); + ws.on('error', this.handleDeliverySocketError.bind(this, ws)); + ws.on('close', this.handleDeliverySocketClose.bind(this, ws, params.reconnect)); + ws.on('message', data => { + try { + if (this.Options.verbose) this.Options.log('deliverySubscribeSingle: Received data:', data); + callback(JSONbig.parse(data)); + } catch (error) { + this.Options.log('Parse error: ' + error.message); + } + }); + return ws; + } + + /** + * Used to subscribe to a combined delivery websocket endpoint + * @param {string} streams - streams to connect to + * @param {function} callback - the function to call when information is received + * @param {object} params - Optional reconnect {boolean} (whether to reconnect on disconnect), openCallback {function}, id {string} + * @return {WebSocket} - websocket reference + */ + deliverySubscribe(streams, callback: Callback, params: Dict = {}) { + if (typeof streams === 'string') return this.deliverySubscribeSingle(streams, callback, params); + if (typeof params === 'boolean') params = { reconnect: params }; + if (!params.reconnect) params.reconnect = false; + if (!params.openCallback) params.openCallback = false; + if (!params.id) params.id = false; + const httpsproxy = this.getHttpsProxy(); + let socksproxy = this.getSocksProxy(); + const queryParams = streams.join('/'); + let ws: any = undefined; + if (socksproxy) { + socksproxy = this.proxyReplacewithIp(socksproxy); + if (this.Options.verbose) this.Options.log(`deliverySubscribe: using socks proxy server ${socksproxy}`); + const agent = new SocksProxyAgent({ + protocol: this.parseProxy(socksproxy)[0], + host: this.parseProxy(socksproxy)[1], + port: this.parseProxy(socksproxy)[2] + }); + ws = new WebSocket((this.Options.test ? this.dstreamTest : this.dstream) + queryParams, { agent }); + } else if (httpsproxy) { + if (this.Options.verbose) this.Options.log(`deliverySubscribe: using proxy server ${httpsproxy}`); + const config = url.parse(httpsproxy); + const agent = new HttpsProxyAgent(config); + ws = new WebSocket((this.Options.test ? this.dstreamTest : this.dstream) + queryParams, { agent }); + } else { + ws = new WebSocket((this.Options.test ? this.dstreamTest : this.dstream) + queryParams); + } + + ws.reconnect = this.Options.reconnect; + ws.endpoint = stringHash(queryParams); + ws.isAlive = false; + if (this.Options.verbose) { + this.Options.log(`deliverySubscribe: Subscribed to [${ws.endpoint}] ${queryParams}`); + } + ws.on('open', this.handleDeliverySocketOpen.bind(this, ws,params.openCallback)); + ws.on('pong', this.handleDeliverySocketHeartbeat.bind(this, ws)); + ws.on('error', this.handleDeliverySocketError.bind(this, ws)); + ws.on('close', this.handleDeliverySocketClose.bind(this, ws, params.reconnect)); + ws.on('message', data => { + try { + if (this.Options.verbose) this.Options.log('deliverySubscribe: Received data:', data); + callback(JSONbig.parse(data).data); + } catch (error) { + this.Options.log(`deliverySubscribe: Parse error: ${error.message}`); + } + }); + return ws; + } + + /** + * Used to terminate a delivery websocket + * @param {string} endpoint - endpoint identifier associated with the web socket + * @param {boolean} reconnect - auto reconnect after termination + * @return {undefined} + */ + deliveryTerminate(endpoint: string, reconnect = false) { + if (this.Options.verbose) this.Options.log('Delivery WebSocket terminating:', endpoint); + const ws = this.deliverySubscriptions[endpoint]; + if (!ws) return; + ws.removeAllListeners('message'); + ws.reconnect = reconnect; + ws.terminate(); + } + + /** + * Combines all delivery OHLC data with the latest update + * @param {string} symbol - the symbol + * @param {string} interval - time interval + * @return {array} - interval data for given symbol + */ + deliveryKlineConcat(symbol: string, interval: Interval) { + const output = this.deliveryTicks[symbol][interval]; + if (typeof this.deliveryRealtime[symbol][interval].time === 'undefined') return output; + const time = this.deliveryRealtime[symbol][interval].time; + const last_updated = Object.keys(this.deliveryTicks[symbol][interval]).pop(); + if (time >= last_updated) { + output[time] = this.deliveryRealtime[symbol][interval]; + //delete output[time].time; + output[last_updated].isFinal = true; + output[time].isFinal = false; + } + return output; + } + + /** + * Used for websocket delivery @kline + * @param {string} symbol - the symbol + * @param {object} kline - object with kline info + * @param {string} firstTime - time filter + * @return {undefined} + */ + deliveryKlineHandler(symbol: string, kline: any, firstTime = 0) { + // eslint-disable-next-line no-unused-vars + const { e: eventType, E: eventTime, k: ticks } = kline; + // eslint-disable-next-line no-unused-vars + const { o: open, h: high, l: low, c: close, v: volume, i: interval, x: isFinal, q: quoteVolume, V: takerBuyBaseVolume, Q: takerBuyQuoteVolume, n: trades, t: time, T: closeTime } = ticks; + if (time <= firstTime) return; + if (!isFinal) { + // if ( typeof this.futuresRealtime[symbol][interval].time !== 'undefined' ) { + // if ( this.futuresRealtime[symbol][interval].time > time ) return; + // } + this.deliveryRealtime[symbol][interval] = { time, closeTime, open, high, low, close, volume, quoteVolume, takerBuyBaseVolume, takerBuyQuoteVolume, trades, isFinal }; + return; + } + const first_updated = Object.keys(this.deliveryTicks[symbol][interval]).shift(); + if (first_updated) delete this.deliveryTicks[symbol][interval][first_updated]; + this.deliveryTicks[symbol][interval][time] = { time, closeTime, open, high, low, close, volume, quoteVolume, takerBuyBaseVolume, takerBuyQuoteVolume, trades, isFinal: false }; + } + + /** + * Converts the delivery liquidation stream data into a friendly object + * @param {object} data - liquidation data callback data type + * @return {object} - user friendly data type + */ + dLiquidationConvertData(data: any) { + const eventType = data.e, eventTime = data.E; + const { + s: symbol, + S: side, + o: orderType, + f: timeInForce, + q: origAmount, + p: price, + ap: avgPrice, + X: orderStatus, + l: lastFilledQty, + z: totalFilledQty, + T: tradeTime + } = data.o; + return { symbol, side, orderType, timeInForce, origAmount, price, avgPrice, orderStatus, lastFilledQty, totalFilledQty, eventType, tradeTime, eventTime }; + } + + /** + * Converts the delivery ticker stream data into a friendly object + * @param {object} data - user data callback data type + * @return {object} - user friendly data type + */ + dTickerConvertData(data: any) { + const friendlyData = (data: any) => { + const { + e: eventType, + E: eventTime, + s: symbol, + p: priceChange, + P: percentChange, + w: averagePrice, + c: close, + Q: closeQty, + o: open, + h: high, + l: low, + v: volume, + q: quoteVolume, + O: openTime, + C: closeTime, + F: firstTradeId, + L: lastTradeId, + n: numTrades + } = data; + return { + eventType, + eventTime, + symbol, + priceChange, + percentChange, + averagePrice, + close, + closeQty, + open, + high, + low, + volume, + quoteVolume, + openTime, + closeTime, + firstTradeId, + lastTradeId, + numTrades + }; + }; + if (Array.isArray(data)) { + const result = []; + for (const obj of data) { + result.push(friendlyData(obj)); + } + return result; + } + return friendlyData(data); + } + + /** + * Converts the delivery miniTicker stream data into a friendly object + * @param {object} data - user data callback data type + * @return {object} - user friendly data type + */ + dMiniTickerConvertData(data: any) { + const friendlyData = (data: any) => { + const { + e: eventType, + E: eventTime, + s: symbol, + c: close, + o: open, + h: high, + l: low, + v: volume, + q: quoteVolume + } = data; + return { + eventType, + eventTime, + symbol, + close, + open, + high, + low, + volume, + quoteVolume + }; + }; + if (Array.isArray(data)) { + const result = []; + for (const obj of data) { + result.push(friendlyData(obj)); + } + return result; + } + return friendlyData(data); + } + + /** + * Converts the delivery bookTicker stream data into a friendly object + * @param {object} data - user data callback data type + * @return {object} - user friendly data type + */ + dBookTickerConvertData(data: any) { + const { + u: updateId, + s: symbol, + b: bestBid, + B: bestBidQty, + a: bestAsk, + A: bestAskQty + } = data; + return { + updateId, + symbol, + bestBid, + bestBidQty, + bestAsk, + bestAskQty + }; + } + + /** + * Converts the delivery markPrice stream data into a friendly object + * @param {object} data - user data callback data type + * @return {object} - user friendly data type + */ + dMarkPriceConvertData(data: any) { + const friendlyData = (data: any) => { + const { + e: eventType, + E: eventTime, + s: symbol, + p: markPrice, + r: fundingRate, + T: fundingTime + } = data; + return { + eventType, + eventTime, + symbol, + markPrice, + fundingRate, + fundingTime + }; + }; + if (Array.isArray(data)) { + const result = []; + for (const obj of data) { + result.push(friendlyData(obj)); + } + return result; + } + return friendlyData(data); + } + + /** + * Converts the delivery aggTrade stream data into a friendly object + * @param {object} data - user data callback data type + * @return {object} - user friendly data type + */ + dAggTradeConvertData(data: any) { + const friendlyData = (data: any) => { + const { + e: eventType, + E: eventTime, + s: symbol, + a: aggTradeId, + p: price, + q: amount, + f: firstTradeId, + l: lastTradeId, + T: timestamp, + m: maker + } = data; + return { + eventType, + eventTime, + symbol, + aggTradeId, + price, + amount, + total: price * amount, + firstTradeId, + lastTradeId, + timestamp, + maker + }; + }; + if (Array.isArray(data)) { + const result = []; + for (const obj of data) { + result.push(friendlyData(obj)); + } + return result; + } + return friendlyData(data); + } + + /** + * Converts the delivery UserData stream ORDER_TRADE_UPDATE data into a friendly object + * @param {object} data - user data callback data type + * @return {object} - user friendly data type + */ + dUserDataOrderUpdateConvertData(data: any) { + const { + e: eventType, + E: eventTime, + T: transaction, // transaction time + } = data; + + let { o: order } = data; + + const orderConverter = (order) => { + const { + s: symbol, + c: clientOrderId, + // special client order id: + // starts with "autoclose-": liquidation order + // "adl_autoclose": ADL auto close order + S: side, + o: orderType, + f: timeInForce, + q: originalQuantity, + p: originalPrice, + ap: averagePrice, + sp: stopPrice, // please ignore with TRAILING_STOP_MARKET order, + x: executionType, + X: orderStatus, + i: orderId, + l: orderLastFilledQuantity, + z: orderFilledAccumulatedQuantity, + L: lastFilledPrice, + ma: marginAsset, + N: commissionAsset, // will not push if no commission + n: commission, // will not push if no commission + T: orderTradeTime, + t: tradeId, + rp: realizedProfit, + b: bidsNotional, + a: askNotional, + m: isMakerSide, // is this trade maker side + R: isReduceOnly, // is this reduce only + wt: stopPriceWorkingType, + ot: originalOrderType, + ps: positionSide, + cp: closeAll, // if close-all, pushed with conditional order + AP: activationPrice, // only pushed with TRAILING_STOP_MARKET order + cr: callbackRate, // only pushed with TRAILING_STOP_MARKET order + pP: priceProtect, // If conditional order trigger is protected + } = order; + return { + symbol, + clientOrderId, + side, + orderType, + timeInForce, + originalQuantity, + originalPrice, + averagePrice, + stopPrice, + executionType, + orderStatus, + orderId, + orderLastFilledQuantity, + orderFilledAccumulatedQuantity, + lastFilledPrice, + marginAsset, + commissionAsset, + commission, + orderTradeTime, + tradeId, + bidsNotional, + askNotional, + isMakerSide, + isReduceOnly, + stopPriceWorkingType, + originalOrderType, + positionSide, + closeAll, + activationPrice, + callbackRate, + realizedProfit, + priceProtect, + }; + }; + order = orderConverter(order); + return { + eventType, + eventTime, + transaction, + order, + }; + } + + /** + * Used as part of the user data websockets callback + * @param {object} data - user data callback data type + * @return {undefined} + */ + userDataHandler(data: any) { + const type = data.e; + this.Options.all_updates_callback(data); + if (type === 'outboundAccountInfo') { + // XXX: Deprecated in 2020-09-08 + } else if (type === 'executionReport') { + if (this.Options.execution_callback) this.Options.execution_callback(data); + } else if (type === 'listStatus') { + if (this.Options.list_status_callback) this.Options.list_status_callback(data); + } else if (type === 'outboundAccountPosition' || type === 'balanceUpdate') { + this.Options.balance_callback(data); + } else { + this.Options.log('Unexpected userData: ' + type); + } + } + + /** + * Used as part of the user data websockets callback + * @param {object} data - user data callback data type + * @return {undefined} + */ + userMarginDataHandler(data: any) { + const type = data.e; + + if (this.Options.margin_all_updates_callback) this.Options.all_updates_callback(data); + + if (type === 'outboundAccountInfo') { + // XXX: Deprecated in 2020-09-08 + } else if (type === 'executionReport') { + if (this.Options.margin_execution_callback) this.Options.margin_execution_callback(data); + } else if (type === 'listStatus') { + if (this.Options.margin_list_status_callback) this.Options.margin_list_status_callback(data); + } else if (type === 'outboundAccountPosition' || type === 'balanceUpdate') { + this.Options.margin_balance_callback(data); + } + } + + /** + * Used as part of the user data websockets callback + * @param {object} data - user data callback data type + * @return {undefined} + */ + userFutureDataHandler(data: any) { + const type = data.e; + + if (this.Options.futures_all_updates_callback) this.Options.futures_all_updates_callback(data); + + if (type === 'MARGIN_CALL') { + this.Options.future_margin_call_callback(this.fUserDataMarginConvertData(data)); + } else if (type === 'ACCOUNT_UPDATE') { + if (this.Options.future_account_update_callback) { + this.Options.future_account_update_callback(this.fUserDataAccountUpdateConvertData(data)); + } + } else if (type === 'ORDER_TRADE_UPDATE' || type === 'TRADE_LITE') { + if (this.Options.future_order_update_callback) { + this.Options.future_order_update_callback(this.fUserDataOrderUpdateConvertData(data)); + } + } else if (type === 'ACCOUNT_CONFIG_UPDATE') { + if (this.Options.future_account_config_update_callback) { + this.Options.future_account_config_update_callback(this.fUserConfigDataAccountUpdateConvertData(data)); + } + } + } + + /** + * Used as part of the user data websockets callback + * @param {object} data - user data callback data type + * @return {undefined} + */ + userDeliveryDataHandler(data: any) { + const type = data.e; + if (type === "MARGIN_CALL") { + this.Options.delivery_margin_call_callback( + this.fUserDataMarginConvertData(data) + ); + } else if (type === "ACCOUNT_UPDATE") { + if (this.Options.delivery_account_update_callback) { + this.Options.delivery_account_update_callback( + this.fUserDataAccountUpdateConvertData(data) + ); + } + } else if (type === "ORDER_TRADE_UPDATE") { + if (this.Options.delivery_order_update_callback) { + this.Options.delivery_order_update_callback( + this.dUserDataOrderUpdateConvertData(data) + ); + } + } else { + this.Options.log("Unexpected userDeliveryData: " + type); + } + } + + /** + * Universal Transfer requires API permissions enabled + * @param {string} type - ENUM , example MAIN_UMFUTURE for SPOT to USDT futures, see https://binance-docs.github.io/apidocs/spot/en/#user-universal-transfer + * @param {string} asset - the asset - example :USDT * + * @param {number} amount - the callback function + * @return {promise} + */ + async universalTransfer(type: string, asset: string, amount: number) { + const parameters = Object.assign({ + asset, + amount, + type, + }); + return await this.privateSpotRequest("v1/asset/transfer", + parameters, + "POST" + ); + } + + /** + * Transfer between main account and futures/delivery accounts + * @param {string} asset - the asset + * @param {number} amount - the asset + * @param {object} options - additional options + * @return {undefined} + */ + async transferBetweenMainAndFutures( + asset: string, + amount: number, + type: any, + ) { + const parameters = Object.assign({ + asset, + amount, + type, + }); + return await this.privateSpotRequest("v1/futures/transfer", + parameters, + "POST" + ); + } + + /** + * Converts the previous day stream into friendly object + * @param {object} data - user data callback data type + * @return {object} - user friendly data type + */ + prevDayConvertData(data: any) { + const convertData = (data: any) => { + const { + e: eventType, + E: eventTime, + s: symbol, + p: priceChange, + P: percentChange, + w: averagePrice, + x: prevClose, + c: close, + Q: closeQty, + b: bestBid, + B: bestBidQty, + a: bestAsk, + A: bestAskQty, + o: open, + h: high, + l: low, + v: volume, + q: quoteVolume, + O: openTime, + C: closeTime, + F: firstTradeId, + L: lastTradeId, + n: numTrades + } = data; + return { + eventType, + eventTime, + symbol, + priceChange, + percentChange, + averagePrice, + prevClose, + close, + closeQty, + bestBid, + bestBidQty, + bestAsk, + bestAskQty, + open, + high, + low, + volume, + quoteVolume, + openTime, + closeTime, + firstTradeId, + lastTradeId, + numTrades + }; + }; + if (Array.isArray(data)) { + const result = []; + for (const obj of data) { + const converted = convertData(obj); + result.push(converted); + } + return result; + // eslint-disable-next-line no-else-return + } else { + return convertData(data); + } + } + + /** + * Parses the previous day stream and calls the user callback with friendly object + * @param {object} data - user data callback data type + * @param {function} callback - user data callback data type + * @return {undefined} + */ + prevDayStreamHandler(data, callback: Callback) { + const converted = this.prevDayConvertData(data); + callback(null, converted); + } + + /** + * Gets the price of a given symbol or symbols + * @param {array} data - array of symbols + * @return {array} - symbols with their current prices + */ + priceData(data: any): { [key: string]: number } { + const prices = {}; + if (Array.isArray(data)) { + for (const obj of data) { + prices[obj.symbol] = obj.price; + } + } else { // Single price returned + prices[data.symbol] = parseFloat(data.price); + } + return prices; + } + + /** + * Used by bookTickers to format the bids and asks given given symbols + * @param {array} data - array of symbols + * @return {object} - symbols with their bids and asks data + */ + bookPriceData(data: any): { [key: string]: BookTicker } { + if (!Array.isArray(data)) { + data = [data]; + } + const prices = {}; + for (const obj of data) { + prices[obj.symbol] = obj; + } + return prices; + } + + /** + * Used by balance to get the balance data + * @param {array} data - account info object + * @return {object} - balances hel with available, onorder amounts + */ + balanceData(data: any) { + const balances = {}; + if (typeof data === 'undefined') return {}; + if (typeof data.balances === 'undefined') { + this.Options.log('balanceData error', data); + return {}; + } + for (const obj of data.balances) { + balances[obj.asset] = { available: obj.free, onOrder: obj.locked }; + } + return balances; + } + + /** + * Used by web sockets depth and populates OHLC and info + * @param {string} symbol - symbol to get candlestick info + * @param {string} interval - time interval, 1m, 3m, 5m .... + * @param {array} ticks - tick array + * @return {undefined} + */ + klineData(symbol, interval, ticks) { // Used for /depth + let last_time = 0; + if (this.isIterable(ticks)) { + for (const tick of ticks) { + // eslint-disable-next-line no-unused-vars + const [time, open, high, low, close, volume, closeTime, assetVolume, trades, buyBaseVolume, buyAssetVolume, ignored] = tick; + this.ohlc[symbol][interval][time] = { open: open, high: high, low: low, close: close, volume: volume }; + last_time = time; + } + + this.info[symbol][interval].timestamp = last_time; + } + } + + /** + * Combines all OHLC data with latest update + * @param {string} symbol - the symbol + * @param {string} interval - time interval, 1m, 3m, 5m .... + * @return {array} - interval data for given symbol + */ + klineConcat(symbol: string, interval: Interval) { + const output = this.ohlc[symbol][interval]; + if (typeof this.ohlcLatest[symbol][interval].time === 'undefined') return output; + const time = this.ohlcLatest[symbol][interval].time; + const last_updated = Object.keys(this.ohlc[symbol][interval]).pop(); + if (time >= last_updated) { + output[time] = this.ohlcLatest[symbol][interval]; + delete output[time].time; + output[time].isFinal = false; + } + return output; + } + + /** + * Used for websocket @kline + * @param {string} symbol - the symbol + * @param {object} kline - object with kline info + * @param {string} firstTime - time filter + * @return {undefined} + */ + klineHandler(symbol, kline, firstTime = 0) { + // TODO: add Taker buy base asset volume + // eslint-disable-next-line no-unused-vars + const { e: eventType, E: eventTime, k: ticks } = kline; + // eslint-disable-next-line no-unused-vars + const { o: open, h: high, l: low, c: close, v: volume, i: interval, x: isFinal, q: quoteVolume, t: time } = ticks; //n:trades, V:buyVolume, Q:quoteBuyVolume + if (time <= firstTime) return; + if (!isFinal) { + if (typeof this.ohlcLatest[symbol][interval].time !== 'undefined') { + if (this.ohlcLatest[symbol][interval].time > time) return; + } + this.ohlcLatest[symbol][interval] = { open: open, high: high, low: low, close: close, volume: volume, time: time }; + return; + } + // Delete an element from the beginning so we don't run out of memory + const first_updated = Object.keys(this.ohlc[symbol][interval]).shift(); + if (first_updated) delete this.ohlc[symbol][interval][first_updated]; + this.ohlc[symbol][interval][time] = { open: open, high: high, low: low, close: close, volume: volume }; + } + + /** + * Used by futures websockets chart cache + * @param {string} symbol - symbol to get candlestick info + * @param {string} interval - time interval, 1m, 3m, 5m .... + * @param {array} ticks - tick array + * @return {undefined} + */ + futuresKlineData(symbol: string, interval: Interval, ticks: any[]) { + let last_time = 0; + if (this.isIterable(ticks)) { + for (const tick of ticks) { + // eslint-disable-next-line no-unused-vars + const [time, open, high, low, close, volume, closeTime, quoteVolume, trades, takerBuyBaseVolume, takerBuyQuoteVolume, ignored] = tick; + this.futuresTicks[symbol][interval][time] = { time, closeTime, open, high, low, close, volume, quoteVolume, takerBuyBaseVolume, takerBuyQuoteVolume, trades }; + last_time = time; + } + this.futuresMeta[symbol][interval].timestamp = last_time; + } + } + + /** + * Used by delivery websockets chart cache + * @param {string} symbol - symbol to get candlestick info + * @param {string} interval - time interval, 1m, 3m, 5m .... + * @param {array} ticks - tick array + * @return {undefined} + */ + deliveryKlineData(symbol, interval, ticks) { + let last_time = 0; + if (this.isIterable(ticks)) { + for (const tick of ticks) { + // eslint-disable-next-line no-unused-vars + const [time, open, high, low, close, volume, closeTime, quoteVolume, trades, takerBuyBaseVolume, takerBuyQuoteVolume, ignored] = tick; + this.deliveryTicks[symbol][interval][time] = { time, closeTime, open, high, low, close, volume, quoteVolume, takerBuyBaseVolume, takerBuyQuoteVolume, trades }; + last_time = time; + } + this.deliveryMeta[symbol][interval].timestamp = last_time; + } + } + + /** + * Used for /depth endpoint + * @param {object} data - containing the bids and asks + * @return {undefined} + */ + depthData(data: any) { + if (!data) return { bids: [], asks: [] }; + const bids = {}, asks = {}; + let obj; + if (typeof data.bids !== 'undefined') { + for (obj of data.bids) { + bids[obj[0]] = parseFloat(obj[1]); + } + } + if (typeof data.asks !== 'undefined') { + for (obj of data.asks) { + asks[obj[0]] = parseFloat(obj[1]); + } + } + return { lastUpdateId: data.lastUpdateId, bids: bids, asks: asks }; + } + + parseOrderBook(data, symbol: string): OrderBook { + const { lastUpdateId, bids, asks } = data; + if (!bids || !asks) { + return data; + } + const orderBook: OrderBook = { + symbol, + lastUpdateId, + bids: bids.map(b => zip(['price', 'quantity'], b)), + asks: asks.map(a => zip(['price', 'quantity'], a)) + }; + return orderBook; + } + + /** + * Used for /depth endpoint + * @param {object} depth - information + * @return {undefined} + */ + depthHandler(depth) { + const symbol = depth.s; + let obj; + const context = this.depthCacheContext[symbol]; + const updateDepthCache = () => { + this.depthCache[symbol].eventTime = depth.E; + for (obj of depth.b) { //bids + if (obj[1] == 0) { + delete this.depthCache[symbol].bids[obj[0]]; + } else { + this.depthCache[symbol].bids[obj[0]] = parseFloat(obj[1]); + } + } + for (obj of depth.a) { //asks + if (obj[1] == 0) { + delete this.depthCache[symbol].asks[obj[0]]; + } else { + this.depthCache[symbol].asks[obj[0]] = parseFloat(obj[1]); + } + } + context.skipCount = 0; + context.lastEventUpdateId = depth.u; + context.lastEventUpdateTime = depth.E; + }; + + // This now conforms 100% to the Binance docs constraints on managing a local order book + if (context.lastEventUpdateId) { + const expectedUpdateId = context.lastEventUpdateId + 1; + if (depth.U <= expectedUpdateId) { + updateDepthCache(); + } else { + let msg = 'depthHandler: [' + symbol + '] The depth cache is out of sync.'; + msg += ' Symptom: Unexpected Update ID. Expected "' + expectedUpdateId + '", got "' + depth.U + '"'; + if (this.Options.verbose) this.Options.log(msg); + throw new Error(msg); + } + } else if (depth.U > context.snapshotUpdateId + 1) { + /* In this case we have a gap between the data of the stream and the snapshot. + This is an out of sync error, and the connection must be torn down and reconnected. */ + let msg = 'depthHandler: [' + symbol + '] The depth cache is out of sync.'; + msg += ' Symptom: Gap between snapshot and first stream data.'; + if (this.Options.verbose) this.Options.log(msg); + throw new Error(msg); + } else if (depth.u < context.snapshotUpdateId + 1) { + /* In this case we've received data that we've already had since the snapshot. + This isn't really an issue, and we can just update the cache again, or ignore it entirely. */ + + // do nothing + } else { + // This is our first legal update from the stream data + updateDepthCache(); + } + } + + /** + * Gets depth cache for given symbol + * @param {string} symbol - the symbol to fetch + * @return {object} - the depth cache object + */ + getDepthCache(symbol: string) { + if (typeof this.depthCache[symbol] === 'undefined') return { bids: {}, asks: {}}; + return this.depthCache[symbol]; + } + + /** + * Calculate Buy/Sell volume from DepthCache + * @param {string} symbol - the symbol to fetch + * @return {object} - the depth volume cache object + */ + depthVolume(symbol: string) { + const cache = this.getDepthCache(symbol); + let quantity, price; + let bidbase = 0, askbase = 0, bidqty = 0, askqty = 0; + for (price in cache.bids) { + quantity = cache.bids[price]; + bidbase += parseFloat((quantity * parseFloat(price)).toFixed(8)); + bidqty += quantity; + } + for (price in cache.asks) { + quantity = cache.asks[price]; + askbase += parseFloat((quantity * parseFloat(price)).toFixed(8)); + askqty += quantity; + } + return { bids: bidbase, asks: askbase, bidQty: bidqty, askQty: askqty }; + } + + /** + * Checks whether or not an array contains any duplicate elements + * @param {array} array - the array to check + * @return {boolean} - true or false + */ + isArrayUnique(array: any[]) { + return new Set(array).size === array.length; + } + + // --- PUBLIC FUNCTIONS --- // + + /** + * Count decimal places + * @param {float} float - get the price precision point + * @return {int} - number of place + */ + getPrecision(float: number) { + if (!float || Number.isInteger(float)) return 0; + return float.toString().split('.')[1].length || 0; + } + + /** + * rounds number with given step + * @param {float} qty - quantity to round + * @param {float} stepSize - stepSize as specified by exchangeInfo + * @return {float} - number + */ + roundStep(qty, stepSize) { + // Integers do not require rounding + if (Number.isInteger(qty)) return qty; + const qtyString = parseFloat(qty).toFixed(16); + const desiredDecimals = Math.max(stepSize.indexOf('1') - 1, 0); + const decimalIndex = qtyString.indexOf('.'); + return parseFloat(qtyString.slice(0, decimalIndex + desiredDecimals + 1)); + } + + /** + * rounds price to required precision + * @param {float} price - price to round + * @param {float} tickSize - tickSize as specified by exchangeInfo + * @return {float} - number + */ + roundTicks(price, tickSize) { + const formatter = new Intl.NumberFormat('en-US', { style: 'decimal', minimumFractionDigits: 0, maximumFractionDigits: 8 }); + const precision = formatter.format(tickSize).split('.')[1].length || 0; + if (typeof price === 'string') price = parseFloat(price); + return price.toFixed(precision); + } + + /** + * Gets percentage of given numbers + * @param {float} min - the smaller number + * @param {float} max - the bigger number + * @param {int} width - percentage width + * @return {float} - percentage + */ + percent(min, max, width = 100) { + return (min * 0.01) / (max * 0.01) * width; + } + + /** + * Gets the sum of an array of numbers + * @param {array} array - the number to add + * @return {float} - sum + */ + sum(array) { + return array.reduce((a, b) => a + b, 0); + } + + /** + * Reverses the keys of an object + * @param {object} object - the object + * @return {object} - the object + */ + reverse(object) { + const range = Object.keys(object).reverse(), output = {}; + for (const price of range) { + output[price] = object[price]; + } + return output; + } + + /** + * Converts an object to an array + * @param {object} obj - the object + * @return {array} - the array + */ + array(obj) { + return Object.keys(obj).map(function (key) { + return [Number(key), obj[key]]; + }); + } + + /** + * Sorts bids + * @param {string} symbol - the object + * @param {int} max - the max number of bids + * @param {string} baseValue - the object + * @return {object} - the object + */ + sortBids(symbol: string, max = Infinity, baseValue?: string) { + const object = {}; + let count = 0, cache; + if (typeof symbol === 'object') cache = symbol; + else cache = this.getDepthCache(symbol).bids; + const sorted = Object.keys(cache).sort((a, b) => parseFloat(b) - parseFloat(a)); + let cumulative = 0; + for (const price of sorted) { + if (!baseValue) object[price] = cache[price]; + else if (baseValue === 'cumulative') { + cumulative += cache[price]; + object[price] = cumulative; + } else object[price] = parseFloat((cache[price] * parseFloat(price)).toFixed(8)); + if (++count >= max) break; + } + return object; + } + + /** + * Sorts asks + * @param {string} symbol - the object + * @param {int} max - the max number of bids + * @param {string} baseValue - the object + * @return {object} - the object + */ + sortAsks(symbol: string, max = Infinity, baseValue?: string) { + let count = 0, cache; + const object = {}; + if (typeof symbol === 'object') cache = symbol; + else cache = this.getDepthCache(symbol).asks; + const sorted = Object.keys(cache).sort((a, b) => parseFloat(a) - parseFloat(b)); + let cumulative = 0; + for (const price of sorted) { + if (!baseValue) object[price] = cache[price]; + else if (baseValue === 'cumulative') { + cumulative += cache[price]; + object[price] = cumulative; + } else object[price] = parseFloat((cache[price] * parseFloat(price)).toFixed(8)); + if (++count >= max) break; + } + return object; + } + + /** + * Returns the first property of an object + * @param {object} object - the object to get the first member + * @return {string} - the object key + */ + first(object) { + return Object.keys(object).shift(); + } + + /** + * Returns the last property of an object + * @param {object} object - the object to get the first member + * @return {string} - the object key + */ + last(object) { + return Object.keys(object).pop(); + } + + /** + * Returns an array of properties starting at start + * @param {object} object - the object to get the properties form + * @param {int} start - the starting index + * @return {array} - the array of entires + */ + slice(object, start = 0) { + return Object.keys(object).slice(start); + } + + /** + * Gets the minimum key form object + * @param {object} object - the object to get the properties form + * @return {string} - the minimum key + */ + min(object) { + // eslint-disable-next-line prefer-spread + return Math.min.apply(Math, Object.keys(object)); + } + + /** + * Gets the maximum key form object + * @param {object} object - the object to get the properties form + * @return {string} - the minimum key + */ + max(object) { + // eslint-disable-next-line prefer-spread + return Math.max.apply(Math, Object.keys(object)); + } + + /** + * Sets an option given a key and value + * @param {string} key - the key to set + * @param {object} value - the value of the key + * @return {undefined} + */ + setOption(key, value) { + this.Options[key] = value; + } + + /** + * Gets an option given a key + * @param {string} key - the key to set + * @return {undefined} + */ + getOption(key: string) { return this.Options[key]; } + + /** + * Returns the entire info object + * @return {object} - the info object + */ + getInfo() { return this.info; } + + /** + * Returns the used weight from the last request + * @return {object} - 1m weight used + */ + usedWeight() { return this.info.usedWeight; } + + /** + * Returns the status code from the last http response + * @return {object} - status code + */ + statusCode() { return this.info.statusCode; } + + /** + * Returns the ping time from the last futures request + * @return {object} - latency/ping (2ms) + */ + futuresLatency() { return this.info.futuresLatency; } + + /** + * Returns the complete URL from the last request + * @return {object} - http address including query string + */ + lastURL() { return this.info.lastURL; } + + /** + * Returns the order count from the last request + * @return {object} - orders allowed per 1m + */ + orderCount() { return this.info.orderCount1m; } + + /** + * Returns the entire options object + * @return {object} - the options object + */ + getOptions() { return this.Options; } + + // /** + // * Gets an option given a key + // * @param {object} opt - the object with the class configuration + // + // * @return {undefined} + // */ + // options() {this.setOptions()} + + // /** + // * Creates a buy order + // * @param {string} symbol - the symbol to buy + // * @param {numeric} quantity - the quantity required + // * @param {numeric} price - the price to pay for each unit + // * @param {object} flags - additional buy order flags + // + // * @return {promise or undefined} - omitting the callback returns a promise + // */ + // buy(symbol, quantity, price, flags: Dict = {}, callback = false) { + // if (!callback) { + // return new Promise((resolve, reject) => { + // callback = (error, response) => { + // if (error) { + // reject(error); + // } else { + // resolve(response); + // } + // } + // order('BUY', symbol, quantity, price, flags, callback); + // }) + // } else { + // order('BUY', symbol, quantity, price, flags, callback); + // } + // } + + // /** + // * Creates a sell order + // * @param {string} symbol - the symbol to sell + // * @param {numeric} quantity - the quantity required + // * @param {numeric} price - the price to sell each unit for + // * @param {object} flags - additional order flags + // + // * @return {promise or undefined} - omitting the callback returns a promise + // */ + // sell(symbol, quantity, price, flags: Dict = {}, callback = false) { + // if (!callback) { + // return new Promise((resolve, reject) => { + // callback = (error, response) => { + // if (error) { + // reject(error); + // } else { + // resolve(response); + // } + // } + // order('SELL', symbol, quantity, price, flags, callback); + // }) + // } else { + // order('SELL', symbol, quantity, price, flags, callback); + // } + // } + + // /** + // * Creates a market sell order + // * @param {string} symbol - the symbol to sell + // * @param {numeric} quantity - the quantity required + // * @param {object} flags - additional sell order flags + // + // * @return {promise or undefined} - omitting the callback returns a promise + // */ + // marketSell(symbol, quantity, flags = { type: 'MARKET' }, callback = false) { + // if (typeof flags === 'function') { // Accept callback as third parameter + // callback = flags; + // flags = { type: 'MARKET' }; + // } + // if (typeof flags.type === 'undefined') flags.type = 'MARKET'; + // if (!callback) { + // return new Promise((resolve, reject) => { + // callback = (error, response) => { + // if (error) { + // reject(error); + // } else { + // resolve(response); + // } + // } + // order('SELL', symbol, quantity, 0, flags, callback); + // }) + // } else { + // order('SELL', symbol, quantity, 0, flags, callback); + // } + // } + + /** + * Gets the depth information for a given symbol + * @see https://developers.binance.com/docs/binance-spot-api-docs/rest-api/market-data-endpoints#order-book + * @param {string} symbol - the symbol + * @param {int} limit - limit the number of returned orders + * @return {promise or undefined} - omitting the callback returns a promise + */ + async depth(symbol: string, limit = 100): Promise { + const data = await this.publicSpotRequest('v3/depth', { symbol: symbol, limit: limit }); + return this.parseOrderBook(data, symbol); + } + + /** + * Gets the average prices of a given symbol + * @see https://developers.binance.com/docs/binance-spot-api-docs/rest-api/market-data-endpoints#current-average-price + * @param {string} symbol - the symbol + * @return {promise or undefined} - omitting the callback returns a promise + */ + async avgPrice(symbol: string, params: Dict = {}) { + params.symbol = symbol; + return await this.publicSpotRequest('v3/avgPrice', params); + } + + /** + * Gets the prices of a given symbol(s) + * @param {string} symbol - the symbol + * @see https://developers.binance.com/docs/binance-spot-api-docs/rest-api/market-data-endpoints#symbol-price-ticker + * @return {promise or undefined} - omitting the callback returns a promise + */ + async prices(symbol?: string, params: Dict = {}): Promise<{ [key: string]: number }> { + if (symbol) params.symbol = symbol; + const data = await this.publicSpotRequest('v3/ticker/price', params); + return this.priceData(data); + } + + /** + * Gets the book tickers of given symbol(s) + * @see https://developers.binance.com/docs/binance-spot-api-docs/testnet/rest-api/market-data-endpoints#symbol-order-book-ticker + * @param {string} symbol - the symbol + * @return {promise or undefined} - omitting the callback returns a promise + */ + async bookTickers(symbol?: string, params: Dict = {}): Promise<{ [key: string]: BookTicker }> { + if (symbol) params.symbol = symbol; + const data = await this.publicSpotRequest('v3/ticker/bookTicker', params); + return this.bookPriceData(data); + } + + /** + * Gets the prevday percentage change + * @see https://developers.binance.com/docs/binance-spot-api-docs/rest-api/market-data-endpoints#24hr-ticker-price-change-statistics + * @param {string} symbol - the symbol or symbols + * @return {promise or undefined} - omitting the callback returns a promise + */ + async prevDay(symbol?: string, params: Dict = {}): Promise { + if (symbol) params.symbol = symbol; + return await this.publicSpotRequest('v3/ticker/24hr', params); + } + + /** + * Gets the prevday percentage change + * @see https://developers.binance.com/docs/binance-spot-api-docs/rest-api/market-data-endpoints#24hr-ticker-price-change-statistics + * @param {string} symbol - the symbol or symbols + * @return {promise or undefined} - omitting the callback returns a promise + */ + async ticker24h(symbol?: string, params: Dict = {}): Promise { + if (symbol) params.symbol = symbol; + return await this.publicSpotRequest('v3/ticker/24hr', params); + } + + /** + * Gets the prevday percentage change + * @see https://developers.binance.com/docs/binance-spot-api-docs/rest-api/market-data-endpoints#24hr-ticker-price-change-statistics + * @param {string} symbol - the symbol or symbols + * @return {promise or undefined} - omitting the callback returns a promise + */ + async dailyStats(symbol?: string, params: Dict = {}): Promise { + if (symbol) params.symbol = symbol; + return await this.publicSpotRequest('v3/ticker/24hr', params); + } + + /** + * Gets the the exchange info + * @see https://developers.binance.com/docs/binance-spot-api-docs/rest-api/general-endpoints#exchange-information + * @return {promise or undefined} - omitting the callback returns a promise + */ + async exchangeInfo() { + return await this.publicSpotRequest('v3/exchangeInfo', {}); + } + + /** + * Gets the dust log for user + * @return {promise or undefined} - omitting the callback returns a promise + */ + async dustLog() { + return await this.privateSpotRequest('v1/asset/dribblet', {}); + } + + async dustTransfer(assets) { + return await this.privateSpotRequest('v1/asset/dust', { asset: assets }, 'POST'); + } + + async assetDividendRecord(params: Dict = {}) { + return await this.privateSpotRequest('v1/asset/assetDividend', params); + } + + /** + * Gets the the system status + * @see https://developers.binance.com/docs/wallet/others/system-status + * @return {promise or undefined} - omitting the callback returns a promise + */ + async systemStatus(params: Dict = {}) { + return await this.publicRequest(this.sapi + 'v1/system/status', params); + } + + /** + * Withdraws asset to given wallet id + * @see https://developers.binance.com/docs/wallet/capital/withdraw + * @param {string} asset - the asset symbol + * @param {string} address - the wallet to transfer it to + * @param {number} amount - the amount to transfer + * @param {string} addressTag - and addtional address tag + * @param {string} name - the name to save the address as. Set falsy to prevent Binance saving to address book + * @return {promise or undefined} - omitting the callback returns a promise + */ + async withdraw(asset: string, address: string, amount: number, addressTag?: string, name?: string, params: Dict = {}): Promise { + // const params = { asset, address, amount }; + params.asset = asset; + params.address = address; + params.amount = amount; + if (name) params.name = name; + if (addressTag) params.addressTag = addressTag; + + return await this.privateSpotRequest('v1/capital/withdraw/apply', params, 'POST'); + } + + /** + * Get the Withdraws history for a given asset + * @see https://developers.binance.com/docs/wallet/capital/withdraw-history + * @param {object} params - supports limit and fromId parameters + * @return {promise or undefined} - omitting the callback returns a promise + */ + async withdrawHistory(params: Dict = {}): Promise { + if (typeof params === 'string') params = { asset: params }; + return await this.privateSpotRequest('v1/capital/withdraw/history', params); + } + + /** + * Get the deposit history + * @see https://developers.binance.com/docs/wallet/capital/deposite-history#http-request + * @param {object} params - additional params + * @return {promise or undefined} - omitting the callback returns a promise + */ + async depositHistory(asset?: string, params: Dict = {}): Promise { + if (asset) params = { asset: asset }; + return await this.privateSpotRequest('v1/capital/deposit/hisrec', params); + } + + /** + * Get the deposit address for given asset + * @see https://developers.binance.com/docs/wallet/capital/deposite-address + * @param {string} coin - the asset + * @return {promise or undefined} - omitting the callback returns a promise + */ + async depositAddress(asset: string, params: Dict = {}): Promise { + return await this.privateSpotRequest('v1/capital/deposit/address', this.extend({ coin: asset }, params)); + } + + /** + * Get the deposit address list for given asset + * @see https://developers.binance.com/docs/wallet/capital/fetch-deposit-address-list-with-network + * @param {string} coin - the asset + * @return {promise or undefined} - omitting the callback returns a promise + */ + async depositAddressList(asset: string, params: Dict = {}) { + return await this.privateSpotRequest('v1/capital/deposit/address/list', this.extend({ coin: asset }, params)); + } + + /** + * Get the account status + * @see https://developers.binance.com/docs/binance-spot-api-docs/rest-api/account-endpoints#account-information-user_data + * @return {promise or undefined} - omitting the callback returns a promise + */ + async accountStatus(params: Dict = {}) { + return await this.privateSpotRequest('v3/account', params); + } + + /** + * @return {promise or undefined} - omitting the callback returns a promise + */ + async apiPermission(params: Dict = {}) { + return await this.privateSpotRequest('v1/account/apiRestrictions', params); + } + + /** + * Get the trade fee + * @see https://developers.binance.com/docs/wallet/asset/trade-fee + * @param {string} symbol (optional) + * @return {promise or undefined} - omitting the callback returns a promise + */ + async tradeFee(symbol?: string) { + const params = symbol ? { symbol: symbol } : {}; + return await this.privateSpotRequest('v1/asset/tradeFee', params); + } + + /** + * Fetch asset detail (minWithdrawAmount, depositStatus, withdrawFee, withdrawStatus, depositTip) + * @see https://developers.binance.com/docs/wallet/asset + * @return {promise or undefined} - omitting the callback returns a promise + */ + async assetDetail(params: Dict = {}) { + return await this.privateSpotRequest('asset/assetDetail', params); + } + + /** + * Get the account + * @see https://developers.binance.com/docs/binance-spot-api-docs/testnet/rest-api/account-endpoints#account-information-user_data + * @return {promise or undefined} - omitting the callback returns a promise + */ + async account(params: Dict = {}): Promise { + return await this.privateSpotRequest('v3/account', params); + } + + /** + * Get the balance data + * @see https://developers.binance.com/docs/binance-spot-api-docs/testnet/rest-api/account-endpoints#account-information-user_data + * @return {promise or undefined} - omitting the callback returns a promise + */ + async balance(params: Dict = {}) { + const data = await this.privateSpotRequest('v3/account', params); + return this.balanceData(data); + } + + /** + * Get private trades for a given symbol + * @see https://developers.binance.com/docs/binance-spot-api-docs/testnet/rest-api/account-endpoints#account-trade-list-user_data + * @param {string} symbol - the symbol + * @param {object} options - additional options + * @return {promise or undefined} - omitting the callback returns a promise + */ + async trades(symbol: string, params: Dict = {}): Promise { + const parameters = this.extend({ symbol: symbol }, params); + return await this.privateSpotRequest('v3/myTrades', parameters); + } + + /** + * Get private trades for a given symbol + * @see https://developers.binance.com/docs/binance-spot-api-docs/testnet/rest-api/account-endpoints#account-trade-list-user_data + * @param {string} symbol - the symbol + * @param {object} options - additional options + * @return {promise or undefined} - omitting the callback returns a promise + */ + async myTrades(symbol: string, params: Dict = {}): Promise { + const parameters = this.extend({ symbol: symbol }, params); + return await this.privateSpotRequest('v3/myTrades', parameters); + } + + /** + * Tell api to use the server time to offset time indexes + * @see https://developers.binance.com/docs/binance-spot-api-docs/rest-api/general-endpoints#check-server-time + * @return {promise or undefined} - omitting the callback returns a promise + */ + async useServerTime() { + const response = await this.publicSpotRequest('v3/time', {}); + this.timeOffset = response.serverTime - new Date().getTime(); + return response; + } + + // /** + // * Get Binance server time + // * @return {promise or undefined} - omitting the callback returns a promise + // */ + // time() { + + // publicRequest(this.getSpotUrl() + 'v3/time', {}, callback); + // } + + /** + * Ping binance + * @see https://developers.binance.com/docs/binance-spot-api-docs/rest-api/general-endpoints#test-connectivity + * @return {promise or undefined} - omitting the callback returns a promise + */ + async ping() { + return await this.publicSpotRequest('v3/ping', {}); + } + + /** + * Get agg trades for given symbol + * @see https://developers.binance.com/docs/binance-spot-api-docs/rest-api/market-data-endpoints#compressedaggregate-trades-list + * @param {string} symbol - the symbol + * @param {object} options - additional optoins + * @return {promise or undefined} - omitting the callback returns a promise + */ + async aggTrades(symbol: string, params: Dict = {}): Promise { //fromId startTime endTime limit + const parameters = Object.assign({ symbol }, params); + return await this.publicSpotRequest('v3/aggTrades', parameters); + } + + /** + * Get the recent trades + * @see https://developers.binance.com/docs/binance-spot-api-docs/rest-api/market-data-endpoints#old-trade-lookup + * @param {string} symbol - the symbol + * @param {int} limit - limit the number of items returned + * @return {promise or undefined} - omitting the callback returns a promise + */ + async recentTrades(symbol: string, limit = 500, params: Dict = {}): Promise { + return await this.publicSpotRequest('v3/trades', this.extend({ symbol: symbol, limit: limit }, params)); + } + + /** + * Get the historical trade info + * @see https://developers.binance.com/docs/binance-spot-api-docs/rest-api/market-data-endpoints#old-trade-lookup + * @param {string} symbol - the symbol + * @param {int} limit - limit the number of items returned + * @param {int} fromId - from this id + * @return {promise or undefined} - omitting the callback returns a promise + */ + async historicalTrades(symbol: string, limit = 500, fromId?: number, params: Dict = {}): Promise { + params.symbol = symbol; + params.limit = limit; + if (fromId) params.fromId = fromId; + return await this.publicSpotRequest('v3/historicalTrades', params); + } + + /** + * Convert chart data to highstock array [timestamp,open,high,low,close] + * @param {object} chart - the chart + * @param {boolean} include_volume - to include the volume or not + * @return {array} - an array + */ + highstock(chart, include_volume = false) { + const array = []; + for (const timestamp in chart) { + const obj = chart[timestamp]; + const line = [ + Number(timestamp), + parseFloat(obj.open), + parseFloat(obj.high), + parseFloat(obj.low), + parseFloat(obj.close) + ]; + if (include_volume) line.push(parseFloat(obj.volume)); + array.push(line); + } + return array; + } + + /** + * Populates OHLC information + * @param {object} chart - the chart + * @return {object} - object with candle information + */ + populateOHLC(chart) { + const open = [], high = [], low = [], close = [], volume = []; + for (const timestamp in chart) { //this.ohlc[symbol][interval] + const obj = chart[timestamp]; + open.push(parseFloat(obj.open)); + high.push(parseFloat(obj.high)); + low.push(parseFloat(obj.low)); + close.push(parseFloat(obj.close)); + volume.push(parseFloat(obj.volume)); + } + return { open: open, high: high, low: low, close: close, volume: volume }; + } + + /** + * Gets the candles information for a given symbol + * @see https://developers.binance.com/docs/binance-spot-api-docs/rest-api/market-data-endpoints#klinecandlestick-data + * intervals: 1m,3m,5m,15m,30m,1h,2h,4h,6h,8h,12h,1d,3d,1w,1M + * @param {string} symbol - the symbol + * @param {function} interval - the callback function + * @param {object} options - additional options + * @return {promise or undefined} - omitting the callback returns a promise + */ + async candlesticks(symbol: string, interval: Interval = '5m', params: Dict = {}): Promise { + if (!params.limit) params.limit = 500; + params = Object.assign({ symbol: symbol, interval: interval }, params); + return await this.publicSpotRequest('v3/klines', params); + } + + /** + * Gets the candles information for a given symbol + * @see https://developers.binance.com/docs/binance-spot-api-docs/rest-api/market-data-endpoints#klinecandlestick-data + * intervals: 1m,3m,5m,15m,30m,1h,2h,4h,6h,8h,12h,1d,3d,1w,1M + * @param {string} symbol - the symbol + * @param {function} interval - the callback function + * @param {object} options - additional options + * @return {promise or undefined} - omitting the callback returns a promise + */ + async candles(symbol: string, interval: Interval = '5m', params: Dict = {}): Promise { + return await this.candlesticks(symbol, interval, params); // make name consistent with futures + } + + // /** + // * Queries the public api + // * @param {string} url - the public api endpoint + // * @param {object} data - the data to send + // * @param {string} method - the http method + // * @return {promise or undefined} - omitting the callback returns a promise + // */ + // publicRequest(url: string, data, method: HttpMethod = 'Get') { + // if (!callback) { + // return new Promise((resolve, reject) => { + // callback = (error, response) => { + // if (error) { + // reject(error); + // } else { + // resolve(response); + // } + // } + // this.publicRequest(url, data, callback, method); + // }) + // } else { + // publicRequest(url, data, callback, method); + // } + // } + + // /** + // * Queries the futures API by default + // * @param {string} url - the signed api endpoint + // * @param {object} data - the data to send + // * @param {object} flags - type of request, authentication method and endpoint url + // */ + // promiseRequest(url, data = {}, flags = {}) { + // return await this.promiseRequest(url, data, flags); + // } + + // /** + // * Queries the signed api + // * @param {string} url - the signed api endpoint + // * @param {object} data - the data to send + // + // * @param {string} method - the http method + // * @param {boolean} noDataInSignature - Prevents data from being added to signature + // * @return {promise or undefined} - omitting the callback returns a promise + // */ + // signedRequest(url, data, callback, method: HttpMethod = 'GET', noDataInSignature = false) { + // if (!callback) { + // return new Promise((resolve, reject) => { + // callback = (error, response) => { + // if (error) { + // reject(error); + // } else { + // resolve(response); + // } + // } + // signedRequest(url, data, callback, method, noDataInSignature); + // }) + // } else { + // signedRequest(url, data, callback, method, noDataInSignature); + // } + // } + + /** + * Gets the market asset of given symbol + * @param {string} symbol - the public api endpoint + * @return {undefined} + */ + getMarket(symbol: string) { + if (symbol.endsWith('BTC')) return 'BTC'; + else if (symbol.endsWith('ETH')) return 'ETH'; + else if (symbol.endsWith('BNB')) return 'BNB'; + else if (symbol.endsWith('XRP')) return 'XRP'; + else if (symbol.endsWith('PAX')) return 'PAX'; + else if (symbol.endsWith('USDT')) return 'USDT'; + else if (symbol.endsWith('USDC')) return 'USDC'; + else if (symbol.endsWith('USDS')) return 'USDS'; + else if (symbol.endsWith('TUSD')) return 'TUSD'; + } + + /** + * Get the account binance lending information + * @return {promise or undefined} - omitting the callback returns a promise + */ + async lending(params: Dict = {}) { + return await this.privateSpotRequest('v1/lending/union/account', params); + } + + //** Futures methods */ + async futuresPing(params: Dict = {}) { + return await this.publicFuturesRequest('v1/ping', params); + } + + async futuresTime(params: Dict = {}) { + return await this.publicFuturesRequest('v1/time', params).then(r => r.serverTime); + } + /** + * @see https://developers.binance.com/docs/derivatives/usds-margined-futures/market-data/rest-api/Exchange-Information + * @returns + */ + async futuresExchangeInfo(params: Dict = {}) { + return await this.publicFuturesRequest('v1/exchangeInfo', params); + } + + /** + * @see https://developers.binance.com/docs/derivatives/usds-margined-futures/market-data/rest-api/Symbol-Price-Ticker-v2 + * + */ + async futuresPrices(symbol?: string, params: Dict = {}): Promise<{ [key: string]: number }> { + if (symbol) params.symbol = symbol; + const data = await this.publicFuturesRequest('v2/ticker/price', params); + return this.priceData(data); + } + + /** + * @see https://developers.binance.com/docs/derivatives/usds-margined-futures/market-data/rest-api/24hr-Ticker-Price-Change-Statistics + */ + async futuresDaily(symbol?: string, params: Dict = {}): Promise { + if (symbol) params.symbol = symbol; + return await this.publicFuturesRequest('v1/ticker/24hr', params); + } + + /** + * @see https://developers.binance.com/docs/derivatives/usds-margined-futures/market-data/rest-api/Open-Interest + */ + async futuresOpenInterest(symbol: string, params: Dict = {}): Promise { + params.symbol = symbol; + return await this.publicFuturesRequest('v1/openInterest', params); + } + + /** + * @see https://developers.binance.com/docs/derivatives/usds-margined-futures/market-data/rest-api/Kline-Candlestick-Data + */ + async futuresCandles(symbol: string, interval: Interval = "30m", params: Dict = {}): Promise { + params.symbol = symbol; + params.interval = interval; + return await this.publicFuturesRequest('v1/klines', params); + } + + /** + * @see https://developers.binance.com/docs/derivatives/usds-margined-futures/market-data/rest-api/Kline-Candlestick-Data + */ + async futuresCandlesticks(symbol: string, interval: Interval = "30m", params: Dict = {}): Promise { + params.symbol = symbol; + params.interval = interval; + return await this.publicFuturesRequest('v1/klines', params); + } + + /** + * @see https://developers.binance.com/docs/derivatives/usds-margined-futures/market-data/rest-api/Mark-Price + */ + async futuresMarkPrice(symbol?: string, params: Dict = {}): Promise { + if (symbol) params.symbol = symbol; + return await this.publicFuturesRequest('v1/premiumIndex', params); + } + + /** + * @see https://developers.binance.com/docs/derivatives/usds-margined-futures/market-data/rest-api/Recent-Trades-List + * @param symbol symbol if the market + * @param params extra parameters to be sent in the request + * @returns + */ + async futuresTrades(symbol: string, params: Dict = {}): Promise { + params.symbol = symbol; + return await this.publicFuturesRequest('v1/trades', params); + } + + /** + * @see https://developers.binance.com/docs/derivatives/usds-margined-futures/market-data/rest-api/Old-Trades-Lookup + * @param symbol symbol if the market + * @param param + * @returns + */ + async futuresHistoricalTrades(symbol: string, params: Dict = {}): Promise { + params.symbol = symbol; + return await this.publicFuturesRequest('v1/historicalTrades', params); + } + + /** + * @see https://developers.binance.com/docs/derivatives/usds-margined-futures/market-data/rest-api/Compressed-Aggregate-Trades-List + */ + async futuresAggTrades(symbol: string, params: Dict = {}): Promise { + params.symbol = symbol; + return await this.publicFuturesRequest('v1/aggTrades', params); + } + + /** + * @see https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/Users-Force-Orders + * @param params extra parameters to be sent in the request + * @returns + */ + async futuresForceOrders(params: Dict = {}): Promise { + return await this.privateFuturesRequest('v1/forceOrders', params); + } + /** + * @see https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/Position-ADL-Quantile-Estimation + * @param params extra parameters to be sent in the request + * @returns + */ + async futuresDeleverageQuantile(params: Dict = {}) { + return await this.privateFuturesRequest('v1/adlQuantile', params); + } + + /** + * @see https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/Account-Trade-List + * @param params extra parameters to be sent in the request + * @returns + */ + async futuresUserTrades(symbol: string, params: Dict = {}): Promise { + params.symbol = symbol; + return await this.privateFuturesRequest('v1/userTrades', params); + } + + async futuresGetDataStream(params: Dict = {}) { + //A User Data Stream listenKey is valid for 60 minutes after creation. setInterval + return await this.privateFuturesRequest('v1/listenKey', params, 'POST'); + } + + async futuresKeepDataStream(params: Dict = {}) { + return await this.privateFuturesRequest('v1/listenKey', params, 'PUT'); + } + + async futuresCloseDataStream(params: Dict = {}) { + return await this.privateFuturesRequest('v1/listenKey', params, 'DELETE'); + } + + /** + * Get the account position risk (v3) + * @see https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/Position-Information-V3 + * @see https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/Position-Information-V2 + * @return {promise or undefined} - omitting the callback returns a promise + */ + async futuresPositionRisk(params: Dict = {}, useV2 = false): Promise { + const endpoint = useV2 ? 'v2/positionRisk' : 'v3/positionRisk'; + return await this.privateFuturesRequest(endpoint, params); + } + + /** + * Get the account position risk (v2) + * @see https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/Position-Information-V3 + * @see https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/Position-Information-V2 + * @return {promise or undefined} - omitting the callback returns a promise + */ + async futuresPositionRiskV2(params: Dict = {}): Promise { + return this.futuresPositionRisk(params, true); + } + + /** + * @see https://developers.binance.com/docs/derivatives/usds-margined-futures/market-data/rest-api/Get-Funding-Rate-Info + * @param symbol symbol if the market + * @param params extra parameters to be sent in the request + * @returns + */ + async futuresFundingRate(symbol: string, params: Dict = {}): Promise { + params.symbol = symbol; + return await this.publicFuturesRequest('v1/fundingRate', params); + } + + /** + * @see https://developers.binance.com/docs/derivatives/usds-margined-futures/account/rest-api/Notional-and-Leverage-Brackets#http-request + * @param symbol symbol if the market + * @param params extra parameters to be sent in the request + * @returns + */ + async futuresLeverageBracket(symbol?: string, params: Dict = {}) { + if (symbol) params.symbol = symbol; + return await this.privateFuturesRequest('v1/leverageBracket', params); + } + + /** + * @see https://developers.binance.com/docs/derivatives/usds-margined-futures/account/rest-api/Futures-Trading-Quantitative-Rules-Indicators#http-request + * @param symbol symbol if the market + * @param params extra parameters to be sent in the request + * @returns + */ + async futuresTradingStatus(symbol?: string, params: Dict = {}) { + if (symbol) params.symbol = symbol; + return await this.privateFuturesRequest('v1/apiTradingStatus', params); + } + + /** + * @see https://developers.binance.com/docs/derivatives/usds-margined-futures/account/rest-api/User-Commission-Rate#http-request + * @param symbol symbol if the market + * @param params extra parameters to be sent in the request + * @returns + */ + async futuresCommissionRate(symbol?: string, params: Dict = {}) { + if (symbol) params.symbol = symbol; + return await this.privateFuturesRequest('v1/commissionRate', params); + } + + // leverage 1 to 125 + /** + * @see https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/Change-Initial-Leverage + */ + async futuresLeverage(symbol: string, leverage: number, params: Dict = {}) { + params.symbol = symbol; + params.leverage = leverage; + return await this.privateFuturesRequest('v1/leverage', params); + } + + // ISOLATED, CROSSED + /** + * @see https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/Change-Margin-Type + * @param symbol symbol if the market + * @param marginType + * @param params extra parameters to be sent in the request + * @returns + */ + async futuresMarginType(symbol: string, marginType: string, params: Dict = {}) { + params.symbol = symbol; + params.marginType = marginType; + return await this.privateFuturesRequest('v1/marginType', params); + } + + // type: 1: Add postion margin,2: Reduce postion margin + async futuresPositionMargin(symbol: string, amount: number, type = 1, params: Dict = {}) { + params.symbol = symbol; + params.amount = amount; + params.type = type; + return await this.privateFuturesRequest('v1/positionMargin', params); + } + + async futuresPositionMarginHistory(symbol: string, params: Dict = {}) { + params.symbol = symbol; + return await this.privateFuturesRequest('v1/positionMargin/history', params); + } + + /** + * @see https://developers.binance.com/docs/derivatives/usds-margined-futures/account/rest-api/Get-Income-History + */ + async futuresIncome(params: Dict = {}) { + return await this.privateFuturesRequest('v1/income', params); + } + + /** + * @see https://developers.binance.com/docs/derivatives/usds-margined-futures/account/rest-api/Futures-Account-Balance-V2 + */ + async futuresBalance(params: Dict = {}): Promise { + return await this.privateFuturesRequest('v2/balance', params); + } + + /** + * @see https://developers.binance.com/docs/derivatives/usds-margined-futures/account/rest-api/Account-Information-V3 + * @param params extra parameters to be sent in the request + * @returns + */ + async futuresAccount(params: Dict = {}): Promise { + return await this.privateFuturesRequest('v3/account', params); + } + + /** + * @see https://developers.binance.com/docs/derivatives/usds-margined-futures/market-data/rest-api/Order-Book + * @param symbol symbol if the market + * @param params extra parameters to be sent in the request + * @returns + */ + async futuresDepth(symbol: string, params: Dict = {}): Promise { + params.symbol = symbol; + const res = await this.publicFuturesRequest('v1/depth', params); + return this.parseOrderBook(res, symbol); + } + + /** + * @see https://developers.binance.com/docs/derivatives/usds-margined-futures/market-data/rest-api/Symbol-Order-Book-Ticker + */ + async futuresQuote(symbol?: string, params: Dict = {}) { + if (symbol) params.symbol = symbol; + const data = await this.publicFuturesRequest('v1/ticker/bookTicker', params); + return this.bookPriceData(data); + } + + /** + * @see https://developers.binance.com/docs/derivatives/usds-margined-futures/market-data/rest-api/Symbol-Order-Book-Ticker + */ + async futuresBookTicker(symbol?: string, params: Dict = {}) { + return await this.futuresQuote(symbol, params); + } + + /** + * @see https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/New-Order + * @param symbol symbol if the market + * @param quantity + * @param price + * @param params extra parameters to be sent in the request + * @returns + */ + async futuresBuy(symbol: string, quantity: number, price: number, params: Dict = {}) { + return await this.futuresOrder('LIMIT', 'BUY', symbol, quantity, price, params); + } + + /** + * @see https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/New-Order + * @param symbol symbol if the market + * @param quantity + * @param price + * @param params extra parameters to be sent in the request + * @returns + */ + async futuresSell(symbol: string, quantity: number, price: number, params: Dict = {}) { + return await this.futuresOrder('LIMIT', 'SELL', symbol, quantity, price, params); + } + + /** + * @see https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/New-Order + * @param symbol symbol if the market + * @param quantity + * @param price + * @param params extra parameters to be sent in the request + * @returns + */ + async futuresMarketBuy(symbol: string, quantity: number, params: Dict = {}) { + return await this.futuresOrder('MARKET', 'BUY', symbol, quantity, undefined, params); + } + + /** + * @description futures limit order + * @see https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/New-Order + * @param side + * @param symbol + * @param quantity + * @param price + * @param params extra parameters to be sent in the request + * @returns + */ + async futuresLimitOrder(side: OrderSide, symbol: string, quantity: number, price: number, params: Dict = {}) { + return await this.futuresOrder('LIMIT', side, symbol, quantity, price, params); + } + + /** + * @see https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/New-Order + * @param symbol symbol if the market + * @param quantity + * @param price + * @param params extra parameters to be sent in the request + * @returns + */ + async futuresMarketSell(symbol: string, quantity: number, params: Dict = {}) { + return await this.futuresOrder('MARKET', 'SELL', symbol, quantity, undefined, params); + } + + /** + * @see https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/Place-Multiple-Orders + */ + async futuresMultipleOrders(orders: Dict[] = []): Promise { + for (let i = 0; i < orders.length; i++) { + if (!orders[i].newClientOrderId) { + orders[i].newClientOrderId = this.CONTRACT_PREFIX + this.uuid22(); + } + } + const params = { batchOrders: JSON.stringify(orders) }; + return await this.privateFuturesRequest('v1/batchOrders', params, 'POST'); + } + + /** + * @see https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/Cancel-Multiple-Orders + */ + async futuresCancelMultipleOrders(symbol: string, params: Dict = {}) { + return await this.privateFuturesRequest('v1/batchOrders', this.extend({ 'symbol': symbol }, params), 'DELETE'); + } + + // futuresOrder, // side symbol quantity [price] [params] + + /** + * @see https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/Query-Order + * @param symbol symbol if the market + * @param params extra parameters to be sent in the request + * @returns + */ + async futuresOrderStatus(symbol: string, params: Dict = {}): Promise { // Either orderId or origClientOrderId must be sent + params.symbol = symbol; + return await this.privateFuturesRequest('v1/order', params); + } + + /** + * @see https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/Cancel-Order + * @param symbol symbol if the market + * @param orderId + * @param params extra parameters to be sent in the request + * @returns + */ + async futuresCancel(symbol: string, orderId?: number | string, params: Dict = {}): Promise { // Either orderId or origClientOrderId must be sent + params.symbol = symbol; + if (orderId) params.orderId = orderId; + return await this.privateFuturesRequest('v1/order', params, 'DELETE'); + } + + /** + * @see https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/Cancel-All-Open-Orders + * @param symbol symbol if the market + * @param params extra parameters to be sent in the request + * @returns + */ + async futuresCancelAll(symbol: string, params: Dict = {}): Promise { + params.symbol = symbol; + return await this.privateFuturesRequest('v1/allOpenOrders', params, 'DELETE'); + } + + /** + * + * @param symbol symbol if the market + * @param countdownTime + * @param params extra parameters to be sent in the request + * @returns + */ + async futuresCountdownCancelAll(symbol, countdownTime = 0, params: Dict = {}) { + params.symbol = symbol; + params.countdownTime = countdownTime; + return await this.privateFuturesRequest('v1/countdownCancelAll', params, 'POST'); + } + + /** + * @see https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/Current-All-Open-Orders + * @param symbol symbol if the market + * @param params extra parameters to be sent in the request + * @returns + */ + async futuresOpenOrders(symbol?: string, params: Dict = {}): Promise { + if (symbol) params.symbol = symbol; + return await this.privateFuturesRequest('v1/openOrders', params); + } + + /** + * @see https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/All-Orders + * @param symbol symbol if the market + * @param params extra parameters to be sent in the request + * @returns + */ + async futuresAllOrders(symbol?: string, params: Dict = {}): Promise { // Get all account orders; active, canceled, or filled. + if (symbol) params.symbol = symbol; + return await this.privateFuturesRequest('v1/allOrders', params); + } + + /** + * + * @param params extra parameters to be sent in the request + * @returns + */ + async futuresPositionSideDual(params: Dict = {}) { + return await this.privateFuturesRequest('v1/positionSide/dual', params); + } + + /** + * + * @param dualSidePosition + * @param params extra parameters to be sent in the request + * @returns + */ + async futuresChangePositionSideDual(dualSidePosition, params: Dict = {}) { + params.dualSidePosition = dualSidePosition; + return await this.privateFuturesRequest('v1/positionSide/dual', params, 'POST'); + } + + /** + * + * @param symbol symbol if the market + * @param params extra parameters to be sent in the request + * @returns + */ + async futuresTransferAsset(asset: string, amount: number, type: string, params: Dict = {}) { + params = Object.assign({ asset, amount, type }); + return await this.privateSpotRequest('v1/futures/transfer', params, 'POST'); + } + + /** + * @see https://developers.binance.com/docs/derivatives/futures-data/market-data + * @param symbol + * @param params + * @returns + */ + async futuresHistDataLink(symbol?: string, params: Dict = {}) { + if (symbol) params.symbol = symbol; + return await this.privateSpotRequest('v1/futuresHistDataId', params); + } + + /** + * @see https://developers.binance.com/docs/derivatives/usds-margined-futures/account/rest-api/Get-Futures-Trade-Download-Link-by-Id + * @param downloadId + * @returns + */ + async futuresTradeDownloadLink(downloadId) { + return await this.privateFuturesRequest('v1/trade/asyn/id', { downloadId }); + } + + // futures websockets support: ticker bookTicker miniTicker aggTrade markPrice + /* TODO: https://binance-docs.github.io/apidocs/futures/en/#change-log + Cancel multiple orders DELETE /fapi/v1/batchOrders + New Future Account Transfer POST https://api.binance.com/sapi/v1/futures/transfer + Get Postion Margin Change History (TRADE) + + wss://fstream.binance.com/ws/ + Diff. Book Depth Streams (250ms, 100ms, or realtime): @depth OR @depth@100ms OR @depth@0ms + Partial Book Depth Streams (5, 10, 20): @depth OR @depth@100ms + All Market Liquidation Order Streams: !forceOrder@arr + Liquidation Order Streams for specific symbol: @forceOrder + Chart data (250ms): @kline_ + SUBSCRIBE, UNSUBSCRIBE, LIST_SUBSCRIPTIONS, SET_PROPERTY, GET_PROPERTY + Live Subscribing/Unsubscribing to streams: requires sending futures subscription id when connecting + futuresSubscriptions { "method": "LIST_SUBSCRIPTIONS", "id": 1 } + futuresUnsubscribe { "method": "UNSUBSCRIBE", "params": [ "btcusdt@depth" ], "id": 1 } + futures depthCache + */ + + /* + const futuresOrder = (side, symbol, quantity, price = 0, flags: Dict = {}, callback = false) => { + let opt = { + symbol: symbol, + side: side, + type: 'LIMIT', + quantity: quantity + }; + if (typeof flags.type !== 'undefined') opt.type = flags.type; + if (opt.type.includes('LIMIT')) { + opt.price = price; + opt.timeInForce = 'GTC'; + } + if (typeof flags.timeInForce !== 'undefined') opt.timeInForce = flags.timeInForce; + signedRequest(`${fapi}v1/order`, opt, function (error, response) { + if (!response) { + if (callback) return callback(error, response); + else return this.options.log('futuresOrder error:', error); + } + if (callback) return callback(error, response); + else return this.options.log(`futuresOrder ${side} (${symbol},${quantity},${price})`, response); + }, 'POST'); + };*/ + + //** Delivery methods */ + + async deliveryPing(params: Dict = {}) { + return await this.publicDeliveryRequest('v1/ping', params); + } + + async deliveryTime(params: Dict = {}) { + return await this.publicDeliveryRequest('v1/time', params); + } + + async deliveryExchangeInfo(params: Dict = {}) { + return await this.publicDeliveryRequest('v1/exchangeInfo', params); + } + + async deliveryPrices(params: Dict = {}) { + const data = await this.publicDeliveryRequest('v1/ticker/price', params); + return data.reduce((out, i) => ((out[i.symbol] = i.price), out), {}); + } + + async deliveryDaily(symbol?: string, params: Dict = {}) { + if (symbol) params.symbol = symbol; + const data = await this.publicDeliveryRequest('v1/ticker/24hr', params); + return symbol ? data : data.reduce((out, i) => ((out[i.symbol] = i), out), {}); + } + + async deliveryOpenInterest(symbol: string, params: Dict = {}) { + params.symbol = symbol; + const res = await this.publicDeliveryRequest('v1/openInterest', params); + return res; + } + + async deliveryCandles(symbol: string, interval: Interval = "30m", params: Dict = {}): Promise { + params.symbol = symbol; + params.interval = interval; + return await this.publicDeliveryRequest('v1/klines', params); + } + + async deliveryContinuousKlines(pair: string, contractType = "CURRENT_QUARTER", interval: Interval = "30m", params: Dict = {}) { + params.pair = pair; + params.interval = interval; + params.contractType = contractType; + return await this.publicDeliveryRequest('v1/continuousKlines', params); + } + + async deliveryIndexKlines(pair: string, interval: Interval = "30m", params: Dict = {}) { + params.pair = pair; + params.interval = interval; + return await this.publicDeliveryRequest('v1/indexPriceKlines', params); + } + + async deliveryMarkPriceKlines(symbol: string, interval: Interval = "30m", params: Dict = {}) { + params.symbol = symbol; + params.interval = interval; + return await this.publicDeliveryRequest('v1/markPriceKlines', params); + } + + async deliveryMarkPrice(symbol?: string, params: Dict = {}) { + if (symbol) params.symbol = symbol; + return await this.publicDeliveryRequest('v1/premiumIndex', params); + } + + async deliveryTrades(symbol: string, params: Dict = {}) { + params.symbol = symbol; + return await this.publicDeliveryRequest('v1/trades', params); + } + + async deliveryHistoricalTrades(symbol: string, params: Dict = {}) { + params.symbol = symbol; + return await this.publicDeliveryRequest('v1/historicalTrades', params); + } + + async deliveryAggTrades(symbol: string, params: Dict = {}) { + params.symbol = symbol; + return await this.publicDeliveryRequest('v1/aggTrades', params); + } + + async deliveryUserTrades(symbol: string, params: Dict = {}) { + params.symbol = symbol; + return await this.privateDeliveryRequest('v1/userTrades', params); + } + + async deliveryCommissionRate(symbol: string, params: Dict = {}) { + if (symbol) params.symbol = symbol; + return await this.privateDeliveryRequest('v1/commissionRate', params); + } + + async deliveryGetDataStream(params: Dict = {}) { + //A User Data Stream listenKey is valid for 60 minutes after creation. setInterval + return await this.privateDeliveryRequest('v1/listenKey', params, 'POST'); + } + + async deliveryKeepDataStream(params: Dict = {}) { + return await this.privateDeliveryRequest('v1/listenKey', params, 'PUT'); + } + + async deliveryCloseDataStream(params: Dict = {}) { + return await this.privateDeliveryRequest('v1/listenKey', params, 'DELETE'); + } + + async deliveryLiquidationOrders(symbol?: string, params: Dict = {}) { + if (symbol) params.symbol = symbol; + return await this.publicDeliveryRequest('v1/allForceOrders', params); + } + + async deliveryPositionRisk(params: Dict = {}) { + return await this.privateDeliveryRequest('v1/positionRisk', params); + } + + async deliveryLeverageBracket(symbol?: string, params: Dict = {}) { + if (symbol) params.symbol = symbol; + return await this.privateDeliveryRequest('v1/leverageBracket', params); + } + + async deliveryLeverageBracketSymbols(symbol?: string, params: Dict = {}) { + if (symbol) params.symbol = symbol; + return await this.privateDeliveryRequest('v2/leverageBracket', params); + } + + // leverage 1 to 125 + async deliveryLeverage(symbol: string, leverage: number, params: Dict = {}) { + params.symbol = symbol; + params.leverage = leverage; + return await this.privateDeliveryRequest('v1/leverage', params, 'POST'); + } + + // ISOLATED, CROSSED + async deliveryMarginType(symbol: string, marginType: string, params: Dict = {}) { + params.symbol = symbol; + params.marginType = marginType; + return await this.privateDeliveryRequest('v1/marginType', params, 'POST'); + } + + // type: 1: Add postion margin,2: Reduce postion margin + async deliveryPositionMargin(symbol: string, amount: number, type = 1, params: Dict = {}) { + params.symbol = symbol; + params.amount = amount; + params.type = type; + return await this.privateDeliveryRequest('v1/positionMargin', params, 'POST'); + } + + async deliveryPositionMarginHistory(symbol: string, params: Dict = {}) { + params.symbol = symbol; + return await this.privateDeliveryRequest('v1/positionMargin/history', params); + } + + async deliveryIncome(params: Dict = {}) { + return await this.privateDeliveryRequest('v1/income', params); + } + + async deliveryBalance(params: Dict = {}) { + return await this.privateDeliveryRequest('v1/balance', params); + } + + async deliveryAccount(params: Dict = {}) { + return await this.privateDeliveryRequest('v1/account', params); + } + + async deliveryDepth(symbol: string, params: Dict = {}): Promise { + params.symbol = symbol; + const res = await this.publicDeliveryRequest('v1/depth', params); + return this.parseOrderBook(res, symbol); + } + + async deliveryQuote(symbol?: string, params: Dict = {}) { + if (symbol) params.symbol = symbol; + //let data = await this.promiseRequest( 'v1/ticker/bookTicker', params, {base:dapi} ); + //return data.reduce((out, i) => ((out[i.symbol] = i), out), {}), + const data = await this.publicDeliveryRequest('v1/ticker/bookTicker', params); + return symbol ? data : data.reduce((out, i) => ((out[i.symbol] = i), out), {}); + } + + async deliveryBuy(symbol: string, quantity: number, price: number, params: Dict = {}) { + return await this.deliveryOrder('LIMIT', 'BUY', symbol, quantity, price, params); + } + + async deliverySell(symbol: string, quantity: number, price: number, params: Dict = {}) { + return await this.deliveryOrder('LIMIT', 'SELL', symbol, quantity, price, params); + } + + async deliveryMarketBuy(symbol: string, quantity: number, params: Dict = {}) { + return await this.deliveryOrder('MARKET', 'BUY', symbol, quantity, undefined, params); + } + + async deliveryMarketSell(symbol: string, quantity: number, params: Dict = {}) { + return await this.deliveryOrder('MARKET', 'SELL', symbol, quantity, undefined, params); + } + + // deliveryOrder, // side symbol quantity [price] [params] + + async deliveryOrderStatus(symbol: string, params: Dict = {}) { // Either orderId or origClientOrderId must be sent + params.symbol = symbol; + return await this.privateDeliveryRequest('v1/order', params); + } + + async deliveryCancel(symbol: string, params: Dict = {}) { // Either orderId or origClientOrderId must be sent + params.symbol = symbol; + return await this.privateDeliveryRequest('v1/order', params, 'DELETE'); + } + + async deliveryCancelAll(symbol: string, params: Dict = {}) { + params.symbol = symbol; + return await this.privateDeliveryRequest('v1/allOpenOrders', params, 'DELETE'); + } + + async deliveryCountdownCancelAll(symbol: string, countdownTime = 0, params: Dict = {}) { + params.symbol = symbol; + params.countdownTime = countdownTime; + return await this.privateDeliveryRequest('v1/countdownCancelAll', params, 'POST'); + } + + async deliveryOpenOrders(symbol?: string, params: Dict = {}) { + if (symbol) params.symbol = symbol; + return await this.privateDeliveryRequest('v1/openOrders', params); + } + + async deliveryAllOrders(symbol?: string, params: Dict = {}) { // Get all account orders; active, canceled, or filled. + if (symbol) params.symbol = symbol; + return await this.privateDeliveryRequest('v1/allOrders', params); + } + + async deliveryPositionSideDual(params: Dict = {}) { + return await this.privateDeliveryRequest('v1/positionSide/dual', params); + } + + async deliveryChangePositionSideDual(dualSidePosition, params: Dict = {}) { + params.dualSidePosition = dualSidePosition; + return await this.privateDeliveryRequest('v1/positionSide/dual', params, 'POST'); + } + + //** Margin methods */ + + /** + * Creates an order + * @see https://developers.binance.com/docs/margin_trading/trade/Margin-Account-New-Order + * @param {string} side - BUY or SELL + * @param {string} symbol - the symbol to buy + * @param {numeric} quantity - the quantity required + * @param {numeric} price - the price to pay for each unit + * @param {object} params - additional buy order flags + * @param {string} isIsolated - the isolate margin option + * @return {undefined} + */ + async mgOrder(type: OrderType, side: string, symbol: string, quantity: number, price: number, params: Dict = {}, isIsolated = 'FALSE') { + return await this.marginOrder(type, side, symbol, quantity, price, { ...params, isIsolated }); + } + + /** + * Creates a buy order + * @see https://developers.binance.com/docs/margin_trading/trade/Margin-Account-New-Order + * @param {string} symbol - the symbol to buy + * @param {numeric} quantity - the quantity required + * @param {numeric} price - the price to pay for each unit + * @param {object} params - additional buy order flags + * @param {string} isIsolated - the isolate margin option + * @return {undefined} + */ + async mgBuy(symbol: string, quantity: number, price: number, params: Dict = {}, isIsolated = 'FALSE') { + return await this.marginOrder('LIMIT', 'BUY', symbol, quantity, price, { ...params, isIsolated }); + } + + /** + * Creates a sell order + * @see https://developers.binance.com/docs/margin_trading/trade/Margin-Account-New-Order + * @param {string} symbol - the symbol to sell + * @param {numeric} quantity - the quantity required + * @param {numeric} price - the price to sell each unit for + * @param {object} flags - additional order flags + * @param {string} isIsolated - the isolate margin option + * @return {undefined} + */ + async mgSell(symbol: string, quantity: number, price: number, flags: Dict = {}, isIsolated = 'FALSE') { + return await this.marginOrder('LIMIT', 'SELL', symbol, quantity, price, { ...flags, isIsolated }); + } + + /** + * Creates a market buy order + * @see https://developers.binance.com/docs/margin_trading/trade/Margin-Account-New-Order + * @param {string} symbol - the symbol to buy + * @param {numeric} quantity - the quantity required + * @param {object} flags - additional buy order flags + * @param {string} isIsolated - the isolate margin option + * @return {undefined} + */ + async mgMarketBuy(symbol: string, quantity: number, params: Dict = {}, isIsolated = 'FALSE') { + return await this.marginOrder('MARKET', 'BUY', symbol, quantity, 0, { ...params, isIsolated }); + } + + /** + * Creates a market sell order + * @see https://developers.binance.com/docs/margin_trading/trade/Margin-Account-New-Order + * @param {string} symbol - the symbol to sell + * @param {numeric} quantity - the quantity required + * @param {object} flags - additional sell order flags + * @param {string} isIsolated - the isolate margin option + * @return {undefined} + */ + async mgMarketSell(symbol: string, quantity: number, params: Dict = {}, isIsolated = 'FALSE') { + return await this.marginOrder('MARKET', 'SELL', symbol, quantity, 0, { ...params, isIsolated }); + } + + /** + * Cancels an order + * @param {string} symbol - the symbol to cancel + * @param {string} orderid - the orderid to cancel + * @return {undefined} + */ + async mgCancel(symbol: string, orderid: number | string, isIsolated = 'FALSE'): Promise { + return await this.privateSpotRequest('v1/margin/order', { symbol: symbol, orderId: orderid, isIsolated }, 'DELETE'); + } + + /** + * Gets all order of a given symbol + * @param {string} symbol - the symbol + * @param {object} options - additional options + * @return {promise or undefined} - omitting the callback returns a promise + */ + async mgAllOrders(symbol: string, params: Dict = {}): Promise { + const parameters = Object.assign({ symbol: symbol }, params); + return await this.privateSpotRequest('v1/margin/allOrders', parameters); + } + + /** + * Gets the status of an order + * @param {string} symbol - the symbol to check + * @param {string} orderid - the orderid to check + * @param {object} flags - any additional flags + * @return {undefined} + */ + async mgOrderStatus(symbol: string, orderid: number | string, flags = {}): Promise { + const parameters = Object.assign({ symbol: symbol, orderId: orderid }, flags); + return await this.privateSpotRequest('v1/margin/order', parameters); + } + + /** + * Gets open orders + * @param {string} symbol - the symbol to get + * @return {undefined} + */ + async mgOpenOrders(symbol?: string, params: Dict = {}): Promise { + if (symbol) params.symbol = symbol; + return await this.privateSpotRequest('v1/margin/openOrders', params); + } + + /** + * Cancels all order of a given symbol + * @param {string} symbol - the symbol to cancel all orders for + * @return {undefined} + */ + async mgCancelOrders(symbol: string, params: Dict = {}) { + // signedRequest(this.sapi + 'v1/margin/openOrders', { symbol: symbol }, function (error, json) { + // if (json.length === 0) { + // if (callback) return callback.call(this, 'No orders present for this symbol', {}, symbol); + // } + // for (let obj of json) { + // let quantity = obj.origQty - obj.executedQty; + // this.options.log('cancel order: ' + obj.side + ' ' + symbol + ' ' + quantity + ' @ ' + obj.price + ' #' + obj.orderId); + // signedRequest(this.sapi + 'v1/margin/order', { symbol: symbol, orderId: obj.orderId }, function (error, data) { + // if (callback) return callback.call(this, error, data, symbol); + // }, 'DELETE'); + // } + // }); // to do check this + return await this.privateSpotRequest('v1/margin/openOrders', this.extend({ symbol: symbol }, params), 'DELETE'); + } + + /** + * Transfer from main account to margin account + * @param {string} asset - the asset + * @param {number} amount - the asset + * @param {object} options - additional options + * @return {undefined} + */ + async mgTransferMainToMargin(asset: string, amount: number, params: Dict = {}) { + params = this.extend({ asset: asset, amount: amount, type: 1 }, params); + return await this.privateSpotRequest('v1/margin/transfer', params, 'POST'); + } + + /** + * Transfer from margin account to main account + * @param {string} asset - the asset + * @param {number} amount - the asset + * @return {undefined} + */ + async mgTransferMarginToMain(asset: string, amount: number, params: Dict = {}) { + const parameters = Object.assign({ asset: asset, amount: amount, type: 2 }); + return await this.privateSpotRequest('v1/margin/transfer', this.extend(parameters, params), 'POST'); + } + // /** + // * Universal Transfer requires API permissions enabled + // * @param {string} type - ENUM , example MAIN_UMFUTURE for SPOT to USDT futures, see https://binance-docs.github.io/apidocs/spot/en/#user-universal-transfer + // * @param {string} asset - the asset - example :USDT + // * @param {number} amount - the callback function + // (optionnal) + // * @return {promise} + // */ + // universalTransfer: (type, asset, amount, callback) => + // universalTransfer(type, asset, amount, callback), + + /** + * Get trades for a given symbol - margin account + * @param {string} symbol - the symbol + * @param {object} options - additional options + * @return {promise or undefined} - omitting the callback returns a promise + */ + async mgTrades(symbol: string, params: Dict = {}): Promise { + const parameters = Object.assign({ symbol: symbol }, params); + return await this.privateSpotRequest('v1/margin/myTrades', parameters); + } + + /** + * Transfer from main account to delivery account + * @param {string} asset - the asset + * @param {number} amount - the asset + * @param {object} options - additional options + * @return {undefined} + */ + async transferMainToFutures(asset: string, amount: number) { + return await this.transferBetweenMainAndFutures(asset, amount, 1); + } + + /** + * Transfer from delivery account to main account + * @param {string} asset - the asset + * @param {number} amount - the asset + (optionnal) + * @return {undefined} + */ + async transferFuturesToMain(asset: string, amount: number) { + return await this.transferBetweenMainAndFutures(asset, amount, 2); + } + + /** + * Transfer from main account to delivery account + * @param {string} asset - the asset + * @param {number} amount - the asset + (optionnal) + * @param {object} options - additional options + * @return {undefined} + */ + async transferMainToDelivery(asset: string, amount: number) { + return await this.transferBetweenMainAndFutures(asset, amount, 3); + } + + /** + * Transfer from delivery account to main account + * @param {string} asset - the asset + * @param {number} amount - the asset + * @return {undefined} + */ + async transferDeliveryToMain(asset: string, amount: number) { + return await this.transferBetweenMainAndFutures(asset, amount, 4); + } + + /** + * Get maximum transfer-out amount of an asset + * @param {string} asset - the asset + * @return {undefined} + */ + async maxTransferable(asset: string) { + return await this.privateSpotRequest('v1/margin/maxTransferable', { asset: asset }); + } + + /** + * Margin account borrow/loan + * @param {string} asset - the asset + * @param {number} amount - the asset + * @param {string} isIsolated - the isolated option + * @param {string} symbol - symbol for isolated margin + * @return {undefined} + */ + async mgBorrow(asset: string, amount: number, isIsolated = 'FALSE', symbol?: string, params: Dict = {}) { + const parameters = Object.assign({ asset: asset, amount: amount }); + if (isIsolated === 'TRUE' && !symbol) throw new Error('If "isIsolated" = "TRUE", "symbol" must be sent'); + const isolatedObj = isIsolated === 'TRUE' ? { + isIsolated, + symbol + } : {}; + return await this.privateSpotRequest('v1/margin/loan', this.extend({ ...parameters, ...isolatedObj }, params), 'POST'); + } + + /** + * Margin account borrow/loan + * @param {string} asset - the asset + * @param {object} options - additional options + * @return {undefined} + */ + async mgQueryLoan(asset: string, options) { + const parameters = Object.assign({ asset: asset }, options); + return await this.privateSpotRequest('v1/margin/loan', { ...parameters }, 'GET'); + } + + /** + * Margin account repay + * @param {string} asset - the asset + * @param {object} params - additional options + * @return {undefined} + */ + async mgQueryRepay(asset: string, params: Dict = {}) { + const parameters = Object.assign({ asset: asset }, params); + return await this.privateSpotRequest('v1/margin/repay', { ...parameters }, 'GET'); + } + + /** + * Margin account repay + * @param {string} asset - the asset + * @param {number} amount - the asset + * @param {string} isIsolated - the isolated option + * @param {string} symbol - symbol for isolated margin + * @return {undefined} + */ + async mgRepay(asset: string, amount: number, isIsolated = 'FALSE', symbol?: string, params: Dict = {}) { + const parameters = Object.assign({ asset: asset, amount: amount }); + if (isIsolated === 'TRUE' && !symbol) throw new Error('If "isIsolated" = "TRUE", "symbol" must be sent'); + const isolatedObj = isIsolated === 'TRUE' ? { + isIsolated, + symbol + } : {}; + return await this.privateSpotRequest('v1/margin/repay', this.extend({ ...parameters, ...isolatedObj }, params), 'POST'); + } + + /** + * Margin account details + * @param {boolean} isIsolated - the callback function + * @return {undefined} + */ + async mgAccount(isIsolated = false, params: Dict = {}) { + let endpoint = 'v1/margin'; + endpoint += (isIsolated) ? '/isolated' : '' + '/account'; + return await this.privateSpotRequest(endpoint, params); + } + /** + * Get maximum borrow amount of an asset + * @param {string} asset - the asset + * @return {undefined} + */ + async maxBorrowable(asset: string, params: Dict = {}) { + params.asset = asset; + return await this.privateSpotRequest('v1/margin/maxBorrowable', params); + } + + // // Futures WebSocket Functions: + // /** + // * Subscribe to a single futures websocket + // * @param {string} url - the futures websocket endpoint + // * @param {function} callback - optional execution callback + // * @param {object} params - Optional reconnect {boolean} (whether to reconnect on disconnect), openCallback {function}, id {string} + // * @return {WebSocket} the websocket reference + // */ + // async this.futuresSubscribeSingle(url, callback: Callback, params: Dict = {}) { + // return this.futuresSubscribeSingle(url, callback, params); + // } + + // /** + // * Subscribe to a combined futures websocket + // * @param {string} streams - the list of websocket endpoints to connect to + // * @param {function} callback - optional execution callback + // * @param {object} params - Optional reconnect {boolean} (whether to reconnect on disconnect), openCallback {function}, id {string} + // * @return {WebSocket} the websocket reference + // */ + // futuresSubscribe(streams, callback: Callback, params: Dict = {}) { + // return futuresSubscribe(streams, callback, params); + // } + + /** + * Returns the known futures websockets subscriptions + * @return {array} array of futures websocket subscriptions + */ + getFuturesSubscriptions() { + return this.futuresSubscriptions; + } + + // /** + // * Terminates a futures websocket + // * @param {string} endpoint - the string associated with the endpoint + // * @return {undefined} + // */ + // // futuresTerminate(endpoint) { + // // if (this.options.verbose) this.options.log('Futures WebSocket terminating:', endpoint); + // // return futuresTerminate(endpoint); + // // } + + /** + * Futures WebSocket aggregated trades + * @param {array/string} symbols - an array or string of symbols to query + * @param {function} callback - callback function + * @return {string} the websocket endpoint + */ + futuresAggTradeStream(symbols: string[] | string, callback: Callback) { + const reconnect = () => { + if (this.Options.reconnect) this.futuresAggTradeStream(symbols, callback); + }; + let subscription; + const cleanCallback = data => callback(this.fAggTradeConvertData(data)); + if (Array.isArray(symbols)) { + if (!this.isArrayUnique(symbols)) throw Error('futuresAggTradeStream: "symbols" cannot contain duplicate elements.'); + const streams = symbols.map(symbol => symbol.toLowerCase() + '@aggTrade'); + subscription = this.futuresSubscribe(streams, cleanCallback, { reconnect }); + } else { + const symbol = symbols as string; + subscription = this.futuresSubscribeSingle(symbol.toLowerCase() + '@aggTrade', cleanCallback, { reconnect }); + } + return (subscription as any).url; + } + + /** + * Futures WebSocket mark price + * @param {symbol} symbol name or false. can also be a callback + * @param {function} callback - callback function + * @param {string} speed - 1 second updates. leave blank for default 3 seconds + * @return {string} the websocket endpoint + */ + futuresMarkPriceStream(symbol?: string, callback = console.log, speed = '@1s') { + const reconnect = () => { + if (this.Options.reconnect) this.futuresMarkPriceStream(symbol, callback, speed); + }; + const endpoint = symbol ? `${symbol.toLowerCase()}@markPrice` : '!markPrice@arr'; + const subscription = this.futuresSubscribeSingle(endpoint + speed, data => callback(this.fMarkPriceConvertData(data)), { reconnect }); + return (subscription as any).url; + } + + /** + * Futures WebSocket liquidations stream + * @param {symbol} symbol name or false. can also be a callback + * @param {function} callback - callback function + * @return {string} the websocket endpoint + */ + futuresLiquidationStream(symbol?: string, callback = console.log) { + const reconnect = () => { + if (this.Options.reconnect) this.futuresLiquidationStream(symbol, callback); + }; + const endpoint = symbol ? `${symbol.toLowerCase()}@forceOrder` : '!forceOrder@arr'; + const subscription = this.futuresSubscribeSingle(endpoint, data => callback(this.fLiquidationConvertData(data)), { reconnect }); + return (subscription as any).url; + } + + /** + * Futures WebSocket prevDay ticker + * @param {symbol} symbol name or false. can also be a callback + * @param {function} callback - callback function + * @return {string} the websocket endpoint + */ + futuresTickerStream(symbol?: string, callback = console.log) { + const reconnect = () => { + if (this.Options.reconnect) this.futuresTickerStream(symbol, callback); + }; + const endpoint = symbol ? `${symbol.toLowerCase()}@ticker` : '!ticker@arr'; + const subscription = this.futuresSubscribeSingle(endpoint, data => callback(this.fTickerConvertData(data)), { reconnect }); + return (subscription as any).url; + } + + /** + * Futures WebSocket miniTicker + * @param {symbol} symbol name or false. can also be a callback + * @param {function} callback - callback function + * @return {string} the websocket endpoint + */ + futuresMiniTickerStream(symbol?: string, callback: Callback = console.log) { + const reconnect = () => { + if (this.Options.reconnect) this.futuresMiniTickerStream(symbol, callback); + }; + const endpoint = symbol ? `${symbol.toLowerCase()}@miniTicker` : '!miniTicker@arr'; + const subscription = this.futuresSubscribeSingle(endpoint, data => callback(this.fMiniTickerConvertData(data)), { reconnect }); + return (subscription as any).url; + } + + /** + * Futures WebSocket bookTicker + * @param {symbol} symbol name or false. can also be a callback + * @param {function} callback - callback function + * @return {string} the websocket endpoint + */ + futuresBookTickerStream(symbol?: string, callback = console.log) { + const reconnect = () => { + if (this.Options.reconnect) this.futuresBookTickerStream(symbol, callback); + }; + const endpoint = symbol ? `${symbol.toLowerCase()}@bookTicker` : '!bookTicker'; + const subscription = this.futuresSubscribeSingle(endpoint, data => callback(this.fBookTickerConvertData(data)), { reconnect }); + return (subscription as any).url; + } + + /** + * Websocket futures klines + * @param {array/string} symbols - an array or string of symbols to query + * @param {string} interval - the time interval + * @param {function} callback - callback function + * @param {int} limit - maximum results, no more than 1000 + * @return {string} the websocket endpoint + */ + futuresChart(symbols: string[] | string, interval: Interval, callback: Callback, limit = 500) { + const reconnect = () => { + if (this.Options.reconnect) this.futuresChart(symbols, interval, callback, limit); + }; + + const futuresChartInit = (symbol: string) => { + if (typeof this.futuresMeta[symbol] === 'undefined') this.futuresMeta[symbol] = {}; + if (typeof this.futuresMeta[symbol][interval] === 'undefined') this.futuresMeta[symbol][interval] = {}; + if (typeof this.futuresTicks[symbol] === 'undefined') this.futuresTicks[symbol] = {}; + if (typeof this.futuresTicks[symbol][interval] === 'undefined') this.futuresTicks[symbol][interval] = {}; + if (typeof this.futuresRealtime[symbol] === 'undefined') this.futuresRealtime[symbol] = {}; + if (typeof this.futuresRealtime[symbol][interval] === 'undefined') this.futuresRealtime[symbol][interval] = {}; + if (typeof this.futuresKlineQueue[symbol] === 'undefined') this.futuresKlineQueue[symbol] = {}; + if (typeof this.futuresKlineQueue[symbol][interval] === 'undefined') this.futuresKlineQueue[symbol][interval] = []; + this.futuresMeta[symbol][interval].timestamp = 0; + }; + + const handleFuturesKlineStream = kline => { + const symbol = kline.s, interval: Interval = kline.k.i; + if (!this.futuresMeta[symbol][interval].timestamp) { + if (typeof (this.futuresKlineQueue[symbol][interval]) !== 'undefined' && kline !== null) { + this.futuresKlineQueue[symbol][interval].push(kline); + } + } else { + //this.options.log('futures klines at ' + kline.k.t); + this.futuresKlineHandler(symbol, kline); + if (callback) callback(symbol, interval, this.futuresKlineConcat(symbol, interval)); + } + }; + + const getFuturesKlineSnapshot = async (symbol: string, limit = 500) => { + const data = await this.publicFuturesRequest('v1/klines', { symbol, interval, limit }); + this.futuresKlineData(symbol, interval, data); + //this.options.log('/futures klines at ' + this.futuresMeta[symbol][interval].timestamp); + if (typeof this.futuresKlineQueue[symbol][interval] !== 'undefined') { + for (const kline of this.futuresKlineQueue[symbol][interval]) this.futuresKlineHandler(symbol, kline, this.futuresMeta[symbol][interval].timestamp); + delete this.futuresKlineQueue[symbol][interval]; + } + if (callback) callback(symbol, interval, this.futuresKlineConcat(symbol, interval)); + }; + + let subscription; + if (Array.isArray(symbols)) { + if (!this.isArrayUnique(symbols)) throw Error('futuresChart: "symbols" array cannot contain duplicate elements.'); + symbols.forEach(futuresChartInit); + const streams = symbols.map(symbol => `${symbol.toLowerCase()}@kline_${interval}`); + subscription = this.futuresSubscribe(streams, handleFuturesKlineStream, reconnect); + symbols.forEach(element => getFuturesKlineSnapshot(element, limit)); + } else { + const symbol = symbols; + futuresChartInit(symbol); + subscription = this.futuresSubscribeSingle(symbol.toLowerCase() + '@kline_' + interval, handleFuturesKlineStream, { reconnect }); + getFuturesKlineSnapshot(symbol, limit); + } + return (subscription as any).url; + } + + /** + * Websocket futures candlesticks + * @param {array/string} symbols - an array or string of symbols to query + * @param {string} interval - the time interval + * @param {function} callback - callback function + * @return {string} the websocket endpoint + */ + futuresCandlesticksStream(symbols: string[] | string, interval: Interval, callback: Callback) { + const reconnect = () => { + if (this.Options.reconnect) this.futuresCandlesticksStream(symbols, interval, callback); + }; + let subscription; + if (Array.isArray(symbols)) { + if (!this.isArrayUnique(symbols)) throw Error('futuresCandlesticks: "symbols" array cannot contain duplicate elements.'); + const streams = symbols.map(symbol => symbol.toLowerCase() + '@kline_' + interval); + subscription = this.futuresSubscribe(streams, callback, { reconnect }); + } else { + const symbol = symbols.toLowerCase(); + subscription = this.futuresSubscribeSingle(symbol + '@kline_' + interval, callback, { reconnect }); + } + return (subscription as any).url; + } + + // Delivery WebSocket Functions: + /** + * Subscribe to a single delivery websocket + * @param {string} url - the delivery websocket endpoint + * @param {function} callback - optional execution callback + * @param {object} params - Optional reconnect {boolean} (whether to reconnect on disconnect), openCallback {function}, id {string} + * @return {WebSocket} the websocket reference + */ + // deliverySubscribeSingle(url, callback: Callback, params: Dict = {}) { + // return deliverySubscribeSingle(url, callback, params); + // } + + // /** + // * Subscribe to a combined delivery websocket + // * @param {string} streams - the list of websocket endpoints to connect to + // * @param {function} callback - optional execution callback + // * @param {object} params - Optional reconnect {boolean} (whether to reconnect on disconnect), openCallback {function}, id {string} + // * @return {WebSocket} the websocket reference + // */ + // deliverySubscribe(streams, callback: Callback, params: Dict = {}) { + // return deliverySubscribe(streams, callback, params); + // } + + /** + * Returns the known delivery websockets subscriptions + * @return {array} array of delivery websocket subscriptions + */ + getDeliverySubscriptions() { + return this.deliverySubscriptions; + } + + // /** + // * Terminates a delivery websocket + // * @param {string} endpoint - the string associated with the endpoint + // * @return {undefined} + // */ + // deliveryTerminate(endpoint) { + // if (this.options.verbose) this.options.log('Delivery WebSocket terminating:', endpoint); + // return deliveryTerminate(endpoint); + // } + + /** + * Delivery WebSocket aggregated trades + * @param {array/string} symbols - an array or string of symbols to query + * @param {function} callback - callback function + * @return {string} the websocket endpoint + */ + deliveryAggTradeStream(symbols: string[] | string, callback: Callback) { + const reconnect = () => { + if (this.Options.reconnect) this.deliveryAggTradeStream(symbols, callback); + }; + let subscription; + const cleanCallback = data => callback(this.dAggTradeConvertData(data)); + if (Array.isArray(symbols)) { + if (!this.isArrayUnique(symbols)) throw Error('deliveryAggTradeStream: "symbols" cannot contain duplicate elements.'); + const streams = symbols.map(symbol => symbol.toLowerCase() + '@aggTrade'); + subscription = this.deliverySubscribe(streams, cleanCallback, { reconnect }); + } else { + const symbol = symbols; + subscription = this.deliverySubscribeSingle(symbol.toLowerCase() + '@aggTrade', cleanCallback, { reconnect }); + } + return (subscription as any).url; + } + + /** + * Delivery WebSocket mark price + * @param {symbol} symbol name or false. can also be a callback + * @param {function} callback - callback function + * @param {string} speed - 1 second updates. leave blank for default 3 seconds + * @return {string} the websocket endpoint + */ + deliveryMarkPriceStream(symbol?: string, callback = console.log, speed = '@1s') { + const reconnect = () => { + if (this.Options.reconnect) this.deliveryMarkPriceStream(symbol, callback); + }; + const endpoint = symbol ? `${symbol.toLowerCase()}@markPrice` : '!markPrice@arr'; + const subscription = this.deliverySubscribeSingle(endpoint + speed, data => callback(this.dMarkPriceConvertData(data)), { reconnect }); + return (subscription as any).url; + } + + /** + * Delivery WebSocket liquidations stream + * @param {symbol} symbol name or false. can also be a callback + * @param {function} callback - callback function + * @return {string} the websocket endpoint + */ + deliveryLiquidationStream(symbol?: string, callback = console.log) { + const reconnect = () => { + if (this.Options.reconnect) this.deliveryLiquidationStream(symbol, callback); + }; + const endpoint = symbol ? `${symbol.toLowerCase()}@forceOrder` : '!forceOrder@arr'; + const subscription = this.deliverySubscribeSingle(endpoint, data => callback(this.dLiquidationConvertData(data)), { reconnect }); + return (subscription as any).url; + } + + /** + * Delivery WebSocket prevDay ticker + * @param {symbol} symbol name or false. can also be a callback + * @param {function} callback - callback function + * @return {string} the websocket endpoint + */ + deliveryTickerStream(symbol?: string, callback = console.log) { + const reconnect = () => { + if (this.Options.reconnect) this.deliveryTickerStream(symbol, callback); + }; + const endpoint = symbol ? `${symbol.toLowerCase()}@ticker` : '!ticker@arr'; + const subscription = this.deliverySubscribeSingle(endpoint, data => callback(this.dTickerConvertData(data)), { reconnect }); + return (subscription as any).url; + } + + /** + * Delivery WebSocket miniTicker + * @param {symbol} symbol name or false. can also be a callback + * @param {function} callback - callback function + * @return {string} the websocket endpoint + */ + deliveryMiniTickerStream(symbol?: string, callback = console.log) { + const reconnect = () => { + if (this.Options.reconnect) this.deliveryMiniTickerStream(symbol, callback); + }; + const endpoint = symbol ? `${symbol.toLowerCase()}@miniTicker` : '!miniTicker@arr'; + const subscription = this.deliverySubscribeSingle(endpoint, data => callback(this.dMiniTickerConvertData(data)), { reconnect }); + return (subscription as any).url; + } + + /** + * Delivery WebSocket bookTicker + * @param {symbol} symbol name or false. can also be a callback + * @param {function} callback - callback function + * @return {string} the websocket endpoint + */ + deliveryBookTickerStream(symbol?: string, callback = console.log) { + const reconnect = () => { + if (this.Options.reconnect) this.deliveryBookTickerStream(symbol, callback); + }; + const endpoint = symbol ? `${symbol.toLowerCase()}@bookTicker` : '!bookTicker'; + const subscription = this.deliverySubscribeSingle(endpoint, data => callback(this.dBookTickerConvertData(data)), { reconnect }); + return (subscription as any).url; + } + + /** + * Websocket delivery klines + * @param {array/string} symbols - an array or string of symbols to query + * @param {string} interval - the time interval + * @param {function} callback - callback function + * @param {int} limit - maximum results, no more than 1000 + * @return {string} the websocket endpoint + */ + deliveryChart(symbols: string[] | string, interval: Interval, callback: Callback, limit = 500) { + const reconnect = () => { + if (this.Options.reconnect) this.deliveryChart(symbols, interval, callback, limit); + }; + + const deliveryChartInit = symbol => { + if (typeof this.deliveryMeta[symbol] === 'undefined') this.deliveryMeta[symbol] = {}; + if (typeof this.deliveryMeta[symbol][interval] === 'undefined') this.deliveryMeta[symbol][interval] = {}; + if (typeof this.deliveryTicks[symbol] === 'undefined') this.deliveryTicks[symbol] = {}; + if (typeof this.deliveryTicks[symbol][interval] === 'undefined') this.deliveryTicks[symbol][interval] = {}; + if (typeof this.deliveryRealtime[symbol] === 'undefined') this.deliveryRealtime[symbol] = {}; + if (typeof this.deliveryRealtime[symbol][interval] === 'undefined') this.deliveryRealtime[symbol][interval] = {}; + if (typeof this.deliveryKlineQueue[symbol] === 'undefined') this.deliveryKlineQueue[symbol] = {}; + if (typeof this.deliveryKlineQueue[symbol][interval] === 'undefined') this.deliveryKlineQueue[symbol][interval] = []; + this.deliveryMeta[symbol][interval].timestamp = 0; + }; + + const handleDeliveryKlineStream = kline => { + const symbol = kline.s, interval: Interval = kline.k.i; + if (!this.deliveryMeta[symbol][interval].timestamp) { + if (typeof (this.deliveryKlineQueue[symbol][interval]) !== 'undefined' && kline !== null) { + this.deliveryKlineQueue[symbol][interval].push(kline); + } + } else { + //this.options.log('futures klines at ' + kline.k.t); + this.deliveryKlineHandler(symbol, kline); + if (callback) callback(symbol, interval, this.deliveryKlineConcat(symbol, interval)); + } + }; + + const getDeliveryKlineSnapshot = async (symbol: string, limit = 500) => { + const data = await this.publicDeliveryRequest('v1/klines', { symbol, interval, limit }); + this.deliveryKlineData(symbol, interval, data); + //this.options.log('/delivery klines at ' + this.deliveryMeta[symbol][interval].timestamp); + if (typeof this.deliveryKlineQueue[symbol][interval] !== 'undefined') { + for (const kline of this.deliveryKlineQueue[symbol][interval]) this.deliveryKlineHandler(symbol, kline, this.deliveryMeta[symbol][interval].timestamp); + delete this.deliveryKlineQueue[symbol][interval]; + } + if (callback) callback(symbol, interval, this.deliveryKlineConcat(symbol, interval)); + }; + + let subscription; + if (Array.isArray(symbols)) { + if (!this.isArrayUnique(symbols)) throw Error('deliveryChart: "symbols" array cannot contain duplicate elements.'); + symbols.forEach(deliveryChartInit); + const streams = symbols.map(symbol => `${symbol.toLowerCase()}@kline_${interval}`); + subscription = this.deliverySubscribe(streams, handleDeliveryKlineStream, reconnect); + symbols.forEach(element => getDeliveryKlineSnapshot(element, limit)); + } else { + const symbol = symbols; + deliveryChartInit(symbol); + subscription = this.deliverySubscribeSingle(symbol.toLowerCase() + '@kline_' + interval, handleDeliveryKlineStream, reconnect); + getDeliveryKlineSnapshot(symbol, limit); + } + return (subscription as any).url; + } + + /** + * Websocket delivery candlesticks + * @param {array/string} symbols - an array or string of symbols to query + * @param {string} interval - the time interval + * @param {function} callback - callback function + * @return {string} the websocket endpoint + */ + deliveryCandlesticks(symbols: string[] | string, interval: Interval, callback: Callback) { + const reconnect = () => { + if (this.Options.reconnect) this.deliveryCandlesticks(symbols, interval, callback); + }; + let subscription; + if (Array.isArray(symbols)) { + if (!this.isArrayUnique(symbols)) throw Error('deliveryCandlesticks: "symbols" array cannot contain duplicate elements.'); + const streams = symbols.map(symbol => symbol.toLowerCase() + '@kline_' + interval); + subscription = this.deliverySubscribe(streams, callback, { reconnect }); + } else { + const symbol = symbols.toLowerCase(); + subscription = this.deliverySubscribeSingle(symbol + '@kline_' + interval, callback, { reconnect }); + } + return (subscription as any).url; + } + + /** + * Userdata websockets function + * @param {function} all_updates_callback + * @param {function} execution_callback - optional execution callback + * @param {function} subscribed_callback - subscription callback + * @param {function} list_status_callback - status callback + * @return {undefined} + */ + userData(all_updates_callback?: Function, balance_callback?: Callback, execution_callback?: Callback, subscribed_callback?: Callback, list_status_callback?: Callback) { + const reconnect = () => { + if (this.Options.reconnect) this.userData(all_updates_callback, balance_callback, execution_callback, subscribed_callback); + }; + this.apiRequest(this.getSpotUrl() + 'v3/userDataStream', {}, 'POST').then((response: any) => { + this.Options.listenKey = response.listenKey; + const keepAlive = this.spotListenKeyKeepAlive; + const self = this; + setTimeout(async function userDataKeepAlive() { // keepalive + try { + await self.apiRequest(self.getSpotUrl() + 'v3/userDataStream?listenKey=' + self.Options.listenKey, {}, 'PUT'); + setTimeout(userDataKeepAlive, keepAlive); // 30 minute keepalive + } catch (error) { + setTimeout(userDataKeepAlive, 60000); // retry in 1 minute + } + }, keepAlive); // 30 minute keepalive + this.Options.all_updates_callback = all_updates_callback; + this.Options.balance_callback = balance_callback; + this.Options.execution_callback = execution_callback ? execution_callback : balance_callback;//This change is required to listen for Orders + this.Options.list_status_callback = list_status_callback; + const subscription = this.subscribe(this.Options.listenKey, this.userDataHandler, reconnect); + if (subscribed_callback) subscribed_callback(subscription.endpoint); + }); + } + + /** + * Margin Userdata websockets function + * @param {function} all_updates_callback + * @param {function} execution_callback - optional execution callback + * @param {function} subscribed_callback - subscription callback + * @param {function} list_status_callback - status callback + * @return {undefined} + */ + userMarginData(all_updates_callback?: Callback, balance_callback?: Callback, execution_callback?: Callback, subscribed_callback?: Callback, list_status_callback?: Callback) { + const reconnect = () => { + if (this.Options.reconnect) this.userMarginData(balance_callback, execution_callback, subscribed_callback); + }; + this.apiRequest(this.sapi + 'v1/userDataStream', {}, 'POST').then((response: any) => { + this.Options.listenMarginKey = response.listenKey; + + const url = this.sapi + 'v1/userDataStream?listenKey=' + this.Options.listenMarginKey; + const apiRequest = this.apiRequest; + const keepAlive = this.spotListenKeyKeepAlive; + setTimeout(async function userDataKeepAlive() { // keepalive + try { + await apiRequest(url, {}, 'PUT'); + // if (err) setTimeout(userDataKeepAlive, 60000); // retry in 1 minute + setTimeout(userDataKeepAlive, keepAlive); // 30 minute keepalive + } catch (error) { + setTimeout(userDataKeepAlive, 60000); // retry in 1 minute + } + }, keepAlive); // 30 minute keepalive + this.Options.margin_all_updates_callback = all_updates_callback; + this.Options.margin_balance_callback = balance_callback; + this.Options.margin_execution_callback = execution_callback; + this.Options.margin_list_status_callback = list_status_callback; + const subscription = this.subscribe(this.Options.listenMarginKey, this.userMarginDataHandler, reconnect); + if (subscribed_callback) subscribed_callback(subscription.endpoint); + }); + } + + /** + * Future Userdata websockets function + * @param {function} all_updates_callback + * @param {function} margin_call_callback + * @param {function} account_update_callback + * @param {function} order_update_callback + * @param {Function} subscribed_callback - subscription callback + */ + userFutureData(all_updates_callback?: Callback, margin_call_callback?: Callback, account_update_callback?: Callback, order_update_callback?: Callback, subscribed_callback?: Callback, account_config_update_callback?: Callback) { + const url = (this.Options.test) ? this.fapiTest : this.fapi; + + const reconnect = () => { + if (this.Options.reconnect) this.userFutureData(all_updates_callback, margin_call_callback, account_update_callback, order_update_callback, subscribed_callback); + }; + + // const response = await this.apiRequest(url + 'v1/listenKey', {}, 'POST'); + this.apiRequest(url + 'v1/listenKey', {}, 'POST').then((response: any) => { + this.Options.listenFutureKey = response.listenKey; + const self = this; + const keepAlive = this.futuresListenKeyKeepAlive; + setTimeout(async function userDataKeepAlive() { // keepalive + try { + await self.apiRequest(url + 'v1/listenKey?listenKey=' + self.Options.listenFutureKey, {}, 'PUT'); + setTimeout(userDataKeepAlive, keepAlive); // 30 minute keepalive + } catch (error) { + setTimeout(userDataKeepAlive, 60000); // retry in 1 minute + } + }, keepAlive); // 30 minute keepalive + this.Options.futures_all_updates_callback = all_updates_callback; + this.Options.future_margin_call_callback = margin_call_callback; + this.Options.future_account_update_callback = account_update_callback; + this.Options.future_account_config_update_callback = account_config_update_callback; + this.Options.future_order_update_callback = order_update_callback; + const subscription = this.futuresSubscribe(this.Options.listenFutureKey, this.userFutureDataHandler, { reconnect }); + if (subscribed_callback) subscribed_callback(subscription.endpoint); + + }); + // const response = await this.apiRequest(url + 'v1/listenKey', {}, 'POST'); + + } + + /** + * Delivery Userdata websockets function + * @param {function} margin_call_callback + * @param {function} account_update_callback + * @param {function} order_update_callback + * @param {Function} subscribed_callback - subscription callback + */ + userDeliveryData( + margin_call_callback: Callback, + account_update_callback?: Callback, + order_update_callback?: Callback, + subscribed_callback?: Callback + ) { + const url = this.Options.test ? this.dapiTest : this.dapi; + + const reconnect = async () => { + if (this.Options.reconnect) + await this.userDeliveryData( + margin_call_callback, + account_update_callback, + order_update_callback, + subscribed_callback + ); + }; + + this.apiRequest(url + "v1/listenKey", {}, "POST").then((response: any) => { + this.Options.listenDeliveryKey = response.listenKey; + const getDeliveryKey = () => this.Options.listenDeliveryKey; + const self = this; + const keepAlive = this.futuresListenKeyKeepAlive; + setTimeout(async function userDataKeepAlive() { + // keepalive + try { + await self.apiRequest( + url + + "v1/listenKey?listenKey=" + + getDeliveryKey(), + {}, + "PUT" + ); + // function (err: any) { + // if (err) setTimeout(userDataKeepAlive, 60000); + // // retry in 1 minute + setTimeout(userDataKeepAlive, keepAlive); // 30 minute keepalive + } catch (error) { + setTimeout(userDataKeepAlive, 60000); // retry in 1 minute + } + }, keepAlive); // 30 minute keepalive + this.Options.delivery_margin_call_callback = margin_call_callback; + this.Options.delivery_account_update_callback = account_update_callback; + this.Options.delivery_order_update_callback = order_update_callback; + const subscription = this.deliverySubscribe( + this.Options.listenDeliveryKey, + this.userDeliveryDataHandler, + { reconnect } + ); + if (subscribed_callback) subscribed_callback(subscription.endpoint); + + }); + + // } + } + + // /** + // * Subscribe to a generic websocket + // * @param {string} url - the websocket endpoint + // * @param {function} callback - optional execution callback + // * @param {boolean} reconnect - subscription callback + // * @return {WebSocket} the websocket reference + // */ + // // subscribe(url, callback, reconnect = false) { + // // return subscribe(url, callback, reconnect); + // // } + + // /** + // * Subscribe to a generic combined websocket + // * @param {string} url - the websocket endpoint + // * @param {function} callback - optional execution callback + // * @param {boolean} reconnect - subscription callback + // * @return {WebSocket} the websocket reference + // */ + // // subscribeCombined(url, callback, reconnect = false) { + // // return subscribeCombined(url, callback, reconnect); + // // } + + // /** + // * Returns the known websockets subscriptions + // * @return {array} array of web socket subscriptions + // */ + getSubscriptions() { + return this.subscriptions; + } + + // /** + // * Terminates a web socket + // * @param {string} endpoint - the string associated with the endpoint + // * @return {undefined} + // */ + // terminate(endpoint) { + // if (this.options.verbose) this.options.log('WebSocket terminating:', endpoint); + // return this.terminate(endpoint); + // } + + /** + * Websocket depth chart + * @param {array/string} symbols - an array or string of symbols to query + * @param {function} callback - callback function + * @return {string} the websocket endpoint + */ + depthStream(symbols: string[] | string, callback: Callback) { + const reconnect = () => { + if (this.Options.reconnect) this.depthStream(symbols, callback); + }; + let subscription; + if (Array.isArray(symbols)) { + if (!this.isArrayUnique(symbols)) throw Error('depth: "symbols" cannot contain duplicate elements.'); + const streams = symbols.map(function (symbol) { + return symbol.toLowerCase() + '@depth@100ms'; + }); + subscription = this.subscribeCombined(streams, callback, reconnect); + } else { + const symbol = symbols; + subscription = this.subscribe(symbol.toLowerCase() + '@depth@100ms', callback, reconnect); + } + return (subscription as any).url; + } + + /** + * Websocket depth cache + * @param {array/string} symbols - an array or string of symbols to query + * @param {function} callback - callback function + * @param {int} limit - the number of entries + * @return {string} the websocket endpoint + */ + depthCacheStream(symbols: string[] | string, callback: Callback, limit = 500) { + const reconnect = () => { + if (this.Options.reconnect) this.depthCacheStream(symbols, callback, limit); + }; + + const symbolDepthInit = symbol => { + if (typeof this.depthCacheContext[symbol] === 'undefined') this.depthCacheContext[symbol] = {}; + const context = this.depthCacheContext[symbol]; + context.snapshotUpdateId = null; + context.lastEventUpdateId = null; + context.messageQueue = []; + this.depthCache[symbol] = { bids: {}, asks: {}}; + }; + + const assignEndpointIdToContext = (symbol, endpointId) => { + if (this.depthCacheContext[symbol]) { + const context = this.depthCacheContext[symbol]; + context.endpointId = endpointId; + } + }; + + const handleDepthStreamData = depth => { + const symbol = depth.s; + const context = this.depthCacheContext[symbol]; + if (context.messageQueue && !context.snapshotUpdateId) { + context.messageQueue.push(depth); + } else { + try { + this.depthHandler(depth); + } catch (err) { + return this.terminate(context.endpointId, true); + } + if (callback) callback(symbol, this.depthCache[symbol], context); + } + }; + + const getSymbolDepthSnapshot = async (symbol: string, cb: Function) => { + const json = await this.publicSpotRequest('v3/depth', { symbol: symbol, limit: limit }); + json.symbol = symbol; + cb(null, json); + }; + + const updateSymbolDepthCache = json => { + // Get previous store symbol + const symbol = json.symbol; + // Initialize depth cache from snapshot + this.depthCache[symbol] = this.depthData(json); + // Prepare depth cache context + const context = this.depthCacheContext[symbol]; + context.snapshotUpdateId = json.lastUpdateId; + context.messageQueue = context.messageQueue.filter(depth => depth.u > context.snapshotUpdateId); + // Process any pending depth messages + for (const depth of context.messageQueue) { + /* Although sync errors shouldn't ever happen here, we catch and swallow them anyway + just in case. The stream handler function above will deal with broken caches. */ + try { + this.depthHandler(depth); + } catch (err) { + // Do nothing + } + } + delete context.messageQueue; + if (callback) callback(symbol, this.depthCache[symbol]); + }; + + /* If an array of symbols are sent we use a combined stream connection rather. + This is transparent to the developer, and results in a single socket connection. + This essentially eliminates "unexpected response" errors when subscribing to a lot of data. */ + let subscription; + if (Array.isArray(symbols)) { + if (!this.isArrayUnique(symbols)) throw Error('depthCache: "symbols" cannot contain duplicate elements.'); + symbols.forEach(symbolDepthInit); + const streams = symbols.map(function (symbol) { + return symbol.toLowerCase() + `@depth@100ms`; + }); + subscription = this.subscribeCombined(streams, handleDepthStreamData, reconnect, function () { + async.mapLimit(symbols, 50, getSymbolDepthSnapshot, (err, results) => { + if (err) throw err; + results.forEach(updateSymbolDepthCache); + }); + }); + symbols.forEach(s => assignEndpointIdToContext(s, subscription.endpoint)); + } else { + const symbol = symbols; + symbolDepthInit(symbol); + subscription = this.subscribe(symbol.toLowerCase() + `@depth@100ms`, handleDepthStreamData, reconnect, function () { + async.mapLimit([symbol], 1, getSymbolDepthSnapshot, (err, results) => { + if (err) throw err; + results.forEach(updateSymbolDepthCache); + }); + }); + assignEndpointIdToContext(symbol, subscription.endpoint); + } + return (subscription as any).url; + } + + /** + * Clear Websocket depth cache + * @param {String|Array} symbols - a single symbol, or an array of symbols, to clear the cache of + * @returns {void} + */ + clearDepthCache(symbols: string[] | string) { + const symbolsArr = Array.isArray(symbols) ? symbols : [symbols]; + symbolsArr.forEach(thisSymbol => { + delete this.depthCache[thisSymbol]; + }); + } + + /** + * Websocket staggered depth cache + * @param {array/string} symbols - an array of symbols to query + * @param {function} callback - callback function + * @param {int} limit - the number of entries + * @param {int} stagger - ms between each depth cache + * @return {Promise} the websocket endpoint + */ + depthCacheStaggered(symbols: string[] | string, callback: Callback, limit = 100, stagger = 200) { + if (!Array.isArray(symbols)) symbols = [symbols]; + let chain = null; + + symbols.forEach(symbol => { + const promise = () => new Promise(resolve => { + this.depthCacheStream(symbol, callback, limit); + setTimeout(resolve, stagger); + }); + chain = chain ? chain.then(promise) : promise(); + }); + + return chain; + } + + /** + * Websocket aggregated trades + * @param {array/string} symbols - an array or string of symbols to query + * @param {function} callback - callback function + * @return {string} the websocket endpoint + */ + aggTradesStream(symbols: string[] | string, callback: Callback) { + const reconnect = () => { + if (this.Options.reconnect) this.aggTradesStream(symbols, callback); + }; + let subscription; + if (Array.isArray(symbols)) { + if (!this.isArrayUnique(symbols)) throw Error('trades: "symbols" cannot contain duplicate elements.'); + const streams = symbols.map(function (symbol) { + return symbol.toLowerCase() + '@aggTrade'; + }); + subscription = this.subscribeCombined(streams, callback, reconnect); + } else { + const symbol = symbols; + subscription = this.subscribe(symbol.toLowerCase() + '@aggTrade', callback, reconnect); + } + return (subscription as any).url; + } + + /** + * Websocket raw trades + * @param {array/string} symbols - an array or string of symbols to query + * @param {function} callback - callback function + * @return {string} the websocket endpoint + */ + tradesStream(symbols: string[] | string, callback: Callback) { + const reconnect = () => { + if (this.Options.reconnect) this.tradesStream(symbols, callback); + }; + + let subscription; + if (Array.isArray(symbols)) { + if (!this.isArrayUnique(symbols)) throw Error('trades: "symbols" cannot contain duplicate elements.'); + const streams = symbols.map(function (symbol) { + return symbol.toLowerCase() + '@trade'; + }); + subscription = this.subscribeCombined(streams, callback, reconnect); + } else { + const symbol = symbols as string; + subscription = this.subscribe(symbol.toLowerCase() + '@trade', callback, reconnect); + } + return (subscription as any).url; + } + + /** + * Websocket klines + * @param {array/string} symbols - an array or string of symbols to query + * @param {string} interval - the time interval + * @param {function} callback - callback function + * @param {int} limit - maximum results, no more than 1000 + * @return {string} the websocket endpoint + */ + chart(symbols: string[] | string, interval: Interval, callback: Callback, limit = 500) { + const reconnect = () => { + if (this.Options.reconnect) this.chart(symbols, interval, callback, limit); + }; + + const symbolChartInit = symbol => { + if (typeof this.info[symbol] === 'undefined') this.info[symbol] = {}; + if (typeof this.info[symbol][interval] === 'undefined') this.info[symbol][interval] = {}; + if (typeof this.ohlc[symbol] === 'undefined') this.ohlc[symbol] = {}; + if (typeof this.ohlc[symbol][interval] === 'undefined') this.ohlc[symbol][interval] = {}; + if (typeof this.ohlcLatest[symbol] === 'undefined') this.ohlcLatest[symbol] = {}; + if (typeof this.ohlcLatest[symbol][interval] === 'undefined') this.ohlcLatest[symbol][interval] = {}; + if (typeof this.klineQueue[symbol] === 'undefined') this.klineQueue[symbol] = {}; + if (typeof this.klineQueue[symbol][interval] === 'undefined') this.klineQueue[symbol][interval] = []; + this.info[symbol][interval].timestamp = 0; + }; + + const handleKlineStreamData = kline => { + const symbol = kline.s, interval: Interval = kline.k.i; + if (!this.info[symbol][interval].timestamp) { + if (typeof (this.klineQueue[symbol][interval]) !== 'undefined' && kline !== null) { + this.klineQueue[symbol][interval].push(kline); + } + } else { + //this.options.log('@klines at ' + kline.k.t); + this.klineHandler(symbol, kline); + if (callback) callback(symbol, interval, this.klineConcat(symbol, interval)); + } + }; + + const getSymbolKlineSnapshot = async (symbol: string, limit = 500) => { + const data = await this.publicSpotRequest('v3/klines', { symbol: symbol, interval: interval, limit: limit }); + // function (error, data) { + // klineData(symbol, interval, data); + // //this.options.log('/klines at ' +this.info[symbol][interval].timestamp); + // if (typeof this.klineQueue[symbol][interval] !== 'undefined') { + // for (let kline of this.klineQueue[symbol][interval]) klineHandler(symbol, kline, this.info[symbol][interval].timestamp); + // delete this.klineQueue[symbol][interval]; + // } + // if (callback) callback(symbol, interval, this.klineConcat(symbol, interval)); + // } + this.klineData(symbol, interval, data); + if (typeof this.klineQueue[symbol][interval] !== 'undefined') { + for (const kline of this.klineQueue[symbol][interval]) this.klineHandler(symbol, kline, this.info[symbol][interval].timestamp); + delete this.klineQueue[symbol][interval]; + } + if (callback) callback(symbol, interval, this.klineConcat(symbol, interval)); + }; + + let subscription; + if (Array.isArray(symbols)) { + if (!this.isArrayUnique(symbols)) throw Error('chart: "symbols" cannot contain duplicate elements.'); + symbols.forEach(symbolChartInit); + const streams = symbols.map(function (symbol) { + return symbol.toLowerCase() + '@kline_' + interval; + }); + subscription = this.subscribeCombined(streams, handleKlineStreamData, reconnect); + symbols.forEach(element => getSymbolKlineSnapshot(element, limit)); + } else { + const symbol = symbols; + symbolChartInit(symbol); + subscription = this.subscribe(symbol.toLowerCase() + '@kline_' + interval, handleKlineStreamData, reconnect); + getSymbolKlineSnapshot(symbol, limit); + } + return (subscription as any).url; + } + + /** + * Websocket candle sticks + * @param {array/string} symbols - an array or string of symbols to query + * @param {string} interval - the time interval + * @param {function} callback - callback function + * @return {string} the websocket endpoint + */ + candlesticksStream(symbols: string[] | string, interval: Interval, callback: Callback) { + const reconnect = () => { + if (this.Options.reconnect) this.candlesticksStream(symbols, interval, callback); + }; + + /* If an array of symbols are sent we use a combined stream connection rather. + This is transparent to the developer, and results in a single socket connection. + This essentially eliminates "unexpected response" errors when subscribing to a lot of data. */ + let subscription; + if (Array.isArray(symbols)) { + if (!this.isArrayUnique(symbols)) throw Error('candlesticks: "symbols" cannot contain duplicate elements.'); + const streams = symbols.map(function (symbol) { + return symbol.toLowerCase() + '@kline_' + interval; + }); + subscription = this.subscribeCombined(streams, callback, reconnect); + } else { + const symbol = symbols.toLowerCase(); + subscription = this.subscribe(symbol + '@kline_' + interval, callback, reconnect); + } + return (subscription as any).url; + } + + /** + * Websocket mini ticker + * @param {function} callback - callback function + * @return {string} the websocket endpoint + */ + miniTicker(callback) { + const reconnect = () => { + if (this.Options.reconnect) this.miniTicker(callback); + }; + const subscription = this.subscribe('!miniTicker@arr', function (data: any) { + const markets = {}; + for (const obj of data) { + markets[obj.s] = { + close: obj.c, + open: obj.o, + high: obj.h, + low: obj.l, + volume: obj.v, + quoteVolume: obj.q, + eventTime: obj.E + }; + } + callback(markets); + }, reconnect); + return (subscription as any).url; + } + + /** + * Spot WebSocket bookTicker (bid/ask quotes including price & amount) + * @param {symbol} symbol name or false. can also be a callback + * @param {function} callback - callback function + * @return {string} the websocket endpoint + */ + bookTickersStream(symbol?: string, callback = console.log) { + const reconnect = () => { + if (this.Options.reconnect) this.bookTickersStream(symbol, callback); + }; + const endpoint = symbol ? `${symbol.toLowerCase()}@bookTicker` : '!bookTicker'; + const subscription = this.subscribe(endpoint, data => callback(this.fBookTickerConvertData(data)), reconnect); + return (subscription as any).url; + } + + /** + * Websocket prevday percentage + * @param {array/string} symbols - an array or string of symbols to query + * @param {function} callback - callback function + * @param {boolean} singleCallback - avoid call one callback for each symbol in data array + * @return {string} the websocket endpoint + */ + prevDayStream(symbols: string[] | string | undefined, callback?: Callback, singleCallback?: Callback) { + const reconnect = () => { + if (this.Options.reconnect) this.prevDayStream(symbols, callback, singleCallback); + }; + + let subscription; + const prevDayStreamHandler = this.prevDayStreamHandler.bind(this); + // Combine stream for array of symbols + if (Array.isArray(symbols)) { + if (!this.isArrayUnique(symbols)) throw Error('prevDay: "symbols" cannot contain duplicate elements.'); + const streams = symbols.map(function (symbol) { + return symbol.toLowerCase() + '@ticker'; + }); + subscription = this.subscribeCombined(streams, function (data: any) { + prevDayStreamHandler(data, callback); + }, reconnect); + // Raw stream for a single symbol + } else if (symbols) { + const symbol = symbols; + subscription = this.subscribe(symbol.toLowerCase() + '@ticker', function (data: any) { + prevDayStreamHandler(data, callback); + }, reconnect); + // Raw stream of all listed symbols + } else { + subscription = this.subscribe('!ticker@arr', function (data: any) { + if (singleCallback) { + prevDayStreamHandler(data, callback); + } else { + for (const line of data) { + prevDayStreamHandler(line, callback); + } + } + }, reconnect); + } + return (subscription as any).url; + } +} \ No newline at end of file diff --git a/src/types.ts b/src/types.ts new file mode 100644 index 00000000..d90b9e56 --- /dev/null +++ b/src/types.ts @@ -0,0 +1,587 @@ + +// trying to keep them compatible with +// https://github.com/ViewBlock/binance-api-node/blob/master/index.d.ts + +export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' + +export type Interval = + | '1m' + | '3m' + | '5m' + | '15m' + | '30m' + | '1h' + | '2h' + | '4h' + | '6h' + | '8h' + | '12h' + | '1d' + | '3d' + | '1w' + | '1M' + +export type OrderType = + | 'LIMIT' + | 'MARKET' + | 'STOP' + | 'STOP_MARKET' + | 'TAKE_PROFIT' + | 'TAKE_PROFIT_MARKET' + | 'LIMIT_MAKER' + | 'TRAILING_STOP_MARKET' + +export type OrderSide = 'BUY' | 'SELL' + +export type OrderStatus = + | 'CANCELED' + | 'EXPIRED' + | 'FILLED' + | 'NEW' + | 'PARTIALLY_FILLED' + | 'PENDING_CANCEL' + | 'REJECTED' + +export type TimeInForce = 'GTC' | 'IOC' | 'FOK' | 'GTE_GTC' | 'GTD' + +export interface Candle { + openTime: number + open: string + high: string + low: string + close: string + volume: string + closeTime: number + quoteVolume: string + trades: number + baseAssetVolume: string + quoteAssetVolume: string + } + +export interface OrderFill { + tradeId: number + price: string + qty: string + commission: string + commissionAsset: string +} + +export interface Order { + clientOrderId: string + cummulativeQuoteQty: string + executedQty: string + fills?: OrderFill[] + icebergQty?: string + isIsolated?: boolean + isWorking: boolean + orderId: number + orderListId: number + origQty: string + price: string + side: OrderSide + status: OrderStatus + stopPrice?: string + symbol: string + time: number + timeInForce: TimeInForce + transactTime?: number + type: OrderType + updateTime: number + } + +export interface FuturesOrder { + clientOrderId: string + cumQty: string + cumQuote: string + executedQty: string + orderId: number + avgPrice: string + origQty: string + price: string + reduceOnly: boolean + side: OrderSide + positionSide: PositionSide + status: OrderStatus + stopPrice: string + closePosition: boolean + symbol: string + timeInForce: TimeInForce + type: OrderType + origType: OrderType + activatePrice: string + priceRate: string + updateTime: number + workingType: WorkingType +} + +export type PositionSide = 'BOTH' | 'SHORT' | 'LONG' + +export type WorkingType = 'MARK_PRICE' | 'CONTRACT_PRICE' + +// export type symbol = string; +// eslint-disable-next-line +export type Callback = (...args: any) => any; + +export interface IConstructorArgs { + APIKEY: string; + APISECRET: string; + PRIVATEKEY: string; // when using RSA/EDCSA keys + PRIVATEKEYPASSWORD: string; // when using RSA/EDCSA keys + recvWindow: number; + useServerTime: boolean; + reconnect: boolean; + test: boolean; + hedgeMode: boolean; + httpsProxy: string; + socksProxy: string; + domain: string; + headers: Record; + // eslint-disable-next-line + log: (...args: any[]) => void; + verbose: boolean; + keepAlive: boolean; + localAddress: boolean; + family: boolean; + urls: Partial<{ + base: string; + wapi: string; + sapi: string; + fapi: string; + fapiTest: string; + stream: string; + combineStream: string; + fstream: string; + fstreamSingle: string; + fstreamTest: string; + fstreamSingleTest: string; + dstream: string; + dstreamSingle: string; + dstreamTest: string; + dstreamSingleTest: string; + }>; + timeOffset: number; +} + +export interface IWebsocketsMethods { + // deprecated, using it for backward compatibility + /* eslint-disable */ + userData(call_updates_callback?: Callback, balance_callback?: Callback, subscribedCallback?: Callback, list_statusCallback?: Callback); + userMarginData(call_updates_callback?: Callback, balance_callback?: Callback, executionCallback?: Callback, subscribedCallback?: Callback, list_statusCallback?: Callback); + depthCacheStaggered(symbols :string |string[], callback?: Callback, limit?: number, stagger?: number); + userFutureData(all_updates_callback?: Callback, margin_callCallback?: Callback, account_updateCallback?: Callback, order_updateCallback?: Callback, subscribedCallback?: Callback); + userDeliveryData(all_updates_callback?: Callback, margin_callCallback?: Callback, account_updateCallback?: Callback, order_updateCallback?: Callback, subscribedCallback?: Callback): any; + subscribeCombined(url: string, callback: Callback, reconnect?: Callback, opened_callback?: Callback); + subscribe(endpoint: string, callback: Callback, reconnect?: Callback, opened_callback?: Callback); + subscriptions(...args: any): any; + futuresSubcriptions(...args: any): any; + deliverySubcriptions(...args: any): any; + terminate(endpoint: string): any; + futuresTerminate(endpoint: string, reconnect?: boolean) + deliveryTerminate(endpoint: string, reconnect?: boolean) + depth(...args: any): any; + depthCache(symbols: string[] | string, callback?: Callback, limit?: number): any; + clearDepthCache(symbols: string | string[]): any; + depthCacheStaggered(symbols: string[] | string, callback: Callback, limit?: number, stagger?: number) + aggTrades(symbols: string | string[], callback: Callback): any; + trades(symbols: string | string[], callback: Callback): string; + chart(symbols: string | string[], interval: Interval, callback?: Callback, limit?: number) + candlesticks(symbols: string | string[], interval: Interval, callback: Callback) + miniTicker(callback: Callback): string; + bookTickers(symbol: string, callback: Callback): string; + prevDay(symbols: string | string[] | undefined, callback?: Callback, singleCallback?: Callback) + futuresCandlesticks(symbols: string[] | string, interval: Interval, callback: Callback) + futuresTicker(symbol?: string, callback?: Callback) + futuresMiniTicker(symbol?: string, callback?: Callback) + futuresAggTrades(symbols: string[] | string, callback: Callback) + futuresMarkPrice(symbol?: string, callback?: Callback, speed?: string) + futuresLiquidation(symbol?: string, callback?: Callback) + futuresTicker(symbol?: string, callback?: Callback) + futuresBookTicker(symbol?: string, callback?: Callback) + futuresChart(symbols: string[] | string, interval: Interval, callback: Callback, limit?: number) + deliveryAggTrade(symbols: string[] | string, callback: Callback) + deliveryMarkPrice(symbol?: string, callback?: Callback, speed?: string) + deliveryLiquidation(symbol?: string, callback?: Callback) + deliveryTicker(symbol?: string, callback?: Callback) + deliveryMiniTicker(symbol?: string, callback?: Callback) + deliveryBookTicker(symbol?: string, callback?: Callback) + deliveryChart(symbols: string[] | string, interval: Interval, callback: Callback, limit?: number) + deliveryCandlesticks(symbols: string[] | string, interval: Interval, callback: Callback) +} + +export interface FundingRate { + symbol: string + fundingRate: string + fundingTime: number + time: number + } + +export interface PositionRisk { + entryPrice: string + marginType: 'isolated' | 'cross' + isAutoAddMargin: string + isolatedMargin: string + leverage: string + liquidationPrice: string + markPrice: string + maxNotionalValue: string + positionAmt: string + symbol: string + unRealizedProfit: string + positionSide: PositionSide + notional: string + isolatedWallet: string + updateTime: number +} + +export interface CancelOrder{ + symbol: string + origClientOrderId: string + orderId: number + orderListId: number + clientOrderId: string + price: string + origQty: string + executedQty: string + cummulativeQuoteQty: string + status: string + timeInForce: string + type: OrderType + side: OrderSide +} + +export interface AggregatedTrade { + aggId: number + symbol: string + price: string + quantity: string + firstId: number + lastId: number + timestamp: number + isBuyerMaker: boolean + wasBestPrice: boolean +} + +export interface Trade { + id: number + price: string + qty: string + quoteQty: string + time: number + isBuyerMaker: boolean + isBestMatch: boolean + } + +export interface MyTrade { + id: number + symbol: string + orderId: number + orderListId: number + price: string + qty: string + quoteQty: string + commission: string + commissionAsset: string + time: number + isBuyer: boolean + isMaker: boolean + isBestMatch: boolean +} + +export type WithdrawStatus = 0 | 1 | 2 | 3 | 4 | 5 | 6 + +export interface WithdrawHistoryResponse { + [index: number]: { + id: string + amount: string + transactionFee: string + address: string + coin: string + txId: string + applyTime: number + status: WithdrawStatus + network: string + transferType?: number + withdrawOrderId?: string + } +} + +export interface DepositHistoryResponse { + [index: number]: { + insertTime: number + amount: string + coin: string + network: string + address: string + txId: string + status: DepositStatus + addressTag?: string + transferType?: number + confirmTimes?: string + } +} + +export interface CancelOrder { + symbol: string + origClientOrderId: string + orderId: number + orderListId: number + clientOrderId: string + price: string + origQty: string + executedQty: string + cummulativeQuoteQty: string + status: string + timeInForce: string + type: OrderType + side: OrderSide +} + +export interface FuturesUserTrade { + buyer: boolean + commission: string + commissionAsset: string + id: number + maker: boolean + orderId: number + price: string + qty: string + quoteQty: string + realizedPnl: string + side: OrderSide + positionSide: PositionSide + symbol: string + time: number + } + +export interface DepositAddress { + address: string + tag: string + coin: string + url: string + } + +export interface WithdrawResponse { + id: string + } + +export type DepositStatus = 0 | 1 + +export interface FuturesCancelAllOpenOrder { + code: number + msg: string +} + +export interface OrderBook { + symbol: string + lastUpdateId: number + asks: Bid[] + bids: Bid[] +} + +export interface Bid { + price: string + quantity: string +} + +export interface BookTicker { + symbol: string + bidPrice: string + bidQty: string + askPrice: string + askQty: string +} + +export interface DailyStats { + symbol: string + priceChange: string + priceChangePercent: string + weightedAvgPrice: string + prevClosePrice: string + lastPrice: string + lastQty: string + bidPrice: string + bidQty: string + askPrice: string + askQty: string + openPrice: string + highPrice: string + lowPrice: string + volume: string + quoteVolume: string + openTime: number + closeTime: number + firstId: number // First tradeId + lastId: number // Last tradeId + count: number // Trade count +} + +export interface Ticker { + eventType: string + eventTime: number + symbol: string + priceChange: string + priceChangePercent: string + weightedAvg: string + prevDayClose: string + curDayClose: string + closeTradeQuantity: string + bestBid: string + bestBidQnt: string + bestAsk: string + bestAskQnt: string + open: string + high: string + low: string + volume: string + volumeQuote: string + openTime: number + closeTime: number + firstTradeId: number + lastTradeId: number + totalTrades: number + } + +// export { +// Interval as interval, +// string as symbol, +// Callback as callback, +// IConstructorArgs +// } + +export type TradingType = 'MARGIN' | 'SPOT' + +export interface Account { + accountType: TradingType + balances: AssetBalance[] + buyerCommission: number + canDeposit: boolean + canTrade: boolean + canWithdraw: boolean + makerCommission: number + permissions: TradingType[] + sellerCommission: number + takerCommission: number + updateTime: number +} + +export interface AssetBalance { + asset: string + free: string + locked: string +} + +export interface FuturesAccountInfo { + feeTier: number + canTrade: boolean + canDeposit: boolean + canWithdraw: boolean + updateTime: number + totalInitialMargin: string + totalMaintMargin: string + totalWalletBalance: string + totalUnrealizedProfit: string + totalMarginBalance: string + totalPositionInitialMargin: string + totalOpenOrderInitialMargin: string + totalCrossWalletBalance: string + totalCrossUnPnl: string + availableBalance: string + maxWithdrawAmount: string + assets: FuturesAsset[] + positions: FuturesAccountPosition[] + } + +export interface FuturesAccountPosition { + symbol: string + initialMargin: string + maintMargin: string + unrealizedProfit: string + positionInitialMargin: string + openOrderInitialMargin: string + leverage: string + isolated: boolean + entryPrice: string + maxNotional: string + positionSide: PositionSide + positionAmt: string + notional: string + isolatedWallet: string + updateTime: number + bidNotional: string + askNotional: string + } + +export type FuturesAssetType = + | 'DOT' + | 'BTC' + | 'SOL' + | 'BNB' + | 'ETH' + | 'ADA' + | 'USDT' + | 'XRP' + | 'BUSD' + +export type FuturesAsset = { + asset: FuturesAssetType + walletBalance: string + unrealizedProfit: string + marginBalance: string + maintMargin: string + initialMargin: string + positionInitialMargin: string + openOrderInitialMargin: string + maxWithdrawAmount: string + crossWalletBalance: string + crossUnPnl: string + availableBalance: string + marginAvailable: boolean + updateTime: number +} + +export interface FuturesBalance { + accountAlias: string + asset: string + balance: string + crossWalletBalance: string + crossUnPnl: string + availableBalance: string + maxWithdrawAmount: string + } + +export interface QueryOrder { + clientOrderId: string + cummulativeQuoteQty: string + executedQty: string + icebergQty: string + isWorking: boolean + orderId: number + orderListId: number + origQty: string + origQuoteOrderQty: string + price: string + side: OrderSide + status: OrderStatus + stopPrice: string + symbol: string + time: number + timeInForce: TimeInForce + type: OrderType + updateTime: number + } + +export interface PremiumIndex { + symbol: string + markPrice: string + indexPrice: string + lastFundingRate: string + nextFundingTime: number + estimatedSettlePrice: string + time: number +} + +export interface OpenInterest { + openInterest: string + symbol: string + time: number +} diff --git a/test.js b/test.js deleted file mode 100644 index 0bb6b49c..00000000 --- a/test.js +++ /dev/null @@ -1,1347 +0,0 @@ -/* ============================================================ - * node-binance-api - * https://github.com/jaggedsoft/node-binance-api - * ============================================================ - * Copyright 2017-, Jon Eyrick - * Released under the MIT License - * ============================================================ */ - -'use strict'; - -const WARN_SHOULD_BE_OBJ = 'should be an object'; -const WARN_SHOULD_BE_NULL = 'should be null'; -const WARN_SHOULD_BE_NOT_NULL = 'should not be null'; -const WARN_SHOULD_HAVE_KEY = 'should have key '; -const WARN_SHOULD_NOT_HAVE_KEY = 'should not have key '; -const WARN_SHOULD_BE_UNDEFINED = 'should be undefined'; -const WARN_SHOULD_BE_TYPE = 'should be a '; -const TIMEOUT = 10000; - -let chai = require('chai'); -let assert = chai.assert; -let path = require('path'); -let Binance = require(path.resolve(__dirname, 'node-binance-api.js')); -let binance = new Binance(); -let util = require('util'); - -let num_pairs = 299; -let num_currencies = 155; - -let logger = { - log: function (msg) { - let logLineDetails = ((new Error().stack).split('at ')[3]).trim(); - let logLineNum = logLineDetails.split(':'); - console.log('DEBUG', logLineNum[1] + ':' + logLineNum[2], msg); - } -} - -let debug = function (x) { - if (typeof (process.env.node_binance_api) === 'undefined') { - return; - } - logger.log(typeof (x)); - logger.log(util.inspect(x)); -} - -let stopSockets = function (log = false) { - //stopSocketsRunning = true; - let endpoints = binance.websockets.subscriptions(); - for (let endpoint in endpoints) { - if (log) console.log('Terminated ws endpoint: ' + endpoint); - binance.websockets.terminate(endpoint); - } -} - -debug('Begin'); - -/*global describe*/ -/*eslint no-undef: "error"*/ -describe('Construct', function () { - /*global it*/ - /*eslint no-undef: "error"*/ - it('Construct the binance object', function (done) { - binance.options({ - APIKEY: '5enQYcMQk2J3syHCao9xgJOnnPoGtDMhSRRAzG2Gxo90TBzXPG1itcXikQc2VRDh', - APISECRET: 'uWJQXigS3AjftKe8c6xK2t3rkTqkmfeeNPwcycBLGXXsuU4eUvLkPY9qcOnB2UYI', - useServerTime: true, - reconnect: false, - verbose: true, - log: debug - }); - assert(typeof (binance) === 'object', 'Binance is not an object'); - done(); - }).timeout(TIMEOUT); -}); - - -describe('UseServerTime', function () { - it('Call use server time', function (done) { - binance.useServerTime(); - done(); - }).timeout(TIMEOUT); -}); - -describe('Prices', function () { - it('Checks the price of BNBBTC', function (done) { - binance.prices('BNBBTC', (error, ticker) => { - debug(error); - debug(ticker); - assert(typeof (error) === 'object', WARN_SHOULD_BE_OBJ); - assert(typeof (ticker) === 'object', WARN_SHOULD_BE_OBJ); - assert(error === null, WARN_SHOULD_BE_NULL); - assert(ticker !== null, WARN_SHOULD_BE_NOT_NULL); - assert(Object.prototype.hasOwnProperty.call(ticker, 'BNBBTC'), WARN_SHOULD_HAVE_KEY + 'BNBBTC'); - assert(Object.prototype.hasOwnProperty.call(ticker, 'ETHBTC') === false, WARN_SHOULD_NOT_HAVE_KEY + 'ETHBTC'); - done(); - }); - }).timeout(TIMEOUT); -}); - -describe('All Prices', function () { - it('Checks the prices of coin pairs', function (done) { - binance.prices((error, ticker) => { - debug(error); - debug(ticker); - assert(typeof (error) === 'object', WARN_SHOULD_BE_OBJ); - assert(typeof (ticker) === 'object', WARN_SHOULD_BE_OBJ); - assert(error === null, WARN_SHOULD_BE_NULL); - assert(ticker !== null, WARN_SHOULD_BE_NOT_NULL); - assert(Object.prototype.hasOwnProperty.call(ticker, 'BNBBTC'), WARN_SHOULD_HAVE_KEY + 'BNBBTC'); - assert(Object.keys(ticker).length >= num_pairs, 'should at least ' + num_pairs + 'currency pairs?'); - done(); - }); - }).timeout(TIMEOUT); -}); - -describe('Balances', function () { - it('Get the balances in the account', function (done) { - binance.balance((error, balances) => { - debug(error); - debug(balances); - assert(error === null, WARN_SHOULD_BE_NULL); - assert(balances !== null, WARN_SHOULD_BE_NOT_NULL); - assert(balances); - assert(Object.prototype.hasOwnProperty.call(balances, 'BNB'), WARN_SHOULD_HAVE_KEY + 'BNB'); - assert(Object.prototype.hasOwnProperty.call(balances.BNB, 'available'), WARN_SHOULD_HAVE_KEY + 'available'); - assert(Object.prototype.hasOwnProperty.call(balances.BNB, 'onOrder'), WARN_SHOULD_HAVE_KEY + 'onOrder'); - assert(Object.keys(balances).length >= num_currencies, 'should at least ' + num_currencies + 'currencies?'); - done(); - }); - }).timeout(TIMEOUT); -}); - -describe('Book Ticker', function () { - it('Get the BNB book ticker', function (done) { - binance.bookTickers('BNBBTC', (error, ticker) => { - debug(error); - debug(ticker); - assert(error === null, WARN_SHOULD_BE_NULL); - assert(ticker !== null, WARN_SHOULD_BE_NOT_NULL); - assert(ticker); - - let members = ['symbol', 'bidPrice', 'bidQty', 'askPrice', 'askQty']; - members.forEach(function (value) { - assert(Object.prototype.hasOwnProperty.call(ticker, value), WARN_SHOULD_HAVE_KEY + value); - }); - done(); - }); - }).timeout(TIMEOUT); - - it('Get all book tickers', function (done) { - binance.bookTickers(false, (error, ticker) => { - assert(ticker); - /* - assert( error === null, WARN_SHOULD_BE_NULL ); - assert( ticker !== null, WARN_SHOULD_BE_NOT_NULL ); - assert( ticker ); - - let members = ['symbol', 'bidPrice', 'bidQty', 'askPrice', 'askQty']; - members.forEach( function( value ) { - assert( Object.prototype.hasOwnProperty.call(ticker, value ), WARN_SHOULD_HAVE_KEY + value ); - }); - */ - done(); - }); - }).timeout(TIMEOUT); -}); - -describe('Booker Tickers', function () { - it('Get the tickers for all pairs', function (done) { - binance.bookTickers((error, ticker) => { - debug(error); - debug(ticker); - assert(typeof (error) === 'object', WARN_SHOULD_BE_OBJ); - assert(typeof (ticker) === 'object', WARN_SHOULD_BE_OBJ); - assert(error === null, WARN_SHOULD_BE_NULL); - assert(ticker !== null, WARN_SHOULD_BE_NOT_NULL); - assert(Object.keys(ticker).length >= num_pairs, 'should at least ' + num_pairs + 'currency pairs?'); - - let members = ['symbol', 'bidPrice', 'bidQty', 'askPrice', 'askQty']; - ticker.forEach(function (obj) { - members.forEach(function (member) { - assert(Object.prototype.hasOwnProperty.call(obj, member), WARN_SHOULD_HAVE_KEY + member); - }); - }); - done(); - }); - }).timeout(TIMEOUT); -}); - -describe('Market', function () { - it('Get the market base symbol of a symbol pair', function (done) { - let tocheck = ['TRXBNB', 'BNBBTC', 'BNBETH', 'BNBUSDT']; - tocheck.forEach(function (element) { - let mark = binance.getMarket(element); - assert(typeof (mark) === 'string', WARN_SHOULD_BE_TYPE + 'string'); - assert(element.endsWith(mark), 'should end with: ' + mark); - }); - - assert.isNotOk(binance.getMarket('ABCDEFG'), WARN_SHOULD_BE_UNDEFINED); - done(); - }).timeout(TIMEOUT); -}); - -describe('Depth chart BNB', function () { - it('Get the depth chart information for BNBBTC', function (done) { - binance.depth('BNBBTC', (error, depth, symbol) => { - debug(error); - debug(depth); - debug(symbol); - assert(error === null, WARN_SHOULD_BE_NULL); - assert(depth !== null, WARN_SHOULD_BE_NOT_NULL); - assert(symbol !== null, WARN_SHOULD_BE_NOT_NULL); - assert(typeof (symbol) === 'string', 'should be type of string'); - assert(symbol === 'BNBBTC', 'should be BNBBTC'); - assert(typeof (depth) === 'object', WARN_SHOULD_BE_OBJ); - assert(Object.keys(depth).length === 3, 'should have length 3'); - - let members = ['lastUpdateId', 'asks', 'bids']; - members.forEach(function (value) { - assert(Object.prototype.hasOwnProperty.call(depth, value), WARN_SHOULD_HAVE_KEY + value); - }); - done(); - }); - }).timeout(TIMEOUT); -}); - -describe('Buy', function () { - it('Attempt to buy ETH', function (done) { - let quantity = 1; - let price = 0.069; - assert(typeof (binance.buy('ETHBTC', quantity, price)) === 'undefined', WARN_SHOULD_BE_UNDEFINED); - done(); - }).timeout(TIMEOUT); -}); - -describe('Sell', function () { - it('Attempt to sell ETH', function (done) { - let quantity = 1; - let price = 0.069; - assert(typeof (binance.sell('ETHBTC', quantity, price)) === 'undefined', WARN_SHOULD_BE_UNDEFINED); - done(); - }).timeout(TIMEOUT); -}); - -describe('MarketBuy', function () { - it('Attempt to buy ETH at market price', function (done) { - let quantity = 1; - assert(typeof (binance.marketBuy('BNBBTC', quantity)) === 'undefined', WARN_SHOULD_BE_UNDEFINED); - done(); - }).timeout(TIMEOUT); -}); - -describe('MarketSell', function () { - it('Attempt to sell ETH at market price', function (done) { - let quantity = 1; - assert(typeof (binance.marketSell('ETHBTC', quantity)) === 'undefined', WARN_SHOULD_BE_UNDEFINED); - done(); - }).timeout(TIMEOUT); -}); - -describe('Buy order advanced', function () { - it('Attempt to buy BNB specifying order type', function (done) { - let type = 'LIMIT'; - let quantity = 1; - let price = 0.069; - assert(typeof (binance.buy('BNBETH', quantity, price, { type: type })) === 'undefined', WARN_SHOULD_BE_UNDEFINED); - done(); - }).timeout(TIMEOUT); -}); - -describe('Sell Stop loess', function () { - it('Attempt to create a stop loss order', function (done) { - let type = 'STOP_LOSS'; - let quantity = 1; - let price = 0.069; - let stopPrice = 0.068; - assert(typeof (binance.sell('ETHBTC', quantity, price, { stopPrice: stopPrice, type: type })) === 'undefined', WARN_SHOULD_BE_UNDEFINED); - done(); - }).timeout(TIMEOUT); -}); - -describe('Iceberg sell order', function () { - it('Attempt to create a sell order', function (done) { - let quantity = 1; - let price = 0.069; - assert(typeof (binance.sell('ETHBTC', quantity, price, { icebergQty: 10 })) === 'undefined', WARN_SHOULD_BE_UNDEFINED); - done(); - }).timeout(TIMEOUT); -}); - -describe('Cancel order', function () { - it('Attempt to cancel an order', function (done) { - let orderid = '7610385'; - binance.cancel('ETHBTC', orderid, (error, response, symbol) => { - debug(error); - debug(response); - debug(symbol); - assert(typeof (error) === 'object', WARN_SHOULD_BE_OBJ); - assert(typeof (response) === 'object', WARN_SHOULD_BE_OBJ); - assert(typeof (symbol) === 'string', WARN_SHOULD_BE_TYPE + 'string'); - assert(symbol === 'ETHBTC'); - assert(error !== null, WARN_SHOULD_BE_NOT_NULL); - assert(response !== null, WARN_SHOULD_BE_NOT_NULL); - assert(error.body === '{"code":-2011,"msg":"UNKNOWN_ORDER"}'); - assert(typeof (response.orderId) === 'undefined', WARN_SHOULD_BE_UNDEFINED); - assert(Object.keys(response).length === 0); - done(); - }); - }).timeout(TIMEOUT); -}); - -describe('Cancel orders', function () { - it('Attempt to cancel all orders given a symbol', function (done) { - binance.cancelOrders('XMRBTC', (error, response, symbol) => { - debug(error); - debug(response); - debug(symbol); - assert(typeof (error) === 'string', WARN_SHOULD_BE_OBJ); - assert(typeof (response) === 'object', WARN_SHOULD_BE_OBJ); - assert(typeof (symbol) === 'string', WARN_SHOULD_BE_TYPE + 'string'); - assert(symbol === 'XMRBTC'); - assert(error !== null, WARN_SHOULD_BE_NOT_NULL); - assert(error === 'No orders present for this symbol', WARN_SHOULD_BE_TYPE + 'string'); - done(); - }); - }).timeout(TIMEOUT); -}); - -describe('Open Orders', function () { - it('Attempt to show all orders to ETHBTC', function (done) { - binance.openOrders('ETHBTC', (error, openOrders, symbol) => { - debug(error); - debug(openOrders); - debug(symbol); - assert(typeof (error) === 'object', WARN_SHOULD_BE_OBJ); - assert(typeof (openOrders) === 'object', WARN_SHOULD_BE_OBJ); - assert(typeof (symbol) === 'string', WARN_SHOULD_BE_TYPE + 'string'); - assert(symbol === 'ETHBTC'); - assert(error === null, WARN_SHOULD_BE_NULL); - assert(openOrders !== null, WARN_SHOULD_BE_NOT_NULL); - assert(symbol !== null, WARN_SHOULD_BE_NOT_NULL); - assert(Object.keys(openOrders).length === 0); - done(); - }); - }).timeout(TIMEOUT); -}); - -describe('Open Orders', function () { - it('Attempt to show all orders for all symbols', function (done) { - binance.openOrders(false, (error, openOrders) => { - debug(error); - debug(openOrders); - assert(typeof (error) === 'object', WARN_SHOULD_BE_OBJ); - assert(typeof (openOrders) === 'object', WARN_SHOULD_BE_OBJ); - assert(error === null, WARN_SHOULD_BE_NULL); - assert(openOrders !== null, WARN_SHOULD_BE_NOT_NULL); - assert(Object.keys(openOrders).length === 0); - done(); - }); - }).timeout(TIMEOUT); -}); - -describe('Order status', function () { - it('Attempt to get the order status for a given order id', function (done) { - binance.orderStatus('ETHBTC', '1234567890', (error, orderStatus, symbol) => { - debug(error); - debug(orderStatus); - debug(symbol); - assert(typeof (error) === 'object', WARN_SHOULD_BE_OBJ); - assert(typeof (orderStatus) === 'object', WARN_SHOULD_BE_OBJ); - assert(typeof (symbol) === 'string', WARN_SHOULD_BE_TYPE + 'string'); - assert(symbol === 'ETHBTC'); - assert(error !== null, WARN_SHOULD_BE_NOT_NULL); - assert(orderStatus !== null, WARN_SHOULD_BE_NOT_NULL); - assert(error.body === '{"code":-2013,"msg":"Order does not exist."}'); - assert(Object.keys(orderStatus).length === 0); - done(); - }); - }).timeout(TIMEOUT); -}); - -describe('trades', function () { - it('Attempt get all trade history for given symbol', function (done) { - binance.trades('SNMBTC', (error, trades, symbol) => { - debug(error); - debug(trades); - debug(symbol); - assert(typeof (error) === 'object', WARN_SHOULD_BE_OBJ); - assert(typeof (trades) === 'object', WARN_SHOULD_BE_OBJ); - assert(typeof (symbol) === 'string', WARN_SHOULD_BE_TYPE + 'string'); - assert(symbol === 'SNMBTC'); - assert(error === null, WARN_SHOULD_BE_NULL); - assert(trades !== null, WARN_SHOULD_BE_NOT_NULL); - assert(Object.keys(trades).length === 0); - done(); - }); - }).timeout(TIMEOUT); -}); - -describe('Orders', function () { - it('Attempt get all orders for given symbol', function (done) { - binance.allOrders('ETHBTC', (error, orders, symbol) => { - debug(error); - debug(orders); - debug(symbol); - assert(typeof (error) === 'object', WARN_SHOULD_BE_OBJ); - assert(typeof (orders) === 'object', WARN_SHOULD_BE_OBJ); - assert(typeof (symbol) === 'string', WARN_SHOULD_BE_TYPE + 'string'); - assert(symbol === 'ETHBTC'); - assert(error === null, WARN_SHOULD_BE_NULL); - assert(orders !== null, WARN_SHOULD_BE_NOT_NULL); - assert(Object.keys(orders).length === 0); - done(); - }); - }).timeout(TIMEOUT); -}); - -describe('Prevday all symbols', function () { - it('Attempt get prevday trade status for all symbols', function (done) { - binance.prevDay(false, (error, prevDay) => { - debug(error); - debug(prevDay); - assert(typeof (error) === 'object', WARN_SHOULD_BE_OBJ); - assert(typeof (prevDay) === 'object', WARN_SHOULD_BE_OBJ); - assert(error === null, WARN_SHOULD_BE_NULL); - assert(prevDay !== null, WARN_SHOULD_BE_NOT_NULL); - assert(Object.keys(prevDay).length >= num_pairs, 'should at least ' + num_pairs + 'currency pairs?'); - - let members = [ - 'symbol', 'priceChange', 'priceChangePercent', 'weightedAvgPrice', 'prevClosePrice', - 'lastPrice', 'lastQty', 'bidPrice', 'bidQty', 'askQty', 'openPrice', 'highPrice', 'lowPrice', - 'volume', 'quoteVolume', 'openTime', 'closeTime', 'firstId', 'lastId', 'count' - ]; - prevDay.forEach(function (obj) { - members.forEach(function (key) { - assert(Object.prototype.hasOwnProperty.call(obj, key), WARN_SHOULD_HAVE_KEY + key); - }); - }); - done(); - }); - }).timeout(TIMEOUT); -}); - -describe('Prevday', function () { - it('Attempt get prevday trade status for given symbol', function (done) { - binance.prevDay('BNBBTC', (error, prevDay, symbol) => { - debug(error); - debug(prevDay); - debug(symbol); - assert(typeof (error) === 'object', WARN_SHOULD_BE_OBJ); - assert(typeof (prevDay) === 'object', WARN_SHOULD_BE_OBJ); - assert(typeof (symbol) === 'string', WARN_SHOULD_BE_TYPE + 'string'); - assert(symbol === 'BNBBTC', 'Should be BNBBTC'); - assert(error === null, WARN_SHOULD_BE_NULL); - assert(prevDay !== null, WARN_SHOULD_BE_NOT_NULL); - - let members = [ - 'symbol', 'priceChange', 'priceChangePercent', 'weightedAvgPrice', 'prevClosePrice', - 'lastPrice', 'lastQty', 'bidPrice', 'bidQty', 'askQty', 'openPrice', 'highPrice', 'lowPrice', - 'volume', 'quoteVolume', 'openTime', 'closeTime', 'firstId', 'lastId', 'count' - ]; - members.forEach(function (key) { - assert(Object.prototype.hasOwnProperty.call(prevDay, key), WARN_SHOULD_HAVE_KEY + key); - }); - done(); - }); - }).timeout(TIMEOUT); -}); - -describe('Candle sticks', function () { - it('Attempt get candlesticks for a given symbol', function (done) { - binance.candlesticks('BNBBTC', '5m', (error, ticks, symbol) => { - debug(error); - debug(ticks); - debug(symbol); - assert(typeof (error) === 'object', WARN_SHOULD_BE_OBJ); - assert(typeof (ticks) === 'object', WARN_SHOULD_BE_OBJ); - assert(typeof (symbol) === 'string', WARN_SHOULD_BE_TYPE + 'string'); - assert(symbol === 'BNBBTC', 'Should be BNBBTC'); - assert(error === null, WARN_SHOULD_BE_NULL); - assert(ticks !== null, WARN_SHOULD_BE_NOT_NULL); - - ticks.forEach(function (tick) { - assert(tick.length === 12); - }); - done(); - }, { - limit: 500, - endTime: 1514764800000 - }); - }).timeout(TIMEOUT); -}); - -describe('Object keys', function () { - describe('First', function () { - it('Gets the first key', function (done) { - let first = binance.first({ first: '1', second: '2', third: '3' }); - assert.strictEqual('first', first, 'should be first'); - done(); - }).timeout(TIMEOUT); - }); - - describe('Last', function () { - it('Gets the last key', function (done) { - let last = binance.last({ first: '1', second: '2', third: '3' }); - assert.strictEqual('third', last, 'should be third'); - done(); - }).timeout(TIMEOUT); - }); - - describe('slice', function () { - it('Gets slice of the object keys', function (done) { - let slice = binance.slice({ first: '1', second: '2', third: '3' }, 2); - assert.deepEqual(['third'], slice, 'should be ian array with the third'); - done(); - }).timeout(TIMEOUT); - }); - - describe('Min', function () { - it('Gets the math min of object', function (done) { - binance.min({ first: '1', second: '2', third: '3' }); - done(); - }).timeout(TIMEOUT); - }); - - describe('Max', function () { - it('Gets the math max of object', function (done) { - binance.max({ first: '1', second: '2', third: '3' }); - done(); - }).timeout(TIMEOUT); - }); -}); - -describe('Set/Get options', function () { - it('Sets/Gets option to specified value', function (done) { - binance.setOption('test', 'value'); - assert.equal(binance.getOption('test'), 'value', 'should be value'); - done(); - }).timeout(TIMEOUT); -}); - -describe('Get options', function () { - it('Gets all options', function (done) { - assert(typeof (binance.getOptions()) === 'object', 'should be object'); - done(); - }).timeout(TIMEOUT); -}); - -describe('Percent', function () { - it('Get Percentage of two values', function (done) { - assert(binance.percent(25, 100) === 25, 'should be 25 percent'); - done(); - }).timeout(TIMEOUT); -}); - -describe('Sum', function () { - it('Get sum of array of values', function (done) { - assert(binance.sum([1, 2, 3]) === 6, 'should be 6'); - done(); - }).timeout(TIMEOUT); -}); - -describe('Reverse', function () { - it('Reverse the keys in an object', function (done) { - assert(binance.reverse({ '3': 3, '2': 2, '1': 1 }).toString() === { '1': 1, '2': 2, '3': 3 }.toString(), 'should be {\'1\': 1, \'2\': 2, \'3\': 3 }'); - done(); - }).timeout(TIMEOUT); -}); - -describe('Array', function () { - it('Convert object to an array', function (done) { - let actual = binance.array({ 'a': 1, 'b': 2, 'c': 3 }); - let expected = [[NaN, 1], [NaN, 2], [NaN, 3]]; - assert.isArray(actual, 'should be an array'); - assert(actual.length === 3, 'should be of lenght 3'); - assert.deepEqual(actual, expected, 'should be both arrays with same vlaues'); - done(); - }).timeout(TIMEOUT); -}); - -describe('sortBids', function () { - it('Sorts symbols bids and returns an object', function (done) { - /* let actual = binance.sortBids( 'BNBBTC' ); - debug( actual ); */ - debug('todo'); - done(); - }); -}); - -describe('sortAsks', function () { - it('Sorts symbols asks and returns an object', function (done) { - //let actual = binance.sortBids( 'BNBBTC' ); - debug('todo'); - done(); - }).timeout(TIMEOUT); -}); - -describe('Exchange Info', function () { - let async_error; - let async_data; - /*global beforeEach*/ - /*eslint no-undef: "error"*/ - beforeEach(function (done) { - binance.exchangeInfo(function (error, data) { - async_error = error; - async_data = data; - done(error); - }) - }); - - it('Gets the exchange info as an object', function () { - assert(typeof (async_error) === 'object', 'error should be object'); - assert(async_error === null, 'Error should be null'); - assert(typeof (async_data) === 'object', 'data should be object'); - assert(async_data !== null, 'data should not be null'); - assert(Object.prototype.hasOwnProperty.call(async_data, 'symbols'), 'data should have property \'symbols\''); - - let symbolMembers = ['status', 'orderTypes', 'icebergAllowed', 'baseAsset', 'baseAssetPrecision', 'quoteAsset', 'quotePrecision']; - async_data.symbols.forEach(function (symbol) { - symbolMembers.forEach(function (member) { - assert(Object.prototype.hasOwnProperty.call(symbol, member), WARN_SHOULD_HAVE_KEY + member); - }); - }); - }).timeout(TIMEOUT); -}); - -describe('System status', function () { - let async_error; - let async_data; - /*global beforeEach*/ - beforeEach(function (done) { - binance.systemStatus(function (error, data) { - async_error = error; - async_data = data; - done(error); - }) - }); - - it('Gets the system status info as an object', function () { - debug(async_error); - debug(async_data); - assert(typeof (async_error) === 'object', 'error should be object'); - assert(async_error === null, 'Error should be null'); - assert(typeof (async_data) === 'object', 'data should be object'); - assert(async_data !== null, 'data should not be null'); - assert(Object.prototype.hasOwnProperty.call(async_data, 'msg'), WARN_SHOULD_HAVE_KEY + 'msg'); - assert(Object.prototype.hasOwnProperty.call(async_data, 'status'), WARN_SHOULD_HAVE_KEY + 'status'); - - let members = ['msg', 'status']; - members.forEach(function (member) { - assert(Object.prototype.hasOwnProperty.call(async_data, member), WARN_SHOULD_HAVE_KEY + member); - }); - }).timeout(TIMEOUT); -}); - -describe('Withdraw', function () { - it('Attempt to withdraw BNB to another address', function (done) { - binance.withdraw('BNBBTC', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', '5', false, (error, result) => { - debug(error); - debug(result); - assert(typeof (error) === 'object', WARN_SHOULD_BE_OBJ); - assert(typeof (result) === 'object', WARN_SHOULD_BE_OBJ); - assert(error === null, WARN_SHOULD_BE_NULL); - assert(result !== null, WARN_SHOULD_BE_NOT_NULL); - assert(Object.prototype.hasOwnProperty.call(result, 'msg'), WARN_SHOULD_HAVE_KEY + 'msg'); - assert(result.msg === 'You don\'t have permission.'); - assert(Object.prototype.hasOwnProperty.call(result, 'success'), WARN_SHOULD_HAVE_KEY + 'success'); - assert(result.success === false); - done(); - }); - }).timeout(TIMEOUT); - - it('Attempt to withdraw BNB to another address with address tag', function (done) { - binance.withdraw('BNBBTC', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', '5', 'AQSWDEFRGT', (error, result) => { - debug(error); - debug(result); - assert(typeof (error) === 'object', WARN_SHOULD_BE_OBJ); - assert(typeof (result) === 'object', WARN_SHOULD_BE_OBJ); - assert(error === null, WARN_SHOULD_BE_NULL); - assert(result !== null, WARN_SHOULD_BE_NOT_NULL); - assert(Object.prototype.hasOwnProperty.call(result, 'msg'), WARN_SHOULD_HAVE_KEY + 'msg'); - assert(result.msg === 'You don\'t have permission.'); - assert(Object.prototype.hasOwnProperty.call(result, 'success'), WARN_SHOULD_HAVE_KEY + 'success'); - assert(result.success === false); - done(); - }); - }).timeout(TIMEOUT); -}); - -describe('Withdraw history', function () { - it('Attempt to get withdraw history for BTC', function (done) { - binance.withdrawHistory((error, result) => { - debug(error); - debug(result); - assert(typeof (error) === 'object', WARN_SHOULD_BE_OBJ); - assert(typeof (result) === 'object', WARN_SHOULD_BE_OBJ); - assert(error === null, WARN_SHOULD_BE_NULL); - assert(result !== null, WARN_SHOULD_BE_NOT_NULL); - assert(Object.prototype.hasOwnProperty.call(result, 'withdrawList'), WARN_SHOULD_HAVE_KEY + 'withdrawList'); - assert(Array.isArray(result.withdrawList)); - assert(Object.prototype.hasOwnProperty.call(result, 'success'), WARN_SHOULD_HAVE_KEY + 'success'); - assert(result.success === true); - done(); - }, 'BTC'); - }).timeout(TIMEOUT); - - it('Attempt to get withdraw history for all assets', function (done) { - binance.withdrawHistory((error, result) => { - debug(error); - debug(result); - assert(typeof (error) === 'object', WARN_SHOULD_BE_OBJ); - assert(typeof (result) === 'object', WARN_SHOULD_BE_OBJ); - assert(error === null, WARN_SHOULD_BE_NULL); - assert(result !== null, WARN_SHOULD_BE_NOT_NULL); - assert(Object.prototype.hasOwnProperty.call(result, 'withdrawList'), WARN_SHOULD_HAVE_KEY + 'withdrawList'); - assert(Array.isArray(result.withdrawList)); - assert(Object.prototype.hasOwnProperty.call(result, 'success'), WARN_SHOULD_HAVE_KEY + 'success'); - assert(result.success === true); - done(); - }); - }).timeout(TIMEOUT); -}); - - -describe('Deposit history', function () { - it('Attempt to get deposit history for all assets', function (done) { - binance.depositHistory((error, result) => { - debug(error); - debug(result); - assert(typeof (error) === 'object', WARN_SHOULD_BE_OBJ); - assert(typeof (result) === 'object', WARN_SHOULD_BE_OBJ); - assert(error === null, WARN_SHOULD_BE_NULL); - assert(result !== null, WARN_SHOULD_BE_NOT_NULL); - assert(Object.prototype.hasOwnProperty.call(result, 'depositList'), WARN_SHOULD_HAVE_KEY + 'depositList'); - assert(Array.isArray(result.depositList)); - assert(Object.prototype.hasOwnProperty.call(result, 'success'), WARN_SHOULD_HAVE_KEY + 'success'); - assert(result.success === true); - done(); - }); - }).timeout(TIMEOUT); -}); - -describe('Deposit address', function () { - it('Attempt to get deposit address for BTC', function (done) { - binance.depositAddress('BTC', (error, result) => { - debug(error); - debug(result); - assert(typeof (error) === 'object', WARN_SHOULD_BE_OBJ); - assert(typeof (result) === 'object', WARN_SHOULD_BE_OBJ); - assert(error === null, WARN_SHOULD_BE_NULL); - assert(result !== null, WARN_SHOULD_BE_NOT_NULL); - assert(Object.prototype.hasOwnProperty.call(result, 'address'), WARN_SHOULD_HAVE_KEY + 'address'); - assert(Object.prototype.hasOwnProperty.call(result, 'success'), WARN_SHOULD_HAVE_KEY + 'success'); - assert(Object.prototype.hasOwnProperty.call(result, 'addressTag'), WARN_SHOULD_HAVE_KEY + 'addressTag'); - assert(Object.prototype.hasOwnProperty.call(result, 'asset'), WARN_SHOULD_HAVE_KEY + 'asset'); - assert(result.asset === 'BTC'); - assert(result.success === true); - done(); - }); - }).timeout(TIMEOUT); - - it('Attempt to get deposit address for XYZ', function (done) { - binance.depositAddress('XYZ', (error, result) => { - debug(error); - debug(result); - assert(typeof (error) === 'object', WARN_SHOULD_BE_OBJ); - assert(typeof (result) === 'object', WARN_SHOULD_BE_OBJ); - assert(error === null, WARN_SHOULD_BE_NULL); - assert(result !== null, WARN_SHOULD_BE_NOT_NULL); - assert(Object.prototype.hasOwnProperty.call(result, 'address') === false, WARN_SHOULD_NOT_HAVE_KEY + 'address'); - assert(Object.prototype.hasOwnProperty.call(result, 'success'), WARN_SHOULD_NOT_HAVE_KEY + 'success'); - assert(Object.prototype.hasOwnProperty.call(result, 'addressTag') === false, WARN_SHOULD_NOT_HAVE_KEY + 'addressTag'); - assert(Object.prototype.hasOwnProperty.call(result, 'asset') === false, WARN_SHOULD_NOT_HAVE_KEY + 'asset'); - assert(result.success === false); - done(); - }); - }).timeout(TIMEOUT); -}); - -describe('Account status', function () { - it('Attempt to get account status', function (done) { - binance.accountStatus((error, data) => { - debug(error); - debug(data); - assert(typeof (error) === 'object', WARN_SHOULD_BE_OBJ); - assert(typeof (data) === 'object', WARN_SHOULD_BE_OBJ); - assert(error === null, WARN_SHOULD_BE_NULL); - assert(data !== null, WARN_SHOULD_BE_NOT_NULL); - done(); - }); - }).timeout(TIMEOUT); -}); - -describe('Account', function () { - it('Attempt to get account information', function (done) { - binance.account((error, data) => { - debug(error); - debug(data); - assert(typeof (error) === 'object', WARN_SHOULD_BE_OBJ); - assert(typeof (data) === 'object', WARN_SHOULD_BE_OBJ); - assert(error === null, WARN_SHOULD_BE_NULL); - assert(data !== null, WARN_SHOULD_BE_NOT_NULL); - done(); - }); - }).timeout(TIMEOUT); -}); - -describe('Time', function () { - it('Attempt to get server time', function (done) { - binance.time((error, data) => { - debug(error); - debug(data); - assert(typeof (error) === 'object', WARN_SHOULD_BE_OBJ); - assert(typeof (data) === 'object', WARN_SHOULD_BE_OBJ); - assert(error === null, WARN_SHOULD_BE_NULL); - assert(data !== null, WARN_SHOULD_BE_NOT_NULL); - assert(Object.prototype.hasOwnProperty.call(data, 'serverTime'), WARN_SHOULD_HAVE_KEY + 'serverTime'); - done(); - }); - }).timeout(TIMEOUT); -}); - -describe('Aggtrades', function () { - it('Attempt to get aggTrades for given symbol', function (done) { - binance.aggTrades('BNBBTC', { limit: 500 }, (error, response) => { - assert(typeof (error) === 'object', WARN_SHOULD_BE_OBJ); - assert(typeof (response) === 'object', WARN_SHOULD_BE_OBJ); - assert(error === null, WARN_SHOULD_BE_NULL); - assert(response !== null, WARN_SHOULD_BE_NOT_NULL); - done(); - }); - }).timeout(TIMEOUT); -}); - -describe('Recent Trades', function () { - it('Attempt get recent Trades for a given symbol', function (done) { - binance.recentTrades('BNBBTC', (error, data) => { - debug(error); - debug(data); - assert(typeof (error) === 'object', WARN_SHOULD_BE_OBJ); - assert(typeof (data) === 'object', WARN_SHOULD_BE_OBJ); - assert(error === null, WARN_SHOULD_BE_NULL); - assert(data !== null, WARN_SHOULD_BE_NOT_NULL); - assert(data.length > 0); - data.forEach(function (obj) { - assert(Object.prototype.hasOwnProperty.call(obj, 'id'), WARN_SHOULD_HAVE_KEY + 'id'); - assert(Object.prototype.hasOwnProperty.call(obj, 'price'), WARN_SHOULD_HAVE_KEY + 'price'); - assert(Object.prototype.hasOwnProperty.call(obj, 'qty'), WARN_SHOULD_HAVE_KEY + 'qty'); - assert(Object.prototype.hasOwnProperty.call(obj, 'time'), WARN_SHOULD_HAVE_KEY + 'time'); - assert(Object.prototype.hasOwnProperty.call(obj, 'isBuyerMaker'), WARN_SHOULD_HAVE_KEY + 'isBuyerMaker'); - assert(Object.prototype.hasOwnProperty.call(obj, 'isBestMatch'), WARN_SHOULD_HAVE_KEY + 'isBestMatch'); - }); - done(); - }); - }).timeout(TIMEOUT); -}); - -describe('Historical Trades', function () { - it('Attempt get Historical Trades for a given symbol', function (done) { - binance.historicalTrades('BNBBTC', (error, data) => { - debug(error); - debug(data); - assert(typeof (error) === 'object', WARN_SHOULD_BE_OBJ); - assert(typeof (data) === 'object', WARN_SHOULD_BE_OBJ); - assert(error === null, WARN_SHOULD_BE_NULL); - assert(data !== null, WARN_SHOULD_BE_NOT_NULL); - assert(data.length > 0); - data.forEach(function (obj) { - assert(Object.prototype.hasOwnProperty.call(obj, 'id'), WARN_SHOULD_HAVE_KEY + 'id'); - assert(Object.prototype.hasOwnProperty.call(obj, 'price'), WARN_SHOULD_HAVE_KEY + 'price'); - assert(Object.prototype.hasOwnProperty.call(obj, 'qty'), WARN_SHOULD_HAVE_KEY + 'qty'); - assert(Object.prototype.hasOwnProperty.call(obj, 'time'), WARN_SHOULD_HAVE_KEY + 'time'); - assert(Object.prototype.hasOwnProperty.call(obj, 'isBuyerMaker'), WARN_SHOULD_HAVE_KEY + 'isBuyerMaker'); - assert(Object.prototype.hasOwnProperty.call(obj, 'isBestMatch'), WARN_SHOULD_HAVE_KEY + 'isBestMatch'); - }); - done(); - }); - }).timeout(TIMEOUT); -}); - -describe('getInfo', function () { - it('Gets the info array form the binance object', function (done) { - assert(typeof (binance.getInfo()) === 'object', 'Should be of type array') - done(); - }).timeout(TIMEOUT); -}); - -describe('Websockets candlesticks', function () { - let candlesticks; - let cnt = 0; - /*global beforeEach*/ - beforeEach(function (done) { - this.timeout(TIMEOUT); - binance.websockets.candlesticks(['BNBBTC'], '1m', a_candlesticks => { - cnt++; - if (cnt > 1) return; - candlesticks = a_candlesticks; - stopSockets(); - done(); - }); - }); - - it('Calls candlesticks websocket', function () { - assert(typeof (candlesticks) === 'object', WARN_SHOULD_BE_OBJ); - assert(candlesticks !== null, WARN_SHOULD_BE_NOT_NULL); - assert(Object.keys(candlesticks).length >= 0, 'should at least 1 currency pairs?'); - - let keys = ['t', 'T', 's', 'i', 'f', 'L', 'o', 'c', 'h', 'l', 'v', 'n', 'x', 'q', 'V', 'Q', 'B']; - assert(Object.prototype.hasOwnProperty.call(candlesticks, 'e'), WARN_SHOULD_HAVE_KEY + 'e'); - assert(Object.prototype.hasOwnProperty.call(candlesticks, 'E'), WARN_SHOULD_HAVE_KEY + 'E'); - assert(Object.prototype.hasOwnProperty.call(candlesticks, 's'), WARN_SHOULD_HAVE_KEY + 's'); - assert(Object.prototype.hasOwnProperty.call(candlesticks, 's'), WARN_SHOULD_HAVE_KEY + 'k'); - - keys.forEach(function (key) { - assert(Object.prototype.hasOwnProperty.call(candlesticks.k, key), WARN_SHOULD_HAVE_KEY + key); - }); - }); -}); - -describe('Websockets depth', function () { - let depth; - let cnt = 0; - /*global beforeEach*/ - beforeEach(function (done) { - this.timeout(TIMEOUT); - binance.websockets.depth(['BNBBTC'], e_depth => { - cnt++; - if (cnt > 1) return; - depth = e_depth; - stopSockets(); - done(); - }); - }); - - it('Calls depth websocket', function () { - assert(typeof (depth) === 'object', WARN_SHOULD_BE_OBJ); - assert(depth !== null, WARN_SHOULD_BE_NOT_NULL); - }); -}); - -describe('Websockets aggregated trades', function () { - let trades; - let cnt = 0; - /*global beforeEach*/ - beforeEach(function (done) { - this.timeout(TIMEOUT); - binance.websockets.aggTrades(['BNBBTC', 'ETHBTC'], e_trades => { - cnt++; - if (cnt > 1) return; - trades = e_trades; - stopSockets(); - done(); - }); - }); - - it('Calls trades websocket', function () { - assert(typeof (trades) === 'object', WARN_SHOULD_BE_OBJ); - assert(trades !== null, WARN_SHOULD_BE_NOT_NULL); - }); -}); - - -describe('Websockets (raw) trades', function () { - let trades; - let cnt = 0; - /*global beforeEach*/ - beforeEach(function (done) { - this.timeout(TIMEOUT); - binance.websockets.trades(['BNBBTC', 'ETHBTC'], e_trades => { - cnt++; - if (cnt > 1) return; - trades = e_trades; - stopSockets(); - done(); - }); - }); - - it('Calls trades websocket', function () { - assert(typeof (trades) === 'object', WARN_SHOULD_BE_OBJ); - assert(trades !== null, WARN_SHOULD_BE_NOT_NULL); - }); -}); - -describe('depthCache', function () { - it('depthCache', function (done) { - binance.depthCache('BNBBTC'); - done(); - }).timeout(TIMEOUT); -}); - -describe('depthVolume', function () { - it('depthVolume', function (done) { - binance.depthVolume('BNBBTC'); - done(); - }).timeout(TIMEOUT); -}); - -describe('getPrecision', function () { - it('getPrecision', function (done) { - binance.getPrecision(1.9999999); - done(); - }).timeout(TIMEOUT); -}); - -describe('roundStep', function () { - it('roundStep', function (done) { - binance.roundStep(10, 0.8); - done(); - }).timeout(TIMEOUT); -}); - -describe('Websockets miniticker', function () { - let markets; - let cnt = 0; - beforeEach(function (done) { - this.timeout(TIMEOUT); - binance.websockets.miniTicker(tick => { - cnt++; - if (cnt > 1) return; - markets = tick; - stopSockets(); - done(); - }); - }); - - it('check miniticker websocket', function () { - assert(typeof (markets) === 'object', WARN_SHOULD_BE_OBJ); - assert(markets !== null, WARN_SHOULD_BE_NOT_NULL); - assert(Object.keys(markets).length >= 0, 'should at least 1 currency pairs?'); - - Object.keys(markets).forEach(function (symbol) { - assert(Object.prototype.hasOwnProperty.call(markets[symbol], 'close'), WARN_SHOULD_HAVE_KEY + 'close'); - assert(Object.prototype.hasOwnProperty.call(markets[symbol], 'open'), WARN_SHOULD_HAVE_KEY + 'open'); - assert(Object.prototype.hasOwnProperty.call(markets[symbol], 'high'), WARN_SHOULD_HAVE_KEY + 'high'); - assert(Object.prototype.hasOwnProperty.call(markets[symbol], 'low'), WARN_SHOULD_HAVE_KEY + 'low'); - assert(Object.prototype.hasOwnProperty.call(markets[symbol], 'volume'), WARN_SHOULD_HAVE_KEY + 'volume'); - assert(Object.prototype.hasOwnProperty.call(markets[symbol], 'quoteVolume'), WARN_SHOULD_HAVE_KEY + 'quoteVolume'); - assert(Object.prototype.hasOwnProperty.call(markets[symbol], 'eventTime'), WARN_SHOULD_HAVE_KEY + 'eventTime'); - }); - }); -}); - -describe('Websockets symbol depthcache', function () { - let symbol; - let bids; - let asks; - let cnt = 0; - beforeEach(function (done) { - this.timeout(TIMEOUT); - binance.websockets.depthCache('BNBBTC', (a_symbol, a_depth) => { - cnt++; - if (cnt > 1) return; - stopSockets(true); - symbol = a_symbol; - bids = a_depth.bids; - asks = a_depth.asks; - done(); - }); - }); - - bids = binance.sortBids(bids); - asks = binance.sortAsks(asks); - - it('check result of depth cache', function () { - assert(typeof (bids) === 'object', WARN_SHOULD_BE_OBJ); - assert(typeof (asks) === 'object', WARN_SHOULD_BE_OBJ); - assert(typeof (symbol) === 'string', WARN_SHOULD_BE_OBJ); - assert(bids !== null, WARN_SHOULD_BE_NOT_NULL); - assert(asks !== null, WARN_SHOULD_BE_NOT_NULL); - assert(symbol !== null, WARN_SHOULD_BE_NOT_NULL); - assert(Object.keys(asks).length !== 0, 'should not be 0'); - assert(Object.keys(bids).length !== 0, 'should not be 0'); - }); -}); - -describe('Websockets array depthcache', function () { - let symbol; - let bids; - let asks; - let cnt = 0; - beforeEach(function (done) { - this.timeout(TIMEOUT); - binance.websockets.depthCache(['BNBBTC', 'TRXBTC'], (a_symbol, a_depth) => { - cnt++; - if (cnt > 1) return; - stopSockets(); - symbol = a_symbol; - bids = a_depth.bids; - asks = a_depth.asks; - done(); - }); - }); - - bids = binance.sortBids(bids); - asks = binance.sortAsks(asks); - - it('check result of symbols array depth cache', function () { - assert(typeof (bids) === 'object', WARN_SHOULD_BE_OBJ); - assert(typeof (asks) === 'object', WARN_SHOULD_BE_OBJ); - assert(typeof (symbol) === 'string', WARN_SHOULD_BE_OBJ); - assert(bids !== null, WARN_SHOULD_BE_NOT_NULL); - assert(asks !== null, WARN_SHOULD_BE_NOT_NULL); - assert(symbol !== null, WARN_SHOULD_BE_NOT_NULL); - assert(Object.keys(asks).length !== 0, 'should not be 0'); - assert(Object.keys(bids).length !== 0, 'should not be 0'); - }); -}); - -describe('Staggered websockets symbol depthcache', function () { - let symbol; - let bids; - let asks; - let cnt = 0; - beforeEach(function (done) { - this.timeout(TIMEOUT); - binance.websockets.depthCacheStaggered('BNBBTC', (a_symbol, a_depth) => { - cnt++; - if (cnt > 1) return; - stopSockets(true); - symbol = a_symbol; - bids = a_depth.bids; - asks = a_depth.asks; - done(); - }); - }); - - bids = binance.sortBids(bids); - asks = binance.sortAsks(asks); - - it('check result of depth cache', function () { - assert(typeof (bids) === 'object', WARN_SHOULD_BE_OBJ); - assert(typeof (asks) === 'object', WARN_SHOULD_BE_OBJ); - assert(typeof (symbol) === 'string', WARN_SHOULD_BE_OBJ); - assert(bids !== null, WARN_SHOULD_BE_NOT_NULL); - assert(asks !== null, WARN_SHOULD_BE_NOT_NULL); - assert(symbol !== null, WARN_SHOULD_BE_NOT_NULL); - assert(Object.keys(asks).length !== 0, 'should not be 0'); - assert(Object.keys(bids).length !== 0, 'should not be 0'); - }); -}); - -describe('Staggered Websockets array depthcache', function () { - let symbol; - let bids; - let asks; - let cnt = 0; - beforeEach(function (done) { - this.timeout(TIMEOUT); - binance.websockets.depthCacheStaggered(['BNBBTC', 'TRXBTC'], (a_symbol, a_depth) => { - cnt++; - if (cnt > 1) return; - stopSockets(); - symbol = a_symbol; - bids = a_depth.bids; - asks = a_depth.asks; - done(); - }); - }); - - bids = binance.sortBids(bids); - asks = binance.sortAsks(asks); - - it('check result of symbols array depth cache', function () { - assert(typeof (bids) === 'object', WARN_SHOULD_BE_OBJ); - assert(typeof (asks) === 'object', WARN_SHOULD_BE_OBJ); - assert(typeof (symbol) === 'string', WARN_SHOULD_BE_OBJ); - assert(bids !== null, WARN_SHOULD_BE_NOT_NULL); - assert(asks !== null, WARN_SHOULD_BE_NOT_NULL); - assert(symbol !== null, WARN_SHOULD_BE_NOT_NULL); - assert(Object.keys(asks).length !== 0, 'should not be 0'); - assert(Object.keys(bids).length !== 0, 'should not be 0'); - }); -}); - -describe('Websockets prevDay', function () { - let response; - let cnt = 0; - - beforeEach(function (done) { - this.timeout(TIMEOUT); - binance.websockets.prevDay(false, a_response => { - cnt++; - if (cnt > 1) return; - stopSockets(); - response = a_response; - done(); - }) - }); - - it('Calls prevDay websocket for symbol', function () { - assert(typeof (response) === 'object', WARN_SHOULD_BE_OBJ); - }); -}); - -describe('Websockets prevDay array', function () { - let response; - let cnt = 0; - - beforeEach(function (done) { - this.timeout(TIMEOUT); - binance.websockets.prevDay(['BNBBTC', 'TRXBTC'], a_response => { - cnt++; - if (cnt > 1) return; - stopSockets(); - response = a_response; - done(); - }) - }); - - it('Calls prevDay websocket for array of symbols', function () { - assert(typeof (response) === 'object', WARN_SHOULD_BE_OBJ); - }); -}); - -describe('Websockets prevDay single symbol', function () { - let response; - let cnt = 0; - - beforeEach(function (done) { - this.timeout(TIMEOUT); - binance.websockets.prevDay('BNBBTC', a_response => { - cnt++; - if (cnt > 1) return; - stopSockets(); - response = a_response; - done(); - }) - }); - - it('Calls prevDay websocket for a single symbol', function () { - assert(typeof (response) === 'object', WARN_SHOULD_BE_OBJ); - }); -}); - -describe('Websockets chart', function () { - let chart; - let interval; - let symbol; - let cnt = 0; - beforeEach(function (done) { - this.timeout(TIMEOUT); - binance.websockets.chart('BNBBTC', '1m', (a_symbol, a_interval, a_chart) => { - cnt++; - if (cnt > 1) { - stopSockets(); - return; - } - chart = a_chart; - interval = a_interval; - symbol = a_symbol; - stopSockets(); - done(); - }); - }); - - it('Calls chart websocket', function () { - assert(typeof (chart) === 'object', WARN_SHOULD_BE_OBJ); - assert(typeof (symbol) === 'string', WARN_SHOULD_BE_OBJ); - assert(typeof (interval) === 'string', WARN_SHOULD_BE_OBJ); - assert(chart !== null, WARN_SHOULD_BE_NOT_NULL); - assert(symbol !== null, WARN_SHOULD_BE_NOT_NULL); - assert(interval !== null, WARN_SHOULD_BE_NOT_NULL); - - let keys = ['open', 'high', 'open', 'close', 'volume']; - assert(Object.keys(chart).length > 0, 'Should not be empty'); - - Object.keys(chart).forEach(function (c) { - keys.forEach(function (key) { - assert(Object.prototype.hasOwnProperty.call(chart[c], key), WARN_SHOULD_HAVE_KEY + key); - }); - }); - }); -}); - -describe('ohlc', function () { - let chart = [{ 'open': 1.11111, 'high': 6.6666, 'low': 8.88888, 'close': 4.44444, 'volume': 3.333333 }, { 'open': 1.11111, 'high': 6.6666, 'low': 8.88888, 'close': 4.44444, 'volume': 3.333333 }]; - it('Calls ohlc', function () { - binance.ohlc(chart); - }); -}); - -describe('highstock', function () { - let chart = [{ 'open': 1.11111, 'high': 6.6666, 'low': 8.88888, 'close': 4.44444, 'volume': 3.333333 }, { 'open': 1.11111, 'high': 6.6666, 'low': 8.88888, 'close': 4.44444, 'volume': 3.333333 }]; - it('Calls highstock', function () { - binance.highstock(chart); - }); -}); - -describe('Websockets chart array', function () { - let chart; - let interval; - let symbol; - let cnt = 0; - beforeEach(function (done) { - this.timeout(TIMEOUT); - binance.websockets.chart(['BNBBTC', 'TRXBTC'], '1m', (a_symbol, a_interval, a_chart) => { - cnt++; - if (cnt > 1) { - stopSockets(); - return; - } - chart = a_chart; - interval = a_interval; - symbol = a_symbol; - stopSockets(); - done(); - }); - }); - - it('Calls chart websocket array', function () { - assert(typeof (chart) === 'object', WARN_SHOULD_BE_OBJ); - assert(typeof (symbol) === 'string', WARN_SHOULD_BE_OBJ); - assert(typeof (interval) === 'string', WARN_SHOULD_BE_OBJ); - assert(chart !== null, WARN_SHOULD_BE_NOT_NULL); - assert(symbol !== null, WARN_SHOULD_BE_NOT_NULL); - assert(interval !== null, WARN_SHOULD_BE_NOT_NULL); - - let keys = ['open', 'high', 'open', 'close', 'volume']; - assert(Object.keys(chart).length > 0, 'Should not be empty'); - - Object.keys(chart).forEach(function (c) { - keys.forEach(function (key) { - assert(Object.prototype.hasOwnProperty.call(chart[c], key), WARN_SHOULD_HAVE_KEY + key); - }); - }); - }); -}); - - -/* -describe( 'Websockets userdata', function() { - let userdata; - beforeEach(function (done) { - this.timeout( TIMEOUT ); - binance.websockets.userData( data => { - userdata = data; - stopSockets(); - done(); - }); - }); - - it( 'Calls userdata websocket', function() { - assert( typeof ( userdata ) === 'object', WARN_SHOULD_BE_OBJ ); - assert( userdata !== null, WARN_SHOULD_BE_NOT_NULL ); - }); -}); -*/ diff --git a/tests/binance-class-live.test.ts b/tests/binance-class-live.test.ts new file mode 100644 index 00000000..3decee0e --- /dev/null +++ b/tests/binance-class-live.test.ts @@ -0,0 +1,716 @@ +import Binance from '../src/node-binance-api'; +import { assert } from 'chai'; +import util from 'util'; + +const WARN_SHOULD_BE_OBJ = 'should be an object'; +const WARN_SHOULD_BE_NULL = 'should be null'; +const WARN_SHOULD_BE_NOT_NULL = 'should not be null'; +const WARN_SHOULD_HAVE_KEY = 'should have key '; +const WARN_SHOULD_NOT_HAVE_KEY = 'should not have key '; +const WARN_SHOULD_BE_UNDEFINED = 'should be undefined'; +const WARN_SHOULD_BE_TYPE = 'should be a '; +const TIMEOUT = 40000; + +let spotOrderId: number; +let futuresOrderId: number; // used to fetch order status + +let ethusdtPrice = 0; + + +let logger = { + log: function (msg) { + let logLineDetails = ((new Error().stack).split('at ')[3]).trim(); + let logLineNum = logLineDetails.split(':'); + console.log('DEBUG', logLineNum[1] + ':' + logLineNum[2], msg); + } +} + +let debug = function (x) { + if (typeof (process.env.node_binance_api) === 'undefined') { + return; + } + logger.log(typeof (x)); + logger.log(util.inspect(x)); +} + + +const binance = new Binance().options({ + APIKEY: 'X4BHNSimXOK6RKs2FcKqExquJtHjMxz5hWqF0BBeVnfa5bKFMk7X0wtkfEz0cPrJ', + APISECRET: 'x8gLihunpNq0d46F2q0TWJmeCDahX5LMXSlv3lSFNbMI3rujSOpTDKdhbcmPSf2i', + test: true +}); + +const futuresBinance = new Binance().options({ + APIKEY: '227719da8d8499e8d3461587d19f259c0b39c2b462a77c9b748a6119abd74401', + APISECRET: 'b14b935f9cfacc5dec829008733c40da0588051f29a44625c34967b45c11d73c', + hedgeMode: true, + test: true +}); + +/*global describe*/ +/*eslint no-undef: "error"*/ +describe('Construct', function () { + /*global it*/ + /*eslint no-undef: "error"*/ + it('Construct the binance object', function () { + binance.options({ + APIKEY: 'X4BHNSimXOK6RKs2FcKqExquJtHjMxz5hWqF0BBeVnfa5bKFMk7X0wtkfEz0cPrJ', + APISECRET: 'x8gLihunpNq0d46F2q0TWJmeCDahX5LMXSlv3lSFNbMI3rujSOpTDKdhbcmPSf2i', + useServerTime: true, + reconnect: false, + verbose: true, + test: true, + log: debug + }); + assert(typeof (binance) === 'object', 'Binance is not an object'); + + }).timeout(TIMEOUT); + + it('Construct the binance object in various ways', function () { + + let keyOffset = 1000; + let key = keyOffset; + let secret = "secret"; + + // Every variant is listed twice to make sure that the options are not shared (which happened in the past) + let objs = [ + new Binance().options({ APIKEY: key++, APISECRET: secret }), + new Binance().options({ APIKEY: key++, APISECRET: secret }), + // Binance().options( { APIKEY: key++, APISECRET: secret } ), + // Binance().options( { APIKEY: key++, APISECRET: secret } ), + new Binance({ APIKEY: key++, APISECRET: secret }), + new Binance({ APIKEY: key++, APISECRET: secret }), + // Binance( { APIKEY: key++, APISECRET: secret } ), + // Binance( { APIKEY: key++, APISECRET: secret } ), + ]; + + // Make sure that all objects have their own options + for (let i = 0; i < objs.length; i++) { + let expectedKey = keyOffset + i; + let actualKey = objs[i].getOption("APIKEY"); + assert(expectedKey === actualKey, `APIKEY: ${expectedKey} != ${actualKey}`); + } + + }); + +}); + +describe('UseServerTime', function () { + it('Call use server time', async function () { + await binance.useServerTime(); + + }).timeout(TIMEOUT); +}); + +describe('Prices', function () { + it('Checks the price of ETHUSDT', async function () { + const ticker = await binance.prices('ETHUSDT') + ethusdtPrice = ticker.ETHUSDT; + assert(typeof (ticker) === 'object', WARN_SHOULD_BE_OBJ); + assert(ticker !== null, WARN_SHOULD_BE_NOT_NULL); + //done() + }).timeout(TIMEOUT); + + it('Checks the price of ETHUSDT contract', async function () { + const ticker = await futuresBinance.futuresPrices('ETHUSDT') + assert(typeof (ticker) === 'object', WARN_SHOULD_BE_OBJ); + assert(ticker !== null, WARN_SHOULD_BE_NOT_NULL); + assert(Object.prototype.hasOwnProperty.call(ticker, 'ETHUSDT'), WARN_SHOULD_HAVE_KEY + 'ETHUSDT'); + //done() + }).timeout(TIMEOUT); +}); + +describe('All Prices', function () { + it('Checks the prices of coin pairs', async function () { + const ticker = await binance.prices(); + assert(typeof (ticker) === 'object', WARN_SHOULD_BE_OBJ); + assert(ticker !== null, WARN_SHOULD_BE_NOT_NULL); + }).timeout(TIMEOUT); +}); + +describe('All Futures Prices', function () { + it('Checks the prices of coin pairs', async function () { + const ticker = await futuresBinance.futuresPrices(); + assert(typeof (ticker) === 'object', WARN_SHOULD_BE_OBJ); + assert(ticker !== null, WARN_SHOULD_BE_NOT_NULL); + }).timeout(TIMEOUT); +}); + +describe('Balances', function () { + it('Get the balances in the account', async function () { + const balances = await binance.balance(); + assert(balances !== null, WARN_SHOULD_BE_NOT_NULL); + assert(balances); + assert(Object.prototype.hasOwnProperty.call(balances, 'BNB'), WARN_SHOULD_HAVE_KEY + 'BNB'); + assert(Object.prototype.hasOwnProperty.call(balances.BNB, 'available'), WARN_SHOULD_HAVE_KEY + 'available'); + assert(Object.prototype.hasOwnProperty.call(balances.BNB, 'onOrder'), WARN_SHOULD_HAVE_KEY + 'onOrder'); + //done() + }).timeout(TIMEOUT); +}); + +describe('Book Ticker', function () { + it('Get the BNB book ticker', async function () { + const tickers = await binance.bookTickers('BNBBTC') + const ticker = tickers['BNBBTC']; + assert(ticker !== null, WARN_SHOULD_BE_NOT_NULL); + assert(ticker); + let members = ['bidPrice', 'bidQty', 'askPrice', 'askQty']; + members.forEach(function (value) { + assert(Object.prototype.hasOwnProperty.call(ticker, value), WARN_SHOULD_HAVE_KEY + value); + }); + //done() + }).timeout(TIMEOUT); + + it('Get all book tickers', async function () { + const tickers = await binance.bookTickers() + assert(tickers !== undefined); + //done() + }).timeout(TIMEOUT); +}); + +describe('Booker Tickers', function () { + it('Get the tickers for all pairs', async function () { + const ticker = await binance.bookTickers(); + assert(typeof (ticker) === 'object', WARN_SHOULD_BE_OBJ); + assert(ticker !== null, WARN_SHOULD_BE_NOT_NULL); + + let members = ['bidPrice', 'bidQty', 'askPrice', 'askQty']; + const tickers = Object.values(ticker); + tickers.forEach(function (obj) { + members.forEach(function (member) { + assert(Object.prototype.hasOwnProperty.call(obj, member), WARN_SHOULD_HAVE_KEY + member); + }); + }); + //done() + }).timeout(TIMEOUT * 2); +}); + +describe('Market', function () { + it('Get the market base symbol of a symbol pair', function () { + let tocheck = ['TRXBNB', 'BNBBTC', 'BNBETH', 'BNBUSDT']; + tocheck.forEach(function (element) { + let mark = binance.getMarket(element); + assert(typeof (mark) === 'string', WARN_SHOULD_BE_TYPE + 'string'); + assert(element.endsWith(mark), 'should end with: ' + mark); + }); + + assert.isNotOk(binance.getMarket('ABCDEFG'), WARN_SHOULD_BE_UNDEFINED); + + }).timeout(TIMEOUT); +}); + +describe('ping', function () { + it('call ping', async function () { + await binance.ping(); + + }).timeout(TIMEOUT); +}); + +describe('Depth chart BNB', function () { + it('Get the depth chart information for BNBBTC', async function () { + const depth = await binance.depth('BNBBTC'); + assert(depth !== null, WARN_SHOULD_BE_NOT_NULL); + assert(typeof (depth) === 'object', WARN_SHOULD_BE_OBJ); + assert(Object.keys(depth).length === 4, 'should have length 3'); + + let members = ['lastUpdateId', 'asks', 'bids', 'symbol']; + members.forEach(function (value) { + assert(Object.prototype.hasOwnProperty.call(depth, value), WARN_SHOULD_HAVE_KEY + value); + }); + //done() + }).timeout(TIMEOUT * 2); +}); + +// describe( 'Buy', function () { +// it( 'Attempt to buy ETH', function ( ) { +// let quantity = 1; +// let price = 0.069; +// assert( typeof ( binance.buy( 'ETHBTC', quantity, price ) ) === 'undefined', WARN_SHOULD_BE_UNDEFINED ); +// +// } ).timeout( TIMEOUT ); +// } ); + +// describe( 'Sell', function () { +// it( 'Attempt to sell ETH', function ( ) { +// let quantity = 1; +// let price = 0.069; +// assert( typeof ( binance.sell( 'ETHBTC', quantity, price ) ) === 'undefined', WARN_SHOULD_BE_UNDEFINED ); +// +// } ).timeout( TIMEOUT ); +// } ); + +describe('MarketBuy', function () { + it('Attempt to buy LTC at market price', async function () { + let quantity = 0.5; + const res = await binance.marketBuy('LTCUSDT', quantity) + assert(res['orderId'] !== undefined) + spotOrderId = res['orderId']; + + }).timeout(TIMEOUT); +}); + + +describe('MarketBuy/Sell WithCost', function () { + it('Attempt to buy ETH using the cost', async function () { + let quantity = 100; // usd + const res = await binance.marketBuyWithCost('ETHUSDT', quantity) + assert(res['orderId'] !== undefined) + }).timeout(TIMEOUT); + it('Attempt to sell ETH using the cost', async function () { + let quantity = 100; // usd + const res = await binance.marketSellWithCost('ETHUSDT', quantity) + assert(res['orderId'] !== undefined) + }).timeout(TIMEOUT); +}); + +describe('Limit buy Order', function () { + it('Attempt to buy ETH', async function () { + if (ethusdtPrice !== 0) { + try { + let quantity = 0.1; + const res = await binance.order('LIMIT', 'BUY', 'ETHUSDT', quantity, Math.round(ethusdtPrice * 0.8)) + assert(res['orderId'] !== undefined) + spotOrderId = res['orderId']; + } catch (e) { + assert(e.toString().includes('{"code":-2010,"msg":"Account has insufficient balance for requested action."}')); + } + } + + }).timeout(TIMEOUT); +}); + +describe('MarketSell', function () { + it('Attempt to buy LTC at market price', async function () { + + try { + let quantity = 0.5; + const res = await binance.marketSell('LTCUSDT', quantity) + assert(res['orderId'] !== undefined) + } catch (e) { + assert(e.toString().includes('{"code":-2010,"msg":"Account has insufficient balance for requested action."}')); + } + + }).timeout(TIMEOUT); +}); + +describe('Futures MarketBuy', function () { + it('futures Attempt to buy ETH at market price', async function () { + try { + let quantity = 0.1; + const res = await futuresBinance.futuresMarketBuy('ETHUSDT', quantity) + assert(res['orderId'] !== undefined) + futuresOrderId = res['orderId']; + } catch (e) { + assert(e.toString().includes('{"code":-2010,"msg":"Account has insufficient balance for requested action."}')); + } + + }).timeout(TIMEOUT); +}); + +describe('Futures Limit buy Order', function () { + it('Attempt to buy ETH', async function () { + if (ethusdtPrice !== 0) { + let quantity = 0.1; + const res = await futuresBinance.futuresOrder('LIMIT', 'BUY', 'ETHUSDT', quantity, Math.round(ethusdtPrice * 0.8)) + assert(res['orderId'] !== undefined) + futuresOrderId = res['orderId']; + } + }).timeout(TIMEOUT); +}); + +describe('Futures MarketSell', function () { + it('futures Attempt to buy ETH at market price', async function () { + let quantity = 0.1; + const res = await futuresBinance.futuresMarketSell('ETHUSDT', quantity) + assert(res['orderId'] !== undefined) + + }).timeout(TIMEOUT); +}); + +// describe( 'MarketSell', function () { +// it( 'Attempt to sell ETH at market price', function ( ) { +// let quantity = 1; +// assert( typeof ( binance.marketSell( 'ETHBTC', quantity ) ) === 'undefined', WARN_SHOULD_BE_UNDEFINED ); +// +// } ).timeout( TIMEOUT ); +// } ); + +// describe( 'Buy order advanced', function () { +// it( 'Attempt to buy BNB specifying order type', function ( ) { +// let type = 'LIMIT'; +// let quantity = 1; +// let price = 0.069; +// assert( typeof ( binance.buy( 'BNBETH', quantity, price, { type: type } ) ) === 'undefined', WARN_SHOULD_BE_UNDEFINED ); +// +// } ).timeout( TIMEOUT ); +// } ); + +// describe( 'Sell Stop loess', function () { +// it( 'Attempt to create a stop loss order', function ( ) { +// let type = 'STOP_LOSS'; +// let quantity = 1; +// let price = 0.069; +// let stopPrice = 0.068; +// assert( typeof ( binance.sell( 'ETHBTC', quantity, price, { stopPrice: stopPrice, type: type } ) ) === 'undefined', WARN_SHOULD_BE_UNDEFINED ); +// +// } ).timeout( TIMEOUT ); +// } ); + +// describe( 'Iceberg sell order', function () { +// it( 'Attempt to create a sell order', function ( ) { +// let quantity = 1; +// let price = 0.069; +// assert( typeof ( binance.sell( 'ETHBTC', quantity, price, { icebergQty: 10 } ) ) === 'undefined', WARN_SHOULD_BE_UNDEFINED ); +// +// } ).timeout( TIMEOUT ); +// } ); + +describe('Cancel order', function () { + it('Attempt to cancel an order', async function () { + let orderid = spotOrderId; + try { + const res = await binance.cancel('ETHBTC', orderid) + assert(res !== null, WARN_SHOULD_BE_NOT_NULL); + } catch (e) { + assert(e.toString().includes('{"code":-2011,"msg":"Unknown order sent."}')); + } + + }).timeout(TIMEOUT); +}); + +describe('Futures Cancel order', function () { + it('Attempt to cancel an order', async function () { + let orderid = futuresOrderId ?? "123123"; + try { + const res = await futuresBinance.futuresCancel('ETHUSDT', orderid) + assert(res !== null, WARN_SHOULD_BE_NOT_NULL); + } catch (e) { + assert(e.toString().includes('{"code":-2011,"msg":"Unknown order sent."}')); + } + + }).timeout(TIMEOUT); +}); + +// describe( 'Cancel orders', function () { +// it( 'Attempt to cancel all orders given a symbol', async function ( ) { +// try { +// await binance.cancelOrders( 'XMRBTC'); +// } catch(e) { + +// } +// +// } ).timeout( TIMEOUT ); +// } ); + +describe('Open Orders', function () { + it('Attempt to show all orders to ETHBTC', async function () { + const openOrders = await binance.openOrders('ETHBTC'); + assert(typeof (openOrders) === 'object', WARN_SHOULD_BE_OBJ); + assert(openOrders !== null, WARN_SHOULD_BE_NOT_NULL); + + }).timeout(TIMEOUT); +}); + +describe('Open Orders', function () { + it('Attempt to show all orders for all symbols', async function () { + const openOrders = await binance.openOrders(); + assert(openOrders !== null, WARN_SHOULD_BE_NOT_NULL); + + }).timeout(TIMEOUT); +}); + +describe('Order status', function () { + it('Attempt to get the order status for a given order id', async function () { + try { + const orderStatus = await binance.orderStatus('ETHBTC', '1234567890'); + assert(typeof (orderStatus) === 'object', WARN_SHOULD_BE_OBJ); + assert(orderStatus !== null, WARN_SHOULD_BE_NOT_NULL); + assert(Object.keys(orderStatus).length === 0); + } catch (e) { + assert(e.toString().includes('{"code":-2013,"msg":"Order does not exist."}')); + } + + }).timeout(TIMEOUT); +}); + +describe('trades', function () { + it('Attempt get all trade history for given symbol', async function () { + const trades = await binance.trades('BTCUSDT'); + assert(typeof (trades) === 'object', WARN_SHOULD_BE_OBJ); + assert(trades !== null, WARN_SHOULD_BE_NOT_NULL); + assert(Object.keys(trades).length === 0); + + }).timeout(TIMEOUT); +}); + +describe('Orders', function () { + it('Attempt get all orders for given symbol', async function () { + const orders = await binance.allOrders('ETHBTC'); + assert(typeof (orders) === 'object', WARN_SHOULD_BE_OBJ); + assert(orders !== null, WARN_SHOULD_BE_NOT_NULL); + assert(Object.keys(orders).length === 0); + + }).timeout(TIMEOUT); +}); + +describe('Prevday all symbols', function () { + it('Attempt get prevday trade status for all symbols', async function () { + const prevDay = await binance.prevDay(); + assert(typeof (prevDay) === 'object', WARN_SHOULD_BE_OBJ); + assert(prevDay !== null, WARN_SHOULD_BE_NOT_NULL); + // assert( Object.keys( prevDay ).length >= num_pairs, 'should at least ' + num_pairs + 'currency pairs?' ); + + let members = [ + 'symbol', 'priceChange', 'priceChangePercent', 'weightedAvgPrice', 'prevClosePrice', + 'lastPrice', 'lastQty', 'bidPrice', 'bidQty', 'askQty', 'openPrice', 'highPrice', 'lowPrice', + 'volume', 'quoteVolume', 'openTime', 'closeTime', 'firstId', 'lastId', 'count' + ]; + (prevDay as any[]).forEach(function (obj) { + members.forEach(function (key) { + assert(Object.prototype.hasOwnProperty.call(obj, key), WARN_SHOULD_HAVE_KEY + key); + }); + }); + + }).timeout(TIMEOUT); +}); + +describe('Prevday', function () { + it('Attempt get prevday trade status for given symbol', async function () { + const prevDay = await binance.prevDay('BNBBTC'); + assert(typeof (prevDay) === 'object', WARN_SHOULD_BE_OBJ); + assert(prevDay !== null, WARN_SHOULD_BE_NOT_NULL); + + let members = [ + 'symbol', 'priceChange', 'priceChangePercent', 'weightedAvgPrice', 'prevClosePrice', + 'lastPrice', 'lastQty', 'bidPrice', 'bidQty', 'askQty', 'openPrice', 'highPrice', 'lowPrice', + 'volume', 'quoteVolume', 'openTime', 'closeTime', 'firstId', 'lastId', 'count' + ]; + members.forEach(function (key) { + assert(Object.prototype.hasOwnProperty.call(prevDay, key), WARN_SHOULD_HAVE_KEY + key); + }); + + }).timeout(TIMEOUT); +}); + +describe('Candle sticks', function () { + it('Attempt get candlesticks for a given symbol', async function () { + const ticks = await binance.candlesticks('BNBBTC', '5m', { + limit: 500, + endTime: 1514764800000 + }); + assert(typeof (ticks) === 'object', WARN_SHOULD_BE_OBJ); + assert(ticks !== null, WARN_SHOULD_BE_NOT_NULL); + + ticks.forEach(function (tick) { + assert(tick.high !== undefined); + assert(tick.low !== undefined); + assert(tick.open !== undefined); + }); + + }).timeout(TIMEOUT); +}); + +describe('Object keys', function () { + describe('First', function () { + it('Gets the first key', function () { + let first = binance.first({ first: '1', second: '2', third: '3' }); + assert.strictEqual('first', first, 'should be first'); + + }).timeout(TIMEOUT); + }); + + describe('Last', function () { + it('Gets the last key', function () { + let last = binance.last({ first: '1', second: '2', third: '3' }); + assert.strictEqual('third', last, 'should be third'); + + }).timeout(TIMEOUT); + }); + + describe('slice', function () { + it('Gets slice of the object keys', function () { + let slice = binance.slice({ first: '1', second: '2', third: '3' }, 2); + assert.deepEqual(['third'], slice, 'should be ian array with the third'); + + }).timeout(TIMEOUT); + }); + + describe('Min', function () { + it('Gets the math min of object', function () { + binance.min({ first: '1', second: '2', third: '3' }); + + }).timeout(TIMEOUT); + }); + + describe('Max', function () { + it('Gets the math max of object', function () { + binance.max({ first: '1', second: '2', third: '3' }); + + }).timeout(TIMEOUT); + }); +}); + +describe('Set/Get options', function () { + it('Sets/Gets option to specified value', function () { + binance.setOption('test', 'value'); + assert.equal(binance.getOption('test'), 'value', 'should be value'); + + }).timeout(TIMEOUT); +}); + +describe('Get options', function () { + it('Gets all options', function () { + assert(typeof (binance.getOptions()) === 'object', 'should be object'); + + }).timeout(TIMEOUT); +}); + +describe('Percent', function () { + it('Get Percentage of two values', function () { + assert(binance.percent(25, 100) === 25, 'should be 25 percent'); + + }).timeout(TIMEOUT); +}); + +describe('Sum', function () { + it('Get sum of array of values', function () { + assert(binance.sum([1, 2, 3]) === 6, 'should be 6'); + + }).timeout(TIMEOUT); +}); + +describe('Reverse', function () { + it('Reverse the keys in an object', function () { + assert(binance.reverse({ '3': 3, '2': 2, '1': 1 }).toString() === { '1': 1, '2': 2, '3': 3 }.toString(), 'should be {\'1\': 1, \'2\': 2, \'3\': 3 }'); + + }).timeout(TIMEOUT); +}); + +describe('Array', function () { + it('Convert object to an array', function () { + let actual = binance.array({ 'a': 1, 'b': 2, 'c': 3 }); + let expected = [[NaN, 1], [NaN, 2], [NaN, 3]]; + assert.isArray(actual, 'should be an array'); + assert(actual.length === 3, 'should be of lenght 3'); + assert.deepEqual(actual, expected, 'should be both arrays with same vlaues'); + + }).timeout(TIMEOUT); +}); + +describe('sortBids', function () { + it('Sorts symbols bids and returns an object', function () { + /* let actual = binance.sortBids( 'BNBBTC' ); + debug( actual ); */ + // debug( 'todo' ); + + }); +}); + +describe('sortAsks', function () { + it('Sorts symbols asks and returns an object', function () { + //let actual = binance.sortBids( 'BNBBTC' ); + // debug( 'todo' ); + + }).timeout(TIMEOUT); +}); + +describe('Exchange Info', function () { + let async_error; + let async_data; + /*global beforeEach*/ + /*eslint no-undef: "error"*/ + // beforeEach( async function () { + // binance.exchangeInfo( function ( error, data ) { + // async_error = error; + // async_data = data; + // done( error ); + // } ) + // } ).timeout( TIMEOUT * 5 ); + + // it( 'Gets the exchange info as an object', function () { + // assert( typeof ( async_error ) === 'object', 'error should be object' ); + // assert( async_error === null, 'Error should be null' ); + // assert( typeof ( async_data ) === 'object', 'data should be object' ); + // assert( async_data !== null, 'data should not be null' ); + // assert( Object.prototype.hasOwnProperty.call( async_data, 'symbols' ), 'data should have property \'symbols\'' ); + + // let symbolMembers = [ 'status', 'orderTypes', 'icebergAllowed', 'baseAsset', 'baseAssetPrecision', 'quoteAsset', 'quotePrecision', 'quoteAssetPrecision' ]; + // async_data.symbols.forEach( function ( symbol ) { + // symbolMembers.forEach( function ( member ) { + // assert( Object.prototype.hasOwnProperty.call( symbol, member ), WARN_SHOULD_HAVE_KEY + member ); + // } ); + // } ); + // } ).timeout( TIMEOUT * 5 ); +}); + + + +describe('Account', function () { + it('Attempt to get account information', async function () { + const data = await binance.account(); + assert(typeof (data) === 'object', WARN_SHOULD_BE_OBJ); + assert(data !== null, WARN_SHOULD_BE_NOT_NULL); + + }).timeout(TIMEOUT); +}); + +describe('Time', function () { + it('Attempt to get server time', async function () { + const data = await binance.time() + assert(typeof (data) === 'object', WARN_SHOULD_BE_OBJ); + assert(data !== null, WARN_SHOULD_BE_NOT_NULL); + assert(Object.prototype.hasOwnProperty.call(data, 'serverTime'), WARN_SHOULD_HAVE_KEY + 'serverTime'); + + }).timeout(TIMEOUT); +}); + +describe('Aggtrades', function () { + it('Attempt to get aggTrades for given symbol', async function () { + const response = await binance.aggTrades('BNBBTC', { limit: 500 }); + assert(typeof (response) === 'object', WARN_SHOULD_BE_OBJ); + assert(response !== null, WARN_SHOULD_BE_NOT_NULL); + + }).timeout(TIMEOUT); +}); + +describe('Recent Trades', function () { + it('Attempt get recent Trades for a given symbol', async function () { + const data = await binance.recentTrades('BNBBTC'); + assert(typeof (data) === 'object', WARN_SHOULD_BE_OBJ); + assert(data !== null, WARN_SHOULD_BE_NOT_NULL); + assert(data.length > 0); + data.forEach(function (obj) { + assert(Object.prototype.hasOwnProperty.call(obj, 'id'), WARN_SHOULD_HAVE_KEY + 'id'); + assert(Object.prototype.hasOwnProperty.call(obj, 'price'), WARN_SHOULD_HAVE_KEY + 'price'); + assert(Object.prototype.hasOwnProperty.call(obj, 'qty'), WARN_SHOULD_HAVE_KEY + 'qty'); + assert(Object.prototype.hasOwnProperty.call(obj, 'time'), WARN_SHOULD_HAVE_KEY + 'time'); + assert(Object.prototype.hasOwnProperty.call(obj, 'isBuyerMaker'), WARN_SHOULD_HAVE_KEY + 'isBuyerMaker'); + assert(Object.prototype.hasOwnProperty.call(obj, 'isBestMatch'), WARN_SHOULD_HAVE_KEY + 'isBestMatch'); + }); + }).timeout(TIMEOUT); +}); + +describe('Historical Trades', function () { + it('Attempt get Historical Trades for a given symbol', async function () { + const data = await binance.historicalTrades('BNBBTC'); + assert(typeof (data) === 'object', WARN_SHOULD_BE_OBJ); + assert(data !== null, WARN_SHOULD_BE_NOT_NULL); + assert(data.length > 0); + data.forEach(function (obj) { + assert(Object.prototype.hasOwnProperty.call(obj, 'id'), WARN_SHOULD_HAVE_KEY + 'id'); + assert(Object.prototype.hasOwnProperty.call(obj, 'price'), WARN_SHOULD_HAVE_KEY + 'price'); + assert(Object.prototype.hasOwnProperty.call(obj, 'qty'), WARN_SHOULD_HAVE_KEY + 'qty'); + assert(Object.prototype.hasOwnProperty.call(obj, 'time'), WARN_SHOULD_HAVE_KEY + 'time'); + assert(Object.prototype.hasOwnProperty.call(obj, 'isBuyerMaker'), WARN_SHOULD_HAVE_KEY + 'isBuyerMaker'); + assert(Object.prototype.hasOwnProperty.call(obj, 'isBestMatch'), WARN_SHOULD_HAVE_KEY + 'isBestMatch'); + }); + }).timeout(TIMEOUT); +}); + +describe('getInfo', function () { + it('Gets the info array form the binance object', function () { + assert(typeof (binance.getInfo()) === 'object', 'Should be of type array') + + }).timeout(TIMEOUT); +}); + diff --git a/tests/binance-class-static.test.ts b/tests/binance-class-static.test.ts new file mode 100644 index 00000000..4edd61dc --- /dev/null +++ b/tests/binance-class-static.test.ts @@ -0,0 +1,269 @@ +import Binance from '../src/node-binance-api'; +import { assert } from 'chai'; +import util from 'util'; +import nock from 'nock'; + +const binance = new Binance({ + APIKEY: 'XXXXXXXXXXXXXXXXXXXXXXX', + APISECRET: 'YYYYYYYYYYYYYYYYYYYYYY', +}) + + +function urlToObject(queryString) { + const params = new URLSearchParams(queryString); + const obj = Object.fromEntries(params.entries()); + return obj; +} + +describe( 'Static tests', async function () { + + let interceptedUrl = null; + let interceptedBody = null; + + beforeEach(() => { + + interceptedUrl = null; + interceptedBody = null; + nock(/.*/) + .get(/.*/) + .reply(200, function (uri, requestBody) { + interceptedUrl = `${this.req.options.proto}://${this.req.options.hostname}${uri}`; + interceptedBody = requestBody; // Capture the request body + return { success: true }; + }); + nock(/.*/) + .post(/.*/) + .reply(200, function (uri, requestBody) { + interceptedUrl = `${this.req.options.proto}://${this.req.options.hostname}${uri}`; + interceptedBody = requestBody; // Capture the request body + return { success: true }; + }); + nock(/.*/) + .delete(/.*/) + .reply(200, function (uri, requestBody) { + interceptedUrl = `${this.req.options.proto}://${this.req.options.hostname}${uri}`; + interceptedBody = requestBody; // Capture the request body + return { success: true }; + }); + }); + + it( 'FetchTicker', async function ( ) { + await binance.prices( 'BNBBTC' ) + assert.equal( interceptedUrl, 'https://api.binance.com/api/v3/ticker/price?symbol=BNBBTC' ) + }) + + it( 'FetchOrderBook', async function ( ) { + await binance.depth( 'BTCUSDT' ) + assert.equal( interceptedUrl, 'https://api.binance.com/api/v3/depth?symbol=BTCUSDT&limit=100' ) + + }) + + it( 'Futures OrderBook', async function ( ) { + await binance.futuresDepth( 'BTCUSDT' ) + assert.equal( interceptedUrl, 'https://fapi.binance.com/fapi/v1/depth?symbol=BTCUSDT' ) + + }) + + it( 'OHLCVS', async function ( ) { + await binance.candlesticks( 'BTCUSDT' ) + assert.equal( interceptedUrl, 'https://api.binance.com/api/v3/klines?symbol=BTCUSDT&interval=5m&limit=500' ) + + }) + + it( 'Futures OHLCVS', async function ( ) { + await binance.futuresCandles( 'BTCUSDT' ) + assert.equal( interceptedUrl, 'https://fapi.binance.com/fapi/v1/klines?symbol=BTCUSDT&interval=30m' ) + + }) + + it( 'Trades', async function ( ) { + await binance.aggTrades( 'BTCUSDT' ) + assert.equal( interceptedUrl, 'https://api.binance.com/api/v3/aggTrades?symbol=BTCUSDT' ) + + }) + + it( 'FuturesTrades', async function ( ) { + await binance.futuresTrades( 'BTCUSDT' ) + assert.equal( interceptedUrl, 'https://fapi.binance.com/fapi/v1/trades?symbol=BTCUSDT' ) + + }) + + it( 'PositionRisk V3', async function ( ) { + await binance.futuresPositionRisk() + assert.isTrue( interceptedUrl.startsWith('https://fapi.binance.com/fapi/v3/positionRisk') ) + + }) + + it( 'PositionRisk V2', async function ( ) { + await binance.futuresPositionRiskV2() + assert.isTrue( interceptedUrl.startsWith('https://fapi.binance.com/fapi/v2/positionRisk') ) + + }) + + it( 'CancelOrder', async function ( ) { + await binance.cancel( 'LTCUSDT', '34234234' ) + assert( interceptedUrl.startsWith('https://api.binance.com/api/v3/order' )) + const obj = urlToObject( interceptedUrl.replace('https://api.binance.com/api/v3/order', '') ) + assert.equal( obj.symbol, 'LTCUSDT' ) + assert.equal( obj.orderId, '34234234') + }) + + it( 'Futures CancelOrder', async function ( ) { + await binance.futuresCancel( 'LTCUSDT', '34234234') + assert( interceptedUrl.startsWith('https://fapi.binance.com/fapi/v1/order')) + const obj = urlToObject( interceptedUrl.replace('https://fapi.binance.com/fapi/v1/order', '') ) + assert.equal( obj.symbol, 'LTCUSDT' ) + assert.equal( obj.orderId, '34234234') + }) + + const SPOT_PREFIX = "x-HNA2TXFJ" + + it( 'MarketBuy', async function ( ) { + await binance.marketBuy( 'LTCUSDT', 0.5 ) + assert.equal( interceptedUrl, 'https://api.binance.com/api/v3/order' ) + const obj = urlToObject( interceptedBody ) + assert.equal( obj.symbol, 'LTCUSDT' ) + assert.equal( obj.side, 'BUY' ) + assert.equal( obj.type, 'MARKET' ) + assert.equal( obj.quantity, 0.5 ) + assert(obj.newClientOrderId.startsWith(SPOT_PREFIX)) + }) + + it( 'MarketSell', async function ( ) { + await binance.marketSell( 'LTCUSDT', 0.5 ) + assert.equal( interceptedUrl, 'https://api.binance.com/api/v3/order' ) + const obj = urlToObject( interceptedBody ) + assert.equal( obj.symbol, 'LTCUSDT' ) + assert.equal( obj.side, 'SELL' ) + assert.equal( obj.type, 'MARKET' ) + assert.equal( obj.quantity, 0.5 ) + assert(obj.newClientOrderId.startsWith(SPOT_PREFIX)) + }) + + it( 'LimitBuy', async function ( ) { + await binance.order('LIMIT', 'BUY', 'LTCUSDT', 0.5 ) + assert.equal( interceptedUrl, 'https://api.binance.com/api/v3/order' ) + const obj = urlToObject( interceptedBody ) + assert.equal( obj.symbol, 'LTCUSDT' ) + assert.equal( obj.side, 'BUY' ) + assert.equal( obj.type, 'LIMIT' ) + assert.equal( obj.quantity, 0.5 ) + assert(obj.newClientOrderId.startsWith(SPOT_PREFIX)) + }) + + it( 'LimitSell', async function ( ) { + await binance.order('LIMIT', 'SELL', 'LTCUSDT', 0.5 ) + assert.equal( interceptedUrl, 'https://api.binance.com/api/v3/order' ) + const obj = urlToObject( interceptedBody ) + assert.equal( obj.symbol, 'LTCUSDT' ) + assert.equal( obj.side, 'SELL' ) + assert.equal( obj.type, 'LIMIT' ) + assert.equal( obj.quantity, 0.5 ) + assert(obj.newClientOrderId.startsWith(SPOT_PREFIX)) + }) + + it( 'cancel order', async function ( ) { + await binance.cancel( 'LTCUSDT', '34234234' ) + const url = 'https://api.binance.com/api/v3/order' + assert.isTrue( interceptedUrl.startsWith(url) ) + const obj = urlToObject( interceptedUrl.replace(url, '') ) + assert.equal( obj.orderId, '34234234' ) + assert.equal( obj.symbol, 'LTCUSDT' ) + }) + + const CONTRACT_PREFIX = "x-Cb7ytekJ" + + it( 'Futures MarketBuy', async function ( ) { + await binance.futuresMarketBuy( 'LTCUSDT', 0.5 ) + assert.isTrue( interceptedUrl.startsWith('https://fapi.binance.com/fapi/v1/order' )) + const obj = urlToObject( interceptedUrl.replace('https://fapi.binance.com/fapi/v1/order?', '') ) + assert.equal( obj.symbol, 'LTCUSDT' ) + assert.equal( obj.side, 'BUY' ) + assert.equal( obj.type, 'MARKET' ) + assert.equal( obj.quantity, 0.5 ) + assert(obj.newClientOrderId.startsWith(CONTRACT_PREFIX)) + }) + + it( 'Futures MarketSell', async function ( ) { + await binance.futuresMarketSell( 'LTCUSDT', 0.5 ) + assert.isTrue( interceptedUrl.startsWith('https://fapi.binance.com/fapi/v1/order' )) + const obj = urlToObject( interceptedUrl.replace('https://fapi.binance.com/fapi/v1/order?', '') ) + assert.equal( obj.symbol, 'LTCUSDT' ) + assert.equal( obj.side, 'SELL' ) + assert.equal( obj.type, 'MARKET' ) + assert.equal( obj.quantity, 0.5 ) + assert(obj.newClientOrderId.startsWith(CONTRACT_PREFIX)) + }) + + it( 'Futures LimitBuy', async function ( ) { + await binance.futuresOrder('LIMIT', 'BUY', 'LTCUSDT', 0.5, 100 ) + assert.isTrue( interceptedUrl.startsWith('https://fapi.binance.com/fapi/v1/order' )) + const obj = urlToObject( interceptedUrl.replace('https://fapi.binance.com/fapi/v1/order?', '') ) + assert.equal( obj.symbol, 'LTCUSDT' ) + assert.equal( obj.side, 'BUY' ) + assert.equal( obj.type, 'LIMIT' ) + assert.equal( obj.quantity, 0.5 ) + assert(obj.newClientOrderId.startsWith(CONTRACT_PREFIX)) + }) + + it( 'Futures LimitSell', async function ( ) { + await binance.futuresOrder('LIMIT', 'SELL', 'LTCUSDT', 0.5, 100 ) + assert.isTrue( interceptedUrl.startsWith('https://fapi.binance.com/fapi/v1/order' )) + const obj = urlToObject( interceptedUrl.replace('https://fapi.binance.com/fapi/v1/order?', '') ) + assert.equal( obj.symbol, 'LTCUSDT' ) + assert.equal( obj.side, 'SELL' ) + assert.equal( obj.type, 'LIMIT' ) + assert.equal( obj.quantity, 0.5 ) + assert(obj.newClientOrderId.startsWith(CONTRACT_PREFIX)) + }) + + it( 'cancel order', async function ( ) { + await binance.futuresCancel( 'LTCUSDT', '34234234' ) + const url = 'https://fapi.binance.com/fapi/v1/order' + assert.isTrue( interceptedUrl.startsWith(url) ) + const obj = urlToObject( interceptedUrl.replace(url, '') ) + assert.equal( obj.orderId, '34234234' ) + assert.equal( obj.symbol, 'LTCUSDT' ) + }) + + it( 'MarketBuy test', async function ( ) { + await binance.marketBuy( 'LTCUSDT', 0.5, {'test': true}) + assert.equal( interceptedUrl, 'https://api.binance.com/api/v3/order/test' ) + const obj = urlToObject( interceptedBody ) + assert.equal( obj.symbol, 'LTCUSDT' ) + assert.equal( obj.side, 'BUY' ) + assert.equal( obj.type, 'MARKET' ) + assert.equal( obj.quantity, 0.5 ) + assert(obj.newClientOrderId.startsWith(SPOT_PREFIX)) + }) + + it( 'spot order with custom clientorderId', async function ( ) { + await binance.order( 'LIMIT', 'BUY', 'LTCUSDT', 0.5, 100, {'newClientOrderId': 'myid'}) + assert.equal( interceptedUrl, 'https://api.binance.com/api/v3/order' ) + const obj = urlToObject( interceptedBody ) + assert.equal( obj.symbol, 'LTCUSDT' ) + assert.equal( obj.side, 'BUY' ) + assert.equal( obj.type, 'LIMIT' ) + assert.equal( obj.quantity, 0.5 ) + assert.equal( obj.price, 100 ) + assert.equal( obj.newClientOrderId, 'myid') + }) + + it( 'delivery OrderBook', async function ( ) { + await binance.deliveryDepth( 'BTCUSD_PERP' ) + assert.equal( interceptedUrl, 'https://dapi.binance.com/dapi/v1/depth?symbol=BTCUSD_PERP' ) + + }) + + it( 'delivery MarketBuy', async function ( ) { + await binance.deliveryOrder( 'MARKET', 'BUY', 'BTCUSD_PERP', 0.1 ) + assert.isTrue( interceptedUrl.startsWith('https://dapi.binance.com/dapi/v1/order' )) + const obj = urlToObject( interceptedUrl.replace('https://dapi.binance.com/dapi/v1/order', '') ) + assert.equal( obj.symbol, 'BTCUSD_PERP' ) + assert.equal( obj.side, 'BUY' ) + assert.equal( obj.type, 'MARKET' ) + assert.equal( obj.quantity, 0.1 ) + assert(obj.newClientOrderId.startsWith(CONTRACT_PREFIX)) + }) + +}) \ No newline at end of file diff --git a/tests/binance-ws-futures.test.ts b/tests/binance-ws-futures.test.ts new file mode 100644 index 00000000..7f7d4d98 --- /dev/null +++ b/tests/binance-ws-futures.test.ts @@ -0,0 +1,177 @@ +import Binance from '../src/node-binance-api'; +import { assert } from 'chai'; +import util from 'util'; + +const WARN_SHOULD_BE_OBJ = 'should be an object'; +const WARN_SHOULD_BE_NULL = 'should be null'; +const WARN_SHOULD_BE_NOT_NULL = 'should not be null'; +const WARN_SHOULD_HAVE_KEY = 'should have key '; +const WARN_SHOULD_NOT_HAVE_KEY = 'should not have key '; +const WARN_SHOULD_BE_UNDEFINED = 'should be undefined'; +const WARN_SHOULD_BE_TYPE = 'should be a '; +const TIMEOUT = 40000; + + +const futuresBinance = new Binance().options({ + APIKEY: '227719da8d8499e8d3461587d19f259c0b39c2b462a77c9b748a6119abd74401', + APISECRET: 'b14b935f9cfacc5dec829008733c40da0588051f29a44625c34967b45c11d73c', + hedgeMode: true, + test: true +}); + + +const stopSockets = function ( log = false ) { + let endpoints = futuresBinance.websockets.subscriptions(); + for ( let endpoint in endpoints ) { + if ( log ) console.log( 'Terminated ws endpoint: ' + endpoint ); + futuresBinance.websockets.terminate( endpoint ); + } +} + +describe( 'Websockets candlesticks', function () { + let candlesticks; + let cnt = 0; + /*global beforeEach*/ + beforeEach( function ( done ) { + this.timeout( TIMEOUT ); + futuresBinance.futuresCandlesticksStream( [ 'BTCUSDT' ], '1m', a_candlesticks => { + cnt++; + if ( cnt > 1 ) return; + candlesticks = a_candlesticks; + stopSockets(); + done(); + } ); + } ); + + it( 'Calls spot candlesticks websocket', function () { + assert( typeof ( candlesticks ) === 'object', WARN_SHOULD_BE_OBJ ); + assert( candlesticks !== null, WARN_SHOULD_BE_NOT_NULL ); + assert( Object.keys( candlesticks ).length >= 0, 'should at least 1 currency pairs?' ); + + let keys = [ 't', 'T', 's', 'i', 'f', 'L', 'o', 'c', 'h', 'l', 'v', 'n', 'x', 'q', 'V', 'Q', 'B' ]; + assert( Object.prototype.hasOwnProperty.call( candlesticks, 'e' ), WARN_SHOULD_HAVE_KEY + 'e' ); + assert( Object.prototype.hasOwnProperty.call( candlesticks, 'E' ), WARN_SHOULD_HAVE_KEY + 'E' ); + assert( Object.prototype.hasOwnProperty.call( candlesticks, 's' ), WARN_SHOULD_HAVE_KEY + 's' ); + assert( Object.prototype.hasOwnProperty.call( candlesticks, 's' ), WARN_SHOULD_HAVE_KEY + 'k' ); + + keys.forEach( function ( key ) { + assert( Object.prototype.hasOwnProperty.call( candlesticks.k, key ), WARN_SHOULD_HAVE_KEY + key ); + } ); + } ); +} ); + +describe( 'Websockets futures ticker stream', function () { + let response; + let cnt = 0; + + beforeEach( function ( done ) { + this.timeout( TIMEOUT ); + futuresBinance.websockets.futuresTicker( undefined, a_response => { + cnt++; + if ( cnt > 1 ) return; + stopSockets(); + response = a_response; + done(); + } ) + } ); + + it( 'Calls prevDay websocket for symbol', function () { + assert( typeof ( response ) === 'object', WARN_SHOULD_BE_OBJ ); + } ); +} ); + + +describe( 'Websockets miniticker', function () { + let markets; + let cnt = 0; + beforeEach( function ( done ) { + this.timeout( TIMEOUT ); + futuresBinance.websockets.futuresMiniTicker( undefined, tick => { + cnt++; + if ( cnt > 1 ) return; + markets = tick; + stopSockets(); + done(); + } ); + } ); + + it( 'check miniticker websocket', function () { + assert( typeof ( markets ) === 'object', WARN_SHOULD_BE_OBJ ); + assert( markets !== null, WARN_SHOULD_BE_NOT_NULL ); + assert( Object.keys( markets ).length >= 0, 'should at least 1 currency pairs?' ); + + Object.keys( markets ).forEach( function ( symbol ) { + assert( Object.prototype.hasOwnProperty.call( markets[symbol], 'close' ), WARN_SHOULD_HAVE_KEY + 'close' ); + assert( Object.prototype.hasOwnProperty.call( markets[symbol], 'open' ), WARN_SHOULD_HAVE_KEY + 'open' ); + assert( Object.prototype.hasOwnProperty.call( markets[symbol], 'high' ), WARN_SHOULD_HAVE_KEY + 'high' ); + assert( Object.prototype.hasOwnProperty.call( markets[symbol], 'low' ), WARN_SHOULD_HAVE_KEY + 'low' ); + assert( Object.prototype.hasOwnProperty.call( markets[symbol], 'volume' ), WARN_SHOULD_HAVE_KEY + 'volume' ); + assert( Object.prototype.hasOwnProperty.call( markets[symbol], 'quoteVolume' ), WARN_SHOULD_HAVE_KEY + 'quoteVolume' ); + assert( Object.prototype.hasOwnProperty.call( markets[symbol], 'eventTime' ), WARN_SHOULD_HAVE_KEY + 'eventTime' ); + } ); + } ); +} ); + + + +describe( 'Websockets chart', function () { + let chart; + let interval; + let symbol; + let cnt = 0; + beforeEach( function ( done ) { + this.timeout( TIMEOUT ); + futuresBinance.websockets.futuresChart( 'BTCUSDT', '1m', ( a_symbol, a_interval, a_chart ) => { + cnt++; + if ( cnt > 1 ) { + stopSockets(); + return; + } + chart = a_chart; + interval = a_interval; + symbol = a_symbol; + stopSockets(); + done(); + } ); + } ); + + it( 'Calls chart websocket', function () { + assert( typeof ( chart ) === 'object', WARN_SHOULD_BE_OBJ ); + assert( typeof ( symbol ) === 'string', WARN_SHOULD_BE_OBJ ); + assert( typeof ( interval ) === 'string', WARN_SHOULD_BE_OBJ ); + assert( chart !== null, WARN_SHOULD_BE_NOT_NULL ); + assert( symbol !== null, WARN_SHOULD_BE_NOT_NULL ); + assert( interval !== null, WARN_SHOULD_BE_NOT_NULL ); + + let keys = [ 'open', 'high', 'open', 'close', 'volume' ]; + assert( Object.keys( chart ).length > 0, 'Should not be empty' ); + + Object.keys( chart ).forEach( function ( c ) { + keys.forEach( function ( key ) { + assert( Object.prototype.hasOwnProperty.call( chart[c], key ), WARN_SHOULD_HAVE_KEY + key ); + } ); + } ); + } ); +} ); + + +describe( 'Websockets aggregated trades', function () { + let trades; + let cnt = 0; + /*global beforeEach*/ + beforeEach( function ( done ) { + this.timeout( TIMEOUT ); + futuresBinance.websockets.futuresAggTrades( [ 'BTCUSDT', 'ETHBTC' ], e_trades => { + cnt++; + if ( cnt > 1 ) return; + trades = e_trades; + stopSockets(); + done(); + } ); + } ); + + it( 'Calls trades websocket', function () { + assert( typeof ( trades ) === 'object', WARN_SHOULD_BE_OBJ ); + assert( trades !== null, WARN_SHOULD_BE_NOT_NULL ); + } ); +} ); \ No newline at end of file diff --git a/tests/binance-ws-spot.test.ts b/tests/binance-ws-spot.test.ts new file mode 100644 index 00000000..b1cfaa58 --- /dev/null +++ b/tests/binance-ws-spot.test.ts @@ -0,0 +1,360 @@ +import Binance from '../src/node-binance-api'; +import { assert } from 'chai'; +import util from 'util'; + +const WARN_SHOULD_BE_OBJ = 'should be an object'; +const WARN_SHOULD_BE_NULL = 'should be null'; +const WARN_SHOULD_BE_NOT_NULL = 'should not be null'; +const WARN_SHOULD_HAVE_KEY = 'should have key '; +const WARN_SHOULD_NOT_HAVE_KEY = 'should not have key '; +const WARN_SHOULD_BE_UNDEFINED = 'should be undefined'; +const WARN_SHOULD_BE_TYPE = 'should be a '; +const TIMEOUT = 40000; + + +const binance = new Binance().options({ + APIKEY: 'X4BHNSimXOK6RKs2FcKqExquJtHjMxz5hWqF0BBeVnfa5bKFMk7X0wtkfEz0cPrJ', + APISECRET: 'x8gLihunpNq0d46F2q0TWJmeCDahX5LMXSlv3lSFNbMI3rujSOpTDKdhbcmPSf2i', + test: true +}); + +const futuresBinance = new Binance().options({ + APIKEY: '227719da8d8499e8d3461587d19f259c0b39c2b462a77c9b748a6119abd74401', + APISECRET: 'b14b935f9cfacc5dec829008733c40da0588051f29a44625c34967b45c11d73c', + hedgeMode: true, + test: true +}); + +const stopSockets = function ( log = false ) { + let endpoints = binance.websockets.subscriptions(); + for ( let endpoint in endpoints ) { + if ( log ) console.log( 'Terminated ws endpoint: ' + endpoint ); + binance.websockets.terminate( endpoint ); + } +} + +describe( 'Websockets candlesticks', function () { + let candlesticks; + let cnt = 0; + /*global beforeEach*/ + beforeEach( function ( done ) { + this.timeout( TIMEOUT ); + binance.websockets.candlesticks( [ 'BTCUSDT' ], '1m', a_candlesticks => { + cnt++; + if ( cnt > 1 ) return; + candlesticks = a_candlesticks; + stopSockets(); + done(); + } ); + } ); + + it( 'Calls spot candlesticks websocket', function () { + assert( typeof ( candlesticks ) === 'object', WARN_SHOULD_BE_OBJ ); + assert( candlesticks !== null, WARN_SHOULD_BE_NOT_NULL ); + assert( Object.keys( candlesticks ).length >= 0, 'should at least 1 currency pairs?' ); + + let keys = [ 't', 'T', 's', 'i', 'f', 'L', 'o', 'c', 'h', 'l', 'v', 'n', 'x', 'q', 'V', 'Q', 'B' ]; + assert( Object.prototype.hasOwnProperty.call( candlesticks, 'e' ), WARN_SHOULD_HAVE_KEY + 'e' ); + assert( Object.prototype.hasOwnProperty.call( candlesticks, 'E' ), WARN_SHOULD_HAVE_KEY + 'E' ); + assert( Object.prototype.hasOwnProperty.call( candlesticks, 's' ), WARN_SHOULD_HAVE_KEY + 's' ); + assert( Object.prototype.hasOwnProperty.call( candlesticks, 's' ), WARN_SHOULD_HAVE_KEY + 'k' ); + + keys.forEach( function ( key ) { + assert( Object.prototype.hasOwnProperty.call( candlesticks.k, key ), WARN_SHOULD_HAVE_KEY + key ); + } ); + } ); +} ); + +describe( 'Websockets prevDay', function () { + let response; + let cnt = 0; + + beforeEach( function ( done ) { + this.timeout( TIMEOUT ); + binance.websockets.prevDay( undefined, a_response => { + cnt++; + if ( cnt > 1 ) return; + stopSockets(); + response = a_response; + done(); + } ) + } ); + + it( 'Calls prevDay websocket for symbol', function () { + assert( typeof ( response ) === 'object', WARN_SHOULD_BE_OBJ ); + } ); +} ); + + +describe( 'Websockets miniticker', function () { + let markets; + let cnt = 0; + beforeEach( function ( done ) { + this.timeout( TIMEOUT ); + binance.websockets.miniTicker( tick => { + cnt++; + if ( cnt > 1 ) return; + markets = tick; + stopSockets(); + done(); + } ); + } ); + + it( 'check miniticker websocket', function () { + assert( typeof ( markets ) === 'object', WARN_SHOULD_BE_OBJ ); + assert( markets !== null, WARN_SHOULD_BE_NOT_NULL ); + assert( Object.keys( markets ).length >= 0, 'should at least 1 currency pairs?' ); + + Object.keys( markets ).forEach( function ( symbol ) { + assert( Object.prototype.hasOwnProperty.call( markets[symbol], 'close' ), WARN_SHOULD_HAVE_KEY + 'close' ); + assert( Object.prototype.hasOwnProperty.call( markets[symbol], 'open' ), WARN_SHOULD_HAVE_KEY + 'open' ); + assert( Object.prototype.hasOwnProperty.call( markets[symbol], 'high' ), WARN_SHOULD_HAVE_KEY + 'high' ); + assert( Object.prototype.hasOwnProperty.call( markets[symbol], 'low' ), WARN_SHOULD_HAVE_KEY + 'low' ); + assert( Object.prototype.hasOwnProperty.call( markets[symbol], 'volume' ), WARN_SHOULD_HAVE_KEY + 'volume' ); + assert( Object.prototype.hasOwnProperty.call( markets[symbol], 'quoteVolume' ), WARN_SHOULD_HAVE_KEY + 'quoteVolume' ); + assert( Object.prototype.hasOwnProperty.call( markets[symbol], 'eventTime' ), WARN_SHOULD_HAVE_KEY + 'eventTime' ); + } ); + } ); +} ); + + +describe( 'Websockets symbol depthcache', function () { + let symbol; + let bids; + let asks; + let cnt = 0; + beforeEach( function ( done ) { + this.timeout( TIMEOUT ); + binance.websockets.depthCache( 'BTCUSDT', ( a_symbol, a_depth ) => { + cnt++; + if ( cnt > 1 ) return; + stopSockets( true ); + symbol = a_symbol; + bids = a_depth.bids; + asks = a_depth.asks; + done(); + } ); + } ); + + bids = binance.sortBids( bids ); + asks = binance.sortAsks( asks ); + + it( 'check result of depth cache', function () { + assert( typeof ( bids ) === 'object', WARN_SHOULD_BE_OBJ ); + assert( typeof ( asks ) === 'object', WARN_SHOULD_BE_OBJ ); + assert( typeof ( symbol ) === 'string', WARN_SHOULD_BE_OBJ ); + assert( bids !== null, WARN_SHOULD_BE_NOT_NULL ); + assert( asks !== null, WARN_SHOULD_BE_NOT_NULL ); + assert( symbol !== null, WARN_SHOULD_BE_NOT_NULL ); + assert( Object.keys( asks ).length !== 0, 'should not be 0' ); + assert( Object.keys( bids ).length !== 0, 'should not be 0' ); + } ); +} ); + + +describe( 'Websockets array depthcache', function () { + let symbol; + let bids; + let asks; + let cnt = 0; + beforeEach( function ( done ) { + this.timeout( TIMEOUT ); + binance.websockets.depthCache( [ 'BTCUSDT', 'ETHUSDT' ], ( a_symbol, a_depth ) => { + cnt++; + if ( cnt > 1 ) return; + stopSockets(); + symbol = a_symbol; + bids = a_depth.bids; + asks = a_depth.asks; + done(); + } ); + } ); + + bids = binance.sortBids( bids ); + asks = binance.sortAsks( asks ); + + it( 'check result of symbols array depth cache', function () { + assert( typeof ( bids ) === 'object', WARN_SHOULD_BE_OBJ ); + assert( typeof ( asks ) === 'object', WARN_SHOULD_BE_OBJ ); + assert( typeof ( symbol ) === 'string', WARN_SHOULD_BE_OBJ ); + assert( bids !== null, WARN_SHOULD_BE_NOT_NULL ); + assert( asks !== null, WARN_SHOULD_BE_NOT_NULL ); + assert( symbol !== null, WARN_SHOULD_BE_NOT_NULL ); + assert( Object.keys( asks ).length !== 0, 'should not be 0' ); + assert( Object.keys( bids ).length !== 0, 'should not be 0' ); + } ); +} ); + + +describe( 'Staggered websockets symbol depthcache', function () { + let symbol; + let bids; + let asks; + let cnt = 0; + beforeEach( function ( done ) { + this.timeout( TIMEOUT ); + binance.websockets.depthCacheStaggered( 'BTCUSDT', ( a_symbol, a_depth ) => { + cnt++; + if ( cnt > 1 ) return; + stopSockets( true ); + symbol = a_symbol; + bids = a_depth.bids; + asks = a_depth.asks; + done(); + } ); + } ); + + bids = binance.sortBids( bids ); + asks = binance.sortAsks( asks ); + + it( 'check result of depth cache', function () { + assert( typeof ( bids ) === 'object', WARN_SHOULD_BE_OBJ ); + assert( typeof ( asks ) === 'object', WARN_SHOULD_BE_OBJ ); + assert( typeof ( symbol ) === 'string', WARN_SHOULD_BE_OBJ ); + assert( bids !== null, WARN_SHOULD_BE_NOT_NULL ); + assert( asks !== null, WARN_SHOULD_BE_NOT_NULL ); + assert( symbol !== null, WARN_SHOULD_BE_NOT_NULL ); + assert( Object.keys( asks ).length !== 0, 'should not be 0' ); + assert( Object.keys( bids ).length !== 0, 'should not be 0' ); + } ); +} ); + + +describe( 'Staggered Websockets array depthcache', function () { + let symbol; + let bids; + let asks; + let cnt = 0; + beforeEach( function ( done ) { + this.timeout( TIMEOUT ); + binance.websockets.depthCacheStaggered( [ 'BTCUSDT', 'ETHUSDT' ], ( a_symbol, a_depth ) => { + cnt++; + if ( cnt > 1 ) return; + stopSockets(); + symbol = a_symbol; + bids = a_depth.bids; + asks = a_depth.asks; + done(); + } ); + } ); + + bids = binance.sortBids( bids ); + asks = binance.sortAsks( asks ); + + it( 'check result of symbols array depth cache', function () { + assert( typeof ( bids ) === 'object', WARN_SHOULD_BE_OBJ ); + assert( typeof ( asks ) === 'object', WARN_SHOULD_BE_OBJ ); + assert( typeof ( symbol ) === 'string', WARN_SHOULD_BE_OBJ ); + assert( bids !== null, WARN_SHOULD_BE_NOT_NULL ); + assert( asks !== null, WARN_SHOULD_BE_NOT_NULL ); + assert( symbol !== null, WARN_SHOULD_BE_NOT_NULL ); + assert( Object.keys( asks ).length !== 0, 'should not be 0' ); + assert( Object.keys( bids ).length !== 0, 'should not be 0' ); + } ); +} ); + + +describe( 'Websockets chart', function () { + let chart; + let interval; + let symbol; + let cnt = 0; + beforeEach( function ( done ) { + this.timeout( TIMEOUT ); + binance.websockets.chart( 'BTCUSDT', '1m', ( a_symbol, a_interval, a_chart ) => { + cnt++; + if ( cnt > 1 ) { + stopSockets(); + return; + } + chart = a_chart; + interval = a_interval; + symbol = a_symbol; + stopSockets(); + done(); + } ); + } ); + + it( 'Calls chart websocket', function () { + assert( typeof ( chart ) === 'object', WARN_SHOULD_BE_OBJ ); + assert( typeof ( symbol ) === 'string', WARN_SHOULD_BE_OBJ ); + assert( typeof ( interval ) === 'string', WARN_SHOULD_BE_OBJ ); + assert( chart !== null, WARN_SHOULD_BE_NOT_NULL ); + assert( symbol !== null, WARN_SHOULD_BE_NOT_NULL ); + assert( interval !== null, WARN_SHOULD_BE_NOT_NULL ); + + let keys = [ 'open', 'high', 'open', 'close', 'volume' ]; + assert( Object.keys( chart ).length > 0, 'Should not be empty' ); + + Object.keys( chart ).forEach( function ( c ) { + keys.forEach( function ( key ) { + assert( Object.prototype.hasOwnProperty.call( chart[c], key ), WARN_SHOULD_HAVE_KEY + key ); + } ); + } ); + } ); +} ); + + +describe( 'Websockets depth', function () { + let depth; + let cnt = 0; + /*global beforeEach*/ + beforeEach( function ( done ) { + this.timeout( TIMEOUT ); + binance.websockets.depth( [ 'BNBBTC' ], e_depth => { + cnt++; + if ( cnt > 1 ) return; + depth = e_depth; + stopSockets(); + done(); + } ); + } ); + + it( 'Calls depth websocket', function () { + assert( typeof ( depth ) === 'object', WARN_SHOULD_BE_OBJ ); + assert( depth !== null, WARN_SHOULD_BE_NOT_NULL ); + } ); +} ); + +describe( 'Websockets aggregated trades', function () { + let trades; + let cnt = 0; + /*global beforeEach*/ + beforeEach( function ( done ) { + this.timeout( TIMEOUT ); + binance.websockets.aggTrades( [ 'BNBBTC', 'ETHBTC' ], e_trades => { + cnt++; + if ( cnt > 1 ) return; + trades = e_trades; + stopSockets(); + done(); + } ); + } ); + + it( 'Calls trades websocket', function () { + assert( typeof ( trades ) === 'object', WARN_SHOULD_BE_OBJ ); + assert( trades !== null, WARN_SHOULD_BE_NOT_NULL ); + } ); +} ); + + +describe( 'Websockets (raw) trades', function () { + let trades; + let cnt = 0; + /*global beforeEach*/ + beforeEach( function ( done ) { + this.timeout( TIMEOUT ); + binance.websockets.trades( [ 'BNBBTC', 'ETHBTC' ], e_trades => { + cnt++; + if ( cnt > 1 ) return; + trades = e_trades; + stopSockets(); + done(); + } ); + } ); + + it( 'Calls trades websocket', function () { + assert( typeof ( trades ) === 'object', WARN_SHOULD_BE_OBJ ); + assert( trades !== null, WARN_SHOULD_BE_NOT_NULL ); + } ); +} ); \ No newline at end of file diff --git a/tests/cjs-test.cjs b/tests/cjs-test.cjs new file mode 100644 index 00000000..324782e4 --- /dev/null +++ b/tests/cjs-test.cjs @@ -0,0 +1,37 @@ +const path = require( 'path' ); +const chai = require( 'chai' ); +const assert = chai.assert; +const Binance = require( path.resolve( __dirname, '../dist/cjs/node-binance-api.cjs' ) ); + +const apiKey = 'XXXXXXXXXXXXXXXX' +const apiSecret = 'YYYYYYYYYYYYYYYYYYYYYY' + +const binanceWithoutNew = new Binance().options({ + APIKEY: apiKey, + APISECRET: apiSecret, +}) + +assert(binanceWithoutNew.getOptions().APIKEY === apiKey) +assert(binanceWithoutNew.getOptions().APISECRET === apiSecret) + +const binanceWithOptions = new Binance().options({ + APIKEY: apiKey, + APISECRET: apiSecret, +}) + +assert(binanceWithOptions.getOptions().APIKEY === apiKey) +assert(binanceWithOptions.getOptions().APISECRET === apiSecret) + +const binance = new Binance({ + APIKEY: apiKey, + APISECRET: apiSecret, +}); + +assert(binance.getOptions().APIKEY === apiKey) +assert(binance.getOptions().APISECRET === apiSecret) + + +// async function main() { +// const ticker = await binanceWithOptions.prices( 'BNBBTC' ) +// console.log(ticker) +// } diff --git a/tests/crypto.test.ts b/tests/crypto.test.ts new file mode 100644 index 00000000..3b1c5149 --- /dev/null +++ b/tests/crypto.test.ts @@ -0,0 +1,50 @@ +import Binance from '../src/node-binance-api'; +import { assert } from 'chai'; + + +const testCases = [ + { + "description": "Unencrypted PKCS8 ed22519 private key", + "private_key": "-----BEGIN PRIVATE KEY-----\nMC4CAQAwBQYDK2VwBCIEIPQmzwVKJETqVd7L9E/DFbkvrOigy1tLL+9QF0mSn6dV\n-----END PRIVATE KEY-----\n", + "password": undefined, + "expected_signature": "a4Pm3p02D2HXtNfo3DBaVCe9Ov7kledewgYtGjekotFmZ5wXa3mC5AtLB7CpAphyNjeyovIuDP+9fyjYmsojCw==", + }, + { + "description": "Unencrypted PKCS8 ed22519 private key in bytes", + "private_key": "-----BEGIN PRIVATE KEY-----\nMC4CAQAwBQYDK2VwBCIEIPQmzwVKJETqVd7L9E/DFbkvrOigy1tLL+9QF0mSn6dV\n-----END PRIVATE KEY-----\n", + "password": undefined, + "expected_signature": "a4Pm3p02D2HXtNfo3DBaVCe9Ov7kledewgYtGjekotFmZ5wXa3mC5AtLB7CpAphyNjeyovIuDP+9fyjYmsojCw==", + }, + { + "description": "Encrypted PKCS8 RSA private key", + "private_key": "-----BEGIN ENCRYPTED PRIVATE KEY-----\nMIIFNTBfBgkqhkiG9w0BBQ0wUjAxBgkqhkiG9w0BBQwwJAQQWW+iEMYYCPUntrPq\nZ2RCMAICCAAwDAYIKoZIhvcNAgkFADAdBglghkgBZQMEASoEEIw3ViSuTp8JeN43\n5VGlHt0EggTQBvEzd2w2F561CzU+MDouZDOPj4RTIStC471z0/bxTgYqH3gYchoe\nOfi2lsLuD8B+ivIRuXB8GT66BIseIOMV8t/tiMe97rFI/cV4h6DrBO1xlmSrBG97\nvFF9qPA5yPRlrHtWKkGxhXteNVsT3w/7Y7KsulO/gA2KpsOElMElOhUP462Yd0Wl\nOxAIV3+knl2niozws2Kq3EdzTF3N6hlavUPryiU/w4RRsPN5qgjchVVLq/sYRYhx\nN8uWJbkjhCcHsULkD5KkdgddR0VOhpQPXIdY+gPkSBJq1ltRWy/TYdXiU2fEBNZW\nhFUVrxnS76+u2R3vukY2IAX8zTC6h2AbCBG+r4XXzgk/l/4peySKHsPQRzQ0in39\na9o5sctOmUNeD4uJ6cClXDdqyEwXhnPmRKZjJ8qeH4D9wl7HOG7iQsYiyfJe/igi\nFEXVRZOtLBdbwX45rU6wiWWjxzY+mDnw4BXE31ZBPwgtoh+CLTyK8NI8LnCV/CgO\nzOY4sm/KDWmbfTTZjLSdYRFj7wEpOdUWjZ13viDFZqnmy/o1auvLmBcqbRrCyW+B\nOMI7aHE0mZ/52vEFQYU1tH0BxMmRfWXUCJj0TjwxDY6BQmmW4YlhsrgGNekLFDo1\n6phFd0pA4UPqGXfNLzHp1dtLhUEb4YzcpDn+HMzMf1gfez7qeqU28nNFg/AwwqHZ\nTWdGclCFjiah7SfvOslob4vdLGwkUhgCBKQUQoU1DltX2GOgIv9SNY3q6X0NwdZG\nL5gqk225WVUwIRzmi5nfUEXlbaTvyHg3BuGedUKJ91IhRCW1ZjvU8GQcfVsu8bse\nTCKMdr7wi/zEZXSldCza6vL4m3tmBLtWkHVOW8bcDWvoVwRswbFHfleHzckl7EeC\n9C4TRa66gA5UOv14SrpC8noQUNpSegg+1KI4BSNvwaheiSUqjQbisb0qYCxML0ZP\nmQodwVsXG6LYo+Y6y6CpHbT7UYkfa59q/CGOZByL1bEzzgd98ZHwjihOjHVaV6sY\nBW018AvGxr7kjEU4LNqIteydTp0o31ZJN/qK78w5EQFfJxfImrx/E4nYKtg4higj\nKOQCgJALKIveidqQEFsbGWsulYrMXwnu0nPThofR1D8eCJZpdTxvOh2nIrNrAeY8\nZMAwG1uQos5A0yEZ1auHxz+rb4errnk92OnVlWnElf1TwwlkFFNLdNDl8VpiMP40\n6en9VtlOfgH8AwB03WsoeuEQsxYTIcRKWZZPRsLx3hd0BsOw0FcYDSX2XIGPkVVW\niYf9hzFSQsWV3d6utloIm4nG8XONfNaRimGECbUSZyHZimrO1m4Gga5pE3LKuDri\nJKR2lR7b6XPR7+FS+lG1zq5KY7onAVQY1oABfTjpJRju6pQGWt70hairo6EaVC3u\nrBy8UkLwBbfDuigSvsVk+sF2+Ic0IzX6IniU0F5kMe+MKqGB4aicXP6FFGBpPFTe\nv6yHD+DYAu1rnlXrqmFL50CfutTF78uPPJ9D2Sm0DcGPFj+6IrCigj48uxoHR9Qb\nFeNzfsmVwoFAWWq/MpkPbX6Aql8ddCbpMxDUUkybwVV9rJmEMTLil44FrxKAKFhP\n0Av7JeFvdz15pfnf/IQ3IOvVhHGFChFS13sbYSvFHMQF3P0BiyvjhBI=\n-----END ENCRYPTED PRIVATE KEY-----\n", + "password": "testpwd", + "expected_signature": "S4l9IONXGHIdt4NjwmpCIhawDTitjUQls73d+mi0HJTSbTGyn95NabX5hC9+n6HsTqLcWPvxKgTvLFMnTaf6Jxl+xwQMbu9/6mw88KF7i1pEQizerKcr91rPUPVBQ4OY10Q018QEamIAymRgo/eoRYSm7CqCdeibGyO0XfXZBaJnVGFJ9hgrPIwSKHgeUnfK8qMenULvL0qKMEJ6ziYPiqh7k9xX3xIV7lGIpokk+ekqlFd01f/Lov45osJCFuccJO4xuUUZewZnVGF7Uw6Rim3UsKhXKZUN9WZWa5RT+dpBIJ5DTBIXBSvowwj3GZC3j+XvWw8Sn0Ls9836l89BXw==", + }, + { + "description": "Encrypted PKCS8 RSA private key in bytes", + "private_key": "-----BEGIN ENCRYPTED PRIVATE KEY-----\nMIIFNTBfBgkqhkiG9w0BBQ0wUjAxBgkqhkiG9w0BBQwwJAQQWW+iEMYYCPUntrPq\nZ2RCMAICCAAwDAYIKoZIhvcNAgkFADAdBglghkgBZQMEASoEEIw3ViSuTp8JeN43\n5VGlHt0EggTQBvEzd2w2F561CzU+MDouZDOPj4RTIStC471z0/bxTgYqH3gYchoe\nOfi2lsLuD8B+ivIRuXB8GT66BIseIOMV8t/tiMe97rFI/cV4h6DrBO1xlmSrBG97\nvFF9qPA5yPRlrHtWKkGxhXteNVsT3w/7Y7KsulO/gA2KpsOElMElOhUP462Yd0Wl\nOxAIV3+knl2niozws2Kq3EdzTF3N6hlavUPryiU/w4RRsPN5qgjchVVLq/sYRYhx\nN8uWJbkjhCcHsULkD5KkdgddR0VOhpQPXIdY+gPkSBJq1ltRWy/TYdXiU2fEBNZW\nhFUVrxnS76+u2R3vukY2IAX8zTC6h2AbCBG+r4XXzgk/l/4peySKHsPQRzQ0in39\na9o5sctOmUNeD4uJ6cClXDdqyEwXhnPmRKZjJ8qeH4D9wl7HOG7iQsYiyfJe/igi\nFEXVRZOtLBdbwX45rU6wiWWjxzY+mDnw4BXE31ZBPwgtoh+CLTyK8NI8LnCV/CgO\nzOY4sm/KDWmbfTTZjLSdYRFj7wEpOdUWjZ13viDFZqnmy/o1auvLmBcqbRrCyW+B\nOMI7aHE0mZ/52vEFQYU1tH0BxMmRfWXUCJj0TjwxDY6BQmmW4YlhsrgGNekLFDo1\n6phFd0pA4UPqGXfNLzHp1dtLhUEb4YzcpDn+HMzMf1gfez7qeqU28nNFg/AwwqHZ\nTWdGclCFjiah7SfvOslob4vdLGwkUhgCBKQUQoU1DltX2GOgIv9SNY3q6X0NwdZG\nL5gqk225WVUwIRzmi5nfUEXlbaTvyHg3BuGedUKJ91IhRCW1ZjvU8GQcfVsu8bse\nTCKMdr7wi/zEZXSldCza6vL4m3tmBLtWkHVOW8bcDWvoVwRswbFHfleHzckl7EeC\n9C4TRa66gA5UOv14SrpC8noQUNpSegg+1KI4BSNvwaheiSUqjQbisb0qYCxML0ZP\nmQodwVsXG6LYo+Y6y6CpHbT7UYkfa59q/CGOZByL1bEzzgd98ZHwjihOjHVaV6sY\nBW018AvGxr7kjEU4LNqIteydTp0o31ZJN/qK78w5EQFfJxfImrx/E4nYKtg4higj\nKOQCgJALKIveidqQEFsbGWsulYrMXwnu0nPThofR1D8eCJZpdTxvOh2nIrNrAeY8\nZMAwG1uQos5A0yEZ1auHxz+rb4errnk92OnVlWnElf1TwwlkFFNLdNDl8VpiMP40\n6en9VtlOfgH8AwB03WsoeuEQsxYTIcRKWZZPRsLx3hd0BsOw0FcYDSX2XIGPkVVW\niYf9hzFSQsWV3d6utloIm4nG8XONfNaRimGECbUSZyHZimrO1m4Gga5pE3LKuDri\nJKR2lR7b6XPR7+FS+lG1zq5KY7onAVQY1oABfTjpJRju6pQGWt70hairo6EaVC3u\nrBy8UkLwBbfDuigSvsVk+sF2+Ic0IzX6IniU0F5kMe+MKqGB4aicXP6FFGBpPFTe\nv6yHD+DYAu1rnlXrqmFL50CfutTF78uPPJ9D2Sm0DcGPFj+6IrCigj48uxoHR9Qb\nFeNzfsmVwoFAWWq/MpkPbX6Aql8ddCbpMxDUUkybwVV9rJmEMTLil44FrxKAKFhP\n0Av7JeFvdz15pfnf/IQ3IOvVhHGFChFS13sbYSvFHMQF3P0BiyvjhBI=\n-----END ENCRYPTED PRIVATE KEY-----\n", + "password": "testpwd", + "expected_signature": "S4l9IONXGHIdt4NjwmpCIhawDTitjUQls73d+mi0HJTSbTGyn95NabX5hC9+n6HsTqLcWPvxKgTvLFMnTaf6Jxl+xwQMbu9/6mw88KF7i1pEQizerKcr91rPUPVBQ4OY10Q018QEamIAymRgo/eoRYSm7CqCdeibGyO0XfXZBaJnVGFJ9hgrPIwSKHgeUnfK8qMenULvL0qKMEJ6ziYPiqh7k9xX3xIV7lGIpokk+ekqlFd01f/Lov45osJCFuccJO4xuUUZewZnVGF7Uw6Rim3UsKhXKZUN9WZWa5RT+dpBIJ5DTBIXBSvowwj3GZC3j+XvWw8Sn0Ls9836l89BXw==", + }, +] + +describe('Test crypto signature', function () { + + it('RSA and ed22519 tests ', function () { + + const dataQuery = 'price=50000&quantity=1&side=BUY&symbol=BTCUSDT×tamp=1631234567890&type=LIMIT' + + for (const testCase of testCases) { + const binance = new Binance({ + APISECRET: testCase.private_key, + PRIVATEKEYPASSWORD: testCase.password, + }) + + const signature = binance.generateSignature(dataQuery, false); + assert.equal(signature, testCase.expected_signature, testCase.description); + } + + }); + +}); \ No newline at end of file diff --git a/tests/live-tests.cjs b/tests/live-tests.cjs new file mode 100644 index 00000000..24a0daeb --- /dev/null +++ b/tests/live-tests.cjs @@ -0,0 +1,1400 @@ +/* ============================================================ + * node-binance-api + * https://github.com/jaggedsoft/node-binance-api + * ============================================================ + * Copyright 2017-, Jon Eyrick + * Released under the MIT License + * ============================================================ */ + +'use strict'; + +const WARN_SHOULD_BE_OBJ = 'should be an object'; +const WARN_SHOULD_BE_NULL = 'should be null'; +const WARN_SHOULD_BE_NOT_NULL = 'should not be null'; +const WARN_SHOULD_HAVE_KEY = 'should have key '; +const WARN_SHOULD_NOT_HAVE_KEY = 'should not have key '; +const WARN_SHOULD_BE_UNDEFINED = 'should be undefined'; +const WARN_SHOULD_BE_TYPE = 'should be a '; +const TIMEOUT = 10000; + +const chai = require( 'chai' ); +const assert = chai.assert; +const path = require( 'path' ); +const Binance = require( path.resolve( __dirname, '../dist/cjs/node-binance-api.js' ) ); +const binance = new Binance().options( { + APIKEY: 'X4BHNSimXOK6RKs2FcKqExquJtHjMxz5hWqF0BBeVnfa5bKFMk7X0wtkfEz0cPrJ', + APISECRET: 'x8gLihunpNq0d46F2q0TWJmeCDahX5LMXSlv3lSFNbMI3rujSOpTDKdhbcmPSf2i', + test: true +} ); +const futuresBinance = new Binance().options( { + APIKEY: '227719da8d8499e8d3461587d19f259c0b39c2b462a77c9b748a6119abd74401', + APISECRET: 'b14b935f9cfacc5dec829008733c40da0588051f29a44625c34967b45c11d73c', + hedgeMode: true, + test: true +} ); +// binance.options.APIKEY = "X4BHNSimXOK6RKs2FcKqExquJtHjMxz5hWqF0BBeVnfa5bKFMk7X0wtkfEz0cPrJ" +// binance.options.APISECRET = "x8gLihunpNq0d46F2q0TWJmeCDahX5LMXSlv3lSFNbMI3rujSOpTDKdhbcmPSf2i" +// binance.options.test = true; +let util = require( 'util' ); + +let num_pairs = 299; +let num_currencies = 155; + +let logger = { + log: function ( msg ) { + let logLineDetails = ( ( new Error().stack ).split( 'at ' )[3] ).trim(); + let logLineNum = logLineDetails.split( ':' ); + console.log( 'DEBUG', logLineNum[1] + ':' + logLineNum[2], msg ); + } +} + +let debug = function ( x ) { + if ( typeof ( process.env.node_binance_api ) === 'undefined' ) { + return; + } + logger.log( typeof ( x ) ); + logger.log( util.inspect( x ) ); +} + +let stopSockets = function ( log = false ) { + //stopSocketsRunning = true; + let endpoints = binance.websockets.subscriptions(); + for ( let endpoint in endpoints ) { + if ( log ) console.log( 'Terminated ws endpoint: ' + endpoint ); + binance.websockets.terminate( endpoint ); + } +} + +debug( 'Begin' ); + +/*global describe*/ +/*eslint no-undef: "error"*/ +describe( 'Construct', function () { + /*global it*/ + /*eslint no-undef: "error"*/ + it( 'Construct the binance object', function ( done ) { + binance.options( { + APIKEY: 'X4BHNSimXOK6RKs2FcKqExquJtHjMxz5hWqF0BBeVnfa5bKFMk7X0wtkfEz0cPrJ', + APISECRET: 'x8gLihunpNq0d46F2q0TWJmeCDahX5LMXSlv3lSFNbMI3rujSOpTDKdhbcmPSf2i', + useServerTime: true, + reconnect: false, + verbose: true, + test: true, + log: debug + } ); + assert( typeof ( binance ) === 'object', 'Binance is not an object' ); + done(); + } ).timeout( TIMEOUT ); + + it( 'Construct the binance object in various ways', function () { + + let keyOffset = 1000; + let key = keyOffset; + let secret = "secret"; + + // Every variant is listed twice to make sure that the options are not shared (which happened in the past) + let objs = [ + new Binance().options( { APIKEY: key++, APISECRET: secret } ), + new Binance().options( { APIKEY: key++, APISECRET: secret } ), + Binance().options( { APIKEY: key++, APISECRET: secret } ), + Binance().options( { APIKEY: key++, APISECRET: secret } ), + new Binance( { APIKEY: key++, APISECRET: secret } ), + new Binance( { APIKEY: key++, APISECRET: secret } ), + Binance( { APIKEY: key++, APISECRET: secret } ), + Binance( { APIKEY: key++, APISECRET: secret } ), + ]; + + // Make sure that all objects have their own options + for ( let i = 0; i < objs.length; i++ ) { + let expectedKey = keyOffset + i; + let actualKey = objs[i].getOption( "APIKEY" ); + assert( expectedKey === actualKey, `APIKEY: ${ expectedKey } != ${ actualKey }` ); + } + + } ); + +} ); + + +describe( 'UseServerTime', function () { + it( 'Call use server time', function ( done ) { + binance.useServerTime(); + done(); + } ).timeout( TIMEOUT ); +} ); + +describe( 'Prices', function () { + it( 'Checks the price of BNBBTC', async function () { + const ticker = await binance.prices( 'BNBBTC' ) + assert( typeof ( ticker ) === 'object', WARN_SHOULD_BE_OBJ ); + assert( ticker !== null, WARN_SHOULD_BE_NOT_NULL ); + assert( Object.prototype.hasOwnProperty.call( ticker, 'BNBBTC' ), WARN_SHOULD_HAVE_KEY + 'BNBBTC' ); + assert( Object.prototype.hasOwnProperty.call( ticker, 'ETHBTC' ) === false, WARN_SHOULD_NOT_HAVE_KEY + 'ETHBTC' ); + } ).timeout( TIMEOUT ); +} ); + +describe( 'All Prices', function () { + it( 'Checks the prices of coin pairs', async function () { + const ticker = await binance.prices(); + assert( typeof ( ticker ) === 'object', WARN_SHOULD_BE_OBJ ); + assert( ticker !== null, WARN_SHOULD_BE_NOT_NULL ); + } ).timeout( TIMEOUT ); +} ); + +describe( 'Balances', function () { + it( 'Get the balances in the account', async function () { + const balances = await binance.balance(); + assert( balances !== null, WARN_SHOULD_BE_NOT_NULL ); + assert( balances ); + assert( Object.prototype.hasOwnProperty.call( balances, 'BNB' ), WARN_SHOULD_HAVE_KEY + 'BNB' ); + assert( Object.prototype.hasOwnProperty.call( balances.BNB, 'available' ), WARN_SHOULD_HAVE_KEY + 'available' ); + assert( Object.prototype.hasOwnProperty.call( balances.BNB, 'onOrder' ), WARN_SHOULD_HAVE_KEY + 'onOrder' ); + assert( Object.keys( balances ).length >= num_currencies, 'should at least ' + num_currencies + 'currencies?' ); + } ).timeout( TIMEOUT ); +} ); + +describe( 'Book Ticker', function () { + it( 'Get the BNB book ticker', async function () { + const ticker = await binance.bookTickers( 'BNBBTC' ) + assert( ticker !== null, WARN_SHOULD_BE_NOT_NULL ); + assert( ticker ); + let members = [ 'symbol', 'bidPrice', 'bidQty', 'askPrice', 'askQty' ]; + members.forEach( function ( value ) { + assert( Object.prototype.hasOwnProperty.call( ticker, value ), WARN_SHOULD_HAVE_KEY + value ); + } ); + } ).timeout( TIMEOUT ); + + it( 'Get all book tickers', async function () { + const tickers = await binance.bookTickers( false ) + assert( tickers !== undefined ); + } ).timeout( TIMEOUT ); +} ); + +describe( 'Booker Tickers', function () { + it( 'Get the tickers for all pairs', function ( done ) { + binance.bookTickers( ( error, ticker ) => { + debug( error ); + debug( ticker ); + assert( typeof ( error ) === 'object', WARN_SHOULD_BE_OBJ ); + assert( typeof ( ticker ) === 'object', WARN_SHOULD_BE_OBJ ); + assert( error === null, WARN_SHOULD_BE_NULL ); + assert( ticker !== null, WARN_SHOULD_BE_NOT_NULL ); + assert( Object.keys( ticker ).length >= num_pairs, 'should at least ' + num_pairs + 'currency pairs?' ); + + let members = [ 'symbol', 'bidPrice', 'bidQty', 'askPrice', 'askQty' ]; + ticker.forEach( function ( obj ) { + members.forEach( function ( member ) { + assert( Object.prototype.hasOwnProperty.call( obj, member ), WARN_SHOULD_HAVE_KEY + member ); + } ); + } ); + done(); + } ); + } ).timeout( TIMEOUT ); +} ); + +describe( 'Market', function () { + it( 'Get the market base symbol of a symbol pair', function ( done ) { + let tocheck = [ 'TRXBNB', 'BNBBTC', 'BNBETH', 'BNBUSDT' ]; + tocheck.forEach( function ( element ) { + let mark = binance.getMarket( element ); + assert( typeof ( mark ) === 'string', WARN_SHOULD_BE_TYPE + 'string' ); + assert( element.endsWith( mark ), 'should end with: ' + mark ); + } ); + + assert.isNotOk( binance.getMarket( 'ABCDEFG' ), WARN_SHOULD_BE_UNDEFINED ); + done(); + } ).timeout( TIMEOUT ); +} ); + +describe( 'ping', function () { + it( 'call ping', function ( done ) { + binance.ping(); + done(); + } ).timeout( TIMEOUT ); +} ); + +describe( 'Depth chart BNB', function () { + it( 'Get the depth chart information for BNBBTC', function ( done ) { + binance.depth( 'BNBBTC', ( error, depth, symbol ) => { + debug( error ); + debug( depth ); + debug( symbol ); + assert( error === null, WARN_SHOULD_BE_NULL ); + assert( depth !== null, WARN_SHOULD_BE_NOT_NULL ); + assert( symbol !== null, WARN_SHOULD_BE_NOT_NULL ); + assert( typeof ( symbol ) === 'string', 'should be type of string' ); + assert( symbol === 'BNBBTC', 'should be BNBBTC' ); + assert( typeof ( depth ) === 'object', WARN_SHOULD_BE_OBJ ); + assert( Object.keys( depth ).length === 3, 'should have length 3' ); + + let members = [ 'lastUpdateId', 'asks', 'bids' ]; + members.forEach( function ( value ) { + assert( Object.prototype.hasOwnProperty.call( depth, value ), WARN_SHOULD_HAVE_KEY + value ); + } ); + done(); + } ); + } ).timeout( TIMEOUT ); +} ); + +// describe( 'Buy', function () { +// it( 'Attempt to buy ETH', function ( done ) { +// let quantity = 1; +// let price = 0.069; +// assert( typeof ( binance.buy( 'ETHBTC', quantity, price ) ) === 'undefined', WARN_SHOULD_BE_UNDEFINED ); +// done(); +// } ).timeout( TIMEOUT ); +// } ); + +// describe( 'Sell', function () { +// it( 'Attempt to sell ETH', function ( done ) { +// let quantity = 1; +// let price = 0.069; +// assert( typeof ( binance.sell( 'ETHBTC', quantity, price ) ) === 'undefined', WARN_SHOULD_BE_UNDEFINED ); +// done(); +// } ).timeout( TIMEOUT ); +// } ); + +describe( 'MarketBuy', function () { + it( 'Attempt to buy LTC at market price', async function () { + let quantity = 0.5; + const res = await binance.marketBuy( 'LTCUSDT', quantity ) + assert( res['orderId'] !== undefined ) + } ).timeout( TIMEOUT ); +} ); + + +describe( 'MarketSell', function () { + it( 'Attempt to buy LTC at market price', async function () { + let quantity = 0.5; + const res = await binance.marketSell( 'LTCUSDT', quantity ) + assert( res['orderId'] !== undefined ) + } ).timeout( TIMEOUT ); +} ); + +describe( 'Futures MarketBuy', function () { + it( 'futures Attempt to buy ETH at market price', async function () { + let quantity = 0.1; + const res = await futuresBinance.futuresMarketBuy( 'ETHUSDT', quantity ) + assert( res['orderId'] !== undefined ) + } ).timeout( TIMEOUT ); +} ); + + +describe( 'Futures MarketSell', function () { + it( 'futures Attempt to buy ETH at market price', async function () { + let quantity = 0.1; + const res = await futuresBinance.futuresMarketSell( 'ETHUSDT', quantity ) + assert( res['orderId'] !== undefined ) + } ).timeout( TIMEOUT ); +} ); + +// describe( 'MarketSell', function () { +// it( 'Attempt to sell ETH at market price', function ( done ) { +// let quantity = 1; +// assert( typeof ( binance.marketSell( 'ETHBTC', quantity ) ) === 'undefined', WARN_SHOULD_BE_UNDEFINED ); +// done(); +// } ).timeout( TIMEOUT ); +// } ); + +// describe( 'Buy order advanced', function () { +// it( 'Attempt to buy BNB specifying order type', function ( done ) { +// let type = 'LIMIT'; +// let quantity = 1; +// let price = 0.069; +// assert( typeof ( binance.buy( 'BNBETH', quantity, price, { type: type } ) ) === 'undefined', WARN_SHOULD_BE_UNDEFINED ); +// done(); +// } ).timeout( TIMEOUT ); +// } ); + +// describe( 'Sell Stop loess', function () { +// it( 'Attempt to create a stop loss order', function ( done ) { +// let type = 'STOP_LOSS'; +// let quantity = 1; +// let price = 0.069; +// let stopPrice = 0.068; +// assert( typeof ( binance.sell( 'ETHBTC', quantity, price, { stopPrice: stopPrice, type: type } ) ) === 'undefined', WARN_SHOULD_BE_UNDEFINED ); +// done(); +// } ).timeout( TIMEOUT ); +// } ); + +// describe( 'Iceberg sell order', function () { +// it( 'Attempt to create a sell order', function ( done ) { +// let quantity = 1; +// let price = 0.069; +// assert( typeof ( binance.sell( 'ETHBTC', quantity, price, { icebergQty: 10 } ) ) === 'undefined', WARN_SHOULD_BE_UNDEFINED ); +// done(); +// } ).timeout( TIMEOUT ); +// } ); + +describe( 'Cancel order', function () { + it( 'Attempt to cancel an order', function ( done ) { + let orderid = '7610385'; + binance.cancel( 'ETHBTC', orderid, ( error, response, symbol ) => { + debug( error ); + debug( response ); + debug( symbol ); + assert( typeof ( error ) === 'object', WARN_SHOULD_BE_OBJ ); + assert( typeof ( response ) === 'object', WARN_SHOULD_BE_OBJ ); + assert( typeof ( symbol ) === 'string', WARN_SHOULD_BE_TYPE + 'string' ); + assert( symbol === 'ETHBTC' ); + assert( error !== null, WARN_SHOULD_BE_NOT_NULL ); + assert( response !== null, WARN_SHOULD_BE_NOT_NULL ); + assert( error.body === '{"code":-2011,"msg":"Unknown order sent."}' ); + assert( typeof ( response.orderId ) === 'undefined', WARN_SHOULD_BE_UNDEFINED ); + assert( Object.keys( response ).length === 0 ); + done(); + } ); + } ).timeout( TIMEOUT ); +} ); + +describe( 'Cancel orders', function () { + it( 'Attempt to cancel all orders given a symbol', function ( done ) { + binance.cancelOrders( 'XMRBTC', ( error, response, symbol ) => { + debug( error ); + // debug( response ); + // debug( symbol ); + assert( typeof ( error ) === 'string', WARN_SHOULD_BE_OBJ ); + assert( typeof ( response ) === 'object', WARN_SHOULD_BE_OBJ ); + assert( typeof ( symbol ) === 'string', WARN_SHOULD_BE_TYPE + 'string' ); + assert( symbol === 'XMRBTC' ); + assert( error !== null, WARN_SHOULD_BE_NOT_NULL ); + assert( error === 'No orders present for this symbol', WARN_SHOULD_BE_TYPE + 'string' ); + done(); + } ); + } ).timeout( TIMEOUT ); +} ); + +describe( 'Open Orders', function () { + it( 'Attempt to show all orders to ETHBTC', function ( done ) { + binance.openOrders( 'ETHBTC', ( error, openOrders, symbol ) => { + debug( error ); + debug( openOrders ); + debug( symbol ); + assert( typeof ( error ) === 'object', WARN_SHOULD_BE_OBJ ); + assert( typeof ( openOrders ) === 'object', WARN_SHOULD_BE_OBJ ); + assert( typeof ( symbol ) === 'string', WARN_SHOULD_BE_TYPE + 'string' ); + assert( symbol === 'ETHBTC' ); + assert( error === null, WARN_SHOULD_BE_NULL ); + assert( openOrders !== null, WARN_SHOULD_BE_NOT_NULL ); + assert( symbol !== null, WARN_SHOULD_BE_NOT_NULL ); + assert( Object.keys( openOrders ).length === 0 ); + done(); + } ); + } ).timeout( TIMEOUT ); +} ); + +describe( 'Open Orders', function () { + it( 'Attempt to show all orders for all symbols', function ( done ) { + binance.openOrders( false, ( error, openOrders ) => { + debug( error ); + debug( openOrders ); + assert( typeof ( error ) === 'object', WARN_SHOULD_BE_OBJ ); + assert( typeof ( openOrders ) === 'object', WARN_SHOULD_BE_OBJ ); + assert( error === null, WARN_SHOULD_BE_NULL ); + assert( openOrders !== null, WARN_SHOULD_BE_NOT_NULL ); + assert( Object.keys( openOrders ).length === 0 ); + done(); + } ); + } ).timeout( TIMEOUT ); +} ); + +describe( 'Order status', function () { + it( 'Attempt to get the order status for a given order id', function ( done ) { + binance.orderStatus( 'ETHBTC', '1234567890', ( error, orderStatus, symbol ) => { + debug( error ); + debug( orderStatus ); + debug( symbol ); + assert( typeof ( error ) === 'object', WARN_SHOULD_BE_OBJ ); + assert( typeof ( orderStatus ) === 'object', WARN_SHOULD_BE_OBJ ); + assert( typeof ( symbol ) === 'string', WARN_SHOULD_BE_TYPE + 'string' ); + assert( symbol === 'ETHBTC' ); + assert( error !== null, WARN_SHOULD_BE_NOT_NULL ); + assert( orderStatus !== null, WARN_SHOULD_BE_NOT_NULL ); + assert( error.body === '{"code":-2013,"msg":"Order does not exist."}' ); + assert( Object.keys( orderStatus ).length === 0 ); + done(); + } ); + } ).timeout( TIMEOUT ); +} ); + +describe( 'trades', function () { + it( 'Attempt get all trade history for given symbol', function ( done ) { + binance.trades( 'BTCUSDT', ( error, trades, symbol ) => { + debug( error ); + debug( trades ); + debug( symbol ); + assert( typeof ( error ) === 'object', WARN_SHOULD_BE_OBJ ); + assert( typeof ( trades ) === 'object', WARN_SHOULD_BE_OBJ ); + assert( typeof ( symbol ) === 'string', WARN_SHOULD_BE_TYPE + 'string' ); + assert( symbol === 'BTCUSDT' ); + assert( error === null, WARN_SHOULD_BE_NULL ); + assert( trades !== null, WARN_SHOULD_BE_NOT_NULL ); + assert( Object.keys( trades ).length === 0 ); + done(); + } ); + } ).timeout( TIMEOUT ); +} ); + +describe( 'Orders', function () { + it( 'Attempt get all orders for given symbol', function ( done ) { + binance.allOrders( 'ETHBTC', ( error, orders, symbol ) => { + debug( error ); + debug( orders ); + debug( symbol ); + assert( typeof ( error ) === 'object', WARN_SHOULD_BE_OBJ ); + assert( typeof ( orders ) === 'object', WARN_SHOULD_BE_OBJ ); + assert( typeof ( symbol ) === 'string', WARN_SHOULD_BE_TYPE + 'string' ); + assert( symbol === 'ETHBTC' ); + assert( error === null, WARN_SHOULD_BE_NULL ); + assert( orders !== null, WARN_SHOULD_BE_NOT_NULL ); + assert( Object.keys( orders ).length === 0 ); + done(); + } ); + } ).timeout( TIMEOUT ); +} ); + +describe( 'Prevday all symbols', function () { + it( 'Attempt get prevday trade status for all symbols', function ( done ) { + binance.prevDay( false, ( error, prevDay ) => { + debug( error ); + debug( prevDay ); + assert( typeof ( error ) === 'object', WARN_SHOULD_BE_OBJ ); + assert( typeof ( prevDay ) === 'object', WARN_SHOULD_BE_OBJ ); + assert( error === null, WARN_SHOULD_BE_NULL ); + assert( prevDay !== null, WARN_SHOULD_BE_NOT_NULL ); + assert( Object.keys( prevDay ).length >= num_pairs, 'should at least ' + num_pairs + 'currency pairs?' ); + + let members = [ + 'symbol', 'priceChange', 'priceChangePercent', 'weightedAvgPrice', 'prevClosePrice', + 'lastPrice', 'lastQty', 'bidPrice', 'bidQty', 'askQty', 'openPrice', 'highPrice', 'lowPrice', + 'volume', 'quoteVolume', 'openTime', 'closeTime', 'firstId', 'lastId', 'count' + ]; + prevDay.forEach( function ( obj ) { + members.forEach( function ( key ) { + assert( Object.prototype.hasOwnProperty.call( obj, key ), WARN_SHOULD_HAVE_KEY + key ); + } ); + } ); + done(); + } ); + } ).timeout( TIMEOUT ); +} ); + +describe( 'Prevday', function () { + it( 'Attempt get prevday trade status for given symbol', function ( done ) { + binance.prevDay( 'BNBBTC', ( error, prevDay, symbol ) => { + debug( error ); + debug( prevDay ); + debug( symbol ); + assert( typeof ( error ) === 'object', WARN_SHOULD_BE_OBJ ); + assert( typeof ( prevDay ) === 'object', WARN_SHOULD_BE_OBJ ); + assert( typeof ( symbol ) === 'string', WARN_SHOULD_BE_TYPE + 'string' ); + assert( symbol === 'BNBBTC', 'Should be BNBBTC' ); + assert( error === null, WARN_SHOULD_BE_NULL ); + assert( prevDay !== null, WARN_SHOULD_BE_NOT_NULL ); + + let members = [ + 'symbol', 'priceChange', 'priceChangePercent', 'weightedAvgPrice', 'prevClosePrice', + 'lastPrice', 'lastQty', 'bidPrice', 'bidQty', 'askQty', 'openPrice', 'highPrice', 'lowPrice', + 'volume', 'quoteVolume', 'openTime', 'closeTime', 'firstId', 'lastId', 'count' + ]; + members.forEach( function ( key ) { + assert( Object.prototype.hasOwnProperty.call( prevDay, key ), WARN_SHOULD_HAVE_KEY + key ); + } ); + done(); + } ); + } ).timeout( TIMEOUT ); +} ); + +describe( 'Candle sticks', function () { + it( 'Attempt get candlesticks for a given symbol', function ( done ) { + binance.candlesticks( 'BNBBTC', '5m', ( error, ticks, symbol ) => { + debug( error ); + debug( ticks ); + debug( symbol ); + assert( typeof ( error ) === 'object', WARN_SHOULD_BE_OBJ ); + assert( typeof ( ticks ) === 'object', WARN_SHOULD_BE_OBJ ); + assert( typeof ( symbol ) === 'string', WARN_SHOULD_BE_TYPE + 'string' ); + assert( symbol === 'BNBBTC', 'Should be BNBBTC' ); + assert( error === null, WARN_SHOULD_BE_NULL ); + assert( ticks !== null, WARN_SHOULD_BE_NOT_NULL ); + + ticks.forEach( function ( tick ) { + assert( tick.length === 12 ); + } ); + done(); + }, { + limit: 500, + endTime: 1514764800000 + } ); + } ).timeout( TIMEOUT ); +} ); + +describe( 'Object keys', function () { + describe( 'First', function () { + it( 'Gets the first key', function ( done ) { + let first = binance.first( { first: '1', second: '2', third: '3' } ); + assert.strictEqual( 'first', first, 'should be first' ); + done(); + } ).timeout( TIMEOUT ); + } ); + + describe( 'Last', function () { + it( 'Gets the last key', function ( done ) { + let last = binance.last( { first: '1', second: '2', third: '3' } ); + assert.strictEqual( 'third', last, 'should be third' ); + done(); + } ).timeout( TIMEOUT ); + } ); + + describe( 'slice', function () { + it( 'Gets slice of the object keys', function ( done ) { + let slice = binance.slice( { first: '1', second: '2', third: '3' }, 2 ); + assert.deepEqual( [ 'third' ], slice, 'should be ian array with the third' ); + done(); + } ).timeout( TIMEOUT ); + } ); + + describe( 'Min', function () { + it( 'Gets the math min of object', function ( done ) { + binance.min( { first: '1', second: '2', third: '3' } ); + done(); + } ).timeout( TIMEOUT ); + } ); + + describe( 'Max', function () { + it( 'Gets the math max of object', function ( done ) { + binance.max( { first: '1', second: '2', third: '3' } ); + done(); + } ).timeout( TIMEOUT ); + } ); +} ); + +describe( 'Set/Get options', function () { + it( 'Sets/Gets option to specified value', function ( done ) { + binance.setOption( 'test', 'value' ); + assert.equal( binance.getOption( 'test' ), 'value', 'should be value' ); + done(); + } ).timeout( TIMEOUT ); +} ); + +describe( 'Get options', function () { + it( 'Gets all options', function ( done ) { + assert( typeof ( binance.getOptions() ) === 'object', 'should be object' ); + done(); + } ).timeout( TIMEOUT ); +} ); + +describe( 'Percent', function () { + it( 'Get Percentage of two values', function ( done ) { + assert( binance.percent( 25, 100 ) === 25, 'should be 25 percent' ); + done(); + } ).timeout( TIMEOUT ); +} ); + +describe( 'Sum', function () { + it( 'Get sum of array of values', function ( done ) { + assert( binance.sum( [ 1, 2, 3 ] ) === 6, 'should be 6' ); + done(); + } ).timeout( TIMEOUT ); +} ); + +describe( 'Reverse', function () { + it( 'Reverse the keys in an object', function ( done ) { + assert( binance.reverse( { '3': 3, '2': 2, '1': 1 } ).toString() === { '1': 1, '2': 2, '3': 3 }.toString(), 'should be {\'1\': 1, \'2\': 2, \'3\': 3 }' ); + done(); + } ).timeout( TIMEOUT ); +} ); + +describe( 'Array', function () { + it( 'Convert object to an array', function ( done ) { + let actual = binance.array( { 'a': 1, 'b': 2, 'c': 3 } ); + let expected = [ [ NaN, 1 ], [ NaN, 2 ], [ NaN, 3 ] ]; + assert.isArray( actual, 'should be an array' ); + assert( actual.length === 3, 'should be of lenght 3' ); + assert.deepEqual( actual, expected, 'should be both arrays with same vlaues' ); + done(); + } ).timeout( TIMEOUT ); +} ); + +describe( 'sortBids', function () { + it( 'Sorts symbols bids and returns an object', function ( done ) { + /* let actual = binance.sortBids( 'BNBBTC' ); + debug( actual ); */ + debug( 'todo' ); + done(); + } ); +} ); + +describe( 'sortAsks', function () { + it( 'Sorts symbols asks and returns an object', function ( done ) { + //let actual = binance.sortBids( 'BNBBTC' ); + debug( 'todo' ); + done(); + } ).timeout( TIMEOUT ); +} ); + +describe( 'Exchange Info', function () { + let async_error; + let async_data; + /*global beforeEach*/ + /*eslint no-undef: "error"*/ + beforeEach( function ( done ) { + binance.exchangeInfo( function ( error, data ) { + async_error = error; + async_data = data; + done( error ); + } ) + } ).timeout( TIMEOUT * 5 ); + + it( 'Gets the exchange info as an object', function () { + assert( typeof ( async_error ) === 'object', 'error should be object' ); + assert( async_error === null, 'Error should be null' ); + assert( typeof ( async_data ) === 'object', 'data should be object' ); + assert( async_data !== null, 'data should not be null' ); + assert( Object.prototype.hasOwnProperty.call( async_data, 'symbols' ), 'data should have property \'symbols\'' ); + + let symbolMembers = [ 'status', 'orderTypes', 'icebergAllowed', 'baseAsset', 'baseAssetPrecision', 'quoteAsset', 'quotePrecision', 'quoteAssetPrecision' ]; + async_data.symbols.forEach( function ( symbol ) { + symbolMembers.forEach( function ( member ) { + assert( Object.prototype.hasOwnProperty.call( symbol, member ), WARN_SHOULD_HAVE_KEY + member ); + } ); + } ); + } ).timeout( TIMEOUT * 5 ); +} ); + +// describe( 'System status', function () { +// let async_error; +// let async_data; +// /*global beforeEach*/ +// beforeEach( function ( done ) { +// binance.systemStatus( function ( error, data ) { +// async_error = error; +// async_data = data; +// done( error ); +// } ) +// } ); + +// it( 'Gets the system status info as an object', function () { +// assert( typeof ( async_error ) === 'object', 'error should be object' ); +// assert( async_error === null, 'Error should be null' ); +// assert( typeof ( async_data ) === 'object', 'data should be object' ); +// assert( async_data !== null, 'data should not be null' ); +// assert( Object.prototype.hasOwnProperty.call( async_data, 'msg' ), WARN_SHOULD_HAVE_KEY + 'msg' ); +// assert( Object.prototype.hasOwnProperty.call( async_data, 'status' ), WARN_SHOULD_HAVE_KEY + 'status' ); + +// let members = [ 'msg', 'status' ]; +// members.forEach( function ( member ) { +// assert( Object.prototype.hasOwnProperty.call( async_data, member ), WARN_SHOULD_HAVE_KEY + member ); +// } ); +// } ).timeout( TIMEOUT ); +// } ); + +// describe( 'Withdraw', function () { +// it( 'Attempt to withdraw BNB to another address', function ( done ) { +// binance.withdraw( 'BNBBTC', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', '5', false, ( error, result ) => { +// debug( error ); +// debug( result ); +// assert( typeof ( error ) === 'object', WARN_SHOULD_BE_OBJ ); +// assert( typeof ( result ) === 'object', WARN_SHOULD_BE_OBJ ); +// assert( error === null, WARN_SHOULD_BE_NULL ); +// assert( result !== null, WARN_SHOULD_BE_NOT_NULL ); +// assert( Object.prototype.hasOwnProperty.call( result, 'msg' ), WARN_SHOULD_HAVE_KEY + 'msg' ); +// assert( result.msg === 'You don\'t have permission.' ); +// assert( Object.prototype.hasOwnProperty.call( result, 'success' ), WARN_SHOULD_HAVE_KEY + 'success' ); +// assert( result.success === false ); +// done(); +// } ); +// } ).timeout( TIMEOUT ); + +// it( 'Attempt to withdraw BNB to another address with address tag', function ( done ) { +// binance.withdraw( 'BNBBTC', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', '5', 'AQSWDEFRGT', ( error, result ) => { +// debug( error ); +// debug( result ); +// assert( typeof ( error ) === 'object', WARN_SHOULD_BE_OBJ ); +// assert( typeof ( result ) === 'object', WARN_SHOULD_BE_OBJ ); +// assert( error === null, WARN_SHOULD_BE_NULL ); +// assert( result !== null, WARN_SHOULD_BE_NOT_NULL ); +// assert( Object.prototype.hasOwnProperty.call( result, 'msg' ), WARN_SHOULD_HAVE_KEY + 'msg' ); +// assert( result.msg === 'You don\'t have permission.' ); +// assert( Object.prototype.hasOwnProperty.call( result, 'success' ), WARN_SHOULD_HAVE_KEY + 'success' ); +// assert( result.success === false ); +// done(); +// } ); +// } ).timeout( TIMEOUT ); + +// it( 'Attempt to withdraw BNB without saving to address book', function ( done ) { +// binance.withdraw( 'BNBBTC', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', '5', false, ( error, result ) => { +// debug( error ); +// debug( result ); +// assert( typeof ( error ) === 'object', WARN_SHOULD_BE_OBJ ); +// assert( typeof ( result ) === 'object', WARN_SHOULD_BE_OBJ ); +// assert( error === null, WARN_SHOULD_BE_NULL ); +// assert( result !== null, WARN_SHOULD_BE_NOT_NULL ); +// assert( Object.prototype.hasOwnProperty.call( result, 'msg' ), WARN_SHOULD_HAVE_KEY + 'msg' ); +// assert( result.msg === 'You don\'t have permission.' ); +// assert( Object.prototype.hasOwnProperty.call( result, 'success' ), WARN_SHOULD_HAVE_KEY + 'success' ); +// assert( result.success === false ); +// done(); +// }, false ); +// } ).timeout( TIMEOUT ); +// } ); + +// describe( 'Withdraw history', function () { +// it( 'Attempt to get withdraw history for BTC', function ( done ) { +// binance.withdrawHistory( ( error, result ) => { +// debug( error ); +// debug( result ); +// assert( typeof ( error ) === 'object', WARN_SHOULD_BE_OBJ ); +// assert( typeof ( result ) === 'object', WARN_SHOULD_BE_OBJ ); +// assert( error === null, WARN_SHOULD_BE_NULL ); +// assert( result !== null, WARN_SHOULD_BE_NOT_NULL ); +// assert( Object.prototype.hasOwnProperty.call( result, 'withdrawList' ), WARN_SHOULD_HAVE_KEY + 'withdrawList' ); +// assert( Array.isArray( result.withdrawList ) ); +// assert( Object.prototype.hasOwnProperty.call( result, 'success' ), WARN_SHOULD_HAVE_KEY + 'success' ); +// assert( result.success === true ); +// done(); +// }, 'BTC' ); +// } ).timeout( TIMEOUT ); + +// it( 'Attempt to get withdraw history for all assets', function ( done ) { +// binance.withdrawHistory( ( error, result ) => { +// debug( error ); +// debug( result ); +// assert( typeof ( error ) === 'object', WARN_SHOULD_BE_OBJ ); +// assert( typeof ( result ) === 'object', WARN_SHOULD_BE_OBJ ); +// assert( error === null, WARN_SHOULD_BE_NULL ); +// assert( result !== null, WARN_SHOULD_BE_NOT_NULL ); +// assert( Object.prototype.hasOwnProperty.call( result, 'withdrawList' ), WARN_SHOULD_HAVE_KEY + 'withdrawList' ); +// assert( Array.isArray( result.withdrawList ) ); +// assert( Object.prototype.hasOwnProperty.call( result, 'success' ), WARN_SHOULD_HAVE_KEY + 'success' ); +// assert( result.success === true ); +// done(); +// } ); +// } ).timeout( TIMEOUT ); +// } ); + + +// describe( 'Deposit history', function () { +// it( 'Attempt to get deposit history for all assets', function ( done ) { +// binance.depositHistory( ( error, result ) => { +// debug( error ); +// debug( result ); +// assert( typeof ( error ) === 'object', WARN_SHOULD_BE_OBJ ); +// assert( typeof ( result ) === 'object', WARN_SHOULD_BE_OBJ ); +// assert( error === null, WARN_SHOULD_BE_NULL ); +// assert( result !== null, WARN_SHOULD_BE_NOT_NULL ); +// assert( Object.prototype.hasOwnProperty.call( result, 'depositList' ), WARN_SHOULD_HAVE_KEY + 'depositList' ); +// assert( Array.isArray( result.depositList ) ); +// assert( Object.prototype.hasOwnProperty.call( result, 'success' ), WARN_SHOULD_HAVE_KEY + 'success' ); +// assert( result.success === true ); +// done(); +// } ); +// } ).timeout( TIMEOUT ); +// } );// + +// describe( 'Deposit address', function () { +// it( 'Attempt to get deposit address for BTC', function ( done ) { +// binance.depositAddress( 'BTC', ( error, result ) => { +// debug( error ); +// debug( result ); +// assert( typeof ( error ) === 'object', WARN_SHOULD_BE_OBJ ); +// assert( typeof ( result ) === 'object', WARN_SHOULD_BE_OBJ ); +// assert( error === null, WARN_SHOULD_BE_NULL ); +// assert( result !== null, WARN_SHOULD_BE_NOT_NULL ); +// assert( Object.prototype.hasOwnProperty.call( result, 'address' ), WARN_SHOULD_HAVE_KEY + 'address' ); +// assert( Object.prototype.hasOwnProperty.call( result, 'success' ), WARN_SHOULD_HAVE_KEY + 'success' ); +// assert( Object.prototype.hasOwnProperty.call( result, 'addressTag' ), WARN_SHOULD_HAVE_KEY + 'addressTag' ); +// assert( Object.prototype.hasOwnProperty.call( result, 'asset' ), WARN_SHOULD_HAVE_KEY + 'asset' ); +// assert( result.asset === 'BTC' ); +// assert( result.success === true ); +// done(); +// } ); +// } ).timeout( TIMEOUT ); + +// it( 'Attempt to get deposit address for XYZ', function ( done ) { +// binance.depositAddress( 'XYZ', ( error, result ) => { +// debug( error ); +// debug( result ); +// assert( typeof ( error ) === 'object', WARN_SHOULD_BE_OBJ ); +// assert( typeof ( result ) === 'object', WARN_SHOULD_BE_OBJ ); +// assert( error === null, WARN_SHOULD_BE_NULL ); +// assert( result !== null, WARN_SHOULD_BE_NOT_NULL ); +// assert( Object.prototype.hasOwnProperty.call( result, 'address' ) === false, WARN_SHOULD_NOT_HAVE_KEY + 'address' ); +// assert( Object.prototype.hasOwnProperty.call( result, 'success' ), WARN_SHOULD_NOT_HAVE_KEY + 'success' ); +// assert( Object.prototype.hasOwnProperty.call( result, 'addressTag' ) === false, WARN_SHOULD_NOT_HAVE_KEY + 'addressTag' ); +// assert( Object.prototype.hasOwnProperty.call( result, 'asset' ) === false, WARN_SHOULD_NOT_HAVE_KEY + 'asset' ); +// assert( result.success === false ); +// done(); +// } ); +// } ).timeout( TIMEOUT ); +// } ); + +// describe( 'Account status', function () { +// it( 'Attempt to get account status', function ( done ) { +// binance.accountStatus( ( error, data ) => { +// debug( error ); +// debug( data ); +// assert( typeof ( error ) === 'object', WARN_SHOULD_BE_OBJ ); +// assert( typeof ( data ) === 'object', WARN_SHOULD_BE_OBJ ); +// assert( error === null, WARN_SHOULD_BE_NULL ); +// assert( data !== null, WARN_SHOULD_BE_NOT_NULL ); +// done(); +// } ); +// } ).timeout( TIMEOUT ); +// } ); + +describe( 'Account', function () { + it( 'Attempt to get account information', function ( done ) { + binance.account( ( error, data ) => { + debug( error ); + debug( data ); + assert( typeof ( error ) === 'object', WARN_SHOULD_BE_OBJ ); + assert( typeof ( data ) === 'object', WARN_SHOULD_BE_OBJ ); + assert( error === null, WARN_SHOULD_BE_NULL ); + assert( data !== null, WARN_SHOULD_BE_NOT_NULL ); + done(); + } ); + } ).timeout( TIMEOUT ); +} ); + +describe( 'Time', function () { + it( 'Attempt to get server time', function ( done ) { + binance.time( ( error, data ) => { + debug( error ); + debug( data ); + assert( typeof ( error ) === 'object', WARN_SHOULD_BE_OBJ ); + assert( typeof ( data ) === 'object', WARN_SHOULD_BE_OBJ ); + assert( error === null, WARN_SHOULD_BE_NULL ); + assert( data !== null, WARN_SHOULD_BE_NOT_NULL ); + assert( Object.prototype.hasOwnProperty.call( data, 'serverTime' ), WARN_SHOULD_HAVE_KEY + 'serverTime' ); + done(); + } ); + } ).timeout( TIMEOUT ); +} ); + +describe( 'Aggtrades', function () { + it( 'Attempt to get aggTrades for given symbol', function ( done ) { + binance.aggTrades( 'BNBBTC', { limit: 500 }, ( error, response ) => { + assert( typeof ( error ) === 'object', WARN_SHOULD_BE_OBJ ); + assert( typeof ( response ) === 'object', WARN_SHOULD_BE_OBJ ); + assert( error === null, WARN_SHOULD_BE_NULL ); + assert( response !== null, WARN_SHOULD_BE_NOT_NULL ); + done(); + } ); + } ).timeout( TIMEOUT ); +} ); + +describe( 'Recent Trades', function () { + it( 'Attempt get recent Trades for a given symbol', function ( done ) { + binance.recentTrades( 'BNBBTC', ( error, data ) => { + debug( error ); + debug( data ); + assert( typeof ( error ) === 'object', WARN_SHOULD_BE_OBJ ); + assert( typeof ( data ) === 'object', WARN_SHOULD_BE_OBJ ); + assert( error === null, WARN_SHOULD_BE_NULL ); + assert( data !== null, WARN_SHOULD_BE_NOT_NULL ); + assert( data.length > 0 ); + data.forEach( function ( obj ) { + assert( Object.prototype.hasOwnProperty.call( obj, 'id' ), WARN_SHOULD_HAVE_KEY + 'id' ); + assert( Object.prototype.hasOwnProperty.call( obj, 'price' ), WARN_SHOULD_HAVE_KEY + 'price' ); + assert( Object.prototype.hasOwnProperty.call( obj, 'qty' ), WARN_SHOULD_HAVE_KEY + 'qty' ); + assert( Object.prototype.hasOwnProperty.call( obj, 'time' ), WARN_SHOULD_HAVE_KEY + 'time' ); + assert( Object.prototype.hasOwnProperty.call( obj, 'isBuyerMaker' ), WARN_SHOULD_HAVE_KEY + 'isBuyerMaker' ); + assert( Object.prototype.hasOwnProperty.call( obj, 'isBestMatch' ), WARN_SHOULD_HAVE_KEY + 'isBestMatch' ); + } ); + done(); + } ); + } ).timeout( TIMEOUT ); +} ); + +describe( 'Historical Trades', function () { + it( 'Attempt get Historical Trades for a given symbol', function ( done ) { + binance.historicalTrades( 'BNBBTC', ( error, data ) => { + debug( error ); + debug( data ); + assert( typeof ( error ) === 'object', WARN_SHOULD_BE_OBJ ); + assert( typeof ( data ) === 'object', WARN_SHOULD_BE_OBJ ); + assert( error === null, WARN_SHOULD_BE_NULL ); + assert( data !== null, WARN_SHOULD_BE_NOT_NULL ); + assert( data.length > 0 ); + data.forEach( function ( obj ) { + assert( Object.prototype.hasOwnProperty.call( obj, 'id' ), WARN_SHOULD_HAVE_KEY + 'id' ); + assert( Object.prototype.hasOwnProperty.call( obj, 'price' ), WARN_SHOULD_HAVE_KEY + 'price' ); + assert( Object.prototype.hasOwnProperty.call( obj, 'qty' ), WARN_SHOULD_HAVE_KEY + 'qty' ); + assert( Object.prototype.hasOwnProperty.call( obj, 'time' ), WARN_SHOULD_HAVE_KEY + 'time' ); + assert( Object.prototype.hasOwnProperty.call( obj, 'isBuyerMaker' ), WARN_SHOULD_HAVE_KEY + 'isBuyerMaker' ); + assert( Object.prototype.hasOwnProperty.call( obj, 'isBestMatch' ), WARN_SHOULD_HAVE_KEY + 'isBestMatch' ); + } ); + done(); + } ); + } ).timeout( TIMEOUT ); +} ); + +describe( 'getInfo', function () { + it( 'Gets the info array form the binance object', function ( done ) { + assert( typeof ( binance.getInfo() ) === 'object', 'Should be of type array' ) + done(); + } ).timeout( TIMEOUT ); +} ); + +// describe( 'Websockets candlesticks', function () { +// let candlesticks; +// let cnt = 0; +// /*global beforeEach*/ +// beforeEach( function ( done ) { +// this.timeout( TIMEOUT ); +// binance.websockets.candlesticks( [ 'BNBBTC' ], '1m', a_candlesticks => { +// cnt++; +// if ( cnt > 1 ) return; +// candlesticks = a_candlesticks; +// stopSockets(); +// done(); +// } ); +// } ); + +// it( 'Calls candlesticks websocket', function () { +// assert( typeof ( candlesticks ) === 'object', WARN_SHOULD_BE_OBJ ); +// assert( candlesticks !== null, WARN_SHOULD_BE_NOT_NULL ); +// assert( Object.keys( candlesticks ).length >= 0, 'should at least 1 currency pairs?' ); + +// let keys = [ 't', 'T', 's', 'i', 'f', 'L', 'o', 'c', 'h', 'l', 'v', 'n', 'x', 'q', 'V', 'Q', 'B' ]; +// assert( Object.prototype.hasOwnProperty.call( candlesticks, 'e' ), WARN_SHOULD_HAVE_KEY + 'e' ); +// assert( Object.prototype.hasOwnProperty.call( candlesticks, 'E' ), WARN_SHOULD_HAVE_KEY + 'E' ); +// assert( Object.prototype.hasOwnProperty.call( candlesticks, 's' ), WARN_SHOULD_HAVE_KEY + 's' ); +// assert( Object.prototype.hasOwnProperty.call( candlesticks, 's' ), WARN_SHOULD_HAVE_KEY + 'k' ); + +// keys.forEach( function ( key ) { +// assert( Object.prototype.hasOwnProperty.call( candlesticks.k, key ), WARN_SHOULD_HAVE_KEY + key ); +// } ); +// } ); +// } ); + +// websockets disabled for now +// describe( 'Websockets depth', function () { +// let depth; +// let cnt = 0; +// /*global beforeEach*/ +// beforeEach( function ( done ) { +// this.timeout( TIMEOUT ); +// binance.websockets.depth( [ 'BNBBTC' ], e_depth => { +// cnt++; +// if ( cnt > 1 ) return; +// depth = e_depth; +// stopSockets(); +// done(); +// } ); +// } ); + +// it( 'Calls depth websocket', function () { +// assert( typeof ( depth ) === 'object', WARN_SHOULD_BE_OBJ ); +// assert( depth !== null, WARN_SHOULD_BE_NOT_NULL ); +// } ); +// } ); + +// describe( 'Websockets aggregated trades', function () { +// let trades; +// let cnt = 0; +// /*global beforeEach*/ +// beforeEach( function ( done ) { +// this.timeout( TIMEOUT ); +// binance.websockets.aggTrades( [ 'BNBBTC', 'ETHBTC' ], e_trades => { +// cnt++; +// if ( cnt > 1 ) return; +// trades = e_trades; +// stopSockets(); +// done(); +// } ); +// } ); + +// it( 'Calls trades websocket', function () { +// assert( typeof ( trades ) === 'object', WARN_SHOULD_BE_OBJ ); +// assert( trades !== null, WARN_SHOULD_BE_NOT_NULL ); +// } ); +// } ); + + +// describe( 'Websockets (raw) trades', function () { +// let trades; +// let cnt = 0; +// /*global beforeEach*/ +// beforeEach( function ( done ) { +// this.timeout( TIMEOUT ); +// binance.websockets.trades( [ 'BNBBTC', 'ETHBTC' ], e_trades => { +// cnt++; +// if ( cnt > 1 ) return; +// trades = e_trades; +// stopSockets(); +// done(); +// } ); +// } ); + +// it( 'Calls trades websocket', function () { +// assert( typeof ( trades ) === 'object', WARN_SHOULD_BE_OBJ ); +// assert( trades !== null, WARN_SHOULD_BE_NOT_NULL ); +// } ); +// } ); + +// describe( 'depthCache', function () { +// it( 'depthCache', function ( done ) { +// binance.depthCache( 'BNBBTC' ); +// done(); +// } ).timeout( TIMEOUT ); +// } ); + +// describe( 'depthVolume', function () { +// it( 'depthVolume', function ( done ) { +// binance.depthVolume( 'BNBBTC' ); +// done(); +// } ).timeout( TIMEOUT ); +// } ); + +// describe( 'getPrecision', function () { +// it( 'getPrecision', function ( done ) { +// binance.getPrecision( 1.9999999 ); +// done(); +// } ).timeout( TIMEOUT ); +// } ); + +// describe( 'roundStep', function () { +// it( 'roundStep', function ( done ) { +// binance.roundStep( 10, 0.8 ); +// done(); +// } ).timeout( TIMEOUT ); +// } ); + +// describe( 'Websockets miniticker', function () { +// let markets; +// let cnt = 0; +// beforeEach( function ( done ) { +// this.timeout( TIMEOUT ); +// binance.websockets.miniTicker( tick => { +// cnt++; +// if ( cnt > 1 ) return; +// markets = tick; +// stopSockets(); +// done(); +// } ); +// } ); + +// it( 'check miniticker websocket', function () { +// assert( typeof ( markets ) === 'object', WARN_SHOULD_BE_OBJ ); +// assert( markets !== null, WARN_SHOULD_BE_NOT_NULL ); +// assert( Object.keys( markets ).length >= 0, 'should at least 1 currency pairs?' ); + +// Object.keys( markets ).forEach( function ( symbol ) { +// assert( Object.prototype.hasOwnProperty.call( markets[symbol], 'close' ), WARN_SHOULD_HAVE_KEY + 'close' ); +// assert( Object.prototype.hasOwnProperty.call( markets[symbol], 'open' ), WARN_SHOULD_HAVE_KEY + 'open' ); +// assert( Object.prototype.hasOwnProperty.call( markets[symbol], 'high' ), WARN_SHOULD_HAVE_KEY + 'high' ); +// assert( Object.prototype.hasOwnProperty.call( markets[symbol], 'low' ), WARN_SHOULD_HAVE_KEY + 'low' ); +// assert( Object.prototype.hasOwnProperty.call( markets[symbol], 'volume' ), WARN_SHOULD_HAVE_KEY + 'volume' ); +// assert( Object.prototype.hasOwnProperty.call( markets[symbol], 'quoteVolume' ), WARN_SHOULD_HAVE_KEY + 'quoteVolume' ); +// assert( Object.prototype.hasOwnProperty.call( markets[symbol], 'eventTime' ), WARN_SHOULD_HAVE_KEY + 'eventTime' ); +// } ); +// } ); +// } ); + +// describe( 'Websockets symbol depthcache', function () { +// let symbol; +// let bids; +// let asks; +// let cnt = 0; +// beforeEach( function ( done ) { +// this.timeout( TIMEOUT ); +// binance.websockets.depthCache( 'BNBBTC', ( a_symbol, a_depth ) => { +// cnt++; +// if ( cnt > 1 ) return; +// stopSockets( true ); +// symbol = a_symbol; +// bids = a_depth.bids; +// asks = a_depth.asks; +// done(); +// } ); +// } ); + +// bids = binance.sortBids( bids ); +// asks = binance.sortAsks( asks ); + +// it( 'check result of depth cache', function () { +// assert( typeof ( bids ) === 'object', WARN_SHOULD_BE_OBJ ); +// assert( typeof ( asks ) === 'object', WARN_SHOULD_BE_OBJ ); +// assert( typeof ( symbol ) === 'string', WARN_SHOULD_BE_OBJ ); +// assert( bids !== null, WARN_SHOULD_BE_NOT_NULL ); +// assert( asks !== null, WARN_SHOULD_BE_NOT_NULL ); +// assert( symbol !== null, WARN_SHOULD_BE_NOT_NULL ); +// assert( Object.keys( asks ).length !== 0, 'should not be 0' ); +// assert( Object.keys( bids ).length !== 0, 'should not be 0' ); +// } ); +// } ); + +// describe( 'Websockets array depthcache', function () { +// let symbol; +// let bids; +// let asks; +// let cnt = 0; +// beforeEach( function ( done ) { +// this.timeout( TIMEOUT ); +// binance.websockets.depthCache( [ 'BNBBTC', 'TRXBTC' ], ( a_symbol, a_depth ) => { +// cnt++; +// if ( cnt > 1 ) return; +// stopSockets(); +// symbol = a_symbol; +// bids = a_depth.bids; +// asks = a_depth.asks; +// done(); +// } ); +// } ); + +// bids = binance.sortBids( bids ); +// asks = binance.sortAsks( asks ); + +// it( 'check result of symbols array depth cache', function () { +// assert( typeof ( bids ) === 'object', WARN_SHOULD_BE_OBJ ); +// assert( typeof ( asks ) === 'object', WARN_SHOULD_BE_OBJ ); +// assert( typeof ( symbol ) === 'string', WARN_SHOULD_BE_OBJ ); +// assert( bids !== null, WARN_SHOULD_BE_NOT_NULL ); +// assert( asks !== null, WARN_SHOULD_BE_NOT_NULL ); +// assert( symbol !== null, WARN_SHOULD_BE_NOT_NULL ); +// assert( Object.keys( asks ).length !== 0, 'should not be 0' ); +// assert( Object.keys( bids ).length !== 0, 'should not be 0' ); +// } ); +// } ); + +// describe( 'Staggered websockets symbol depthcache', function () { +// let symbol; +// let bids; +// let asks; +// let cnt = 0; +// beforeEach( function ( done ) { +// this.timeout( TIMEOUT ); +// binance.websockets.depthCacheStaggered( 'BNBBTC', ( a_symbol, a_depth ) => { +// cnt++; +// if ( cnt > 1 ) return; +// stopSockets( true ); +// symbol = a_symbol; +// bids = a_depth.bids; +// asks = a_depth.asks; +// done(); +// } ); +// } ); + +// bids = binance.sortBids( bids ); +// asks = binance.sortAsks( asks ); + +// it( 'check result of depth cache', function () { +// assert( typeof ( bids ) === 'object', WARN_SHOULD_BE_OBJ ); +// assert( typeof ( asks ) === 'object', WARN_SHOULD_BE_OBJ ); +// assert( typeof ( symbol ) === 'string', WARN_SHOULD_BE_OBJ ); +// assert( bids !== null, WARN_SHOULD_BE_NOT_NULL ); +// assert( asks !== null, WARN_SHOULD_BE_NOT_NULL ); +// assert( symbol !== null, WARN_SHOULD_BE_NOT_NULL ); +// assert( Object.keys( asks ).length !== 0, 'should not be 0' ); +// assert( Object.keys( bids ).length !== 0, 'should not be 0' ); +// } ); +// } ); + +// describe( 'Staggered Websockets array depthcache', function () { +// let symbol; +// let bids; +// let asks; +// let cnt = 0; +// beforeEach( function ( done ) { +// this.timeout( TIMEOUT ); +// binance.websockets.depthCacheStaggered( [ 'BNBBTC', 'TRXBTC' ], ( a_symbol, a_depth ) => { +// cnt++; +// if ( cnt > 1 ) return; +// stopSockets(); +// symbol = a_symbol; +// bids = a_depth.bids; +// asks = a_depth.asks; +// done(); +// } ); +// } ); + +// bids = binance.sortBids( bids ); +// asks = binance.sortAsks( asks ); + +// it( 'check result of symbols array depth cache', function () { +// assert( typeof ( bids ) === 'object', WARN_SHOULD_BE_OBJ ); +// assert( typeof ( asks ) === 'object', WARN_SHOULD_BE_OBJ ); +// assert( typeof ( symbol ) === 'string', WARN_SHOULD_BE_OBJ ); +// assert( bids !== null, WARN_SHOULD_BE_NOT_NULL ); +// assert( asks !== null, WARN_SHOULD_BE_NOT_NULL ); +// assert( symbol !== null, WARN_SHOULD_BE_NOT_NULL ); +// assert( Object.keys( asks ).length !== 0, 'should not be 0' ); +// assert( Object.keys( bids ).length !== 0, 'should not be 0' ); +// } ); +// } ); + +// describe( 'Websockets prevDay', function () { +// let response; +// let cnt = 0; + +// beforeEach( function ( done ) { +// this.timeout( TIMEOUT ); +// binance.websockets.prevDay( false, a_response => { +// cnt++; +// if ( cnt > 1 ) return; +// stopSockets(); +// response = a_response; +// done(); +// } ) +// } ); + +// it( 'Calls prevDay websocket for symbol', function () { +// assert( typeof ( response ) === 'object', WARN_SHOULD_BE_OBJ ); +// } ); +// } ); + +// describe( 'Websockets prevDay array', function () { +// let response; +// let cnt = 0; + +// beforeEach( function ( done ) { +// this.timeout( TIMEOUT ); +// binance.websockets.prevDay( [ 'BNBBTC', 'TRXBTC' ], a_response => { +// cnt++; +// if ( cnt > 1 ) return; +// stopSockets(); +// response = a_response; +// done(); +// } ) +// } ); + +// it( 'Calls prevDay websocket for array of symbols', function () { +// assert( typeof ( response ) === 'object', WARN_SHOULD_BE_OBJ ); +// } ); +// } ); + +// describe( 'Websockets prevDay single symbol', function () { +// let response; +// let cnt = 0; + +// beforeEach( function ( done ) { +// this.timeout( TIMEOUT ); +// binance.websockets.prevDay( 'BNBBTC', a_response => { +// cnt++; +// if ( cnt > 1 ) return; +// stopSockets(); +// response = a_response; +// done(); +// } ) +// } ); + +// it( 'Calls prevDay websocket for a single symbol', function () { +// assert( typeof ( response ) === 'object', WARN_SHOULD_BE_OBJ ); +// } ); +// } ); + +// describe( 'Websockets chart', function () { +// let chart; +// let interval; +// let symbol; +// let cnt = 0; +// beforeEach( function ( done ) { +// this.timeout( TIMEOUT ); +// binance.websockets.chart( 'BNBBTC', '1m', ( a_symbol, a_interval, a_chart ) => { +// cnt++; +// if ( cnt > 1 ) { +// stopSockets(); +// return; +// } +// chart = a_chart; +// interval = a_interval; +// symbol = a_symbol; +// stopSockets(); +// done(); +// } ); +// } ); + +// it( 'Calls chart websocket', function () { +// assert( typeof ( chart ) === 'object', WARN_SHOULD_BE_OBJ ); +// assert( typeof ( symbol ) === 'string', WARN_SHOULD_BE_OBJ ); +// assert( typeof ( interval ) === 'string', WARN_SHOULD_BE_OBJ ); +// assert( chart !== null, WARN_SHOULD_BE_NOT_NULL ); +// assert( symbol !== null, WARN_SHOULD_BE_NOT_NULL ); +// assert( interval !== null, WARN_SHOULD_BE_NOT_NULL ); + +// let keys = [ 'open', 'high', 'open', 'close', 'volume' ]; +// assert( Object.keys( chart ).length > 0, 'Should not be empty' ); + +// Object.keys( chart ).forEach( function ( c ) { +// keys.forEach( function ( key ) { +// assert( Object.prototype.hasOwnProperty.call( chart[c], key ), WARN_SHOULD_HAVE_KEY + key ); +// } ); +// } ); +// } ); +// } ); + +// describe( 'ohlc', function () { +// let chart = [ { 'open': 1.11111, 'high': 6.6666, 'low': 8.88888, 'close': 4.44444, 'volume': 3.333333 }, { 'open': 1.11111, 'high': 6.6666, 'low': 8.88888, 'close': 4.44444, 'volume': 3.333333 } ]; +// it( 'Calls ohlc', function () { +// binance.ohlc( chart ); +// } ); +// } ); + +// describe( 'highstock', function () { +// let chart = [ { 'open': 1.11111, 'high': 6.6666, 'low': 8.88888, 'close': 4.44444, 'volume': 3.333333 }, { 'open': 1.11111, 'high': 6.6666, 'low': 8.88888, 'close': 4.44444, 'volume': 3.333333 } ]; +// it( 'Calls highstock', function () { +// binance.highstock( chart ); +// } ); +// } ); + +// describe( 'Websockets chart array', function () { +// let chart; +// let interval; +// let symbol; +// let cnt = 0; +// beforeEach( function ( done ) { +// this.timeout( TIMEOUT ); +// binance.websockets.chart( [ 'BNBBTC', 'TRXBTC' ], '1m', ( a_symbol, a_interval, a_chart ) => { +// cnt++; +// if ( cnt > 1 ) { +// stopSockets(); +// return; +// } +// chart = a_chart; +// interval = a_interval; +// symbol = a_symbol; +// stopSockets(); +// done(); +// } ); +// } ); + +// it( 'Calls chart websocket array', function () { +// assert( typeof ( chart ) === 'object', WARN_SHOULD_BE_OBJ ); +// assert( typeof ( symbol ) === 'string', WARN_SHOULD_BE_OBJ ); +// assert( typeof ( interval ) === 'string', WARN_SHOULD_BE_OBJ ); +// assert( chart !== null, WARN_SHOULD_BE_NOT_NULL ); +// assert( symbol !== null, WARN_SHOULD_BE_NOT_NULL ); +// assert( interval !== null, WARN_SHOULD_BE_NOT_NULL ); + +// let keys = [ 'open', 'high', 'open', 'close', 'volume' ]; +// assert( Object.keys( chart ).length > 0, 'Should not be empty' ); + +// Object.keys( chart ).forEach( function ( c ) { +// keys.forEach( function ( key ) { +// assert( Object.prototype.hasOwnProperty.call( chart[c], key ), WARN_SHOULD_HAVE_KEY + key ); +// } ); +// } ); +// } ); +// } ); + + +/* +describe( 'Websockets userdata', function() { + let userdata; + beforeEach(function (done) { + this.timeout( TIMEOUT ); + binance.websockets.userData( data => { + userdata = data; + stopSockets(); + done(); + }); + }); + + it( 'Calls userdata websocket', function() { + assert( typeof ( userdata ) === 'object', WARN_SHOULD_BE_OBJ ); + assert( userdata !== null, WARN_SHOULD_BE_NOT_NULL ); + }); +}); +*/ diff --git a/tests/package-test/test-cjs.cjs b/tests/package-test/test-cjs.cjs new file mode 100644 index 00000000..cf95e960 --- /dev/null +++ b/tests/package-test/test-cjs.cjs @@ -0,0 +1,10 @@ +const Binance = require('node-binance-api'); + +const client = new Binance({test: true}) + +async function main() { + const ticker = await client.prices('BTCUSDT') + console.log(ticker) +} + +main() \ No newline at end of file diff --git a/tests/package-test/test-esm.mjs b/tests/package-test/test-esm.mjs new file mode 100644 index 00000000..617b1e38 --- /dev/null +++ b/tests/package-test/test-esm.mjs @@ -0,0 +1,10 @@ +import Binance from 'node-binance-api' +const client = new Binance({test: true}) + +async function main() { + const ticker = await client.bookTickers('BTCUSDT') + const res = ticker['BTCUSDT']; + console.log(res) +} + +main() \ No newline at end of file diff --git a/tests/package.sh b/tests/package.sh new file mode 100755 index 00000000..ed5715df --- /dev/null +++ b/tests/package.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +npm pack . --silent +mv node-binance-api*.tgz ./tests/package-test/ +cd ./tests/package-test +npm init -y > /dev/null +npm install node-binance-api*.tgz +node test-esm.mjs +return_code=$? +node test-cjs.cjs +cjs_return_code=$? +rm -rf node_modules node-binance-api*.tgz package-lock.json package.json + +if [ $return_code -eq 0 ] && [ $cjs_return_code -eq 0 ]; then + echo "Package test successful" + exit 0 +else + echo "Package test failed" + exit 1 +fi \ No newline at end of file diff --git a/tests/static-tests.mjs b/tests/static-tests.mjs new file mode 100644 index 00000000..85a3d24d --- /dev/null +++ b/tests/static-tests.mjs @@ -0,0 +1,252 @@ +import chai from 'chai'; +import path from 'path'; +import utils from 'util'; +import Binance from '../dist/cjs/node-binance-api.cjs'; +import nock from 'nock'; +const assert = chai.assert; + +const binance = new Binance({ + APIKEY: 'XXXXXXXXXXXXXXXXXXXXXXX', + APISECRET: 'YYYYYYYYYYYYYYYYYYYYYY', +}) + +const debug = function ( x ) { + if ( typeof ( process.env.node_binance_api ) === 'undefined' ) { + return; + } + logger.log( typeof ( x ) ); + logger.log( util.inspect( x ) ); +} + +function urlToObject(queryString) { + const params = new URLSearchParams(queryString); + const obj = Object.fromEntries(params.entries()); + return obj; +} + +describe( 'Static tests', async function () { + + let interceptedUrl = null; + let interceptedBody = null; + + beforeEach(() => { + + interceptedUrl = null; + interceptedBody = null; + nock(/.*/) + .get(/.*/) + .reply(200, function (uri, requestBody) { + interceptedUrl = `${this.req.options.proto}://${this.req.options.hostname}${uri}`; + interceptedBody = requestBody; // Capture the request body + return { success: true }; + }); + nock(/.*/) + .post(/.*/) + .reply(200, function (uri, requestBody) { + interceptedUrl = `${this.req.options.proto}://${this.req.options.hostname}${uri}`; + interceptedBody = requestBody; // Capture the request body + return { success: true }; + }); + nock(/.*/) + .delete(/.*/) + .reply(200, function (uri, requestBody) { + interceptedUrl = `${this.req.options.proto}://${this.req.options.hostname}${uri}`; + interceptedBody = requestBody; // Capture the request body + return { success: true }; + }); + }); + + it( 'FetchTicker', async function ( ) { + await binance.prices( 'BNBBTC' ) + assert.equal( interceptedUrl, 'https://api.binance.com/api/v3/ticker/price?symbol=BNBBTC' ) + + }) + + it( 'FetchOrderBook', async function ( ) { + await binance.depth( 'BTCUSDT' ) + assert.equal( interceptedUrl, 'https://api.binance.com/api/v3/depth?symbol=BTCUSDT&limit=100' ) + + }) + + it( 'Futures OrderBook', async function ( ) { + await binance.futuresDepth( 'BTCUSDT' ) + assert.equal( interceptedUrl, 'https://fapi.binance.com/fapi/v1/depth?symbol=BTCUSDT' ) + + }) + + it( 'OHLCVS', async function ( ) { + await binance.candlesticks( 'BTCUSDT' ) + assert.equal( interceptedUrl, 'https://api.binance.com/api/v3/klines?symbol=BTCUSDT&interval=5m&limit=500' ) + + }) + + it( 'Futures OHLCVS', async function ( ) { + await binance.futuresCandles( 'BTCUSDT' ) + assert.equal( interceptedUrl, 'https://fapi.binance.com/fapi/v1/klines?symbol=BTCUSDT&interval=30m' ) + + }) + + it( 'Trades', async function ( ) { + await binance.aggTrades( 'BTCUSDT' ) + assert.equal( interceptedUrl, 'https://api.binance.com/api/v3/aggTrades?symbol=BTCUSDT' ) + + }) + + it( 'FuturesTrades', async function ( ) { + await binance.futuresTrades( 'BTCUSDT' ) + assert.equal( interceptedUrl, 'https://fapi.binance.com/fapi/v1/trades?symbol=BTCUSDT' ) + + }) + + it( 'PositionRisk V3', async function ( ) { + await binance.futuresPositionRisk() + assert.isTrue( interceptedUrl.startsWith('https://fapi.binance.com/fapi/v3/positionRisk') ) + }) + + it( 'PositionRisk V2', async function ( ) { + await binance.futuresPositionRiskV2() + assert.isTrue( interceptedUrl.startsWith('https://fapi.binance.com/fapi/v2/positionRisk') ) + }) + + it( 'CancelOrder', async function ( ) { + await binance.cancel( 'LTCUSDT', '34234234' ) + assert( interceptedUrl.startsWith('https://api.binance.com/api/v3/order' )) + const obj = urlToObject( interceptedUrl.replace('https://api.binance.com/api/v3/order', '') ) + assert.equal( obj.symbol, 'LTCUSDT' ) + assert.equal( obj.orderId, '34234234') + }) + + it( 'Futures CancelOrder', async function ( ) { + await binance.futuresCancel( 'LTCUSDT', '34234234') + assert( interceptedUrl.startsWith('https://fapi.binance.com/fapi/v1/order')) + const obj = urlToObject( interceptedUrl.replace('https://fapi.binance.com/fapi/v1/order', '') ) + assert.equal( obj.symbol, 'LTCUSDT' ) + assert.equal( obj.orderId, '34234234') + }) + + const SPOT_PREFIX = "x-HNA2TXFJ" + + it( 'MarketBuy', async function ( ) { + await binance.marketBuy( 'LTCUSDT', 0.5 ) + assert.equal( interceptedUrl, 'https://api.binance.com/api/v3/order' ) + const obj = urlToObject( interceptedBody ) + assert.equal( obj.symbol, 'LTCUSDT' ) + assert.equal( obj.side, 'BUY' ) + assert.equal( obj.type, 'MARKET' ) + assert.equal( obj.quantity, 0.5 ) + assert(obj.newClientOrderId.startsWith(SPOT_PREFIX)) + }) + + it( 'MarketSell', async function ( ) { + await binance.marketSell( 'LTCUSDT', 0.5 ) + assert.equal( interceptedUrl, 'https://api.binance.com/api/v3/order' ) + const obj = urlToObject( interceptedBody ) + assert.equal( obj.symbol, 'LTCUSDT' ) + assert.equal( obj.side, 'SELL' ) + assert.equal( obj.type, 'MARKET' ) + assert.equal( obj.quantity, 0.5 ) + assert(obj.newClientOrderId.startsWith(SPOT_PREFIX)) + }) + + it( 'LimitBuy', async function ( ) { + await binance.order('LIMIT', 'BUY', 'LTCUSDT', 0.5 ) + assert.equal( interceptedUrl, 'https://api.binance.com/api/v3/order' ) + const obj = urlToObject( interceptedBody ) + assert.equal( obj.symbol, 'LTCUSDT' ) + assert.equal( obj.side, 'BUY' ) + assert.equal( obj.type, 'LIMIT' ) + assert.equal( obj.quantity, 0.5 ) + assert(obj.newClientOrderId.startsWith(SPOT_PREFIX)) + }) + + it( 'LimitSell', async function ( ) { + await binance.order('LIMIT', 'SELL', 'LTCUSDT', 0.5 ) + assert.equal( interceptedUrl, 'https://api.binance.com/api/v3/order' ) + const obj = urlToObject( interceptedBody ) + assert.equal( obj.symbol, 'LTCUSDT' ) + assert.equal( obj.side, 'SELL' ) + assert.equal( obj.type, 'LIMIT' ) + assert.equal( obj.quantity, 0.5 ) + assert(obj.newClientOrderId.startsWith(SPOT_PREFIX)) + }) + + const CONTRACT_PREFIX = "x-Cb7ytekJ" + + it( 'Futures MarketBuy', async function ( ) { + await binance.futuresMarketBuy( 'LTCUSDT', 0.5 ) + assert.isTrue( interceptedUrl.startsWith('https://fapi.binance.com/fapi/v1/order' )) + const obj = urlToObject( interceptedUrl.replace('https://fapi.binance.com/fapi/v1/order?', '') ) + assert.equal( obj.symbol, 'LTCUSDT' ) + assert.equal( obj.side, 'BUY' ) + assert.equal( obj.type, 'MARKET' ) + assert.equal( obj.quantity, 0.5 ) + assert(obj.newClientOrderId.startsWith(CONTRACT_PREFIX)) + }) + + it( 'Futures MarketSell', async function ( ) { + await binance.futuresMarketSell( 'LTCUSDT', 0.5 ) + assert.isTrue( interceptedUrl.startsWith('https://fapi.binance.com/fapi/v1/order' )) + const obj = urlToObject( interceptedUrl.replace('https://fapi.binance.com/fapi/v1/order?', '') ) + assert.equal( obj.symbol, 'LTCUSDT' ) + assert.equal( obj.side, 'SELL' ) + assert.equal( obj.type, 'MARKET' ) + assert.equal( obj.quantity, 0.5 ) + assert(obj.newClientOrderId.startsWith(CONTRACT_PREFIX)) + }) + + it( 'Futures LimitBuy', async function ( ) { + await binance.futuresOrder( 'LIMIT', 'BUY', 'LTCUSDT', 0.5, 100 ) + assert.isTrue( interceptedUrl.startsWith('https://fapi.binance.com/fapi/v1/order' )) + const obj = urlToObject( interceptedUrl.replace('https://fapi.binance.com/fapi/v1/order?', '') ) + assert.equal( obj.symbol, 'LTCUSDT' ) + assert.equal( obj.side, 'BUY' ) + assert.equal( obj.type, 'LIMIT' ) + assert.equal( obj.quantity, 0.5 ) + assert(obj.newClientOrderId.startsWith(CONTRACT_PREFIX)) + }) + + it( 'Futures LimitSell', async function ( ) { + await binance.futuresOrder( 'LIMIT','SELL', 'LTCUSDT', 0.5, 100 ) + assert.isTrue( interceptedUrl.startsWith('https://fapi.binance.com/fapi/v1/order' )) + const obj = urlToObject( interceptedUrl.replace('https://fapi.binance.com/fapi/v1/order?', '') ) + assert.equal( obj.symbol, 'LTCUSDT' ) + assert.equal( obj.side, 'SELL' ) + assert.equal( obj.type, 'LIMIT' ) + assert.equal( obj.quantity, 0.5 ) + assert(obj.newClientOrderId.startsWith(CONTRACT_PREFIX)) + }) + + it( 'cancel order', async function ( ) { + await binance.futuresCancel( 'LTCUSDT', '34234234' ) + const url = 'https://fapi.binance.com/fapi/v1/order' + assert.isTrue( interceptedUrl.startsWith(url) ) + const obj = urlToObject( interceptedUrl.replace(url, '') ) + assert.equal( obj.orderId, '34234234' ) + assert.equal( obj.symbol, 'LTCUSDT' ) + }) + + it( 'cancel order', async function ( ) { + await binance.cancel( 'LTCUSDT', '34234234' ) + const url = 'https://api.binance.com/api/v3/order' + assert.isTrue( interceptedUrl.startsWith(url) ) + const obj = urlToObject( interceptedUrl.replace(url, '') ) + assert.equal( obj.orderId, '34234234' ) + assert.equal( obj.symbol, 'LTCUSDT' ) + }) + + it( 'delivery OrderBook', async function ( ) { + await binance.deliveryDepth( 'BTCUSD_PERP' ) + assert.equal( interceptedUrl, 'https://dapi.binance.com/dapi/v1/depth?symbol=BTCUSD_PERP' ) + }) + + it( 'delivery MarketBuy', async function ( ) { + await binance.deliveryOrder( 'MARKET', 'BUY', 'BTCUSD_PERP', 0.1 ) + assert.isTrue( interceptedUrl.startsWith('https://dapi.binance.com/dapi/v1/order' )) + const obj = urlToObject( interceptedUrl.replace('https://dapi.binance.com/dapi/v1/order', '') ) + assert.equal( obj.symbol, 'BTCUSD_PERP' ) + assert.equal( obj.side, 'BUY' ) + assert.equal( obj.type, 'MARKET' ) + assert.equal( obj.quantity, 0.1 ) + assert(obj.newClientOrderId.startsWith(CONTRACT_PREFIX)) + }) +}) \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 00000000..cd5813d4 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,114 @@ +{ + "compilerOptions": { + /* Visit https://aka.ms/tsconfig to read more about this file */ + + /* Projects */ + // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ + // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ + // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ + // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ + // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ + // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ + + /* Language and Environment */ + "target": "ES2022", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ + // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ + // "jsx": "preserve", /* Specify what JSX code is generated. */ + // "libReplacement": true, /* Enable lib replacement. */ + // "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */ + // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ + // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ + // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ + // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ + // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ + // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ + // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ + // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ + + /* Modules */ + "module": "ES2022", /* Specify what module code is generated. */ + "rootDir": "./src/", /* Specify the root folder within your source files. */ + // "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */ + // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ + // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ + // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ + // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ + // "types": [], /* Specify type package names to be included without being referenced in a source file. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ + // "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */ + // "rewriteRelativeImportExtensions": true, /* Rewrite '.ts', '.tsx', '.mts', and '.cts' file extensions in relative import paths to their JavaScript equivalent in output files. */ + // "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */ + // "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */ + // "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */ + // "noUncheckedSideEffectImports": true, /* Check side effect imports. */ + // "resolveJsonModule": true, /* Enable importing .json files. */ + // "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */ + // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ + + /* JavaScript Support */ + // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ + // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ + // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ + + /* Emit */ + "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ + // "declarationMap": true, /* Create sourcemaps for d.ts files. */ + // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ + // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ + // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ + // "noEmit": true, /* Disable emitting files from a compilation. */ + // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ + "outDir": "./dist", /* Specify an output folder for all emitted files. */ + // "removeComments": true, /* Disable emitting comments. */ + // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ + // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ + // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ + // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ + // "newLine": "crlf", /* Set the newline character for emitting files. */ + // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ + // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ + // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ + // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ + // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ + + /* Interop Constraints */ + // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ + // "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */ + // "isolatedDeclarations": true, /* Require sufficient annotation on exports so other tools can trivially generate declaration files. */ + // "erasableSyntaxOnly": true, /* Do not allow runtime constructs that are not part of ECMAScript. */ + // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ + "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ + // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ + "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ + + /* Type Checking */ + "strict": false, /* Enable all strict type-checking options. */ + "noImplicitAny": false, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ + // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ + // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ + // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ + // "strictBuiltinIteratorReturn": true, /* Built-in iterators are instantiated with a 'TReturn' type of 'undefined' instead of 'any'. */ + // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ + // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ + // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ + // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ + // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ + // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ + // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ + // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ + // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ + // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ + // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ + // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ + // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ + /* Completeness */ + // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ + "skipLibCheck": true /* Skip type checking all .d.ts files. */ + }, + "include": ["src/"], + "exclude": ["examples/", "tests/"], +} diff --git a/vue.config.js b/vue.config.js new file mode 100644 index 00000000..8e10c488 --- /dev/null +++ b/vue.config.js @@ -0,0 +1,11 @@ +module.exports = { + configureWebpack: { + node: { + fs: "empty", + dgram: "empty", + net: 'empty', + tls: 'empty', + dns: 'empty' + } + } +}