├── .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 |
5 |
--------------------------------------------------------------------------------
/src/assets/icons/flag-de.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/icons/flag-ie.svg:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/src/assets/icons/flag-ru.svg:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/src/assets/icons/flag-fr.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/icons/flag-ch.svg:
--------------------------------------------------------------------------------
1 |
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 |
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 |
--------------------------------------------------------------------------------
/src/assets/icons/flag-cn.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/icons/flag-gb.svg:
--------------------------------------------------------------------------------
1 |
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 |
--------------------------------------------------------------------------------
/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 |
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 |
--------------------------------------------------------------------------------
/src/assets/icons/flag-sg.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
35 |
--------------------------------------------------------------------------------
/src/assets/icons/flag-us.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/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 |
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 |
86 |
87 |
88 |  |
89 | {{ endPoint.name }} |
90 | {{ endPoint.type }} |
91 | {{ endPoint.latency }} |
92 | {{ (endPoint.version && endPoint.version.useragent) || '?' }} |
93 |
94 | yes
95 |
96 | unreachable
97 | - Last connected {{ endPoint.lastTimeConnected }}
98 |
99 | |
100 |
101 | #{{ endPoint.lastBlock.toLocaleString() }}
102 | ({{(endPoint.lastBlock - vm.netStats.bestBlock).toLocaleString() }})
103 | |
104 | {{ endPoint.peerCount }} |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
--------------------------------------------------------------------------------
/src/assets/icons/flag-br.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------