├── Nodejs 合约 DEMO修改.docx ├── Nodejs-DEMO.docx ├── REST-Node.js-demo ├── README.md ├── config │ └── default.json ├── demo.js ├── framework │ └── httpClient.js ├── main.js ├── package.json └── sdk │ └── hbsdk.js └── WebSocket-Node.js-demo ├── README.md ├── config └── default.json ├── demo.js ├── main.js └── package.json /Nodejs 合约 DEMO修改.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huobiapi/Futures-Node.js-demo/cdac82cc751ddf15faaba302082ad92a519bb2cd/Nodejs 合约 DEMO修改.docx -------------------------------------------------------------------------------- /Nodejs-DEMO.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huobiapi/Futures-Node.js-demo/cdac82cc751ddf15faaba302082ad92a519bb2cd/Nodejs-DEMO.docx -------------------------------------------------------------------------------- /REST-Node.js-demo/README.md: -------------------------------------------------------------------------------- 1 | # README 2 | ## 环境要求 3 | nodejs 6.0以上版本 4 | 5 | ## 使用指南 6 | ``` 7 | npm install 8 | node main.js 9 | ``` 10 | 11 | 出现`Command: `, 随便输入, 然后根据提示操作. 12 | 13 | -------------------------------------------------------------------------------- /REST-Node.js-demo/config/default.json: -------------------------------------------------------------------------------- 1 | { 2 | "huobi": { 3 | "api_key": "----your api key here----", 4 | "secret_key": "----your secret key here----", 5 | "url_prex": "https://api.hbdm.com" 6 | }, 7 | "interfaces": { 8 | "get_methods": [ 9 | { 10 | "tip": "GET_1, 获取合约信息", 11 | "intf_no": "g1", 12 | "context_path": "/api/v1/contract_contract_info" 13 | }, 14 | { 15 | "tip": "GET_2, 获取合约指数信息", 16 | "intf_no": "g2", 17 | "context_path": "/api/v1/contract_index" 18 | }, 19 | { 20 | "tip": "GET_3, 获取合约最高限价和最低限价", 21 | "intf_no": "g3", 22 | "context_path": "/api/v1/contract_price_limit?symbol=BTC&contract_type=this_week" 23 | }, 24 | { 25 | "tip": "GET_4, 获取当前可用合约总持仓量", 26 | "intf_no": "g4", 27 | "context_path": "/api/v1/contract_open_interest" 28 | }, 29 | { 30 | "tip": "GET_5, 获取行情深度数据", 31 | "intf_no": "g5", 32 | "context_path": "/market/depth" 33 | }, 34 | { 35 | "tip": "GET_6, 获取K线数据", 36 | "intf_no": "g6", 37 | "context_path": "/market/history/kline?symbol=BTC_CQ&period=1min&size=200" 38 | }, 39 | { 40 | "tip": "GET_7, 获取聚合行情", 41 | "intf_no": "g7", 42 | "context_path": "/market/detail/merged?symbol=BTC_CQ" 43 | }, 44 | { 45 | "tip": "GET_8, 获取市场最近成交记录", 46 | "intf_no": "g8", 47 | "context_path": "/market/trade?symbol=BTC_CQ" 48 | }, 49 | { 50 | "tip": "GET_9, 批量获取最近的交易记录", 51 | "intf_no": "g9", 52 | "context_path": "/market/history/trade?symbol=BTC_CQ&size=100" 53 | } 54 | ], 55 | "post_methods": [ 56 | { 57 | "tip": "POST_1, 获取用户账户信息", 58 | "intf_no": "p1", 59 | "context_path": "/api/v1/contract_account_info", 60 | "req_body": {"symbol": "BTC"} 61 | }, 62 | { 63 | "tip": "POST_2, 获取用户持仓信息", 64 | "intf_no": "p2", 65 | "context_path": "/api/v1/contract_position_info", 66 | "req_body": {"symbol": "BTC"} 67 | }, 68 | { 69 | "tip": "POST_3, 合约下单", 70 | "intf_no": "p3", 71 | "context_path": "/api/v1/contract_order", 72 | "req_body": {"symbol": "BTC", "contract_type": "next_week", 73 | "client_order_id": "", "contract_code": "BTC181109", 74 | "price": "6306", "volume": "1", "direction": "buy", "offset": "open", 75 | "lever_rate": "5", "order_price_type": "limit"} 76 | }, 77 | { 78 | "tip": "POST_4, 合约批量下单", 79 | "intf_no": "p4", 80 | "context_path": "/api/v1/contract_batchorder", 81 | "req_body": {"orders_data":[ 82 | { 83 | "symbol":"BTC", 84 | "contract_type":"this_week", 85 | "contact_code":"BTC181116", 86 | "client_order_id":"89", 87 | "price":6000, 88 | "volume":10, 89 | "direction":"buy", 90 | "offset":"open", 91 | "lever_rate":10, 92 | "order_price_type":"limit" 93 | },{ 94 | "symbol":"BTC", 95 | "contract_type":"this_week", 96 | "contact_code":"BTC181116", 97 | "client_order_id":"199", 98 | "price":6000, 99 | "volume":1, 100 | "direction":"buy", 101 | "offset":"open", 102 | "lever_rate":10, 103 | "order_price_type":"limit" 104 | } 105 | ]}}, 106 | { 107 | "tip": "POST_5, 撤销订单", 108 | "intf_no": "p5", 109 | "context_path": "/api/v1/contract_cancel", 110 | "req_body": {"symbol": "BTC", "order_id": "123456", "client_orderid":""} 111 | }, 112 | { 113 | "tip": "POST_6, 全部撤单", 114 | "intf_no": "p6", 115 | "context_path": "/api/v1/contract_cancelall", 116 | "req_body": {"symbol": "BTC"} 117 | }, 118 | { 119 | "tip": "POST_7, 获取合约订单信息", 120 | "intf_no": "p7", 121 | "context_path": "/api/v1/contract_order_info", 122 | "req_body": {"symbol": "BTC", "order_id": "4", "client_order_id": ""} 123 | }, 124 | { 125 | "tip": "POST_8, 获取订单明细信息", 126 | "intf_no": "p8", 127 | "context_path": "/api/v1/contract_order_detail", 128 | "req_body": {"symbol": "BTC", "order_id": "12345", "created_at": 1490759594752} 129 | }, 130 | { 131 | "tip": "POST_9, 获取合约当前未成交委托", 132 | "intf_no": "p9", 133 | "context_path": "/api/v1/contract_openorders", 134 | "req_body": {"symbol": "BTC"} 135 | }, 136 | { 137 | "tip": "POST_10, 获取合约历史委托", 138 | "intf_no": "p10", 139 | "context_path": "/api/v1/contract_hisorders", 140 | "req_body": {"symbol": "BTC", "trade_type": 0, "type": 1, "status": 0, "create_date": 7} 141 | } 142 | ] 143 | } 144 | } -------------------------------------------------------------------------------- /REST-Node.js-demo/demo.js: -------------------------------------------------------------------------------- 1 | const hbsdk = require('./sdk/hbsdk'); 2 | var config = require('config'); 3 | var url = require('url'); 4 | 5 | function runget(intf_no) { 6 | var host_prefix = config.huobi.url_prex; 7 | var getIntfs = config.interfaces.get_methods; 8 | var getEle = getIntfs.filter(x=>x.intf_no == intf_no)[0]; 9 | hbsdk.get_intf(getEle.tip, host_prefix + getEle.context_path) 10 | } 11 | function runpost(intf_no) { 12 | var host_prefix = config.huobi.url_prex; 13 | var postIntfs = config.interfaces.post_methods; 14 | var postEle = postIntfs.filter(x=>x.intf_no == intf_no)[0]; 15 | hbsdk.post_intf(postEle) 16 | // postIntfs.forEach(postEle => { 17 | // hbsdk.post_intf(postEle.tip, host_prefix + postEle.context_path) 18 | // }); 19 | } 20 | exports.demoget = runget 21 | exports.demopost = runpost 22 | -------------------------------------------------------------------------------- /REST-Node.js-demo/framework/httpClient.js: -------------------------------------------------------------------------------- 1 | const http = require('http'); 2 | const request = require('request'); 3 | const moment = require('moment'); 4 | const logger = console; 5 | 6 | var default_post_headers = { 7 | 'content-type': 'application/json;charset=utf-8', 8 | } 9 | 10 | var agentOptions = { 11 | keepAlive: true, 12 | maxSockets: 256, 13 | } 14 | 15 | exports.get = function(url, options) { 16 | // console.log(`${moment().format()} HttpGet: ${url}`) 17 | return new Promise((resolve, reject) => { 18 | options = options || {}; 19 | var httpOptions = { 20 | url: url, 21 | method: 'get', 22 | timeout: options.timeout || 3000, 23 | headers: options.headers || default_post_headers, 24 | proxy: options.proxy || '', 25 | agentOptions: agentOptions 26 | } 27 | request.get(httpOptions, function(err, res, body) { 28 | if (err) { 29 | reject(err); 30 | } else { 31 | if (res.statusCode == 200) { 32 | resolve(body); 33 | } else { 34 | reject(res.statusCode); 35 | } 36 | } 37 | }).on('error', logger.error); 38 | }); 39 | } 40 | 41 | exports.post = function(url, postdata, options) { 42 | // console.log(`${moment().format()} HttpPost: ${url}`) 43 | return new Promise((resolve, reject) => { 44 | options = options || {}; 45 | var httpOptions = { 46 | url: url, 47 | body: JSON.stringify(postdata), 48 | method: 'post', 49 | timeout: options.timeout || 3000, 50 | headers: options.headers || default_post_headers, 51 | proxy: options.proxy || '', 52 | agentOptions: agentOptions 53 | }; 54 | request(httpOptions, function(err, res, body) { 55 | if (err) { 56 | reject(err); 57 | } else { 58 | if (res.statusCode == 200) { 59 | resolve(body); 60 | } else { 61 | reject(res.statusCode); 62 | } 63 | } 64 | }).on('error', logger.error); 65 | }); 66 | }; 67 | 68 | exports.form_post = function(url, postdata, options) { 69 | // console.log(`${moment().format()} HttpFormPost: ${url}`) 70 | return new Promise((resolve, reject) => { 71 | options = options || {}; 72 | var httpOptions = { 73 | url: url, 74 | form: postdata, 75 | method: 'post', 76 | timeout: options.timeout || 3000, 77 | headers: options.headers || default_post_headers, 78 | proxy: options.proxy || '', 79 | agentOptions: agentOptions 80 | }; 81 | request(httpOptions, function(err, res, body) { 82 | if (err) { 83 | reject(err); 84 | } else { 85 | if (res.statusCode == 200) { 86 | resolve(body); 87 | } else { 88 | reject(res.statusCode); 89 | } 90 | } 91 | }).on('error', logger.error); 92 | }); 93 | }; -------------------------------------------------------------------------------- /REST-Node.js-demo/main.js: -------------------------------------------------------------------------------- 1 | var readline = require('readline'); 2 | var demoget = require('./demo'); 3 | var config = require('config'); 4 | 5 | var rl = readline.createInterface(process.stdin, process.stdout); 6 | 7 | var recursiveAsyncReadLine = function () { 8 | rl.question('Command: ', function (answer) { 9 | switch (answer){ 10 | case 'exit': 11 | return rl.close(); 12 | case 'g1': 13 | case 'g2': 14 | case 'g3': 15 | case 'g4': 16 | case 'g5': 17 | case 'g6': 18 | case 'g7': 19 | case 'g8': 20 | case 'g9': 21 | demoget.demoget(answer);break; 22 | case 'p1': 23 | case 'p2': 24 | case 'p3': 25 | case 'p4': 26 | case 'p5': 27 | case 'p6': 28 | case 'p7': 29 | case 'p8': 30 | case 'p9': 31 | case 'p10': 32 | demoget.demopost(answer);break; 33 | case 'h': 34 | console.log('Get指令列表如下:'); 35 | var getIntfs = config.interfaces.get_methods; 36 | getIntfs.forEach(e => { 37 | console.log(e.tip, e.intf_no); 38 | }); 39 | console.log('Post指令列表如下:'); 40 | var postIntfs = config.interfaces.post_methods; 41 | postIntfs.forEach(e => { 42 | console.log(e.tip, e.intf_no); 43 | }); 44 | break; 45 | 46 | default: 47 | console.log('请输入指令, 比如g1, g2, p1, p2..., 指令列表请输入h, 退出输入exit');break; 48 | 49 | } 50 | recursiveAsyncReadLine(); //Calling this function again to ask new question 51 | }); 52 | }; 53 | 54 | recursiveAsyncReadLine(); -------------------------------------------------------------------------------- /REST-Node.js-demo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "huobipro-contract-api-test", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "main.js", 6 | "scripts": { 7 | "start": "ENV=dev ./node_modules/.bin/nodemon --harmony main.js", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "bluebird": "^3.3.1", 14 | "config": "^1.28.1", 15 | "crypto-js": "^3.1.9-1", 16 | "moment": "^2.13.0", 17 | "pako": "^1.0.6", 18 | "readline-sync": "^1.4.9", 19 | "request": "^2.72.0", 20 | "ws": "^3.2.0", 21 | "json-bigint": "^0.3.0" 22 | }, 23 | "execMap": { 24 | "js": "node –harmony" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /REST-Node.js-demo/sdk/hbsdk.js: -------------------------------------------------------------------------------- 1 | var config = require('config'); 2 | var CryptoJS = require('crypto-js'); 3 | var Promise = require('bluebird'); 4 | var moment = require('moment'); 5 | var HmacSHA256 = require('crypto-js/hmac-sha256') 6 | var http = require('../framework/httpClient'); 7 | var url = require('url'); 8 | var config = require('config'); 9 | var JSONbig = require('json-bigint'); 10 | 11 | // const URL = 'https://api.huobipro.com'; 12 | 13 | 14 | const DEFAULT_HEADERS = { 15 | "Content-Type": "application/json", 16 | "User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Safari/537.36" 17 | } 18 | 19 | 20 | function sign_sha(method, baseurl, path, data) { 21 | var pars = []; 22 | for (let item in data) { 23 | pars.push(item + "=" + encodeURIComponent(data[item])); 24 | } 25 | var p = pars.sort().join("&"); 26 | var meta = [method, baseurl, path, p].join('\n'); 27 | // console.log(meta); 28 | var hash = HmacSHA256(meta, config.huobi.secret_key); 29 | var Signature = encodeURIComponent(CryptoJS.enc.Base64.stringify(hash)); 30 | // console.log(`Signature: ${Signature}`); 31 | p += `&Signature=${Signature}`; 32 | // console.log(p); 33 | return p; 34 | } 35 | 36 | function get_body() { 37 | return { 38 | AccessKeyId: config.huobi.api_key, 39 | SignatureMethod: "HmacSHA256", 40 | SignatureVersion: 2, 41 | Timestamp: moment.utc().format('YYYY-MM-DDTHH:mm:ss'), 42 | }; 43 | } 44 | 45 | 46 | function call_get(tip, path){ 47 | http.get(path, { 48 | timeout: 1000, 49 | headers: DEFAULT_HEADERS 50 | }).then(data => { 51 | let json = JSONbig.parse(data); 52 | if (json.status == 'ok') { 53 | var outputStr = tip + "......" + path + "\r\n"; 54 | Array.isArray(json.data) ? json.data.forEach(e=>{outputStr += JSONbig.stringify(e)+'\r\n'}): outputStr+=JSONbig.stringify(json); 55 | console.log(outputStr); 56 | } else { 57 | console.log('调用错误', tip, "......", path, "......", json.data); 58 | } 59 | }).catch(ex => { 60 | console.log('GET', path, '异常', ex, tip, '......', path, " 结束\r\n"); 61 | }); 62 | } 63 | 64 | function call_post(tip, path, payload, body){ 65 | var payloadPath = `${path}?${payload}`; 66 | http.post(payloadPath, body, { 67 | timeout: 1000, 68 | headers: DEFAULT_HEADERS 69 | }).then(data => { 70 | let json = JSONbig.parse(data); 71 | if (json.status == 'ok') { 72 | var outputStr = tip + "......" + path + "\r\n"; 73 | Array.isArray(json.data) ? json.data.forEach(e=>{outputStr += JSONbig.stringify(e)+'\r\n'}): outputStr+=JSONbig.stringify(json); 74 | console.log(outputStr); 75 | } else { 76 | console.log(tip + '调用status'+ json.status, json, "\r\n", tip, '......', path, " 结束\r\n"); 77 | } 78 | }).catch(ex => { 79 | console.log(tip, '.........', path, "POST", '异常', ex); 80 | }); 81 | } 82 | 83 | var HUOBI_PRO = { 84 | 85 | get_intf: function(tip, path) { 86 | return call_get(tip, path); 87 | }, 88 | post_intf: function(postEle) { 89 | tip = postEle.tip; 90 | host_prefix = config.huobi.url_prex; 91 | path = host_prefix + postEle.context_path; 92 | var host = url.parse(path).host; 93 | var cpath = url.parse(path).path; 94 | var body = Object.assign(get_body(), postEle.req_body); 95 | var payload = sign_sha('POST', host, cpath, body); 96 | return call_post(tip, path, payload, body); 97 | }, 98 | 99 | 100 | } 101 | 102 | module.exports = HUOBI_PRO; 103 | -------------------------------------------------------------------------------- /WebSocket-Node.js-demo/README.md: -------------------------------------------------------------------------------- 1 | # README 2 | 3 | ## 环境要求 4 | 5 | nodejs 6.0以上版本 6 | 7 | ## Demo说明 8 | 9 | main.js 10 | 演示使用websocket获取深度和K线数据 11 | 12 | ## 使用指南 13 | 14 | ``` 15 | npm install 16 | node main.js 17 | ``` 18 | 19 | 输入`h`获得帮助, 输入`s1`, `s2`...测试socket, `r1`, `r2`...测试请求 20 | 21 | -------------------------------------------------------------------------------- /WebSocket-Node.js-demo/config/default.json: -------------------------------------------------------------------------------- 1 | { 2 | "huobi": { 3 | "ws_url_prex": "wss://www.hbdm.com/ws" 4 | }, 5 | "ws_interfaces": { 6 | "ws_sub": [ 7 | { 8 | "tip": "SUB_1, 订阅 KLine 数据", 9 | "intf_no": "s1", 10 | "context_path": "market.$symbol.kline.$period", 11 | "param_symbol": ["BTC_CW", "BTC_NW", "BTC_CQ"], 12 | "param_period": ["1min", "5min", "15min", "30min", "60min", "1day", "1mon", "1week", "1year"], 13 | "sample": { 14 | "sub": "market.BTC_CQ.kline.1min", 15 | "id": "id1" 16 | } 17 | }, 18 | { 19 | "tip": "SUB_2, 订阅 Market Detail 数据", 20 | "intf_no": "s2", 21 | "context_path": "market.$symbol.detail", 22 | "param_symbol": ["BTC_CW", "BTC_NW", "BTC_CQ"], 23 | "sample": { 24 | "sub": "market.BTC_CQ.detail", 25 | "id": "id6" 26 | } 27 | }, 28 | { 29 | "tip": "SUB_3, 订阅 Trade Detail 数据", 30 | "intf_no": "s3", 31 | "context_path": "market.$symbol.trade.detail", 32 | "param_symbol": ["BTC_CW", "BTC_NW", "BTC_CQ"], 33 | "sample": { 34 | "sub": "market.BTC_CQ.trade.detail", 35 | "id": "id7" 36 | } 37 | } 38 | ], 39 | "ws_req": [ 40 | { 41 | "tip": "REQ_1, 请求 KLine 数据", 42 | "intf_no": "r1", 43 | "context_path": "market.$symbol.kline.$period", 44 | "param_symbol": ["BTC_CW", "BTC_NW", "BTC_CQ"], 45 | "param_period": ["1min", "5min", "15min", "30min", "60min", "1day", "1mon", "1week", "1year"], 46 | "sample": { 47 | "req": "market.BTC_CQ.kline.1min", 48 | "id": "id4" 49 | } 50 | }, 51 | { 52 | "tip": "REQ_2, 请求 Trade Detail 数据", 53 | "intf_no": "r2", 54 | "context_path": "market.$symbol.trade.detail", 55 | "param_symbol": ["BTC_CW", "BTC_NW", "BTC_CQ"], 56 | "sample": { 57 | "req": "market.BTC_CQ.trade.detail", 58 | "id": "id8" 59 | } 60 | } 61 | ] 62 | } 63 | } -------------------------------------------------------------------------------- /WebSocket-Node.js-demo/demo.js: -------------------------------------------------------------------------------- 1 | const config = require('config'); 2 | 3 | 4 | function ws_subscribe(ws, intf_no) { 5 | var targetIntf = config.ws_interfaces.ws_sub.filter(x=>x.intf_no == intf_no)[0]; 6 | targetIntf && ws.send(JSON.stringify(targetIntf.sample)); 7 | } 8 | function ws_request(ws, intf_no) { 9 | var targetIntf = config.ws_interfaces.ws_req.filter(x=>x.intf_no == intf_no)[0]; 10 | targetIntf && ws.send(JSON.stringify(targetIntf.sample)); 11 | } 12 | 13 | exports.run_sub = ws_subscribe; 14 | exports.run_req = ws_request; 15 | -------------------------------------------------------------------------------- /WebSocket-Node.js-demo/main.js: -------------------------------------------------------------------------------- 1 | const readline = require('readline'); 2 | const demo = require('./demo'); 3 | const config = require('config'); 4 | const pako = require('pako'); 5 | const WebSocket = require('ws'); 6 | 7 | var rl = readline.createInterface(process.stdin, process.stdout); 8 | 9 | var recursiveAsyncReadLine = function () { 10 | var ws = new WebSocket(config.huobi.ws_url_prex); 11 | ws.on('open', () => { 12 | console.log('socket open succeed. input your command, or "h" to get help'); 13 | }); 14 | ws.on('close', () => { 15 | console.log('socket close succeed.'); 16 | }); 17 | ws.on('message', (data) => { 18 | let text = pako.inflate(data, { 19 | to: 'string' 20 | }); 21 | let msg = JSON.parse(text); 22 | if (msg.ping) { 23 | ws.send(JSON.stringify({ 24 | pong: msg.ping 25 | })); 26 | } else if (msg.tick) { 27 | console.log(msg); 28 | // handle(msg); 29 | } else { 30 | console.log(text); 31 | } 32 | }); 33 | rl.question('Command: ', function (answer) { 34 | switch (answer){ 35 | case 'exit': 36 | ws.close(); 37 | return rl.close(); 38 | case 's1': 39 | case 's2': 40 | case 's3': 41 | demo.run_sub(ws, answer);break; 42 | case 'r1': 43 | case 'r2': 44 | demo.run_req(ws, answer);break; 45 | case 'h': 46 | console.log('Req请求指令列表如下:'); 47 | var getIntfs = config.ws_interfaces.ws_req; 48 | getIntfs.forEach(e => { 49 | console.log(e.tip, e.intf_no); 50 | }); 51 | console.log('Ws注册指令列表如下:'); 52 | var postIntfs = config.ws_interfaces.ws_sub; 53 | postIntfs.forEach(e => { 54 | console.log(e.tip, e.intf_no); 55 | }); 56 | break; 57 | 58 | default: 59 | console.log('请输入指令, 比如s1, s2, s1, r1, r2..., 指令列表请输入h, 退出输入exit');break; 60 | } 61 | (ws.readyState === WebSocket.OPEN) && ws.close(); 62 | recursiveAsyncReadLine(); //Calling this function again to ask new question 63 | }); 64 | }; 65 | 66 | recursiveAsyncReadLine(); -------------------------------------------------------------------------------- /WebSocket-Node.js-demo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "huobipro", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "main.js", 6 | "scripts": { 7 | "start": "ENV=dev ./node_modules/.bin/nodemon --harmony main.js", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "author": "pjsong", 11 | "license": "ISC", 12 | "dependencies": { 13 | "bluebird": "^3.3.1", 14 | "config": "^1.31.0", 15 | "crypto-js": "^3.1.9-1", 16 | "moment": "^2.13.0", 17 | "pako": "^1.0.6", 18 | "readline-sync": "^1.4.9", 19 | "request": "^2.72.0", 20 | "ws": "^3.2.0" 21 | }, 22 | "execMap": { 23 | "js": "node –harmony" 24 | } 25 | } 26 | --------------------------------------------------------------------------------