├── .gitignore ├── src ├── assets │ ├── css │ │ ├── main.scss │ │ ├── _colors.scss │ │ ├── _overrides.scss │ │ ├── _table.scss │ │ ├── _stats.scss │ │ ├── _reset.scss │ │ └── _page.scss │ └── icons │ │ ├── flag-at.svg │ │ ├── flag-de.svg │ │ ├── flag-ie.svg │ │ ├── flag-ru.svg │ │ ├── flag-fr.svg │ │ ├── flag-ch.svg │ │ ├── flag-nl.svg │ │ ├── flag-jp.svg │ │ ├── flag-cn.svg │ │ ├── flag-gb.svg │ │ ├── flag-ca.svg │ │ ├── circle-info.svg │ │ ├── flag-in.svg │ │ ├── flag-sg.svg │ │ ├── flag-au.svg │ │ ├── flag-kr.svg │ │ ├── flag-hk.svg │ │ ├── flag-us.svg │ │ └── flag-br.svg ├── app │ ├── include.template.js │ ├── app.module.js │ ├── app.run.js │ ├── filters.js │ ├── icon.directive.js │ ├── view.controller.js │ ├── lib │ │ ├── angular.http.js │ │ └── neo.js │ ├── view.html │ └── netstats.factory.js └── index.html ├── package.json ├── .github └── workflows │ └── build.yml ├── LICENSE ├── tests └── jsons.test.js ├── README.md ├── testnet.json └── mainnet.json /.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | node_modules 3 | -------------------------------------------------------------------------------- /src/assets/css/main.scss: -------------------------------------------------------------------------------- 1 | @import "reset"; 2 | @import "overrides"; 3 | @import "colors"; 4 | @import "table"; 5 | @import "stats"; 6 | @import "page"; 7 | -------------------------------------------------------------------------------- /src/app/include.template.js: -------------------------------------------------------------------------------- 1 | angular.module("neomon").run(["$templateCache", function(t) { 2 | t.put("app/view.html", ` 3 | {replace view.html} 4 | `) 5 | }]); 6 | -------------------------------------------------------------------------------- /src/assets/icons/flag-at.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/assets/icons/flag-de.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/assets/icons/flag-ie.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/assets/icons/flag-ru.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/assets/icons/flag-fr.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/assets/icons/flag-ch.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/app/app.module.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | angular.module('neomon', [ 4 | 5 | 'angularMoment', 6 | 7 | 'neo.angularClient', //angularClient used by neo-api-js in app/lib 8 | 9 | 'neomon.filters', 10 | 'neomon.directives.icon', 11 | 'neomon.netstats.factory', 12 | 'neomon.view.controller' 13 | ]); 14 | 15 | })(); 16 | 17 | -------------------------------------------------------------------------------- /src/assets/icons/flag-nl.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/assets/css/_colors.scss: -------------------------------------------------------------------------------- 1 | 2 | .color-best { 3 | color: #10a0de; 4 | fill: #10a0de; 5 | } 6 | 7 | .color-offline { 8 | color: #f65445; 9 | fill: #f65445; 10 | } 11 | 12 | .color-warning { 13 | color: #e4b232; 14 | fill: #e4b232; 15 | } 16 | 17 | .color-success { 18 | color: #7bcc3a; 19 | } 20 | 21 | .color-gray { 22 | color: #767676; 23 | fill: #767676; 24 | } 25 | 26 | .color-orange { 27 | color: #e4b232; 28 | fill: #e4b232; 29 | } -------------------------------------------------------------------------------- /src/app/app.run.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | angular.module('neomon') 5 | .run(Run); 6 | 7 | /* @ngInject */ 8 | function Run ( 9 | $window, 10 | $location, 11 | angularClient 12 | ) { 13 | 14 | if ($location.protocol() === 'https') { 15 | $window.location = 'http://monitor.cityofzion.io/'; 16 | } 17 | 18 | neo.registry.registerProtocolClient(angularClient); 19 | } 20 | 21 | 22 | })(); 23 | 24 | -------------------------------------------------------------------------------- /src/assets/css/_overrides.scss: -------------------------------------------------------------------------------- 1 | a { 2 | color: #0e97c0; 3 | text-decoration: none; 4 | cursor: pointer; 5 | } 6 | 7 | body { 8 | height: 100%; 9 | } 10 | 11 | html { 12 | background: #0c0f14; 13 | color: #FEFEFE; 14 | font-size: 14px; 15 | height: 100%; 16 | overflow-x: hidden; 17 | overflow-y: auto; 18 | -ms-overflow-style: scrollbar; 19 | -webkit-text-size-adjust: 100%; 20 | -ms-text-size-adjust: 100%; 21 | text-size-adjust: 100%; 22 | -moz-osx-font-smoothing: grayscale; 23 | -webkit-font-smoothing: antialiased; 24 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "neomon", 3 | "version": "1.0.0", 4 | "private": true, 5 | "description": "Neo Network Status Monitor", 6 | "dependencies": { 7 | }, 8 | "devDependencies": { 9 | "node-fetch": "^2.6.1", 10 | "jest": "^26.0.1", 11 | "jest-summarizing-reporter": "^1.1.4", 12 | "sass": "^1.26.3" 13 | }, 14 | "scripts": { 15 | "test": "jest --reporters jest-summarizing-reporter", 16 | "build-css": "sass --style=compressed --no-source-map src/assets/css/main.scss dist/main.css" 17 | }, 18 | "author": "", 19 | "license": "ISC" 20 | } 21 | -------------------------------------------------------------------------------- /src/assets/icons/flag-jp.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/assets/icons/flag-cn.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/assets/icons/flag-gb.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/assets/css/_table.scss: -------------------------------------------------------------------------------- 1 | 2 | .stats-table { 3 | border-collapse: collapse; 4 | border-spacing: 0; 5 | line-height: 1; 6 | min-width: 100%; 7 | table-layout: fixed; 8 | margin-bottom: 17px; 9 | margin-top: 17px; 10 | } 11 | 12 | .stats-table-header { 13 | min-width: 1360px; 14 | padding-top: 17px; 15 | padding-left: 8px; 16 | padding-right: 8px; 17 | } 18 | 19 | .stats-table-body { 20 | min-width: 1360px; 21 | margin-top: 5px; 22 | padding-left: 8px; 23 | padding-right: 8px; 24 | } 25 | 26 | 27 | .stats-table__cell { 28 | border-top: 1px solid #767676; 29 | padding: 8px; 30 | vertical-align: top; 31 | } 32 | 33 | .stats-table__header { 34 | border-bottom: 1px solid #767676; 35 | padding-bottom: 8px; 36 | padding-left: 8px; 37 | position: relative; 38 | text-align: left; 39 | vertical-align: top; 40 | } 41 | 42 | .stats-table__icon { 43 | margin-left: 5px; 44 | margin-right: 5px; 45 | } -------------------------------------------------------------------------------- /src/assets/icons/flag-ca.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/assets/css/_stats.scss: -------------------------------------------------------------------------------- 1 | 2 | 3 | .stats-network-row { 4 | display: flex; 5 | flex-direction: row; 6 | color: #7EA63B; 7 | } 8 | 9 | .stats-network-card { 10 | display: flex; 11 | flex-direction: row; 12 | padding: 20px; 13 | } 14 | 15 | .stats-network-card-details { 16 | margin-left: 17px; 17 | margin-right: 17px; 18 | } 19 | 20 | .hourglass-spin { 21 | > [class*="fa-hourglass"] { 22 | animation: showhide 4s steps(1) infinite; 23 | opacity: 0; 24 | } 25 | 26 | > .fa-hourglass-end { animation-delay: 2s; } 27 | > .fa-hourglass-half { animation-delay: 1s; } 28 | > .fa-hourglass-start { animation-delay: 0s; } 29 | 30 | > .fa-hourglass-end.spin { 31 | animation: showhidespin 4s linear infinite; 32 | } 33 | } 34 | 35 | @keyframes showhide { 36 | 0% { opacity: 1 } 37 | 25% { opacity: 0 } 38 | } 39 | @keyframes showhidespin { 40 | 0% { opacity: 0 } 41 | 74.9999999% { opacity: 0 } 42 | 75% { opacity: 1; transform: rotate(0deg); } 43 | 100% { opacity: 1; transform: rotate(180deg); } 44 | } -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build & Deploy 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | test: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v1 10 | - uses: actions/setup-node@v1 11 | with: 12 | version: 11.x 13 | - name: Install dependencies 14 | run: npm ci 15 | - name: Run tests 16 | run: npm test 17 | 18 | build: 19 | runs-on: ubuntu-latest 20 | steps: 21 | - uses: actions/checkout@v1 22 | - uses: actions/setup-node@v1 23 | with: 24 | version: 11.x 25 | - name: Install dependencies 26 | run: npm ci 27 | - name: Build page 28 | run: bash build.sh 29 | - name: Deploy to GitHub Pages 30 | uses: peaceiris/actions-gh-pages@v3 31 | if: success() && github.event_name == 'push' && github.ref == 'refs/heads/master' 32 | with: 33 | publish_dir: ./dist 34 | publish_branch: gh-pages 35 | deploy_key: ${{ secrets.ACTIONS_DEPLOY_KEY }} 36 | cname: monitor.cityofzion.io 37 | -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | NEO Network Status Monitor 6 | 7 | 8 | 9 | 10 | 12 | 13 | 15 | 16 | 17 | 18 | 19 |
20 |
21 |
22 | 23 | 24 | 25 | 26 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /src/assets/icons/circle-info.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 ffox77 and City of Zion 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/assets/icons/flag-in.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/assets/icons/flag-sg.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/app/filters.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | angular.module('neomon.filters', []) 5 | .filter('blockTime', function () { 6 | return function (timestamp) { 7 | if (timestamp === 0) { 8 | return '∞'; 9 | } 10 | var time = new Date().getTime(), diff = Math.floor((time - timestamp) / 1e3); 11 | 12 | return diff < 60 ? Math.round(diff) + ' s ago' : moment.duration(Math.round(diff), 's').humanize() + ' ago'; 13 | }; 14 | }) 15 | .filter('avgTime', function () { 16 | return function (time) { 17 | return time < 60 ? parseFloat(time).toFixed(2) + ' s' : moment.duration(Math.round(time), 's').humanize(); 18 | }; 19 | }) 20 | .filter('statusClass', function () { 21 | return function (endPoint, best) { 22 | if (endPoint.isItUp) { 23 | if (best - endPoint.lastBlock < 3 || endPoint.type === "WEBSOCKETS") { 24 | return 'color-success'; 25 | } 26 | else if (best - endPoint.lastBlock <= 1000) { 27 | return 'color-warning'; 28 | } 29 | 30 | return 'color-orange'; 31 | } 32 | 33 | return 'color-gray'; 34 | }; 35 | }); 36 | })(); 37 | -------------------------------------------------------------------------------- /src/app/icon.directive.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | angular 5 | .module('neomon.directives.icon', []) 6 | .directive('neoIcon', neoIcon); 7 | 8 | function neoIcon () { 9 | 10 | return { 11 | restrict: 'E', 12 | scope: { 13 | name: '@' 14 | }, 15 | link: link 16 | }; 17 | 18 | function link (scope, element, attrs) { 19 | 20 | var value = scope.name || element[0].textContent; 21 | 22 | if (!value) { 23 | element[0].textContent = 'SVG'; 24 | 25 | return; 26 | } 27 | 28 | var properties = { 29 | prefix: '#tsvg-', 30 | class: 'tsvg', 31 | role: 'img' 32 | }; 33 | 34 | if (attrs.class) { 35 | properties.class += ' ' + attrs.class; 36 | } 37 | 38 | var svg = document.createElement('svg'); 39 | var use = document.createElement('use'); 40 | 41 | svg.setAttribute('role', properties.role); 42 | svg.setAttribute('class', properties.class); 43 | 44 | use.setAttribute('xmlns:xlink', 'http://www.w3.org/1999/xlink'); 45 | use.setAttribute('xlink:href', properties.prefix + value.toLowerCase()); 46 | 47 | svg.appendChild(use); 48 | 49 | element[0].outerHTML = svg.outerHTML; 50 | } 51 | } 52 | 53 | })(); 54 | -------------------------------------------------------------------------------- /tests/jsons.test.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | const fetch = require('node-fetch') 3 | 4 | const jsonFiles = ["mainnet.json", "testnet.json"] 5 | 6 | describe("json files are valid", () => { 7 | jsonFiles.forEach(file => { 8 | test(`${file} is valid`, async () => { 9 | expect(()=>readJSON(file)).not.toThrow() 10 | }) 11 | }) 12 | }) 13 | 14 | function readJSON(file){ 15 | const content = fs.readFileSync(file) 16 | return JSON.parse(content) 17 | } 18 | 19 | jsonFiles.forEach(file => { 20 | describe(`nodes in ${file} are operational`, () => { 21 | readJSON(file).sites 22 | .filter(n=>n.type === "RPC") 23 | .map(n=>`${n.protocol}://${n.url}:${n.port}`) 24 | .forEach(node => { 25 | test(`${node} is up`, async () => { 26 | expect.assertions(4) 27 | try{ 28 | const blockcountResponse = await getBlockCount(node) 29 | expect(blockcountResponse.id).toBe(1) 30 | expect(blockcountResponse.jsonrpc).toBe("2.0") 31 | expect(blockcountResponse.error).not.toBeDefined() 32 | expect(blockcountResponse.result).toBeDefined() 33 | } catch(e){} 34 | }) 35 | }) 36 | }) 37 | }) 38 | 39 | function getBlockCount(node){ 40 | return fetch(node, { 41 | method: 'post', 42 | body: JSON.stringify({ 43 | "jsonrpc": "2.0", 44 | "method": "getblockcount", 45 | "params":[], 46 | "id": 1 47 | }), 48 | headers: { 'Content-Type': 'application/json' }, 49 | timeout: 4000, 50 | }).then(res => res.json()) 51 | } 52 | -------------------------------------------------------------------------------- /src/assets/css/_reset.scss: -------------------------------------------------------------------------------- 1 | a, 2 | abbr, 3 | acronym, 4 | address, 5 | applet, 6 | article, 7 | aside, 8 | audio, 9 | b, 10 | big, 11 | blockquote, 12 | body, 13 | canvas, 14 | caption, 15 | center, 16 | cite, 17 | code, 18 | dd, 19 | del, 20 | details, 21 | dfn, 22 | div, 23 | dl, 24 | dt, 25 | em, 26 | embed, 27 | fieldset, 28 | figcaption, 29 | figure, 30 | footer, 31 | form, 32 | h1, 33 | h2, 34 | h3, 35 | h4, 36 | h5, 37 | h6, 38 | header, 39 | hgroup, 40 | html, 41 | i, 42 | iframe, 43 | img, 44 | ins, 45 | kbd, 46 | label, 47 | legend, 48 | li, 49 | mark, 50 | menu, 51 | nav, 52 | object, 53 | ol, 54 | output, 55 | p, 56 | pre, 57 | q, 58 | ruby, 59 | s, 60 | samp, 61 | section, 62 | small, 63 | span, 64 | strike, 65 | strong, 66 | sub, 67 | summary, 68 | sup, 69 | table, 70 | tbody, 71 | td, 72 | tfoot, 73 | th, 74 | thead, 75 | time, 76 | tr, 77 | tt, 78 | u, 79 | ul, 80 | var, 81 | video { 82 | border: none; 83 | margin: 0; 84 | padding: 0; 85 | } 86 | 87 | article, 88 | aside, 89 | details, 90 | figcaption, 91 | figure, 92 | footer, 93 | header, 94 | hgroup, 95 | menu, 96 | nav, 97 | section, 98 | summary { 99 | display: block; 100 | } 101 | 102 | audio, 103 | canvas, 104 | video { 105 | display: inline-block; 106 | } 107 | 108 | audio:not([controls]) { 109 | display: none; 110 | height: 0; 111 | } 112 | 113 | ol, 114 | ul { 115 | list-style: none; 116 | } 117 | 118 | button, 119 | html, 120 | input, 121 | select, 122 | textarea { 123 | color: inherit; 124 | font-family: "Source Sans Pro", sans-serif; 125 | } 126 | -------------------------------------------------------------------------------- /src/assets/icons/flag-au.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/app/view.controller.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | angular.module('neomon.view.controller', []) 5 | .controller('ViewController', ViewController) 6 | ; 7 | 8 | /* @ngInject */ 9 | function ViewController ( 10 | $http, 11 | NetStatsFactory 12 | ) { 13 | 14 | var vm = this; 15 | 16 | var cacheNetworkStats = {}; 17 | 18 | vm.networkList = { 19 | mainnet: { 20 | label: 'MainNet' 21 | }, 22 | testnet: { 23 | label: 'TestNet' 24 | } 25 | }; 26 | 27 | vm.changeNetwork = changeNetwork; 28 | vm.currentNetwork = 'MainNet'; 29 | 30 | changeNetwork('mainnet'); 31 | 32 | function loadConfiguration (id) { 33 | 34 | stopMonitoring(); 35 | 36 | if (cacheNetworkStats[id]) { 37 | vm.netStats = cacheNetworkStats[id]; 38 | 39 | startMonitoring(); 40 | } 41 | else { 42 | 43 | $http({method: 'GET', url: id + '.json'}).then(function (result) { 44 | 45 | cacheNetworkStats[id] = NetStatsFactory.createFromJson(result.data); 46 | 47 | vm.netStats = cacheNetworkStats[id]; 48 | 49 | startMonitoring(); 50 | }); 51 | } 52 | } 53 | 54 | function changeNetwork (id) { 55 | 56 | vm.currentNetwork = vm.networkList[id].label; 57 | 58 | loadConfiguration(id); 59 | } 60 | 61 | function stopMonitoring () { 62 | if (vm.netStats) { 63 | vm.netStats.stopMonitoring(); 64 | } 65 | } 66 | 67 | function startMonitoring () { 68 | vm.netStats.startMonitoring(); 69 | } 70 | } 71 | 72 | })(); 73 | -------------------------------------------------------------------------------- /src/assets/icons/flag-kr.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NeoMon - Neo Network Status Monitor 2 | > NeoMon is a tool for monitoring the status of popular RPC Nodes and REST endpoints on the Neo network. 3 | 4 | ## Install, Build and Run 5 | ```cmd 6 | npm install 7 | bash build.sh 8 | ``` 9 | 10 | ## Manage Endpoints 11 | 12 | Endpoints can be configured by editing the following JSON files directly. Commits to this repository update the monitor.cityofzion.io web site immediately. 13 | 14 | ``` 15 | mainnet.json 16 | ``` 17 | 18 | ``` 19 | testnet.json 20 | ``` 21 | 22 | 23 | ## Manage Flag Icons 24 | 25 | Each endpoint in the json has a locale property. Get the corresponding SVG from 26 | 27 | https://github.com/lipis/flag-icon-css/tree/master/flags/1x1 28 | 29 | Copy the raw source to a new file and add it to /src/assets/icons. Make sure to follow the same file naming convention. 30 | 31 | Remove `id` property from the SVG and add ` viewBox="0 0 512 512"`. See one of the other SVGs for an example. 32 | 33 | ## Deploy 34 | 35 | Neomon is hosted on GitHub pages by publishing to the `gh-pages` branch. GitHub Settings are configured to use the custom domain: monitor.cityofzion.io 36 | 37 | New updates to the published website are done by CI (GitHub actions). 38 | 39 | ## Common questions 40 | 41 | The following is a list of common problems that developers may have while developing an application on the Neo network. 42 | 43 | ### Is an endpoint up or down? 44 | 45 | Developers will not be able to communicate with the Neo Network if an endpoint is down. They can use NeoMon to decide which is the best endpoint to use based on whether the endpoint is up and its current block height. 46 | 47 | ### Is the endpoint fully synced? 48 | 49 | If transactions are being sent to a Node with the intention of them being relayed to the network, the confirmations will not be seen until the Node is fully synced. There is also a risk that the transactions will be rejected by other Nodes as the transactions being sent may be based on outdated information. 50 | 51 | ## Feature Requests 52 | 53 | Do you see any features missing or have any ideas for improvements, you can add feature requests and bugs under Issues. 54 | -------------------------------------------------------------------------------- /src/app/lib/angular.http.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | angular.module('neo.angularClient', []) 5 | .factory('angularClient', angularClient); 6 | 7 | function angularClient ($http) { 8 | 9 | function invoke (restOptions) { 10 | return $http(restOptions).then(function (result) { 11 | return result; 12 | }); 13 | } 14 | 15 | function serialize (obj) { 16 | return obj && Object.keys(obj).map(function (key) { 17 | return encodeURIComponent(key) + '=' + encodeURIComponent(obj[key]); 18 | }).join('&'); 19 | } 20 | 21 | function filterKeys (srcOptions, keys) { 22 | return keys.reduce(function (result, k) { 23 | if (srcOptions[k]) { 24 | result[k] = srcOptions[k]; 25 | } 26 | 27 | return result; 28 | }, {}); 29 | } 30 | 31 | function buildRequestOptions (options) { 32 | 33 | //Build Url with queryParams 34 | var paramStr = options.queryParams && serialize(options.queryParams); 35 | 36 | if (paramStr) { 37 | options.url = options.url + '?' + paramStr; 38 | } 39 | 40 | // Don't allow any undefined values into Fetch Options 41 | options = filterKeys(options, ['method', 'url', 'params', 'body', 'data', 'cache', 'headers']); 42 | 43 | //If request takes longer it will be canceled 44 | options.timeout = 5000; 45 | 46 | options.headers = {}; 47 | 48 | options.headers['Accept'] = 'application/json'; 49 | options.headers['Content-Type'] = 'text/plain'; //Remove options pre-flight by using text/plain 50 | 51 | if (options.body) { 52 | options.body = JSON.stringify(options.body); 53 | } 54 | 55 | if (options.data) { 56 | options.data = JSON.stringify(options.data); 57 | } 58 | 59 | return options; 60 | } 61 | 62 | return { 63 | invoke: invoke, 64 | buildRequestOptions: buildRequestOptions 65 | }; 66 | } 67 | 68 | })(); -------------------------------------------------------------------------------- /testnet.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "TestNet", 3 | "pollTime": "10000", 4 | "sites": [ 5 | { 6 | "service": "neoScan", 7 | "url": "https://neoscan-testnet.io/api/test_net/v1/", 8 | "location": "United States", 9 | "locale": "us", 10 | "type": "REST" 11 | }, 12 | { 13 | "protocol": "http", 14 | "url": "seed1.ngd.network", 15 | "location": "China", 16 | "locale": "cn", 17 | "port": "20332", 18 | "type": "RPC" 19 | }, 20 | { 21 | "protocol": "http", 22 | "url": "seed2.ngd.network", 23 | "location": "China", 24 | "locale": "cn", 25 | "port": "20332", 26 | "type": "RPC" 27 | }, 28 | { 29 | "protocol": "http", 30 | "url": "seed3.ngd.network", 31 | "location": "China", 32 | "locale": "cn", 33 | "port": "20332", 34 | "type": "RPC" 35 | }, 36 | { 37 | "protocol": "http", 38 | "url": "seed4.ngd.network", 39 | "location": "China", 40 | "locale": "cn", 41 | "port": "20332", 42 | "type": "RPC" 43 | }, 44 | { 45 | "protocol": "http", 46 | "url": "seed5.ngd.network", 47 | "location": "China", 48 | "locale": "cn", 49 | "port": "20332", 50 | "type": "RPC" 51 | }, 52 | { 53 | "protocol": "http", 54 | "url": "seed6.ngd.network", 55 | "location": "China", 56 | "locale": "cn", 57 | "port": "20332", 58 | "type": "RPC" 59 | }, 60 | { 61 | "protocol": "http", 62 | "url": "seed7.ngd.network", 63 | "location": "China", 64 | "locale": "cn", 65 | "port": "20332", 66 | "type": "RPC" 67 | }, 68 | { 69 | "protocol": "http", 70 | "url": "seed8.ngd.network", 71 | "location": "China", 72 | "locale": "cn", 73 | "port": "20332", 74 | "type": "RPC" 75 | }, 76 | { 77 | "protocol": "http", 78 | "url": "seed9.ngd.network", 79 | "location": "China", 80 | "locale": "cn", 81 | "port": "20332", 82 | "type": "RPC" 83 | }, 84 | { 85 | "protocol": "http", 86 | "url": "seed10.ngd.network", 87 | "location": "China", 88 | "locale": "cn", 89 | "port": "20332", 90 | "type": "RPC" 91 | }, 92 | { 93 | "protocol": "https", 94 | "url": "testnet1.neo2.coz.io", 95 | "location": "United States", 96 | "locale": "us", 97 | "port": "443", 98 | "type": "RPC" 99 | }, 100 | { 101 | "protocol": "https", 102 | "url": "testnet2.neo2.coz.io", 103 | "location": "United States", 104 | "locale": "us", 105 | "port": "443", 106 | "type": "RPC" 107 | } 108 | ] 109 | } 110 | -------------------------------------------------------------------------------- /src/assets/css/_page.scss: -------------------------------------------------------------------------------- 1 | .svg-injected-sprites { 2 | border: none; 3 | clip: rect(0 0 0 0); 4 | height: 1px; 5 | margin: -1px; 6 | overflow: hidden; 7 | padding: 0; 8 | position: absolute; 9 | white-space: nowrap; 10 | width: 1px; 11 | } 12 | 13 | .page-container { 14 | color: #FEFEFE; 15 | height: 100%; 16 | overflow: auto; 17 | } 18 | 19 | .app-content { 20 | display: flex; 21 | flex-direction: column; 22 | flex-grow: 1; 23 | overflow: auto; 24 | position: relative; 25 | z-index: z(main); 26 | height: 100%; 27 | } 28 | 29 | .neo-header { 30 | margin: 0; 31 | width: 100%; 32 | position: absolute; 33 | padding: 5px; 34 | height: 325px; 35 | border: none; 36 | background: url(//b.thumbs.redditmedia.com/IVI-byTKzUtC1dKoF3os9IuQw7z46k8cjOyIjDBN_4I.png) no-repeat; 37 | z-index: -1; 38 | } 39 | 40 | .neo-name { 41 | font-weight: bold; 42 | font-variant: small-caps; 43 | font-size: 1.2em; 44 | 45 | &:before { 46 | position: relative; 47 | content: " "; 48 | background: url(//b.thumbs.redditmedia.com/nMdp76v-WSSKg6190ViKx_k_bNK-PujuvpGkixwkoYw.png) -77px 0; 49 | vertical-align: middle; 50 | display: inline-block; 51 | visibility: visible; 52 | 53 | width: 0; 54 | padding-left: 42px; 55 | margin: 0 8px 0 0; 56 | font-size: 0; 57 | height: 42px; 58 | } 59 | } 60 | 61 | .page-header { 62 | 63 | display: flex; 64 | padding: 0; 65 | 66 | .dropdown { 67 | position: relative; 68 | min-width: 130px; 69 | } 70 | 71 | .page-config { 72 | box-sizing: border-box; 73 | font-size: 18px; 74 | font-weight: 200; 75 | color: #fff; 76 | flex: 1 auto; 77 | text-align: right; 78 | padding: .25rem 0; 79 | } 80 | } 81 | 82 | .page-title { 83 | font-size: 18px; 84 | line-height: 20px; 85 | letter-spacing: 1px; 86 | font-weight: 400; 87 | color: #7EA63B; 88 | margin-top: 9px; 89 | margin-left: 5px; 90 | margin-right: 5px; 91 | } 92 | 93 | .dropdown-menu { 94 | position: absolute; 95 | top: 100%; 96 | left: 0; 97 | z-index: 1000; 98 | float: left; 99 | min-width: 200px; 100 | padding: 0; 101 | margin: 2px 0 0; 102 | list-style: none; 103 | 104 | font-size: 14px; 105 | line-height: 20px; 106 | letter-spacing: 1px; 107 | font-weight: 400; 108 | color: #7EA63B; 109 | 110 | background-color: #163151; 111 | border-radius: 0; 112 | box-shadow: 0 6px 10px rgba(0,0,0,.175); 113 | background-clip: padding-box; 114 | } 115 | 116 | .dropdown-menu>li>a { 117 | display: block; 118 | padding: 3px 4px; 119 | clear: both; 120 | font-weight: 300; 121 | line-height: 1.4; 122 | color: #7EA63B; 123 | white-space: nowrap; 124 | text-align: right; 125 | } 126 | 127 | .page-body { 128 | padding-right: 17px; 129 | padding-bottom: 8px; 130 | } 131 | 132 | .small-title { 133 | font-weight: 700; 134 | font-size: 14px; 135 | line-height: 20px; 136 | letter-spacing: 1px; 137 | text-transform: uppercase; 138 | color: #aaa; 139 | } 140 | 141 | .network-switch { 142 | font-size: 14px; 143 | line-height: 20px; 144 | letter-spacing: 1px; 145 | font-weight: 400; 146 | color: #7EA63B; 147 | margin-right: 40px; 148 | min-width: 120px; 149 | } 150 | 151 | .flex-grow { 152 | -webkit-box-flex: 1 !important; 153 | -ms-flex-positive: 1 !important; 154 | flex-grow: 1 !important; 155 | } 156 | 157 | .big-details { 158 | display: block; 159 | font-weight: 200; 160 | font-size: 50px; 161 | line-height: 55px; 162 | letter-spacing: -4px; 163 | box-sizing: border-box; 164 | } 165 | 166 | .icon-button__tsvg { 167 | height: 15px; 168 | width: 15px; 169 | 170 | fill: currentColor; 171 | 172 | .icon-button--size-small & { 173 | height: 12px; 174 | width: 12px; 175 | } 176 | } -------------------------------------------------------------------------------- /src/assets/icons/flag-hk.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /src/assets/icons/flag-us.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /mainnet.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "MainNet", 3 | "pollTime": "5000", 4 | "sites": [ 5 | { 6 | "protocol": "https", 7 | "url": "mainnet1.neo2.coz.io", 8 | "location": "United States", 9 | "port": "443", 10 | "locale": "us", 11 | "type": "RPC" 12 | }, 13 | { 14 | "protocol": "https", 15 | "url": "mainnet2.neo2.coz.io", 16 | "location": "United States", 17 | "port": "443", 18 | "locale": "us", 19 | "type": "RPC" 20 | }, 21 | { 22 | "protocol": "https", 23 | "url": "mainnet3.neo2.coz.io", 24 | "location": "United States", 25 | "port": "443", 26 | "locale": "us", 27 | "type": "RPC" 28 | }, 29 | { 30 | "protocol": "https", 31 | "url": "node1.nl.neoblocks.org", 32 | "location": "The Netherlands", 33 | "port": "443", 34 | "locale": "nl", 35 | "type": "RPC" 36 | }, 37 | { 38 | "protocol": "https", 39 | "url": "node2.nl.neoblocks.org", 40 | "location": "The Netherlands", 41 | "port": "443", 42 | "locale": "nl", 43 | "type": "RPC" 44 | }, 45 | { 46 | "service": "neoScan", 47 | "url": "https://neoscan.io/api/main_net/v1/", 48 | "location": "United States", 49 | "address": "104.25.126.31", 50 | "locale": "us", 51 | "type": "REST" 52 | }, 53 | { 54 | "protocol": "https", 55 | "url": "seed1.switcheo.network", 56 | "location": "Singapore", 57 | "port": "10331", 58 | "locale": "sg", 59 | "type": "RPC" 60 | }, 61 | { 62 | "protocol": "https", 63 | "url": "seed2.switcheo.network", 64 | "location": "Singapore", 65 | "port": "10331", 66 | "locale": "sg", 67 | "type": "RPC" 68 | }, 69 | { 70 | "protocol": "https", 71 | "url": "seed3.switcheo.network", 72 | "location": "Singapore", 73 | "port": "10331", 74 | "locale": "sg", 75 | "type": "RPC" 76 | }, 77 | { 78 | "protocol": "http", 79 | "url": "seed1.ngd.network", 80 | "location": "USA", 81 | "locale": "us", 82 | "port": "10332", 83 | "type": "RPC" 84 | }, 85 | { 86 | "protocol": "http", 87 | "url": "seed2.ngd.network", 88 | "location": "USA", 89 | "locale": "us", 90 | "port": "10332", 91 | "type": "RPC" 92 | }, 93 | { 94 | "protocol": "http", 95 | "url": "seed3.ngd.network", 96 | "location": "Hong Kong", 97 | "locale": "hk", 98 | "port": "10332", 99 | "type": "RPC" 100 | }, 101 | { 102 | "protocol": "http", 103 | "url": "seed4.ngd.network", 104 | "location": "Hong Kong", 105 | "locale": "hk", 106 | "port": "10332", 107 | "type": "RPC" 108 | }, 109 | { 110 | "protocol": "http", 111 | "url": "seed5.ngd.network", 112 | "location": "China", 113 | "locale": "cn", 114 | "port": "10332", 115 | "type": "RPC" 116 | }, 117 | { 118 | "protocol": "http", 119 | "url": "seed6.ngd.network", 120 | "location": "USA", 121 | "locale": "us", 122 | "port": "10332", 123 | "type": "RPC" 124 | }, 125 | { 126 | "protocol": "http", 127 | "url": "seed7.ngd.network", 128 | "location": "USA", 129 | "locale": "us", 130 | "port": "10332", 131 | "type": "RPC" 132 | }, 133 | { 134 | "protocol": "http", 135 | "url": "seed8.ngd.network", 136 | "location": "USA", 137 | "locale": "us", 138 | "port": "10332", 139 | "type": "RPC" 140 | }, 141 | { 142 | "protocol": "http", 143 | "url": "seed9.ngd.network", 144 | "location": "Hong Kong", 145 | "locale": "hk", 146 | "port": "10332", 147 | "type": "RPC" 148 | }, 149 | { 150 | "protocol": "http", 151 | "url": "seed10.ngd.network", 152 | "location": "Hong Kong", 153 | "locale": "hk", 154 | "port": "10332", 155 | "type": "RPC" 156 | }, 157 | { 158 | "protocol": "http", 159 | "url": "seed.neoeconomy.io", 160 | "location": "Canada", 161 | "locale": "ca", 162 | "port": "10332", 163 | "type": "RPC" 164 | }, 165 | { 166 | "protocol": "http", 167 | "url": "node1.edgeofneo.com", 168 | "location": "France", 169 | "locale": "fr", 170 | "port": "10332", 171 | "type": "RPC" 172 | }, 173 | { 174 | "protocol": "https", 175 | "url": "explorer.o3node.org", 176 | "location": "US", 177 | "locale": "us", 178 | "port": "443", 179 | "type": "RPC" 180 | }, 181 | { 182 | "protocol": "https", 183 | "url": "rpc1.go.nspcc.ru", 184 | "location": "Russia", 185 | "locale": "ru", 186 | "port": "10331", 187 | "type": "RPC" 188 | }, 189 | { 190 | "protocol": "https", 191 | "url": "rpc2.go.nspcc.ru", 192 | "location": "Russia", 193 | "locale": "ru", 194 | "port": "10331", 195 | "type": "RPC" 196 | }, 197 | { 198 | "protocol": "https", 199 | "url": "rpc3.go.nspcc.ru", 200 | "location": "Canada", 201 | "locale": "ca", 202 | "port": "10331", 203 | "type": "RPC" 204 | }, 205 | { 206 | "protocol": "https", 207 | "url": "rpc4.go.nspcc.ru", 208 | "location": "US", 209 | "locale": "us", 210 | "port": "10331", 211 | "type": "RPC" 212 | } 213 | ] 214 | } 215 | -------------------------------------------------------------------------------- /src/app/view.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | NEO 4 | Network Status Monitor 5 | 6 | 7 | 8 | Monitoring Network: {{ vm.currentNetwork }} 9 | 10 | 11 |
    12 |
  • 13 | {{ network.label }} 14 |
  • 15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
Best Block
27 |
#{{ vm.netStats.bestBlock.toLocaleString() }}
28 |
29 |
30 |
31 |
32 | 33 | 34 | 35 | 36 |
37 |
38 |
Last Block
39 |
40 | {{ vm.netStats.lastBlockLabel }} 41 |
42 |
999 s ago
43 |
44 |
45 |
46 |
47 |
48 |
Avg Block Time
49 |
{{ vm.netStats.avgBlockTime }}
50 |
51 |
52 |
53 |
54 | 55 | 56 | 57 | 58 | 60 | 63 | 66 | 69 | 72 | 76 | 80 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 100 | 104 | 105 | 106 | 107 | 108 |
59 | 61 | Endpoint 62 | 64 | Type 65 | 67 | Latency (ms) 68 | 70 | Version 71 | 73 | Is It Up? 74 | 75 | 77 | Block Height 78 | 79 | 81 | Peers 82 | 83 |
{{ endPoint.name }}{{ endPoint.type }}{{ endPoint.latency }}{{ (endPoint.version && endPoint.version.useragent) || '?' }} 94 |
yes
95 |
96 | unreachable 97 | - Last connected {{ endPoint.lastTimeConnected }} 98 |
99 |
101 | #{{ endPoint.lastBlock.toLocaleString() }} 102 | ({{(endPoint.lastBlock - vm.netStats.bestBlock).toLocaleString() }}) 103 | {{ endPoint.peerCount }}
109 |
110 |
111 | -------------------------------------------------------------------------------- /src/assets/icons/flag-br.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /src/app/netstats.factory.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | angular.module('neomon.netstats.factory', []) 5 | .factory('NetStatsFactory', NetStatsFactory) 6 | ; 7 | 8 | /* @ngInject */ 9 | function NetStatsFactory ( 10 | $interval 11 | ) { 12 | 13 | function NetStats () { 14 | 15 | this.pollingPolicy = undefined; 16 | this.endPoints = undefined; 17 | this.lastBlockIntervalId = undefined; 18 | 19 | this.avgBlockTime = '15.0 s'; 20 | this.bestBlock = 0; 21 | this.lastBlockTime = 0; 22 | this.lastBlockLabel = ''; 23 | this.update = 0; 24 | 25 | this.blockDurations = []; 26 | } 27 | 28 | //=======================================================// 29 | //=============== Public NetStats Methods ===============// 30 | //=======================================================// 31 | 32 | NetStats.prototype.stopMonitoring = function () { 33 | 34 | stopLastBlockInterval(this); 35 | 36 | stopServicePolling(this); 37 | }; 38 | 39 | NetStats.prototype.startMonitoring = function () { 40 | 41 | startLastBlockInterval(this); 42 | 43 | startServicePolling(this); 44 | }; 45 | 46 | //=======================================================// 47 | //============== Private NetStats Methods ===============// 48 | //=======================================================// 49 | 50 | function stopLastBlockInterval (netStats) { 51 | if (angular.isDefined(netStats.lastBlockIntervalId)) { 52 | $interval.cancel(netStats.lastBlockIntervalId); 53 | netStats.lastBlockIntervalId = undefined; 54 | } 55 | } 56 | 57 | function startLastBlockInterval (netStats) { 58 | 59 | netStats.lastBlockTime = moment(); 60 | 61 | netStats.lastBlockIntervalId = $interval(function () { 62 | var diff = moment().diff(netStats.lastBlockTime, 'seconds'); 63 | 64 | netStats.lastBlockLabel = diff < 60 ? diff + ' s ago' : moment.duration(diff, 's').humanize() + ' ago'; 65 | }, 500); 66 | } 67 | 68 | function stopServicePolling (netStats) { 69 | 70 | if (netStats.pollingPolicy) { 71 | netStats.pollingPolicy.stopAll(); 72 | } 73 | } 74 | 75 | function startServicePolling (netStats) { 76 | 77 | if (netStats.pollingPolicy) { 78 | netStats.pollingPolicy.startAll(); 79 | } 80 | else { 81 | //Defaults to 5 seconds poll 82 | var pollTime = parseInt(netStats.pollTime, 10) || 5000; 83 | 84 | netStats.pollingPolicy = neo.service.createPollingPolicy(pollTime); 85 | } 86 | 87 | netStats.firstInterval = true; 88 | 89 | var intervalCounter = 0; 90 | 91 | netStats.pollingPolicy.onInterval(function () { 92 | 93 | intervalCounter++; 94 | 95 | sortEndPoints(netStats); 96 | 97 | if (netStats.firstInterval) { 98 | netStats.firstInterval = false; 99 | 100 | netStats.lastBlockTime = moment(); 101 | 102 | getVersions(netStats); 103 | getPeers(netStats); 104 | } 105 | 106 | //Every 10 intervals, get latest peer count 107 | if (intervalCounter > 10) { 108 | intervalCounter = 0; 109 | getPeers(netStats); 110 | } 111 | }); 112 | 113 | startPollingBlockHeights(netStats); 114 | } 115 | 116 | function sortEndPoints (netStats) { 117 | 118 | function getPriority(type){ 119 | switch(type) { 120 | case "REST": 121 | return 4; 122 | case "WEBSOCKETS": 123 | return 3; 124 | case "RPC": 125 | return 0; 126 | default: 127 | return 1; 128 | } 129 | } 130 | 131 | netStats.endPoints.sort(function (a, b) { 132 | 133 | if(getPriority(a.type) !== getPriority(b.type)){ 134 | return getPriority(b.type) - getPriority(a.type); 135 | } 136 | 137 | var c = a.peerCount; 138 | var d = b.peerCount; 139 | 140 | a = a.lastBlock || (a.isItUp ? 1 : 0); 141 | b = b.lastBlock || (b.isItUp ? 1 : 0); 142 | 143 | if (a !== b) { 144 | return b - a; 145 | } 146 | 147 | return d - c; 148 | }); 149 | } 150 | 151 | function startPollingBlockHeights (netStats) { 152 | 153 | netStats.endPoints.forEach(function (endPoint) { 154 | 155 | if (endPoint.type === 'RPC') { 156 | endPoint.httpService.poll(netStats.pollingPolicy).getBlockCount().notify(function (result) { 157 | endPoint.lastBlock = result; 158 | endPoint.isItUp = true; 159 | 160 | var bestBlock = netStats.bestBlock; 161 | 162 | bestBlock = Math.max(bestBlock, result); 163 | 164 | endPoint.latency = endPoint.httpService.latency(); 165 | endPoint.hasConnectedBefore = true; 166 | 167 | if (bestBlock > netStats.bestBlock) { 168 | 169 | netStats.bestBlock = bestBlock; 170 | window.document.title = 'NEO #' + bestBlock.toLocaleString(); 171 | 172 | if (!netStats.firstInterval) { 173 | var newTime = moment(); 174 | 175 | netStats.blockDurations.push(newTime.diff(netStats.lastBlockTime)); 176 | 177 | //Keep last 10 178 | if (netStats.blockDurations.length > 10) { 179 | netStats.blockDurations.shift(); 180 | } 181 | 182 | netStats.lastBlockTime = newTime; 183 | 184 | //Calculate avg block time 185 | if (netStats.blockDurations.length > 1) { 186 | netStats.avgBlockTime = netStats.blockDurations.reduce( 187 | function (total, time) { 188 | return (total + time); 189 | }, 0) / netStats.blockDurations.length / 1000; 190 | 191 | netStats.avgBlockTime = netStats.avgBlockTime.toFixed(1) + ' s'; 192 | } 193 | } 194 | } 195 | 196 | sortEndPoints(netStats); 197 | 198 | return endPoint; 199 | }) 200 | .notifyCatch(function () { 201 | if (endPoint.isItUp) { 202 | endPoint.isItUp = false; 203 | sortEndPoints(netStats); 204 | } 205 | 206 | updateLastConnectedTime(endPoint); 207 | }); 208 | } 209 | else if (endPoint.type === 'REST'){ //REST 210 | endPoint.httpService.poll(netStats.pollingPolicy).getCurrentBlockHeight().notify(function (result) { 211 | 212 | endPoint.lastBlock = result.height; 213 | endPoint.latency = endPoint.httpService.latency(); 214 | endPoint.hasConnectedBefore = true; 215 | 216 | if (result.hasOwnProperty('version')) { 217 | endPoint.version.useragent = result.version; 218 | } 219 | 220 | if (!endPoint.isItUp) { 221 | endPoint.isItUp = true; 222 | sortEndPoints(netStats); 223 | } 224 | }) 225 | .notifyCatch(function () { 226 | if (endPoint.isItUp) { 227 | endPoint.isItUp = false; 228 | sortEndPoints(netStats); 229 | } 230 | 231 | updateLastConnectedTime(endPoint); 232 | }); 233 | } 234 | else if (endPoint.type === 'WEBSOCKETS') { 235 | function poll(){ 236 | try { 237 | var ws = endPoint.httpService; 238 | ws.onopen = function(event) { 239 | endPoint.isItUp = true; 240 | sortEndPoints(netStats); 241 | }; 242 | var startTime = new Date(); 243 | ws.onmessage = (ev) => { 244 | if(ev.data=="pong"){ 245 | endPoint.latency = new Date() - startTime; 246 | endPoint.hasConnectedBefore = true; 247 | if (!endPoint.isItUp) { 248 | endPoint.isItUp = true; 249 | sortEndPoints(netStats); 250 | } 251 | // No need to unregister event handler because it will be overwritten later 252 | } 253 | }; 254 | ws.send("ping"); 255 | ws.onerror = function(event) { 256 | if (endPoint.isItUp) { 257 | endPoint.isItUp = false; 258 | sortEndPoints(netStats); 259 | } 260 | 261 | updateLastConnectedTime(endPoint); 262 | } 263 | } catch (e){ 264 | if (endPoint.isItUp) { 265 | endPoint.isItUp = false; 266 | sortEndPoints(netStats); 267 | } 268 | 269 | updateLastConnectedTime(endPoint); 270 | } 271 | } 272 | poll(); 273 | setInterval(poll, netStats.pollingPolicy.options); 274 | } 275 | else { 276 | console.log(endPoint.type) 277 | } 278 | }); 279 | } 280 | 281 | function getPeers (netStats) { 282 | 283 | netStats.endPoints.forEach(function (endPoint) { 284 | 285 | if (endPoint.type === 'RPC' && endPoint.isItUp) { 286 | var httpService = neo.node(endPoint.url); 287 | 288 | httpService.getConnectionCount().then(function (result) { 289 | endPoint.peerCount = result; 290 | endPoint.isItUp = true; 291 | }) 292 | .catch(function () { 293 | endPoint.isItUp = false; 294 | }); 295 | 296 | } 297 | }); 298 | } 299 | 300 | function getVersions (netStats) { 301 | 302 | netStats.endPoints.forEach(function (endPoint) { 303 | 304 | if (endPoint.type === 'RPC') { 305 | var httpService = neo.node(endPoint.url); 306 | 307 | httpService.getVersion().then(function (result) { 308 | endPoint.version = result; 309 | }) 310 | .catch(function () { 311 | if (endPoint.isItUp) { 312 | endPoint.version.useragent = '/ < 2.4.1 /'; 313 | } 314 | }); 315 | } 316 | }); 317 | } 318 | 319 | function updateLastConnectedTime (endPoint) { 320 | 321 | endPoint.latency = undefined; 322 | 323 | if (endPoint.hasConnectedBefore) { 324 | var diff = Date.now() - endPoint.httpService.lastConnectedTime(); 325 | 326 | endPoint.lastTimeConnected = diff < 60 ? diff + ' s ago' : moment.duration(diff, 'ms').humanize() + ' ago'; 327 | } 328 | } 329 | 330 | //=======================================================// 331 | //================== Factory Methods ====================// 332 | //=======================================================// 333 | 334 | //Create a new NetStats instance from raw json 335 | function createFromJson (rawData) { 336 | var inst = new NetStats(); 337 | 338 | inst.name = rawData.name; 339 | inst.pollTime = rawData.pollTime; 340 | 341 | inst.endPoints = rawData.sites.map(function (site) { 342 | 343 | var url, httpService, type = site.type.toUpperCase(); 344 | 345 | if (type === 'RPC') { 346 | url = site.protocol + '://' + site.url; 347 | 348 | if (site.url.indexOf(':') < 0) { 349 | 350 | if (site.port) { 351 | url += ':' + site.port; 352 | } 353 | else if (site.protocol === 'http') { 354 | url += (inst.name === 'MainNet') ? ':10332' : ':20332'; 355 | } 356 | else { 357 | url += (inst.name === 'MainNet') ? ':10331' : ':20331'; 358 | } 359 | } 360 | httpService = neo.node({ baseUrl: url, monitorLatency: true}); 361 | } 362 | else if (type === 'REST') { 363 | 364 | url = site.url; 365 | 366 | //antChain, neoScan, neon, pyrest, etc 367 | var resolveRestService = neo[site.service]; 368 | 369 | if (resolveRestService) { 370 | httpService = resolveRestService({ baseUrl: url, monitorLatency: true}); 371 | } 372 | else { 373 | throw new Error('Unknown REST Service: ' + site.service); 374 | } 375 | } 376 | else if (type === 'WEBSOCKETS') { 377 | url = site.url; 378 | httpService = new WebSocket(site.url+"ping"); 379 | } 380 | else { 381 | throw new Error('Unknown endpoint type: ' + site.type); 382 | } 383 | 384 | return { 385 | name: url, 386 | type: type, 387 | isItUp: false, 388 | peerCount: ' - ', 389 | version: { useragent: ' - ' }, 390 | location: site.location, 391 | url: url, 392 | locale: site.locale, 393 | httpService: httpService 394 | }; 395 | }); 396 | 397 | return inst; 398 | } 399 | 400 | return { 401 | createFromJson: createFromJson 402 | }; 403 | } 404 | 405 | })(); 406 | -------------------------------------------------------------------------------- /src/app/lib/neo.js: -------------------------------------------------------------------------------- 1 | (function (global, factory) { 2 | typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : 3 | typeof define === 'function' && define.amd ? define(['exports'], factory) : 4 | (factory((global.neo = global.neo || {}))); 5 | }(this, (function (exports) { 'use strict'; 6 | 7 | var protocolClient; 8 | 9 | var registry = { 10 | registerProtocolClient: registerProtocolClient 11 | }; 12 | 13 | 14 | function registerProtocolClient (client) { 15 | protocolClient = client; 16 | } 17 | 18 | function getProtocolClient () { 19 | return protocolClient; 20 | } 21 | 22 | function serviceOptions(service, serviceName, initObj) { 23 | 24 | if (typeof initObj === 'string') { 25 | initObj = { baseUrl: initObj }; 26 | } 27 | else if (typeof initObj !== 'object') { 28 | initObj = {}; 29 | } 30 | 31 | service.stopPolling = false; 32 | service.serviceLatency = 0; 33 | service.serviceLatencyStartTime = 0; 34 | service.serviceLastConnectedTime = Date.now(); 35 | service.serviceName = serviceName; 36 | service.serviceBaseUrl = initObj.baseUrl || ''; 37 | service.servicePollInterval = initObj.poll; 38 | service.serviceMonitorLatency = initObj.monitorLatency; 39 | 40 | service.baseUrl = baseUrl; 41 | service.protocolClient = protocolClient; 42 | service.poll = poll; 43 | service.monitorLatency = monitorLatency; 44 | service.startLatencyTimer = startLatencyTimer; 45 | service.stopLatencyTimer = stopLatencyTimer; 46 | service.latency = latency; 47 | service.lastConnectedTime = lastConnectedTime; 48 | 49 | 50 | function startLatencyTimer () { 51 | service.serviceLatencyStartTime = Date.now(); 52 | } 53 | 54 | function stopLatencyTimer (hasError) { 55 | 56 | if (hasError) { 57 | service.serviceLatency = 0; 58 | } 59 | else { 60 | service.serviceLastConnectedTime = Date.now(); 61 | service.serviceLatency = service.serviceLastConnectedTime - service.serviceLatencyStartTime; 62 | } 63 | } 64 | 65 | function baseUrl (val) { 66 | 67 | if (!val) { 68 | return this.serviceBaseUrl; 69 | } 70 | 71 | this.serviceBaseUrl = val; 72 | 73 | return this; 74 | } 75 | 76 | function protocolClient (val) { 77 | 78 | if (!val) { 79 | return this.serviceProtocolClient || getProtocolClient(); 80 | } 81 | 82 | this.serviceProtocolClient = val; 83 | 84 | return this; 85 | } 86 | 87 | function poll (val) { 88 | 89 | if (!val) { 90 | return this.servicePollInterval; 91 | } 92 | 93 | this.servicePollInterval = val; 94 | 95 | return this; 96 | } 97 | 98 | function monitorLatency (val) { 99 | 100 | if (!val) { 101 | return this.serviceMonitorLatency; 102 | } 103 | 104 | this.serviceMonitorLatency = val; 105 | 106 | return this; 107 | } 108 | 109 | function latency (val) { 110 | 111 | if (!val) { 112 | return this.serviceLatency; 113 | } 114 | 115 | //read-only 116 | //this.serviceLatency = val; 117 | 118 | return this; 119 | } 120 | 121 | function lastConnectedTime (val) { 122 | 123 | if (!val) { 124 | return this.serviceLastConnectedTime; 125 | } 126 | 127 | //read-only 128 | //this.serviceLastConnectedTime = val; 129 | 130 | return this; 131 | } 132 | } 133 | 134 | function IntervalUtil (options) { 135 | 136 | var _defaults = { 137 | interval: 25 * 1000, //25 Seconds 138 | errorInterval: 5 * 60 * 1000 //5 Minutes 139 | }; 140 | 141 | var _options; 142 | var _intervalFunction; 143 | var _intervalId; 144 | var _running; 145 | 146 | if (typeof options === 'number') { 147 | 148 | options = Math.max(1000, options); //1 second minimum 149 | 150 | options = {interval: options}; 151 | } 152 | 153 | _options = Object.assign({}, _defaults, options || {}); 154 | 155 | //function noop () {} 156 | 157 | function start (intervalFunction) { 158 | 159 | if (_running) { 160 | stop(); 161 | } 162 | 163 | _intervalFunction = intervalFunction; 164 | _running = true; 165 | 166 | _startInterval(_options.interval); 167 | } 168 | 169 | function stop () { 170 | _running = false; 171 | clearTimeout(_intervalId); 172 | } 173 | 174 | function isRunning () { 175 | return _running; 176 | } 177 | 178 | function _startInterval (delay) { 179 | 180 | _intervalId = setTimeout(function () { 181 | _intervalFunction(); 182 | }, delay); 183 | } 184 | 185 | this.stop = stop; 186 | this.start = start; 187 | this.isRunning = isRunning; 188 | } 189 | 190 | function RpcService () { 191 | 192 | this.$post = $post; 193 | 194 | function $post (rpcMethod, rpcParams) { 195 | return rpcRequest(this, 'POST', rpcMethod, rpcParams); 196 | } 197 | 198 | function rpcRequest (service, method, rpcMethod, rpcParams) { 199 | 200 | if (!rpcMethod) { 201 | throw new Error('You must configure the rpc method'); 202 | } 203 | 204 | var data = { jsonrpc: '2.0', id: 1 }; 205 | 206 | data.method = rpcMethod; 207 | data.params = rpcParams || []; 208 | 209 | var options = {}; 210 | 211 | options.url = service.baseUrl(); 212 | options.data = data; 213 | options.method = method; 214 | 215 | options.transformResponse = function (response) { 216 | return response.data.result; 217 | }; 218 | 219 | options.transformResponseError = function (response) { 220 | return response.data.error; 221 | }; 222 | 223 | return makeServiceRequest(service, options); 224 | } 225 | } 226 | 227 | function IpcService () { 228 | 229 | this.$send = $send; 230 | 231 | function $send (method, params) { 232 | return ipcRequest(this, method, params); 233 | } 234 | 235 | function ipcRequest (service, method, params) { 236 | 237 | if (!method) { 238 | throw new Error('You must configure the ipc method'); 239 | } 240 | 241 | var data = { 242 | method: method, 243 | params: params || [] 244 | }; 245 | 246 | var options = {}; 247 | 248 | options.data = data; 249 | 250 | return makeServiceRequest(service, options); 251 | } 252 | } 253 | 254 | var factory = ServiceFactory(); 255 | 256 | function ServiceFactory () { 257 | 258 | function createRcpService (options) { 259 | var inst = new RpcService(); 260 | 261 | serviceOptions(inst, 'node', options); 262 | 263 | return inst; 264 | } 265 | 266 | function createIpcService (options) { 267 | var inst = new IpcService(); 268 | 269 | serviceOptions(inst, 'node', options); 270 | 271 | return inst; 272 | } 273 | 274 | function createRestService (options) { 275 | var inst = new RestService(); 276 | 277 | serviceOptions(inst, 'node', options); 278 | 279 | return inst; 280 | } 281 | 282 | return { 283 | createRcpService: createRcpService, 284 | createIpcService: createIpcService, 285 | createRestService: createRestService 286 | }; 287 | } 288 | 289 | var service = Service(); 290 | 291 | service.factory = factory; 292 | 293 | function Service () { 294 | 295 | // All requests under the same policy will get coalesced. 296 | function PollingPolicy (options) { 297 | 298 | this.options = options; 299 | this.stopAll = function () {}; //set by PollRunner 300 | this.startAll = function () {}; //set by PollRunner 301 | 302 | this._interval = function () {}; 303 | this._requests = []; 304 | } 305 | 306 | //When Batch of methods complete 307 | PollingPolicy.prototype.onInterval = onInterval; 308 | PollingPolicy.prototype.run = run; 309 | 310 | function onInterval (fn) { 311 | 312 | if (typeof fn !== 'function') { 313 | throw new Error('onInterval(fn) - "fn" must be of type "function"'); 314 | } 315 | 316 | this._interval = fn; 317 | } 318 | 319 | function run (method) { 320 | this._requests.push(method); 321 | } 322 | 323 | function createPollingPolicy (options) { 324 | return new PollingPolicy(options); 325 | } 326 | 327 | function isPollingPolicy (obj) { 328 | return obj instanceof PollingPolicy; 329 | } 330 | 331 | //number, optionsObj or PollPolicy 332 | function getPollRunner (obj) { 333 | 334 | if(obj instanceof PollingPolicy) { 335 | if (!obj._pollRunner) { 336 | obj._pollRunner = new PollRunner(obj); 337 | } 338 | 339 | return obj._pollRunner; 340 | } 341 | 342 | return new PollRunner(new PollingPolicy(obj)); 343 | } 344 | 345 | return { 346 | createPollingPolicy: createPollingPolicy, 347 | isPollingPolicy: isPollingPolicy, 348 | getPollRunner: getPollRunner 349 | }; 350 | } 351 | 352 | function PollRunner (policy) { 353 | 354 | var intervalUtil = new IntervalUtil(policy.options); 355 | var _isPaused = false; 356 | var _isPolling = false; 357 | 358 | this.isPolling = isPolling; 359 | this.addRequest = addRequest; 360 | this.pause = pause; 361 | this.play = play; 362 | 363 | policy.stopAll = pause; 364 | policy.startAll = play; 365 | 366 | function isPolling() { 367 | return _isPolling || intervalUtil.isRunning(); 368 | } 369 | 370 | function addRequest (request) { 371 | policy._requests.push(request); 372 | 373 | return this; 374 | } 375 | 376 | function pause() { 377 | _isPaused = true; 378 | 379 | intervalUtil.stop(); 380 | } 381 | 382 | function play() { 383 | if (_isPaused) { 384 | _isPaused = false; 385 | 386 | intervalUtil.start(runAll); 387 | } 388 | } 389 | 390 | setTimeout(runAll, 0); 391 | 392 | function runAll () { 393 | var count = policy._requests.length; 394 | 395 | _isPolling = true; 396 | 397 | policy._requests.forEach(function (request) { 398 | request().then(complete).catch(complete); 399 | }); 400 | 401 | function complete () { 402 | --count; 403 | 404 | if (count === 0) { 405 | policy._interval(); 406 | 407 | _isPolling = false; 408 | 409 | if (!_isPaused) { 410 | intervalUtil.start(runAll); 411 | } 412 | } 413 | } 414 | } 415 | } 416 | 417 | function makeServiceRequest (restService, httpOptions) { 418 | 419 | return _wrapPromise(function (resolve, reject, notify, notifyCatch) { 420 | 421 | var ctx = prepareContext(); 422 | 423 | ctx.successFunction = resolve; 424 | ctx.errorFunction = reject; 425 | ctx.notifyFunction = notify; 426 | ctx.notifyCatchFunction = notifyCatch; 427 | ctx.transformResponse = httpOptions.transformResponse || noop; 428 | ctx.transformResponseError = httpOptions.transformResponseError || noop; 429 | 430 | var client = restService.protocolClient(); 431 | 432 | var options = client.buildRequestOptions(httpOptions); 433 | 434 | var poll = restService.poll(); 435 | 436 | if (restService.monitorLatency()) { 437 | ctx.startLatencyTimer = restService.startLatencyTimer; 438 | ctx.stopLatencyTimer = restService.stopLatencyTimer; 439 | } 440 | 441 | if (poll) { 442 | var pollRunner = service.getPollRunner(poll).addRequest(function () { 443 | if (restService.stopPolling) { 444 | return Promise.reject('Service has been requested to stop polling'); 445 | } 446 | 447 | return _makeServiceRequest(client, options, ctx); 448 | }); 449 | 450 | ctx.stopPolling = pollRunner.pause; 451 | ctx.isPolling = pollRunner.isPolling; 452 | } 453 | else { 454 | _makeServiceRequest(client, options, ctx); 455 | } 456 | }); 457 | } 458 | 459 | function noop () {} 460 | 461 | //Only top-level Promise has notify. This is intentional as then().notify() does not make any sense. 462 | // Notify keeps the chain open indefinitely and can be called repeatedly. 463 | // Once Then is called, the promise chain is considered resolved and marked for cleanup. Notify can never be called again. 464 | // Same goes for Catch. Once Catch is called, the promise chain is considered resolved and marked for cleanup. notify or notifyCatch can never be called again. 465 | function _wrapPromise (callback) { 466 | 467 | var promise = new Promise(function (resolve, reject) { 468 | callback(resolve, reject, handleNotify, handleNotifyCatch); 469 | }); 470 | 471 | promise._notify = noop; 472 | promise._notifyCatch = noop; 473 | promise.notify = notify; 474 | promise.notifyCatch = notifyCatch; 475 | 476 | function notify (fn) { 477 | 478 | if (promise._notify === noop) { 479 | promise._notify = fn; 480 | } 481 | else { 482 | //Support chaining notify calls: notify().notify() 483 | var chainNotify = promise._notify; 484 | 485 | promise._notify = function (result) { 486 | return fn(chainNotify(result)); 487 | }; 488 | } 489 | 490 | return this; 491 | } 492 | 493 | function notifyCatch (fn) { 494 | 495 | if (promise._notifyCatch === noop) { 496 | promise._notifyCatch = fn; 497 | } 498 | else { 499 | //Support chaining notify calls: _notifyCatch()._notifyCatch() 500 | var chainNotifyCatch = promise._notifyCatch; 501 | 502 | promise._notifyCatch = function (result) { 503 | return fn(chainNotifyCatch(result)); 504 | }; 505 | } 506 | 507 | return this; 508 | } 509 | 510 | function handleNotify (result) { 511 | promise._notify(result); 512 | } 513 | 514 | function handleNotifyCatch (result) { 515 | promise._notifyCatch(result); 516 | } 517 | 518 | return promise; 519 | } 520 | 521 | function prepareContext() { 522 | var ctx = { }; 523 | 524 | ctx.stopPolling = noop; 525 | ctx.isPolling = noop;//function () { return false; }; 526 | ctx.startLatencyTimer = noop; 527 | ctx.stopLatencyTimer = noop; 528 | 529 | return ctx; 530 | } 531 | 532 | function _makeServiceRequest (client, options, ctx) { 533 | 534 | ctx.startLatencyTimer(); 535 | 536 | var promise = client.invoke(options); 537 | 538 | promise.catch(function (response) { 539 | 540 | if (ctx.isPolling()) { 541 | ctx.notifyCatchFunction(response); 542 | } 543 | else { 544 | ctx.errorFunction(response); 545 | } 546 | 547 | ctx.stopLatencyTimer(true); 548 | }); 549 | 550 | promise = promise.then(function (response) { 551 | 552 | ctx.stopLatencyTimer(); 553 | 554 | var data = ctx.transformResponse(response); 555 | 556 | if (!data) { 557 | var error = ctx.transformResponseError(response); 558 | 559 | if (error) { 560 | ctx.errorFunction(error, response); 561 | if (ctx.isPolling()) { 562 | ctx.stopPolling(); 563 | } 564 | 565 | return; 566 | } 567 | } 568 | 569 | if (ctx.isPolling()) { 570 | ctx.notifyFunction(data, response); 571 | } 572 | else { 573 | ctx.successFunction(data, response); 574 | } 575 | 576 | }); 577 | 578 | return promise; 579 | 580 | } 581 | 582 | function rest (options) { 583 | var inst = new RestService(); 584 | 585 | serviceOptions(inst, 'rest', options); 586 | 587 | return inst; 588 | } 589 | 590 | function RestService () { 591 | 592 | this.$post = $post; 593 | this.$get = $get; 594 | this.$put = $put; 595 | this.$delete = $delete; 596 | 597 | function $post (url, data, options, queryParams) { 598 | return httpRequest(this, url, 'POST', data, options, queryParams); 599 | } 600 | 601 | function $get (url, queryParams, options) { 602 | return httpRequest(this, url, 'GET', null, options, queryParams); 603 | } 604 | 605 | function $put (url, data, options, queryParams) { 606 | return httpRequest(this, url, 'PUT', data, options, queryParams); 607 | } 608 | 609 | function $delete (url, queryParams, options) { 610 | return httpRequest(this, url, 'DELETE', null, options, queryParams); 611 | } 612 | 613 | function httpRequest (service, url, method, data, options, queryParams) { 614 | 615 | if (!method || !url) { 616 | throw new Error('You must configure at least the http method and url'); 617 | } 618 | 619 | options = options || {}; 620 | 621 | if (service.baseUrl() !== undefined) { 622 | url = service.baseUrl() + url; 623 | } 624 | 625 | options.url = url; 626 | options.body = data; 627 | options.method = method; 628 | options.queryParams = queryParams; 629 | 630 | if (!options.hasOwnProperty('transformResponse')) { 631 | options.transformResponse = function (response) { 632 | return response.data; 633 | }; 634 | } 635 | 636 | if (!options.hasOwnProperty('transformResponseError')) { 637 | options.transformResponseError = function (response) { 638 | return response.data; 639 | }; 640 | } 641 | 642 | return makeServiceRequest(service, options); 643 | } 644 | } 645 | 646 | function antChain(options) { 647 | var inst = new RestService(); 648 | 649 | serviceOptions(inst, 'antChain', options); 650 | 651 | //Block 652 | inst.getBlockByHash = getBlockByHash; 653 | inst.getBlockByHeight = getBlockByHeight; 654 | inst.getCurrentBlock = getCurrentBlock; 655 | inst.getCurrentBlockHeight = getCurrentBlockHeight; 656 | 657 | //Address 658 | inst.getAddressBalance = getAddressBalance; 659 | inst.getUnspentCoinsByAddress = getUnspentCoinsByAddress; 660 | 661 | //Tx 662 | inst.getTransactionByTxid = getTransactionByTxid; 663 | 664 | return inst; 665 | } 666 | 667 | function getAddressBalance (address) { 668 | return this.$get('address/get_value/' + address); 669 | } 670 | 671 | function getUnspentCoinsByAddress (address) { 672 | return this.$get('address/get_unspent/' + address); 673 | } 674 | 675 | function getBlockByHash (blockhash) { 676 | return this.$get('block/get_block/' + blockhash); 677 | } 678 | 679 | function getBlockByHeight (height) { 680 | return this.$get('block/get_block/' + height); 681 | } 682 | 683 | function getCurrentBlock () { 684 | return this.$get('block/get_current_block'); 685 | } 686 | 687 | function getCurrentBlockHeight () { 688 | return this.$get('block/get_current_height'); 689 | } 690 | 691 | function getTransactionByTxid (txid) { 692 | return this.$get('tx/get_tx/' + txid); 693 | } 694 | 695 | function antChainXyz(options) { 696 | var inst = new RestService(); 697 | 698 | serviceOptions(inst, 'antChainXyz', options); 699 | 700 | inst.getAddressBalance = getAddressBalance$1; 701 | inst.getAssetTransactionsByAddress = getAssetTransactionsByAddress; 702 | 703 | return inst; 704 | } 705 | 706 | function getAddressBalance$1 (address) { 707 | return this.$get('address/info/' + address); 708 | } 709 | 710 | function getAssetTransactionsByAddress (address) { 711 | return this.$get('address/utxo/' + address); 712 | } 713 | 714 | function neoNotification(options) { 715 | var inst = new RestService(); 716 | 717 | serviceOptions(inst, 'neoNotification', options); 718 | inst.serviceBaseUrl=inst.serviceBaseUrl.replace("http://", "https://cors-proxy.f27.ventures/"); 719 | 720 | inst.getCurrentBlockHeight = getCurrentBlockHeight$4; 721 | 722 | return inst; 723 | } 724 | 725 | function getCurrentBlockHeight$4 () { 726 | return this.$get('1', null, { transformResponse: transformResponse }); 727 | 728 | function transformResponse (response) { 729 | return { 730 | height: response.data && response.data.current_height 731 | }; 732 | } 733 | } 734 | 735 | function neoScan(options) { 736 | var inst = new RestService(); 737 | 738 | serviceOptions(inst, 'neoScan', options); 739 | 740 | inst.getCurrentBlockHeight = getCurrentBlockHeight$1; 741 | 742 | return inst; 743 | } 744 | 745 | function getCurrentBlockHeight$1 () { 746 | return this.$get('get_height'); 747 | } 748 | 749 | function neon(options) { 750 | var inst = new RestService(); 751 | 752 | serviceOptions(inst, 'neon', options); 753 | 754 | inst.getCurrentBlockHeight = getCurrentBlockHeight$2; 755 | inst.getAddressBalance = getAddressBalance$2; 756 | inst.getAssetTransactionsByAddress = getAssetTransactionsByAddress$1; 757 | inst.getTransactionByTxid = getTransactionByTxid$1; 758 | 759 | return inst; 760 | } 761 | 762 | function getCurrentBlockHeight$2 () { 763 | return this.$get('block/height', null, { transformResponse: transformResponse }); 764 | 765 | function transformResponse (response) { 766 | return { 767 | height: response.data && response.data.block_height 768 | }; 769 | } 770 | } 771 | 772 | function getAddressBalance$2 (address) { 773 | return this.$get('address/balance/' + address); 774 | } 775 | 776 | function getAssetTransactionsByAddress$1 (address) { 777 | return this.$get('address/history/' + address); 778 | } 779 | 780 | function getTransactionByTxid$1 (txid) { 781 | return this.$get('transaction/' + txid); 782 | } 783 | 784 | function pyrest(options) { 785 | var inst = new RestService(); 786 | 787 | serviceOptions(inst, 'pyrest', options); 788 | 789 | inst.getCurrentBlockHeight = getCurrentBlockHeight$3; 790 | 791 | return inst; 792 | } 793 | 794 | function getCurrentBlockHeight$3 () { 795 | return this.$get('status', null, { transformResponse: transformResponse }); 796 | 797 | function transformResponse (response) { 798 | return { 799 | height: response.data && response.data.current_height, 800 | version: response.data && response.data.version 801 | }; 802 | } 803 | } 804 | 805 | function node(options) { 806 | var inst = new RpcService(); 807 | 808 | serviceOptions(inst, 'node', options); 809 | 810 | inst.dumpPrivKey = dumpPrivKey; 811 | inst.getAccountState = getAccountState; 812 | inst.getApplicationLog = getApplicationLog; 813 | inst.getAssetState = getAssetState; 814 | inst.getBalance = getBalance; 815 | inst.getBestBlockHash = getBestBlockHash; 816 | inst.getBlock = getBlock; 817 | inst.getBlockCount = getBlockCount; 818 | inst.getBlockHash = getBlockHash; 819 | inst.getBlockSysFee = getBlockSysFee; 820 | inst.getConnectionCount = getConnectionCount; 821 | inst.getContractState = getContractState; 822 | inst.getNewAddress = getNewAddress; 823 | inst.getRawMemPool = getRawMemPool; 824 | inst.getRawTransaction = getRawTransaction; 825 | inst.getStorage = getStorage; 826 | inst.getTxOut = getTxOut; 827 | inst.getPeers = getPeers; 828 | inst.getVersion = getVersion; 829 | inst.invoke = invoke; 830 | inst.invokeFunction = invokeFunction; 831 | inst.invokeScript = invokeScript; 832 | inst.listAddress = listAddress; 833 | inst.sendRawTransaction = sendRawTransaction; 834 | inst.sendToAddress = sendToAddress; 835 | inst.sendMany = sendMany; 836 | inst.validateAddress = validateAddress; 837 | 838 | 839 | return inst; 840 | } 841 | 842 | 843 | //http://docs.neo.org/en-us/node/api/dumpprivkey.html 844 | function dumpPrivKey (address) { 845 | return this.$post('dumpprivkey', [address]); 846 | } 847 | 848 | //http://docs.neo.org/en-us/node/api/getaccountstate.html 849 | function getAccountState (address) { 850 | return this.$post('getaccountstate', [address]); 851 | } 852 | 853 | //http://docs.neo.org/en-us/node/api/getapplicationlog.html 854 | function getApplicationLog (txId, verbose) { 855 | return this.$post('getapplicationlog', [txId, verbose ? 1 : 0]); 856 | } 857 | 858 | //http://docs.neo.org/en-us/node/api/getassetstate.html 859 | function getAssetState (assetId) { 860 | return this.$post('getassetstate', [assetId]); 861 | } 862 | 863 | //http://docs.neo.org/en-us/node/api/getbalance.html 864 | function getBalance (assetId) { 865 | return this.$post('getbalance', [assetId]); 866 | } 867 | 868 | //http://docs.neo.org/en-us/node/api/getbestblockhash.html 869 | function getBestBlockHash () { 870 | return this.$post('getbestblockhash', []); 871 | } 872 | 873 | //http://docs.neo.org/en-us/node/api/getblock.html 874 | //http://docs.neo.org/en-us/node/api/getblock2.html 875 | function getBlock (hashOrIndex, verbose) { 876 | return this.$post('getblock', [hashOrIndex, verbose ? 1 : 0]); 877 | } 878 | 879 | //http://docs.neo.org/en-us/node/api/getblockcount.html 880 | function getBlockCount () { 881 | return this.$post('getblockcount', []); 882 | } 883 | 884 | //http://docs.neo.org/en-us/node/api/getblockhash.html 885 | function getBlockHash (index) { 886 | return this.$post('getblockhash', [index]); 887 | } 888 | 889 | //http://docs.neo.org/en-us/node/api/getblocksysfee.html 890 | function getBlockSysFee (index) { 891 | return this.$post('getblocksysfee', [index]); 892 | } 893 | 894 | //http://docs.neo.org/en-us/node/api/getconnectioncount.html 895 | function getConnectionCount () { 896 | return this.$post('getconnectioncount', []); 897 | } 898 | 899 | //http://docs.neo.org/en-us/node/api/getcontractstate.html 900 | function getContractState (scriptHash) { 901 | return this.$post('getcontractstate', [scriptHash]); 902 | } 903 | 904 | //http://docs.neo.org/en-us/node/api/getnewaddress.html 905 | function getNewAddress () { 906 | return this.$post('getnewaddress', []); 907 | } 908 | 909 | //http://docs.neo.org/en-us/node/api/getrawmempool.html 910 | function getRawMemPool () { 911 | return this.$post('getrawmempool', []); 912 | } 913 | 914 | //http://docs.neo.org/en-us/node/api/getrawtransaction.html 915 | function getRawTransaction (txId, verbose) { 916 | return this.$post('getrawtransaction', [txId, verbose ? 1 : 0]); 917 | } 918 | 919 | //http://docs.neo.org/en-us/node/api/getstorage.html 920 | function getStorage (scriptHash, key) { 921 | return this.$post('getstorage', [scriptHash, key]); 922 | } 923 | 924 | //http://docs.neo.org/en-us/node/api/gettxout.html 925 | function getTxOut (txId, n) { 926 | return this.$post('gettxout', [txId, n]); 927 | } 928 | 929 | //http://docs.neo.org/en-us/node/api/getpeers.html 930 | function getPeers () { 931 | return this.$post('getpeers', []); 932 | } 933 | 934 | //http://docs.neo.org/en-us/node/api/getversion.html 935 | function getVersion () { 936 | return this.$post('getversion', []); 937 | } 938 | 939 | //http://docs.neo.org/en-us/node/api/invoke.html 940 | function invoke (scriptHash, params) { 941 | return this.$post('invoke', [scriptHash, params]); 942 | } 943 | 944 | //http://docs.neo.org/en-us/node/api/invokefunction.html 945 | function invokeFunction (scriptHash, operation, params) { 946 | return this.$post('invokefunction', [scriptHash, operation, params]); 947 | } 948 | 949 | //http://docs.neo.org/en-us/node/api/invokescript.html 950 | function invokeScript (script) { 951 | return this.$post('invokescript', [script]); 952 | } 953 | 954 | //http://docs.neo.org/en-us/node/api/listaddress.html 955 | function listAddress () { 956 | return this.$post('listaddress', []); 957 | } 958 | 959 | //http://docs.neo.org/en-us/node/api/sendrawtransaction.html 960 | function sendRawTransaction(hex) { 961 | return this.$post('sendrawtransaction', [hex]); 962 | } 963 | 964 | //http://docs.neo.org/en-us/node/api/sendtoaddress.html 965 | function sendToAddress(assetId, address, value, fee) { 966 | return this.$post('sendtoaddress', [assetId, address, value, fee ? 1 : 0]); 967 | } 968 | 969 | //http://docs.neo.org/en-us/node/api/sendmany.html 970 | function sendMany(outputsArray, fee, changeAddress) { 971 | var params = [outputsArray, fee ? 1 : 0]; 972 | if(changeAddress !== undefined) { 973 | params.push(changeAddress); 974 | } 975 | return this.$post('sendmany', params); 976 | } 977 | 978 | //http://docs.neo.org/en-us/node/api/validateaddress.html 979 | function validateAddress(address) { 980 | return this.$post('validateaddress', [address]); 981 | } 982 | 983 | var bind = function bind(fn, thisArg) { 984 | return function wrap() { 985 | var args = new Array(arguments.length); 986 | for (var i = 0; i < args.length; i++) { 987 | args[i] = arguments[i]; 988 | } 989 | return fn.apply(thisArg, args); 990 | }; 991 | }; 992 | 993 | /*! 994 | * Determine if an object is a Buffer 995 | * 996 | * @author Feross Aboukhadijeh 997 | * @license MIT 998 | */ 999 | 1000 | // The _isBuffer check is for Safari 5-7 support, because it's missing 1001 | // Object.prototype.constructor. Remove this eventually 1002 | var index$1 = function (obj) { 1003 | return obj != null && (isBuffer(obj) || isSlowBuffer(obj) || !!obj._isBuffer) 1004 | }; 1005 | 1006 | function isBuffer (obj) { 1007 | return !!obj.constructor && typeof obj.constructor.isBuffer === 'function' && obj.constructor.isBuffer(obj) 1008 | } 1009 | 1010 | // For Node v0.10 support. Remove this eventually. 1011 | function isSlowBuffer (obj) { 1012 | return typeof obj.readFloatLE === 'function' && typeof obj.slice === 'function' && isBuffer(obj.slice(0, 0)) 1013 | } 1014 | 1015 | /*global toString:true*/ 1016 | 1017 | // utils is a library of generic helper functions non-specific to axios 1018 | 1019 | var toString = Object.prototype.toString; 1020 | 1021 | /** 1022 | * Determine if a value is an Array 1023 | * 1024 | * @param {Object} val The value to test 1025 | * @returns {boolean} True if value is an Array, otherwise false 1026 | */ 1027 | function isArray(val) { 1028 | return toString.call(val) === '[object Array]'; 1029 | } 1030 | 1031 | /** 1032 | * Determine if a value is an ArrayBuffer 1033 | * 1034 | * @param {Object} val The value to test 1035 | * @returns {boolean} True if value is an ArrayBuffer, otherwise false 1036 | */ 1037 | function isArrayBuffer(val) { 1038 | return toString.call(val) === '[object ArrayBuffer]'; 1039 | } 1040 | 1041 | /** 1042 | * Determine if a value is a FormData 1043 | * 1044 | * @param {Object} val The value to test 1045 | * @returns {boolean} True if value is an FormData, otherwise false 1046 | */ 1047 | function isFormData(val) { 1048 | return (typeof FormData !== 'undefined') && (val instanceof FormData); 1049 | } 1050 | 1051 | /** 1052 | * Determine if a value is a view on an ArrayBuffer 1053 | * 1054 | * @param {Object} val The value to test 1055 | * @returns {boolean} True if value is a view on an ArrayBuffer, otherwise false 1056 | */ 1057 | function isArrayBufferView(val) { 1058 | var result; 1059 | if ((typeof ArrayBuffer !== 'undefined') && (ArrayBuffer.isView)) { 1060 | result = ArrayBuffer.isView(val); 1061 | } else { 1062 | result = (val) && (val.buffer) && (val.buffer instanceof ArrayBuffer); 1063 | } 1064 | return result; 1065 | } 1066 | 1067 | /** 1068 | * Determine if a value is a String 1069 | * 1070 | * @param {Object} val The value to test 1071 | * @returns {boolean} True if value is a String, otherwise false 1072 | */ 1073 | function isString(val) { 1074 | return typeof val === 'string'; 1075 | } 1076 | 1077 | /** 1078 | * Determine if a value is a Number 1079 | * 1080 | * @param {Object} val The value to test 1081 | * @returns {boolean} True if value is a Number, otherwise false 1082 | */ 1083 | function isNumber(val) { 1084 | return typeof val === 'number'; 1085 | } 1086 | 1087 | /** 1088 | * Determine if a value is undefined 1089 | * 1090 | * @param {Object} val The value to test 1091 | * @returns {boolean} True if the value is undefined, otherwise false 1092 | */ 1093 | function isUndefined(val) { 1094 | return typeof val === 'undefined'; 1095 | } 1096 | 1097 | /** 1098 | * Determine if a value is an Object 1099 | * 1100 | * @param {Object} val The value to test 1101 | * @returns {boolean} True if value is an Object, otherwise false 1102 | */ 1103 | function isObject(val) { 1104 | return val !== null && typeof val === 'object'; 1105 | } 1106 | 1107 | /** 1108 | * Determine if a value is a Date 1109 | * 1110 | * @param {Object} val The value to test 1111 | * @returns {boolean} True if value is a Date, otherwise false 1112 | */ 1113 | function isDate(val) { 1114 | return toString.call(val) === '[object Date]'; 1115 | } 1116 | 1117 | /** 1118 | * Determine if a value is a File 1119 | * 1120 | * @param {Object} val The value to test 1121 | * @returns {boolean} True if value is a File, otherwise false 1122 | */ 1123 | function isFile(val) { 1124 | return toString.call(val) === '[object File]'; 1125 | } 1126 | 1127 | /** 1128 | * Determine if a value is a Blob 1129 | * 1130 | * @param {Object} val The value to test 1131 | * @returns {boolean} True if value is a Blob, otherwise false 1132 | */ 1133 | function isBlob(val) { 1134 | return toString.call(val) === '[object Blob]'; 1135 | } 1136 | 1137 | /** 1138 | * Determine if a value is a Function 1139 | * 1140 | * @param {Object} val The value to test 1141 | * @returns {boolean} True if value is a Function, otherwise false 1142 | */ 1143 | function isFunction(val) { 1144 | return toString.call(val) === '[object Function]'; 1145 | } 1146 | 1147 | /** 1148 | * Determine if a value is a Stream 1149 | * 1150 | * @param {Object} val The value to test 1151 | * @returns {boolean} True if value is a Stream, otherwise false 1152 | */ 1153 | function isStream(val) { 1154 | return isObject(val) && isFunction(val.pipe); 1155 | } 1156 | 1157 | /** 1158 | * Determine if a value is a URLSearchParams object 1159 | * 1160 | * @param {Object} val The value to test 1161 | * @returns {boolean} True if value is a URLSearchParams object, otherwise false 1162 | */ 1163 | function isURLSearchParams(val) { 1164 | return typeof URLSearchParams !== 'undefined' && val instanceof URLSearchParams; 1165 | } 1166 | 1167 | /** 1168 | * Trim excess whitespace off the beginning and end of a string 1169 | * 1170 | * @param {String} str The String to trim 1171 | * @returns {String} The String freed of excess whitespace 1172 | */ 1173 | function trim(str) { 1174 | return str.replace(/^\s*/, '').replace(/\s*$/, ''); 1175 | } 1176 | 1177 | /** 1178 | * Determine if we're running in a standard browser environment 1179 | * 1180 | * This allows axios to run in a web worker, and react-native. 1181 | * Both environments support XMLHttpRequest, but not fully standard globals. 1182 | * 1183 | * web workers: 1184 | * typeof window -> undefined 1185 | * typeof document -> undefined 1186 | * 1187 | * react-native: 1188 | * navigator.product -> 'ReactNative' 1189 | */ 1190 | function isStandardBrowserEnv() { 1191 | if (typeof navigator !== 'undefined' && navigator.product === 'ReactNative') { 1192 | return false; 1193 | } 1194 | return ( 1195 | typeof window !== 'undefined' && 1196 | typeof document !== 'undefined' 1197 | ); 1198 | } 1199 | 1200 | /** 1201 | * Iterate over an Array or an Object invoking a function for each item. 1202 | * 1203 | * If `obj` is an Array callback will be called passing 1204 | * the value, index, and complete array for each item. 1205 | * 1206 | * If 'obj' is an Object callback will be called passing 1207 | * the value, key, and complete object for each property. 1208 | * 1209 | * @param {Object|Array} obj The object to iterate 1210 | * @param {Function} fn The callback to invoke for each item 1211 | */ 1212 | function forEach(obj, fn) { 1213 | // Don't bother if no value provided 1214 | if (obj === null || typeof obj === 'undefined') { 1215 | return; 1216 | } 1217 | 1218 | // Force an array if not already something iterable 1219 | if (typeof obj !== 'object' && !isArray(obj)) { 1220 | /*eslint no-param-reassign:0*/ 1221 | obj = [obj]; 1222 | } 1223 | 1224 | if (isArray(obj)) { 1225 | // Iterate over array values 1226 | for (var i = 0, l = obj.length; i < l; i++) { 1227 | fn.call(null, obj[i], i, obj); 1228 | } 1229 | } else { 1230 | // Iterate over object keys 1231 | for (var key in obj) { 1232 | if (Object.prototype.hasOwnProperty.call(obj, key)) { 1233 | fn.call(null, obj[key], key, obj); 1234 | } 1235 | } 1236 | } 1237 | } 1238 | 1239 | /** 1240 | * Accepts varargs expecting each argument to be an object, then 1241 | * immutably merges the properties of each object and returns result. 1242 | * 1243 | * When multiple objects contain the same key the later object in 1244 | * the arguments list will take precedence. 1245 | * 1246 | * Example: 1247 | * 1248 | * ```js 1249 | * var result = merge({foo: 123}, {foo: 456}); 1250 | * console.log(result.foo); // outputs 456 1251 | * ``` 1252 | * 1253 | * @param {Object} obj1 Object to merge 1254 | * @returns {Object} Result of all merge properties 1255 | */ 1256 | function merge(/* obj1, obj2, obj3, ... */) { 1257 | var result = {}; 1258 | function assignValue(val, key) { 1259 | if (typeof result[key] === 'object' && typeof val === 'object') { 1260 | result[key] = merge(result[key], val); 1261 | } else { 1262 | result[key] = val; 1263 | } 1264 | } 1265 | 1266 | for (var i = 0, l = arguments.length; i < l; i++) { 1267 | forEach(arguments[i], assignValue); 1268 | } 1269 | return result; 1270 | } 1271 | 1272 | /** 1273 | * Extends object a by mutably adding to it the properties of object b. 1274 | * 1275 | * @param {Object} a The object to be extended 1276 | * @param {Object} b The object to copy properties from 1277 | * @param {Object} thisArg The object to bind function to 1278 | * @return {Object} The resulting value of object a 1279 | */ 1280 | function extend(a, b, thisArg) { 1281 | forEach(b, function assignValue(val, key) { 1282 | if (thisArg && typeof val === 'function') { 1283 | a[key] = bind(val, thisArg); 1284 | } else { 1285 | a[key] = val; 1286 | } 1287 | }); 1288 | return a; 1289 | } 1290 | 1291 | var utils = { 1292 | isArray: isArray, 1293 | isArrayBuffer: isArrayBuffer, 1294 | isBuffer: index$1, 1295 | isFormData: isFormData, 1296 | isArrayBufferView: isArrayBufferView, 1297 | isString: isString, 1298 | isNumber: isNumber, 1299 | isObject: isObject, 1300 | isUndefined: isUndefined, 1301 | isDate: isDate, 1302 | isFile: isFile, 1303 | isBlob: isBlob, 1304 | isFunction: isFunction, 1305 | isStream: isStream, 1306 | isURLSearchParams: isURLSearchParams, 1307 | isStandardBrowserEnv: isStandardBrowserEnv, 1308 | forEach: forEach, 1309 | merge: merge, 1310 | extend: extend, 1311 | trim: trim 1312 | }; 1313 | 1314 | var normalizeHeaderName = function normalizeHeaderName(headers, normalizedName) { 1315 | utils.forEach(headers, function processHeader(value, name) { 1316 | if (name !== normalizedName && name.toUpperCase() === normalizedName.toUpperCase()) { 1317 | headers[normalizedName] = value; 1318 | delete headers[name]; 1319 | } 1320 | }); 1321 | }; 1322 | 1323 | /** 1324 | * Update an Error with the specified config, error code, and response. 1325 | * 1326 | * @param {Error} error The error to update. 1327 | * @param {Object} config The config. 1328 | * @param {string} [code] The error code (for example, 'ECONNABORTED'). 1329 | * @param {Object} [request] The request. 1330 | * @param {Object} [response] The response. 1331 | * @returns {Error} The error. 1332 | */ 1333 | var enhanceError = function enhanceError(error, config, code, request, response) { 1334 | error.config = config; 1335 | if (code) { 1336 | error.code = code; 1337 | } 1338 | error.request = request; 1339 | error.response = response; 1340 | return error; 1341 | }; 1342 | 1343 | /** 1344 | * Create an Error with the specified message, config, error code, request and response. 1345 | * 1346 | * @param {string} message The error message. 1347 | * @param {Object} config The config. 1348 | * @param {string} [code] The error code (for example, 'ECONNABORTED'). 1349 | * @param {Object} [request] The request. 1350 | * @param {Object} [response] The response. 1351 | * @returns {Error} The created error. 1352 | */ 1353 | var createError = function createError(message, config, code, request, response) { 1354 | var error = new Error(message); 1355 | return enhanceError(error, config, code, request, response); 1356 | }; 1357 | 1358 | /** 1359 | * Resolve or reject a Promise based on response status. 1360 | * 1361 | * @param {Function} resolve A function that resolves the promise. 1362 | * @param {Function} reject A function that rejects the promise. 1363 | * @param {object} response The response. 1364 | */ 1365 | var settle = function settle(resolve, reject, response) { 1366 | var validateStatus = response.config.validateStatus; 1367 | // Note: status is not exposed by XDomainRequest 1368 | if (!response.status || !validateStatus || validateStatus(response.status)) { 1369 | resolve(response); 1370 | } else { 1371 | reject(createError( 1372 | 'Request failed with status code ' + response.status, 1373 | response.config, 1374 | null, 1375 | response.request, 1376 | response 1377 | )); 1378 | } 1379 | }; 1380 | 1381 | function encode(val) { 1382 | return encodeURIComponent(val). 1383 | replace(/%40/gi, '@'). 1384 | replace(/%3A/gi, ':'). 1385 | replace(/%24/g, '$'). 1386 | replace(/%2C/gi, ','). 1387 | replace(/%20/g, '+'). 1388 | replace(/%5B/gi, '['). 1389 | replace(/%5D/gi, ']'); 1390 | } 1391 | 1392 | /** 1393 | * Build a URL by appending params to the end 1394 | * 1395 | * @param {string} url The base of the url (e.g., http://www.google.com) 1396 | * @param {object} [params] The params to be appended 1397 | * @returns {string} The formatted url 1398 | */ 1399 | var buildURL = function buildURL(url, params, paramsSerializer) { 1400 | /*eslint no-param-reassign:0*/ 1401 | if (!params) { 1402 | return url; 1403 | } 1404 | 1405 | var serializedParams; 1406 | if (paramsSerializer) { 1407 | serializedParams = paramsSerializer(params); 1408 | } else if (utils.isURLSearchParams(params)) { 1409 | serializedParams = params.toString(); 1410 | } else { 1411 | var parts = []; 1412 | 1413 | utils.forEach(params, function serialize(val, key) { 1414 | if (val === null || typeof val === 'undefined') { 1415 | return; 1416 | } 1417 | 1418 | if (utils.isArray(val)) { 1419 | key = key + '[]'; 1420 | } 1421 | 1422 | if (!utils.isArray(val)) { 1423 | val = [val]; 1424 | } 1425 | 1426 | utils.forEach(val, function parseValue(v) { 1427 | if (utils.isDate(v)) { 1428 | v = v.toISOString(); 1429 | } else if (utils.isObject(v)) { 1430 | v = JSON.stringify(v); 1431 | } 1432 | parts.push(encode(key) + '=' + encode(v)); 1433 | }); 1434 | }); 1435 | 1436 | serializedParams = parts.join('&'); 1437 | } 1438 | 1439 | if (serializedParams) { 1440 | url += (url.indexOf('?') === -1 ? '?' : '&') + serializedParams; 1441 | } 1442 | 1443 | return url; 1444 | }; 1445 | 1446 | /** 1447 | * Parse headers into an object 1448 | * 1449 | * ``` 1450 | * Date: Wed, 27 Aug 2014 08:58:49 GMT 1451 | * Content-Type: application/json 1452 | * Connection: keep-alive 1453 | * Transfer-Encoding: chunked 1454 | * ``` 1455 | * 1456 | * @param {String} headers Headers needing to be parsed 1457 | * @returns {Object} Headers parsed into an object 1458 | */ 1459 | var parseHeaders = function parseHeaders(headers) { 1460 | var parsed = {}; 1461 | var key; 1462 | var val; 1463 | var i; 1464 | 1465 | if (!headers) { return parsed; } 1466 | 1467 | utils.forEach(headers.split('\n'), function parser(line) { 1468 | i = line.indexOf(':'); 1469 | key = utils.trim(line.substr(0, i)).toLowerCase(); 1470 | val = utils.trim(line.substr(i + 1)); 1471 | 1472 | if (key) { 1473 | parsed[key] = parsed[key] ? parsed[key] + ', ' + val : val; 1474 | } 1475 | }); 1476 | 1477 | return parsed; 1478 | }; 1479 | 1480 | var isURLSameOrigin = ( 1481 | utils.isStandardBrowserEnv() ? 1482 | 1483 | // Standard browser envs have full support of the APIs needed to test 1484 | // whether the request URL is of the same origin as current location. 1485 | (function standardBrowserEnv() { 1486 | var msie = /(msie|trident)/i.test(navigator.userAgent); 1487 | var urlParsingNode = document.createElement('a'); 1488 | var originURL; 1489 | 1490 | /** 1491 | * Parse a URL to discover it's components 1492 | * 1493 | * @param {String} url The URL to be parsed 1494 | * @returns {Object} 1495 | */ 1496 | function resolveURL(url) { 1497 | var href = url; 1498 | 1499 | if (msie) { 1500 | // IE needs attribute set twice to normalize properties 1501 | urlParsingNode.setAttribute('href', href); 1502 | href = urlParsingNode.href; 1503 | } 1504 | 1505 | urlParsingNode.setAttribute('href', href); 1506 | 1507 | // urlParsingNode provides the UrlUtils interface - http://url.spec.whatwg.org/#urlutils 1508 | return { 1509 | href: urlParsingNode.href, 1510 | protocol: urlParsingNode.protocol ? urlParsingNode.protocol.replace(/:$/, '') : '', 1511 | host: urlParsingNode.host, 1512 | search: urlParsingNode.search ? urlParsingNode.search.replace(/^\?/, '') : '', 1513 | hash: urlParsingNode.hash ? urlParsingNode.hash.replace(/^#/, '') : '', 1514 | hostname: urlParsingNode.hostname, 1515 | port: urlParsingNode.port, 1516 | pathname: (urlParsingNode.pathname.charAt(0) === '/') ? 1517 | urlParsingNode.pathname : 1518 | '/' + urlParsingNode.pathname 1519 | }; 1520 | } 1521 | 1522 | originURL = resolveURL(window.location.href); 1523 | 1524 | /** 1525 | * Determine if a URL shares the same origin as the current location 1526 | * 1527 | * @param {String} requestURL The URL to test 1528 | * @returns {boolean} True if URL shares the same origin, otherwise false 1529 | */ 1530 | return function isURLSameOrigin(requestURL) { 1531 | var parsed = (utils.isString(requestURL)) ? resolveURL(requestURL) : requestURL; 1532 | return (parsed.protocol === originURL.protocol && 1533 | parsed.host === originURL.host); 1534 | }; 1535 | })() : 1536 | 1537 | // Non standard browser envs (web workers, react-native) lack needed support. 1538 | (function nonStandardBrowserEnv() { 1539 | return function isURLSameOrigin() { 1540 | return true; 1541 | }; 1542 | })() 1543 | ); 1544 | 1545 | // btoa polyfill for IE<10 courtesy https://github.com/davidchambers/Base64.js 1546 | 1547 | var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='; 1548 | 1549 | function E() { 1550 | this.message = 'String contains an invalid character'; 1551 | } 1552 | E.prototype = new Error; 1553 | E.prototype.code = 5; 1554 | E.prototype.name = 'InvalidCharacterError'; 1555 | 1556 | function btoa$1(input) { 1557 | var str = String(input); 1558 | var output = ''; 1559 | for ( 1560 | // initialize result and counter 1561 | var block, charCode, idx = 0, map = chars; 1562 | // if the next str index does not exist: 1563 | // change the mapping table to "=" 1564 | // check if d has no fractional digits 1565 | str.charAt(idx | 0) || (map = '=', idx % 1); 1566 | // "8 - idx % 1 * 8" generates the sequence 2, 4, 6, 8 1567 | output += map.charAt(63 & block >> 8 - idx % 1 * 8) 1568 | ) { 1569 | charCode = str.charCodeAt(idx += 3 / 4); 1570 | if (charCode > 0xFF) { 1571 | throw new E(); 1572 | } 1573 | block = block << 8 | charCode; 1574 | } 1575 | return output; 1576 | } 1577 | 1578 | var btoa_1 = btoa$1; 1579 | 1580 | var cookies = ( 1581 | utils.isStandardBrowserEnv() ? 1582 | 1583 | // Standard browser envs support document.cookie 1584 | (function standardBrowserEnv() { 1585 | return { 1586 | write: function write(name, value, expires, path, domain, secure) { 1587 | var cookie = []; 1588 | cookie.push(name + '=' + encodeURIComponent(value)); 1589 | 1590 | if (utils.isNumber(expires)) { 1591 | cookie.push('expires=' + new Date(expires).toGMTString()); 1592 | } 1593 | 1594 | if (utils.isString(path)) { 1595 | cookie.push('path=' + path); 1596 | } 1597 | 1598 | if (utils.isString(domain)) { 1599 | cookie.push('domain=' + domain); 1600 | } 1601 | 1602 | if (secure === true) { 1603 | cookie.push('secure'); 1604 | } 1605 | 1606 | document.cookie = cookie.join('; '); 1607 | }, 1608 | 1609 | read: function read(name) { 1610 | var match = document.cookie.match(new RegExp('(^|;\\s*)(' + name + ')=([^;]*)')); 1611 | return (match ? decodeURIComponent(match[3]) : null); 1612 | }, 1613 | 1614 | remove: function remove(name) { 1615 | this.write(name, '', Date.now() - 86400000); 1616 | } 1617 | }; 1618 | })() : 1619 | 1620 | // Non standard browser env (web workers, react-native) lack needed support. 1621 | (function nonStandardBrowserEnv() { 1622 | return { 1623 | write: function write() {}, 1624 | read: function read() { return null; }, 1625 | remove: function remove() {} 1626 | }; 1627 | })() 1628 | ); 1629 | 1630 | var btoa = (typeof window !== 'undefined' && window.btoa && window.btoa.bind(window)) || btoa_1; 1631 | 1632 | var xhr = function xhrAdapter(config) { 1633 | return new Promise(function dispatchXhrRequest(resolve, reject) { 1634 | var requestData = config.data; 1635 | var requestHeaders = config.headers; 1636 | 1637 | if (utils.isFormData(requestData)) { 1638 | delete requestHeaders['Content-Type']; // Let the browser set it 1639 | } 1640 | 1641 | var request = new XMLHttpRequest(); 1642 | var loadEvent = 'onreadystatechange'; 1643 | var xDomain = false; 1644 | 1645 | // For IE 8/9 CORS support 1646 | // Only supports POST and GET calls and doesn't returns the response headers. 1647 | // DON'T do this for testing b/c XMLHttpRequest is mocked, not XDomainRequest. 1648 | if (process.env.NODE_ENV !== 'test' && 1649 | typeof window !== 'undefined' && 1650 | window.XDomainRequest && !('withCredentials' in request) && 1651 | !isURLSameOrigin(config.url)) { 1652 | request = new window.XDomainRequest(); 1653 | loadEvent = 'onload'; 1654 | xDomain = true; 1655 | request.onprogress = function handleProgress() {}; 1656 | request.ontimeout = function handleTimeout() {}; 1657 | } 1658 | 1659 | // HTTP basic authentication 1660 | if (config.auth) { 1661 | var username = config.auth.username || ''; 1662 | var password = config.auth.password || ''; 1663 | requestHeaders.Authorization = 'Basic ' + btoa(username + ':' + password); 1664 | } 1665 | 1666 | request.open(config.method.toUpperCase(), buildURL(config.url, config.params, config.paramsSerializer), true); 1667 | 1668 | // Set the request timeout in MS 1669 | request.timeout = config.timeout; 1670 | 1671 | // Listen for ready state 1672 | request[loadEvent] = function handleLoad() { 1673 | if (!request || (request.readyState !== 4 && !xDomain)) { 1674 | return; 1675 | } 1676 | 1677 | // The request errored out and we didn't get a response, this will be 1678 | // handled by onerror instead 1679 | // With one exception: request that using file: protocol, most browsers 1680 | // will return status as 0 even though it's a successful request 1681 | if (request.status === 0 && !(request.responseURL && request.responseURL.indexOf('file:') === 0)) { 1682 | return; 1683 | } 1684 | 1685 | // Prepare the response 1686 | var responseHeaders = 'getAllResponseHeaders' in request ? parseHeaders(request.getAllResponseHeaders()) : null; 1687 | var responseData = !config.responseType || config.responseType === 'text' ? request.responseText : request.response; 1688 | var response = { 1689 | data: responseData, 1690 | // IE sends 1223 instead of 204 (https://github.com/mzabriskie/axios/issues/201) 1691 | status: request.status === 1223 ? 204 : request.status, 1692 | statusText: request.status === 1223 ? 'No Content' : request.statusText, 1693 | headers: responseHeaders, 1694 | config: config, 1695 | request: request 1696 | }; 1697 | 1698 | settle(resolve, reject, response); 1699 | 1700 | // Clean up request 1701 | request = null; 1702 | }; 1703 | 1704 | // Handle low level network errors 1705 | request.onerror = function handleError() { 1706 | // Real errors are hidden from us by the browser 1707 | // onerror should only fire if it's a network error 1708 | reject(createError('Network Error', config, null, request)); 1709 | 1710 | // Clean up request 1711 | request = null; 1712 | }; 1713 | 1714 | // Handle timeout 1715 | request.ontimeout = function handleTimeout() { 1716 | reject(createError('timeout of ' + config.timeout + 'ms exceeded', config, 'ECONNABORTED', 1717 | request)); 1718 | 1719 | // Clean up request 1720 | request = null; 1721 | }; 1722 | 1723 | // Add xsrf header 1724 | // This is only done if running in a standard browser environment. 1725 | // Specifically not if we're in a web worker, or react-native. 1726 | if (utils.isStandardBrowserEnv()) { 1727 | var cookies$$1 = cookies; 1728 | 1729 | // Add xsrf header 1730 | var xsrfValue = (config.withCredentials || isURLSameOrigin(config.url)) && config.xsrfCookieName ? 1731 | cookies$$1.read(config.xsrfCookieName) : 1732 | undefined; 1733 | 1734 | if (xsrfValue) { 1735 | requestHeaders[config.xsrfHeaderName] = xsrfValue; 1736 | } 1737 | } 1738 | 1739 | // Add headers to the request 1740 | if ('setRequestHeader' in request) { 1741 | utils.forEach(requestHeaders, function setRequestHeader(val, key) { 1742 | if (typeof requestData === 'undefined' && key.toLowerCase() === 'content-type') { 1743 | // Remove Content-Type if data is undefined 1744 | delete requestHeaders[key]; 1745 | } else { 1746 | // Otherwise add header to the request 1747 | request.setRequestHeader(key, val); 1748 | } 1749 | }); 1750 | } 1751 | 1752 | // Add withCredentials to request if needed 1753 | if (config.withCredentials) { 1754 | request.withCredentials = true; 1755 | } 1756 | 1757 | // Add responseType to request if needed 1758 | if (config.responseType) { 1759 | try { 1760 | request.responseType = config.responseType; 1761 | } catch (e) { 1762 | // Expected DOMException thrown by browsers not compatible XMLHttpRequest Level 2. 1763 | // But, this can be suppressed for 'json' type as it can be parsed by default 'transformResponse' function. 1764 | if (config.responseType !== 'json') { 1765 | throw e; 1766 | } 1767 | } 1768 | } 1769 | 1770 | // Handle progress if needed 1771 | if (typeof config.onDownloadProgress === 'function') { 1772 | request.addEventListener('progress', config.onDownloadProgress); 1773 | } 1774 | 1775 | // Not all browsers support upload events 1776 | if (typeof config.onUploadProgress === 'function' && request.upload) { 1777 | request.upload.addEventListener('progress', config.onUploadProgress); 1778 | } 1779 | 1780 | if (config.cancelToken) { 1781 | // Handle cancellation 1782 | config.cancelToken.promise.then(function onCanceled(cancel) { 1783 | if (!request) { 1784 | return; 1785 | } 1786 | 1787 | request.abort(); 1788 | reject(cancel); 1789 | // Clean up request 1790 | request = null; 1791 | }); 1792 | } 1793 | 1794 | if (requestData === undefined) { 1795 | requestData = null; 1796 | } 1797 | 1798 | // Send the request 1799 | request.send(requestData); 1800 | }); 1801 | }; 1802 | 1803 | var DEFAULT_CONTENT_TYPE = { 1804 | 'Content-Type': 'application/x-www-form-urlencoded' 1805 | }; 1806 | 1807 | function setContentTypeIfUnset(headers, value) { 1808 | if (!utils.isUndefined(headers) && utils.isUndefined(headers['Content-Type'])) { 1809 | headers['Content-Type'] = value; 1810 | } 1811 | } 1812 | 1813 | function getDefaultAdapter() { 1814 | var adapter; 1815 | if (typeof XMLHttpRequest !== 'undefined') { 1816 | // For browsers use XHR adapter 1817 | adapter = xhr; 1818 | } else if (typeof process !== 'undefined') { 1819 | // For node use HTTP adapter 1820 | adapter = xhr; 1821 | } 1822 | return adapter; 1823 | } 1824 | 1825 | var defaults = { 1826 | adapter: getDefaultAdapter(), 1827 | 1828 | transformRequest: [function transformRequest(data, headers) { 1829 | normalizeHeaderName(headers, 'Content-Type'); 1830 | if (utils.isFormData(data) || 1831 | utils.isArrayBuffer(data) || 1832 | utils.isBuffer(data) || 1833 | utils.isStream(data) || 1834 | utils.isFile(data) || 1835 | utils.isBlob(data) 1836 | ) { 1837 | return data; 1838 | } 1839 | if (utils.isArrayBufferView(data)) { 1840 | return data.buffer; 1841 | } 1842 | if (utils.isURLSearchParams(data)) { 1843 | setContentTypeIfUnset(headers, 'application/x-www-form-urlencoded;charset=utf-8'); 1844 | return data.toString(); 1845 | } 1846 | if (utils.isObject(data)) { 1847 | setContentTypeIfUnset(headers, 'application/json;charset=utf-8'); 1848 | return JSON.stringify(data); 1849 | } 1850 | return data; 1851 | }], 1852 | 1853 | transformResponse: [function transformResponse(data) { 1854 | /*eslint no-param-reassign:0*/ 1855 | if (typeof data === 'string') { 1856 | try { 1857 | data = JSON.parse(data); 1858 | } catch (e) { /* Ignore */ } 1859 | } 1860 | return data; 1861 | }], 1862 | 1863 | timeout: 0, 1864 | 1865 | xsrfCookieName: 'XSRF-TOKEN', 1866 | xsrfHeaderName: 'X-XSRF-TOKEN', 1867 | 1868 | maxContentLength: -1, 1869 | 1870 | validateStatus: function validateStatus(status) { 1871 | return status >= 200 && status < 300; 1872 | } 1873 | }; 1874 | 1875 | defaults.headers = { 1876 | common: { 1877 | 'Accept': 'application/json, text/plain, */*' 1878 | } 1879 | }; 1880 | 1881 | utils.forEach(['delete', 'get', 'head'], function forEachMethodNoData(method) { 1882 | defaults.headers[method] = {}; 1883 | }); 1884 | 1885 | utils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) { 1886 | defaults.headers[method] = utils.merge(DEFAULT_CONTENT_TYPE); 1887 | }); 1888 | 1889 | var defaults_1 = defaults; 1890 | 1891 | function InterceptorManager() { 1892 | this.handlers = []; 1893 | } 1894 | 1895 | /** 1896 | * Add a new interceptor to the stack 1897 | * 1898 | * @param {Function} fulfilled The function to handle `then` for a `Promise` 1899 | * @param {Function} rejected The function to handle `reject` for a `Promise` 1900 | * 1901 | * @return {Number} An ID used to remove interceptor later 1902 | */ 1903 | InterceptorManager.prototype.use = function use(fulfilled, rejected) { 1904 | this.handlers.push({ 1905 | fulfilled: fulfilled, 1906 | rejected: rejected 1907 | }); 1908 | return this.handlers.length - 1; 1909 | }; 1910 | 1911 | /** 1912 | * Remove an interceptor from the stack 1913 | * 1914 | * @param {Number} id The ID that was returned by `use` 1915 | */ 1916 | InterceptorManager.prototype.eject = function eject(id) { 1917 | if (this.handlers[id]) { 1918 | this.handlers[id] = null; 1919 | } 1920 | }; 1921 | 1922 | /** 1923 | * Iterate over all the registered interceptors 1924 | * 1925 | * This method is particularly useful for skipping over any 1926 | * interceptors that may have become `null` calling `eject`. 1927 | * 1928 | * @param {Function} fn The function to call for each interceptor 1929 | */ 1930 | InterceptorManager.prototype.forEach = function forEach(fn) { 1931 | utils.forEach(this.handlers, function forEachHandler(h) { 1932 | if (h !== null) { 1933 | fn(h); 1934 | } 1935 | }); 1936 | }; 1937 | 1938 | var InterceptorManager_1 = InterceptorManager; 1939 | 1940 | /** 1941 | * Transform the data for a request or a response 1942 | * 1943 | * @param {Object|String} data The data to be transformed 1944 | * @param {Array} headers The headers for the request or response 1945 | * @param {Array|Function} fns A single function or Array of functions 1946 | * @returns {*} The resulting transformed data 1947 | */ 1948 | var transformData = function transformData(data, headers, fns) { 1949 | /*eslint no-param-reassign:0*/ 1950 | utils.forEach(fns, function transform(fn) { 1951 | data = fn(data, headers); 1952 | }); 1953 | 1954 | return data; 1955 | }; 1956 | 1957 | var isCancel = function isCancel(value) { 1958 | return !!(value && value.__CANCEL__); 1959 | }; 1960 | 1961 | /** 1962 | * Throws a `Cancel` if cancellation has been requested. 1963 | */ 1964 | function throwIfCancellationRequested(config) { 1965 | if (config.cancelToken) { 1966 | config.cancelToken.throwIfRequested(); 1967 | } 1968 | } 1969 | 1970 | /** 1971 | * Dispatch a request to the server using the configured adapter. 1972 | * 1973 | * @param {object} config The config that is to be used for the request 1974 | * @returns {Promise} The Promise to be fulfilled 1975 | */ 1976 | var dispatchRequest = function dispatchRequest(config) { 1977 | throwIfCancellationRequested(config); 1978 | 1979 | // Ensure headers exist 1980 | config.headers = config.headers || {}; 1981 | 1982 | // Transform request data 1983 | config.data = transformData( 1984 | config.data, 1985 | config.headers, 1986 | config.transformRequest 1987 | ); 1988 | 1989 | // Flatten headers 1990 | config.headers = utils.merge( 1991 | config.headers.common || {}, 1992 | config.headers[config.method] || {}, 1993 | config.headers || {} 1994 | ); 1995 | 1996 | utils.forEach( 1997 | ['delete', 'get', 'head', 'post', 'put', 'patch', 'common'], 1998 | function cleanHeaderConfig(method) { 1999 | delete config.headers[method]; 2000 | } 2001 | ); 2002 | 2003 | var adapter = config.adapter || defaults_1.adapter; 2004 | 2005 | return adapter(config).then(function onAdapterResolution(response) { 2006 | throwIfCancellationRequested(config); 2007 | 2008 | // Transform response data 2009 | response.data = transformData( 2010 | response.data, 2011 | response.headers, 2012 | config.transformResponse 2013 | ); 2014 | 2015 | return response; 2016 | }, function onAdapterRejection(reason) { 2017 | if (!isCancel(reason)) { 2018 | throwIfCancellationRequested(config); 2019 | 2020 | // Transform response data 2021 | if (reason && reason.response) { 2022 | reason.response.data = transformData( 2023 | reason.response.data, 2024 | reason.response.headers, 2025 | config.transformResponse 2026 | ); 2027 | } 2028 | } 2029 | 2030 | return Promise.reject(reason); 2031 | }); 2032 | }; 2033 | 2034 | /** 2035 | * Determines whether the specified URL is absolute 2036 | * 2037 | * @param {string} url The URL to test 2038 | * @returns {boolean} True if the specified URL is absolute, otherwise false 2039 | */ 2040 | var isAbsoluteURL = function isAbsoluteURL(url) { 2041 | // A URL is considered absolute if it begins with "://" or "//" (protocol-relative URL). 2042 | // RFC 3986 defines scheme name as a sequence of characters beginning with a letter and followed 2043 | // by any combination of letters, digits, plus, period, or hyphen. 2044 | return /^([a-z][a-z\d\+\-\.]*:)?\/\//i.test(url); 2045 | }; 2046 | 2047 | /** 2048 | * Creates a new URL by combining the specified URLs 2049 | * 2050 | * @param {string} baseURL The base URL 2051 | * @param {string} relativeURL The relative URL 2052 | * @returns {string} The combined URL 2053 | */ 2054 | var combineURLs = function combineURLs(baseURL, relativeURL) { 2055 | return relativeURL 2056 | ? baseURL.replace(/\/+$/, '') + '/' + relativeURL.replace(/^\/+/, '') 2057 | : baseURL; 2058 | }; 2059 | 2060 | /** 2061 | * Create a new instance of Axios 2062 | * 2063 | * @param {Object} instanceConfig The default config for the instance 2064 | */ 2065 | function Axios(instanceConfig) { 2066 | this.defaults = instanceConfig; 2067 | this.interceptors = { 2068 | request: new InterceptorManager_1(), 2069 | response: new InterceptorManager_1() 2070 | }; 2071 | } 2072 | 2073 | /** 2074 | * Dispatch a request 2075 | * 2076 | * @param {Object} config The config specific for this request (merged with this.defaults) 2077 | */ 2078 | Axios.prototype.request = function request(config) { 2079 | /*eslint no-param-reassign:0*/ 2080 | // Allow for axios('example/url'[, config]) a la fetch API 2081 | if (typeof config === 'string') { 2082 | config = utils.merge({ 2083 | url: arguments[0] 2084 | }, arguments[1]); 2085 | } 2086 | 2087 | config = utils.merge(defaults_1, this.defaults, { method: 'get' }, config); 2088 | config.method = config.method.toLowerCase(); 2089 | 2090 | // Support baseURL config 2091 | if (config.baseURL && !isAbsoluteURL(config.url)) { 2092 | config.url = combineURLs(config.baseURL, config.url); 2093 | } 2094 | 2095 | // Hook up interceptors middleware 2096 | var chain = [dispatchRequest, undefined]; 2097 | var promise = Promise.resolve(config); 2098 | 2099 | this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) { 2100 | chain.unshift(interceptor.fulfilled, interceptor.rejected); 2101 | }); 2102 | 2103 | this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) { 2104 | chain.push(interceptor.fulfilled, interceptor.rejected); 2105 | }); 2106 | 2107 | while (chain.length) { 2108 | promise = promise.then(chain.shift(), chain.shift()); 2109 | } 2110 | 2111 | return promise; 2112 | }; 2113 | 2114 | // Provide aliases for supported request methods 2115 | utils.forEach(['delete', 'get', 'head', 'options'], function forEachMethodNoData(method) { 2116 | /*eslint func-names:0*/ 2117 | Axios.prototype[method] = function(url, config) { 2118 | return this.request(utils.merge(config || {}, { 2119 | method: method, 2120 | url: url 2121 | })); 2122 | }; 2123 | }); 2124 | 2125 | utils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) { 2126 | /*eslint func-names:0*/ 2127 | Axios.prototype[method] = function(url, data, config) { 2128 | return this.request(utils.merge(config || {}, { 2129 | method: method, 2130 | url: url, 2131 | data: data 2132 | })); 2133 | }; 2134 | }); 2135 | 2136 | var Axios_1 = Axios; 2137 | 2138 | /** 2139 | * A `Cancel` is an object that is thrown when an operation is canceled. 2140 | * 2141 | * @class 2142 | * @param {string=} message The message. 2143 | */ 2144 | function Cancel(message) { 2145 | this.message = message; 2146 | } 2147 | 2148 | Cancel.prototype.toString = function toString() { 2149 | return 'Cancel' + (this.message ? ': ' + this.message : ''); 2150 | }; 2151 | 2152 | Cancel.prototype.__CANCEL__ = true; 2153 | 2154 | var Cancel_1 = Cancel; 2155 | 2156 | /** 2157 | * A `CancelToken` is an object that can be used to request cancellation of an operation. 2158 | * 2159 | * @class 2160 | * @param {Function} executor The executor function. 2161 | */ 2162 | function CancelToken(executor) { 2163 | if (typeof executor !== 'function') { 2164 | throw new TypeError('executor must be a function.'); 2165 | } 2166 | 2167 | var resolvePromise; 2168 | this.promise = new Promise(function promiseExecutor(resolve) { 2169 | resolvePromise = resolve; 2170 | }); 2171 | 2172 | var token = this; 2173 | executor(function cancel(message) { 2174 | if (token.reason) { 2175 | // Cancellation has already been requested 2176 | return; 2177 | } 2178 | 2179 | token.reason = new Cancel_1(message); 2180 | resolvePromise(token.reason); 2181 | }); 2182 | } 2183 | 2184 | /** 2185 | * Throws a `Cancel` if cancellation has been requested. 2186 | */ 2187 | CancelToken.prototype.throwIfRequested = function throwIfRequested() { 2188 | if (this.reason) { 2189 | throw this.reason; 2190 | } 2191 | }; 2192 | 2193 | /** 2194 | * Returns an object that contains a new `CancelToken` and a function that, when called, 2195 | * cancels the `CancelToken`. 2196 | */ 2197 | CancelToken.source = function source() { 2198 | var cancel; 2199 | var token = new CancelToken(function executor(c) { 2200 | cancel = c; 2201 | }); 2202 | return { 2203 | token: token, 2204 | cancel: cancel 2205 | }; 2206 | }; 2207 | 2208 | var CancelToken_1 = CancelToken; 2209 | 2210 | /** 2211 | * Syntactic sugar for invoking a function and expanding an array for arguments. 2212 | * 2213 | * Common use case would be to use `Function.prototype.apply`. 2214 | * 2215 | * ```js 2216 | * function f(x, y, z) {} 2217 | * var args = [1, 2, 3]; 2218 | * f.apply(null, args); 2219 | * ``` 2220 | * 2221 | * With `spread` this example can be re-written. 2222 | * 2223 | * ```js 2224 | * spread(function(x, y, z) {})([1, 2, 3]); 2225 | * ``` 2226 | * 2227 | * @param {Function} callback 2228 | * @returns {Function} 2229 | */ 2230 | var spread = function spread(callback) { 2231 | return function wrap(arr) { 2232 | return callback.apply(null, arr); 2233 | }; 2234 | }; 2235 | 2236 | /** 2237 | * Create an instance of Axios 2238 | * 2239 | * @param {Object} defaultConfig The default config for the instance 2240 | * @return {Axios} A new instance of Axios 2241 | */ 2242 | function createInstance(defaultConfig) { 2243 | var context = new Axios_1(defaultConfig); 2244 | var instance = bind(Axios_1.prototype.request, context); 2245 | 2246 | // Copy axios.prototype to instance 2247 | utils.extend(instance, Axios_1.prototype, context); 2248 | 2249 | // Copy context to instance 2250 | utils.extend(instance, context); 2251 | 2252 | return instance; 2253 | } 2254 | 2255 | // Create the default instance to be exported 2256 | var axios$1 = createInstance(defaults_1); 2257 | 2258 | // Expose Axios class to allow class inheritance 2259 | axios$1.Axios = Axios_1; 2260 | 2261 | // Factory for creating new instances 2262 | axios$1.create = function create(instanceConfig) { 2263 | return createInstance(utils.merge(defaults_1, instanceConfig)); 2264 | }; 2265 | 2266 | // Expose Cancel & CancelToken 2267 | axios$1.Cancel = Cancel_1; 2268 | axios$1.CancelToken = CancelToken_1; 2269 | axios$1.isCancel = isCancel; 2270 | 2271 | // Expose all/spread 2272 | axios$1.all = function all(promises) { 2273 | return Promise.all(promises); 2274 | }; 2275 | axios$1.spread = spread; 2276 | 2277 | var axios_1 = axios$1; 2278 | 2279 | // Allow use of default import syntax in TypeScript 2280 | var default_1 = axios$1; 2281 | 2282 | axios_1.default = default_1; 2283 | 2284 | var index = axios_1; 2285 | 2286 | //AXIOS workaround - process.env.NODE_ENV 2287 | if (typeof process === 'undefined' && !window.process) { 2288 | window.process = {env: {}}; 2289 | } 2290 | 2291 | var axiosClient = AxiosClient(); 2292 | 2293 | function AxiosClient (){ 2294 | 2295 | function invoke (restOptions) { 2296 | return index(restOptions); 2297 | } 2298 | 2299 | function serialize (obj) { 2300 | return obj && Object.keys(obj).map(function (key) { 2301 | return encodeURIComponent(key) + '=' + encodeURIComponent(obj[key]); 2302 | }).join('&'); 2303 | } 2304 | 2305 | function filterKeys (srcOptions, keys) { 2306 | return keys.reduce(function (result, k) { 2307 | if (srcOptions[k]) { 2308 | result[k] = srcOptions[k]; 2309 | } 2310 | 2311 | return result; 2312 | }, {}); 2313 | } 2314 | 2315 | function buildRequestOptions (options) { 2316 | 2317 | //Build Url with queryParams 2318 | var paramStr = options.queryParams && serialize(options.queryParams); 2319 | 2320 | if(paramStr) { 2321 | options.url = options.url + '?' + paramStr; 2322 | } 2323 | 2324 | // Don't allow any undefined values into Fetch Options 2325 | options = filterKeys(options, ['method', 'url', 'params', 'body', 'data', 'cache', 'headers']); 2326 | 2327 | options.headers = {}; 2328 | 2329 | options.headers['Accept'] = 'application/json'; 2330 | options.headers['Content-Type'] = 'application/json'; 2331 | 2332 | if (options.body) { 2333 | options.body = JSON.stringify(options.body); 2334 | } 2335 | 2336 | if (options.data) { 2337 | options.data = JSON.stringify(options.data); 2338 | } 2339 | 2340 | return options; 2341 | } 2342 | 2343 | return { 2344 | invoke: invoke, 2345 | buildRequestOptions: buildRequestOptions 2346 | }; 2347 | } 2348 | 2349 | registerProtocolClient(axiosClient); 2350 | 2351 | exports.antChain = antChain; 2352 | exports.antChainXyz = antChainXyz; 2353 | exports.neoScan = neoScan; 2354 | exports.neon = neon; 2355 | exports.pyrest = pyrest; 2356 | exports.node = node; 2357 | exports.rest = rest; 2358 | exports.registry = registry; 2359 | exports.neoNotification = neoNotification; 2360 | exports.service = service; 2361 | 2362 | Object.defineProperty(exports, '__esModule', { value: true }); 2363 | 2364 | }))); 2365 | --------------------------------------------------------------------------------