binbot-charts 0.0.21 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/TVChartContainer.js +41 -77
- package/dist/components/datafeed.js +131 -100
- package/dist/components/streaming.js +1 -1
- package/package.json +5 -4
- package/dist/datafeeds/README.md +0 -3
- package/dist/datafeeds/udf/README.md +0 -46
- package/dist/datafeeds/udf/dist/bundle.js +0 -1
- package/dist/datafeeds/udf/lib/data-pulse-provider.js +0 -104
- package/dist/datafeeds/udf/lib/helpers.js +0 -20
- package/dist/datafeeds/udf/lib/history-provider.js +0 -73
- package/dist/datafeeds/udf/lib/iquotes-provider.js +0 -1
- package/dist/datafeeds/udf/lib/quotes-provider.js +0 -25
- package/dist/datafeeds/udf/lib/quotes-pulse-provider.js +0 -44
- package/dist/datafeeds/udf/lib/requester.js +0 -28
- package/dist/datafeeds/udf/lib/symbols-storage.js +0 -180
- package/dist/datafeeds/udf/lib/udf-compatible-datafeed-base.js +0 -252
- package/dist/datafeeds/udf/lib/udf-compatible-datafeed.js +0 -10
- package/dist/datafeeds/udf/package.json +0 -17
- package/dist/datafeeds/udf/rollup.config.js +0 -25
- package/dist/datafeeds/udf/src/data-pulse-provider.ts +0 -152
- package/dist/datafeeds/udf/src/helpers.ts +0 -38
- package/dist/datafeeds/udf/src/history-provider.ts +0 -134
- package/dist/datafeeds/udf/src/iquotes-provider.ts +0 -14
- package/dist/datafeeds/udf/src/quotes-provider.ts +0 -37
- package/dist/datafeeds/udf/src/quotes-pulse-provider.ts +0 -85
- package/dist/datafeeds/udf/src/requester.ts +0 -39
- package/dist/datafeeds/udf/src/symbols-storage.ts +0 -298
- package/dist/datafeeds/udf/src/udf-compatible-datafeed-base.ts +0 -369
- package/dist/datafeeds/udf/src/udf-compatible-datafeed.ts +0 -11
- package/dist/datafeeds/udf/tsconfig.json +0 -25
|
@@ -5,95 +5,59 @@ require("core-js/modules/web.dom-collections.iterator.js");
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", {
|
|
6
6
|
value: true
|
|
7
7
|
});
|
|
8
|
-
exports.default =
|
|
8
|
+
exports.default = TVChartContainer;
|
|
9
9
|
|
|
10
|
-
var
|
|
10
|
+
var _react = _interopRequireWildcard(require("react"));
|
|
11
11
|
|
|
12
12
|
var _charting_library = require("../charting_library");
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
|
|
17
|
-
|
|
18
|
-
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
|
14
|
+
var _datafeed = _interopRequireDefault(require("./datafeed"));
|
|
19
15
|
|
|
20
|
-
|
|
21
|
-
constructor(props) {
|
|
22
|
-
super(props);
|
|
16
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
23
17
|
|
|
24
|
-
|
|
18
|
+
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
|
|
25
19
|
|
|
26
|
-
|
|
27
|
-
}
|
|
20
|
+
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
|
|
28
21
|
|
|
29
|
-
|
|
22
|
+
function TVChartContainer(_ref) {
|
|
23
|
+
let {
|
|
24
|
+
symbol = "AAPL",
|
|
25
|
+
interval = "D",
|
|
26
|
+
libraryPath = "/charting_library/",
|
|
27
|
+
timescaleMarks = [],
|
|
28
|
+
orderLines = []
|
|
29
|
+
} = _ref;
|
|
30
|
+
const containerRef = (0, _react.useRef)(null);
|
|
31
|
+
(0, _react.useEffect)(() => {
|
|
30
32
|
const widgetOptions = {
|
|
31
|
-
symbol:
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
charts_storage_url: this.props.chartsStorageUrl,
|
|
41
|
-
charts_storage_api_version: this.props.chartsStorageApiVersion,
|
|
42
|
-
client_id: this.props.clientId,
|
|
43
|
-
user_id: this.props.userId,
|
|
44
|
-
fullscreen: this.props.fullscreen,
|
|
45
|
-
autosize: this.props.autosize,
|
|
46
|
-
studies_overrides: this.props.studiesOverrides
|
|
33
|
+
symbol: symbol,
|
|
34
|
+
datafeed: new _datafeed.default(timescaleMarks),
|
|
35
|
+
interval: interval,
|
|
36
|
+
container: containerRef.current,
|
|
37
|
+
library_path: libraryPath,
|
|
38
|
+
locale: "en",
|
|
39
|
+
fullscreen: false,
|
|
40
|
+
autosize: true,
|
|
41
|
+
studies_overrides: {}
|
|
47
42
|
};
|
|
48
43
|
const tvWidget = new _charting_library.widget(widgetOptions);
|
|
49
|
-
this.tvWidget = tvWidget;
|
|
50
44
|
tvWidget.onChartReady(() => {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
button.setAttribute('title', 'Click to show a notification popup');
|
|
54
|
-
button.classList.add('apply-common-tooltip');
|
|
55
|
-
button.addEventListener('click', () => tvWidget.showNoticeDialog({
|
|
56
|
-
title: 'Notification',
|
|
57
|
-
body: 'TradingView Charting Library API works correctly',
|
|
58
|
-
callback: () => {
|
|
59
|
-
console.log('Noticed!');
|
|
60
|
-
}
|
|
61
|
-
}));
|
|
62
|
-
button.innerHTML = 'Check API';
|
|
45
|
+
orderLines.forEach(element => {
|
|
46
|
+
tvWidget.chart().createPositionLine().setText(element.text).setTooltip(element.tooltip).setQuantity(element.quantity).setQuantityBackgroundColor(element.color).setQuantityBorderColor(element.color).setLineStyle(0).setLineLength(25).setLineColor(element.color).setBodyBorderColor(element.color).setBodyTextColor(element.color).setPrice(element.price);
|
|
63
47
|
});
|
|
64
|
-
});
|
|
65
|
-
}
|
|
48
|
+
}); // returned function will be called on component unmount
|
|
66
49
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
render() {
|
|
75
|
-
return /*#__PURE__*/React.createElement("div", {
|
|
76
|
-
ref: this.ref,
|
|
77
|
-
style: {
|
|
78
|
-
height: "calc(100vh - 80px)"
|
|
50
|
+
return () => {
|
|
51
|
+
if (this.tvWidget !== null) {
|
|
52
|
+
this.tvWidget.remove();
|
|
53
|
+
this.tvWidget = null;
|
|
79
54
|
}
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
interval: 'D',
|
|
90
|
-
datafeedUrl: 'https://demo_feed.tradingview.com',
|
|
91
|
-
libraryPath: '/charting_library/',
|
|
92
|
-
chartsStorageUrl: 'https://saveload.tradingview.com',
|
|
93
|
-
chartsStorageApiVersion: '1.1',
|
|
94
|
-
clientId: 'tradingview.com',
|
|
95
|
-
userId: 'public_user_id',
|
|
96
|
-
fullscreen: false,
|
|
97
|
-
autosize: true,
|
|
98
|
-
studiesOverrides: {}
|
|
99
|
-
});
|
|
55
|
+
};
|
|
56
|
+
}, []);
|
|
57
|
+
return /*#__PURE__*/_react.default.createElement("div", {
|
|
58
|
+
ref: containerRef,
|
|
59
|
+
style: {
|
|
60
|
+
height: "calc(100vh)"
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
}
|
|
@@ -24,6 +24,10 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
|
|
|
24
24
|
const lastBarsCache = new Map();
|
|
25
25
|
const exchange = "Binance";
|
|
26
26
|
const configurationData = {
|
|
27
|
+
// trading_options: true,
|
|
28
|
+
supports_marks: true,
|
|
29
|
+
supports_time: true,
|
|
30
|
+
supports_timescale_marks: true,
|
|
27
31
|
supported_resolutions: ["1D", "1W", "1M"],
|
|
28
32
|
exchanges: [{
|
|
29
33
|
value: "Binance",
|
|
@@ -58,115 +62,142 @@ async function getAllSymbols() {
|
|
|
58
62
|
return allSymbols;
|
|
59
63
|
}
|
|
60
64
|
|
|
61
|
-
class Datafeed {
|
|
65
|
+
class Datafeed {
|
|
66
|
+
constructor() {
|
|
67
|
+
let timescaleMarks = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
|
|
68
|
+
let lineOrders = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
|
|
62
69
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
console.log("[onReady]: Method call");
|
|
68
|
-
setTimeout(() => callback(configurationData));
|
|
69
|
-
});
|
|
70
|
+
_defineProperty(this, "onReady", callback => {
|
|
71
|
+
console.log("[onReady]: Method call");
|
|
72
|
+
setTimeout(() => callback(configurationData));
|
|
73
|
+
});
|
|
70
74
|
|
|
71
|
-
_defineProperty(
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
});
|
|
75
|
+
_defineProperty(this, "searchSymbols", async (userInput, exchange, symbolType, onResultReadyCallback) => {
|
|
76
|
+
console.log("[searchSymbols]: Method call");
|
|
77
|
+
const symbols = await getAllSymbols();
|
|
78
|
+
const newSymbols = symbols.filter(symbol => {
|
|
79
|
+
const isExchangeValid = exchange === "" || symbol.exchange === exchange;
|
|
80
|
+
const isFullSymbolContainsInput = symbol.full_name.toLowerCase().indexOf(userInput.toLowerCase()) !== -1;
|
|
81
|
+
return isExchangeValid && isFullSymbolContainsInput;
|
|
82
|
+
});
|
|
83
|
+
onResultReadyCallback(newSymbols);
|
|
84
|
+
});
|
|
81
85
|
|
|
82
|
-
_defineProperty(
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
if (!symbolItem) {
|
|
93
|
-
console.log("[resolveSymbol]: Cannot resolve symbol", symbolName);
|
|
94
|
-
onResolveErrorCallback("cannot resolve symbol");
|
|
95
|
-
return;
|
|
96
|
-
}
|
|
86
|
+
_defineProperty(this, "resolveSymbol", async (symbolName, onSymbolResolvedCallback, onResolveErrorCallback) => {
|
|
87
|
+
console.log("[resolveSymbol]: Method call", symbolName);
|
|
88
|
+
const symbols = await getAllSymbols();
|
|
89
|
+
const symbolItem = symbols.find(_ref => {
|
|
90
|
+
let {
|
|
91
|
+
full_name
|
|
92
|
+
} = _ref;
|
|
93
|
+
return full_name === symbolName;
|
|
94
|
+
});
|
|
97
95
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
session: "24x7",
|
|
104
|
-
timezone: "Etc/UTC",
|
|
105
|
-
exchange: symbolItem.exchange,
|
|
106
|
-
minmov: 1,
|
|
107
|
-
pricescale: 100,
|
|
108
|
-
has_intraday: false,
|
|
109
|
-
has_no_volume: true,
|
|
110
|
-
has_weekly_and_monthly: false,
|
|
111
|
-
supported_resolutions: ["1D", "1W", "1M"],
|
|
112
|
-
volume_precision: 2,
|
|
113
|
-
data_status: "streaming"
|
|
114
|
-
};
|
|
115
|
-
console.log("[resolveSymbol]: Symbol resolved", symbolName);
|
|
116
|
-
onSymbolResolvedCallback(symbolInfo);
|
|
117
|
-
});
|
|
96
|
+
if (!symbolItem) {
|
|
97
|
+
console.log("[resolveSymbol]: Cannot resolve symbol", symbolName);
|
|
98
|
+
onResolveErrorCallback("cannot resolve symbol");
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
118
101
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
if (data.Response && data.Response === "Error" || data.Data.length === 0) {
|
|
140
|
-
// "noData" should be set if there is no data in the requested period.
|
|
141
|
-
onHistoryCallback([], {
|
|
142
|
-
noData: true
|
|
143
|
-
});
|
|
144
|
-
return;
|
|
145
|
-
}
|
|
102
|
+
const symbolInfo = {
|
|
103
|
+
ticker: symbolItem.full_name,
|
|
104
|
+
name: symbolItem.symbol,
|
|
105
|
+
description: symbolItem.description,
|
|
106
|
+
type: symbolItem.type,
|
|
107
|
+
session: "24x7",
|
|
108
|
+
timezone: "Etc/UTC",
|
|
109
|
+
exchange: symbolItem.exchange,
|
|
110
|
+
minmov: 1,
|
|
111
|
+
pricescale: 100,
|
|
112
|
+
has_intraday: true,
|
|
113
|
+
has_no_volume: false,
|
|
114
|
+
has_weekly_and_monthly: false,
|
|
115
|
+
supported_resolutions: ["1D", "1W", "1M"],
|
|
116
|
+
volume_precision: 4,
|
|
117
|
+
data_status: "streaming"
|
|
118
|
+
};
|
|
119
|
+
console.log("[resolveSymbol]: Symbol resolved", symbolName);
|
|
120
|
+
onSymbolResolvedCallback(symbolInfo);
|
|
121
|
+
});
|
|
146
122
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
123
|
+
_defineProperty(this, "getBars", async (symbolInfo, resolution, periodParams, onHistoryCallback, onErrorCallback) => {
|
|
124
|
+
const {
|
|
125
|
+
from,
|
|
126
|
+
to,
|
|
127
|
+
firstDataRequest
|
|
128
|
+
} = periodParams;
|
|
129
|
+
console.log("[getBars]: Method call");
|
|
130
|
+
const parsedSymbol = (0, _helpers.parseFullSymbol)(symbolInfo.full_name);
|
|
131
|
+
const urlParameters = {
|
|
132
|
+
e: parsedSymbol.exchange,
|
|
133
|
+
fsym: parsedSymbol.fromSymbol,
|
|
134
|
+
tsym: parsedSymbol.toSymbol,
|
|
135
|
+
toTs: to,
|
|
136
|
+
limit: 2000
|
|
137
|
+
};
|
|
138
|
+
const query = Object.keys(urlParameters).map(name => "".concat(name, "=").concat(encodeURIComponent(urlParameters[name]))).join("&");
|
|
139
|
+
|
|
140
|
+
try {
|
|
141
|
+
const data = await (0, _helpers.makeApiRequest)("data/histoday?".concat(query));
|
|
142
|
+
|
|
143
|
+
if (data.Response && data.Response === "Error" || data.Data.length === 0) {
|
|
144
|
+
// "noData" should be set if there is no data in the requested period.
|
|
145
|
+
onHistoryCallback([], {
|
|
146
|
+
noData: true
|
|
147
|
+
});
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
let bars = [];
|
|
152
|
+
data.Data.forEach(bar => {
|
|
153
|
+
if (bar.time >= from && bar.time < to) {
|
|
154
|
+
bars = [...bars, {
|
|
155
|
+
time: bar.time * 1000,
|
|
156
|
+
low: bar.low,
|
|
157
|
+
high: bar.high,
|
|
158
|
+
open: bar.open,
|
|
159
|
+
close: bar.close
|
|
160
|
+
}];
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
if (firstDataRequest) {
|
|
165
|
+
lastBarsCache.set(symbolInfo.full_name, _objectSpread({}, bars[bars.length - 1]));
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
console.log("[getBars]: returned ".concat(bars.length, " bar(s)"));
|
|
169
|
+
onHistoryCallback(bars, {
|
|
170
|
+
noData: false
|
|
171
|
+
});
|
|
172
|
+
} catch (error) {
|
|
173
|
+
console.log("[getBars]: Get error", error);
|
|
174
|
+
onErrorCallback(error);
|
|
157
175
|
}
|
|
158
176
|
});
|
|
159
177
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
178
|
+
_defineProperty(this, "subscribeBars", (symbolInfo, resolution, onRealtimeCallback, subscribeUID, onResetCacheNeededCallback) => {
|
|
179
|
+
console.log("[subscribeBars]: Method call with subscribeUID:", subscribeUID);
|
|
180
|
+
(0, _streaming.subscribeOnStream)(symbolInfo, resolution, onRealtimeCallback, subscribeUID, onResetCacheNeededCallback, lastBarsCache.get(symbolInfo.full_name));
|
|
181
|
+
});
|
|
163
182
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
183
|
+
_defineProperty(this, "unsubscribeBars", subscriberUID => {
|
|
184
|
+
console.log("[unsubscribeBars]: Method call with subscriberUID:", subscriberUID);
|
|
185
|
+
(0, _streaming.unsubscribeFromStream)(subscriberUID);
|
|
167
186
|
});
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
187
|
+
|
|
188
|
+
this.timescaleMarks = timescaleMarks;
|
|
189
|
+
this.lineOrders = lineOrders;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
getTimescaleMarks(symbolInfo, from, to, onDataCallback, resolution) {
|
|
193
|
+
let timescaleMarks = [];
|
|
194
|
+
|
|
195
|
+
if (this.timescaleMarks.length > 0) {
|
|
196
|
+
timescaleMarks = this.timescaleMarks;
|
|
197
|
+
onDataCallback(timescaleMarks);
|
|
198
|
+
}
|
|
171
199
|
}
|
|
172
|
-
|
|
200
|
+
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
exports.default = Datafeed;
|
|
@@ -41,7 +41,7 @@ socket.on("error", error => {
|
|
|
41
41
|
});
|
|
42
42
|
socket.on("m", data => {
|
|
43
43
|
console.log("[socket] Message:", data);
|
|
44
|
-
const [eventTypeStr, exchange, fromSymbol, toSymbol
|
|
44
|
+
const [eventTypeStr, exchange, fromSymbol, toSymbol, tradeTimeStr, tradePriceStr] = data.split("~");
|
|
45
45
|
|
|
46
46
|
if (parseInt(eventTypeStr) !== 0) {
|
|
47
47
|
// skip all non-TRADE events
|
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "0.
|
|
2
|
+
"version": "0.1.1",
|
|
3
3
|
"name": "binbot-charts",
|
|
4
4
|
"dependencies": {
|
|
5
5
|
"@babel/cli": "^7.18.10",
|
|
@@ -9,12 +9,13 @@
|
|
|
9
9
|
"react": "^17.0.1",
|
|
10
10
|
"react-dom": "^17.0.1",
|
|
11
11
|
"react-scripts": "5.0.1",
|
|
12
|
-
"socket.io-client": "^4.5.2"
|
|
12
|
+
"socket.io-client": "^4.5.2",
|
|
13
|
+
"use-immer": "^0.7.0"
|
|
13
14
|
},
|
|
14
15
|
"scripts": {
|
|
15
16
|
"start": "react-scripts start",
|
|
16
|
-
"build": "rm -rf dist && NODE_ENV=production babel src/components/ -d dist/components && cp -r src/charting_library dist/charting_library
|
|
17
|
-
"
|
|
17
|
+
"build": "rm -rf dist && NODE_ENV=production babel src/components/ -d dist/components && cp -r src/charting_library dist/charting_library",
|
|
18
|
+
"release": "yarn build && npm publish"
|
|
18
19
|
},
|
|
19
20
|
"description": "Binbot charts is the default candlestick bars chart used in terminal.binbot.com to render bots graphically.",
|
|
20
21
|
"repository": {
|
package/dist/datafeeds/README.md
DELETED
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
# UDF Compatible Datafeed
|
|
2
|
-
|
|
3
|
-
This folder contains [UDF](https://github.com/tradingview/charting_library/wiki/UDF) datafeed adapter. It implements [JS API](https://github.com/tradingview/charting_library/wiki/JS%20API) and makes HTTP requests using [UDF](https://github.com/tradingview/charting_library/wiki/UDF) protocol.
|
|
4
|
-
|
|
5
|
-
You can use this datafeed adapter to plug your data if you implement [UDF](https://github.com/tradingview/charting_library/wiki/UDF) on your server. You can also scrutinize how it works before writing your own adapter.
|
|
6
|
-
|
|
7
|
-
This datafeed is implemented in [TypeScript](https://github.com/Microsoft/TypeScript/).
|
|
8
|
-
|
|
9
|
-
## Folders content
|
|
10
|
-
|
|
11
|
-
- `./src` folder contains the source code in TypeScript.
|
|
12
|
-
|
|
13
|
-
- `./lib` folder contains transpiled in es5 code. So, if you do not know how to use TypeScript - you can modify these files to change the result bundle later.
|
|
14
|
-
|
|
15
|
-
- `./dist` folder contains bundled JavaScript files which can be inlined into a page and used in the Widget Constructor.
|
|
16
|
-
|
|
17
|
-
## Build & bundle
|
|
18
|
-
|
|
19
|
-
Before building or bundling your code you need to run `npm install` to install dependencies.
|
|
20
|
-
|
|
21
|
-
`package.json` contains some handy scripts to build or generate the bundle:
|
|
22
|
-
|
|
23
|
-
- `npm run compile` to compile TypeScript source code into JavaScript files (output will be in `./lib` folder)
|
|
24
|
-
- `npm run bundle-js` to bundle multiple JavaScript files into one bundle (it also bundle polyfills)
|
|
25
|
-
- `npm run build` to compile and bundle (it is a combination of all above commands)
|
|
26
|
-
|
|
27
|
-
NOTE: if you want to minify the bundle code, you need to set `ENV` environment variable to a value different from `development`.
|
|
28
|
-
|
|
29
|
-
For example:
|
|
30
|
-
|
|
31
|
-
```bash
|
|
32
|
-
export ENV=prod
|
|
33
|
-
npm run bundle-js # or npm run build
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
or
|
|
37
|
-
|
|
38
|
-
```bash
|
|
39
|
-
ENV=prod npm run bundle-js
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
or
|
|
43
|
-
|
|
44
|
-
```bash
|
|
45
|
-
ENV=prod npm run build
|
|
46
|
-
```
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
!function(e,s){"object"==typeof exports&&"undefined"!=typeof module?s(exports):"function"==typeof define&&define.amd?define(["exports"],s):s((e="undefined"!=typeof globalThis?globalThis:e||self).Datafeeds={})}(this,(function(e){"use strict";function s(e){return void 0===e?"":"string"==typeof e?e:e.message}class t{constructor(e,s){this._datafeedUrl=e,this._requester=s}getBars(e,t,r){const o={symbol:e.ticker||"",resolution:t,from:r.from,to:r.to};return void 0!==r.countBack&&(o.countback=r.countBack),void 0!==e.currency_code&&(o.currencyCode=e.currency_code),void 0!==e.unit_id&&(o.unitId=e.unit_id),new Promise(((e,t)=>{this._requester.sendRequest(this._datafeedUrl,"history",o).then((s=>{if("ok"!==s.s&&"no_data"!==s.s)return void t(s.errmsg);const r=[],o={noData:!1};if("no_data"===s.s)o.noData=!0,o.nextTime=s.nextTime;else{const e=void 0!==s.v,t=void 0!==s.o;for(let o=0;o<s.t.length;++o){const i={time:1e3*s.t[o],close:parseFloat(s.c[o]),open:parseFloat(s.c[o]),high:parseFloat(s.c[o]),low:parseFloat(s.c[o])};t&&(i.open=parseFloat(s.o[o]),i.high=parseFloat(s.h[o]),i.low=parseFloat(s.l[o])),e&&(i.volume=parseFloat(s.v[o])),r.push(i)}}e({bars:r,meta:o})})).catch((e=>{const r=s(e);console.warn(`HistoryProvider: getBars() failed, error=${r}`),t(r)}))}))}}class r{constructor(e,s){this._subscribers={},this._requestsPending=0,this._historyProvider=e,setInterval(this._updateData.bind(this),s)}subscribeBars(e,s,t,r){this._subscribers.hasOwnProperty(r)||(this._subscribers[r]={lastBarTime:null,listener:t,resolution:s,symbolInfo:e},e.name)}unsubscribeBars(e){delete this._subscribers[e]}_updateData(){if(!(this._requestsPending>0)){this._requestsPending=0;for(const e in this._subscribers)this._requestsPending+=1,this._updateDataForSubscriber(e).then((()=>{this._requestsPending-=1,this._requestsPending})).catch((e=>{this._requestsPending-=1,s(e),this._requestsPending}))}}_updateDataForSubscriber(e){const s=this._subscribers[e],t=parseInt((Date.now()/1e3).toString()),r=t-function(e,s){let t=0;t="D"===e||"1D"===e?s:"M"===e||"1M"===e?31*s:"W"===e||"1W"===e?7*s:s*parseInt(e)/1440;return 24*t*60*60}(s.resolution,10);return this._historyProvider.getBars(s.symbolInfo,s.resolution,{from:r,to:t,countBack:2,firstDataRequest:!1}).then((s=>{this._onSubscriberDataReceived(e,s)}))}_onSubscriberDataReceived(e,s){if(!this._subscribers.hasOwnProperty(e))return;const t=s.bars;if(0===t.length)return;const r=t[t.length-1],o=this._subscribers[e];if(null!==o.lastBarTime&&r.time<o.lastBarTime)return;if(null!==o.lastBarTime&&r.time>o.lastBarTime){if(t.length<2)throw new Error("Not enough bars in history for proper pulse update. Need at least 2.");const e=t[t.length-2];o.listener(e)}o.lastBarTime=r.time,o.listener(r)}}class o{constructor(e){this._subscribers={},this._requestsPending=0,this._quotesProvider=e,setInterval(this._updateQuotes.bind(this,1),1e4),setInterval(this._updateQuotes.bind(this,0),6e4)}subscribeQuotes(e,s,t,r){this._subscribers[r]={symbols:e,fastSymbols:s,listener:t}}unsubscribeQuotes(e){delete this._subscribers[e]}_updateQuotes(e){if(!(this._requestsPending>0))for(const t in this._subscribers){this._requestsPending++;const r=this._subscribers[t];this._quotesProvider.getQuotes(1===e?r.fastSymbols:r.symbols).then((e=>{this._requestsPending--,this._subscribers.hasOwnProperty(t)&&(r.listener(e),this._requestsPending)})).catch((e=>{this._requestsPending--,s(e),this._requestsPending}))}}}function i(e,s,t,r){const o=e[s];return!Array.isArray(o)||r&&!Array.isArray(o[0])?o:o[t]}function n(e,s,t){return e+(void 0!==s?"_%|#|%_"+s:"")+(void 0!==t?"_%|#|%_"+t:"")}class a{constructor(e,s,t){this._exchangesList=["NYSE","FOREX","AMEX"],this._symbolsInfo={},this._symbolsList=[],this._datafeedUrl=e,this._datafeedSupportedResolutions=s,this._requester=t,this._readyPromise=this._init(),this._readyPromise.catch((e=>{console.error(`SymbolsStorage: Cannot init, error=${e.toString()}`)}))}resolveSymbol(e,s,t){return this._readyPromise.then((()=>{const r=this._symbolsInfo[n(e,s,t)];return void 0===r?Promise.reject("invalid symbol"):Promise.resolve(r)}))}searchSymbols(e,s,t,r){return this._readyPromise.then((()=>{const o=[],i=0===e.length;e=e.toUpperCase();for(const r of this._symbolsList){const n=this._symbolsInfo[r];if(void 0===n)continue;if(t.length>0&&n.type!==t)continue;if(s&&s.length>0&&n.exchange!==s)continue;const a=n.name.toUpperCase().indexOf(e),u=n.description.toUpperCase().indexOf(e);if(i||a>=0||u>=0){if(!o.some((e=>e.symbolInfo===n))){const e=a>=0?a:8e3+u;o.push({symbolInfo:n,weight:e})}}}const n=o.sort(((e,s)=>e.weight-s.weight)).slice(0,r).map((e=>{const s=e.symbolInfo;return{symbol:s.name,full_name:s.full_name,description:s.description,exchange:s.exchange,params:[],type:s.type,ticker:s.name}}));return Promise.resolve(n)}))}_init(){const e=[],s={};for(const t of this._exchangesList)s[t]||(s[t]=!0,e.push(this._requestExchangeData(t)));return Promise.all(e).then((()=>{this._symbolsList.sort()}))}_requestExchangeData(e){return new Promise(((t,r)=>{this._requester.sendRequest(this._datafeedUrl,"symbol_info",{group:e}).then((s=>{try{this._onExchangeDataReceived(e,s)}catch(e){return void r(e)}t()})).catch((e=>{s(e),t()}))}))}_onExchangeDataReceived(e,s){let t=0;try{const e=s.symbol.length,r=void 0!==s.ticker;for(;t<e;++t){const e=s.symbol[t],o=i(s,"exchange-listed",t),a=i(s,"exchange-traded",t),c=a+":"+e,h=i(s,"currency-code",t),l=i(s,"unit-id",t),d=r?i(s,"ticker",t):e,_={ticker:d,name:e,base_name:[o+":"+e],full_name:c,listed_exchange:o,exchange:a,currency_code:h,original_currency_code:i(s,"original-currency-code",t),unit_id:l,original_unit_id:i(s,"original-unit-id",t),unit_conversion_types:i(s,"unit-conversion-types",t,!0),description:i(s,"description",t),has_intraday:u(i(s,"has-intraday",t),!1),has_no_volume:u(i(s,"has-no-volume",t),!1),minmov:i(s,"minmovement",t)||i(s,"minmov",t)||0,minmove2:i(s,"minmove2",t)||i(s,"minmov2",t),fractional:i(s,"fractional",t),pricescale:i(s,"pricescale",t),type:i(s,"type",t),session:i(s,"session-regular",t),session_holidays:i(s,"session-holidays",t),corrections:i(s,"corrections",t),timezone:i(s,"timezone",t),supported_resolutions:u(i(s,"supported-resolutions",t,!0),this._datafeedSupportedResolutions),has_daily:u(i(s,"has-daily",t),!0),intraday_multipliers:u(i(s,"intraday-multipliers",t,!0),["1","5","15","30","60"]),has_weekly_and_monthly:i(s,"has-weekly-and-monthly",t),has_empty_bars:i(s,"has-empty-bars",t),volume_precision:u(i(s,"volume-precision",t),0),format:"price"};this._symbolsInfo[d]=_,this._symbolsInfo[e]=_,this._symbolsInfo[c]=_,void 0===h&&void 0===l||(this._symbolsInfo[n(d,h,l)]=_,this._symbolsInfo[n(e,h,l)]=_,this._symbolsInfo[n(c,h,l)]=_),this._symbolsList.push(e)}}catch(r){throw new Error(`SymbolsStorage: API error when processing exchange ${e} symbol #${t} (${s.symbol[t]}): ${r.message}`)}}}function u(e,s){return void 0!==e?e:s}function c(e,s,t){const r=e[s];return Array.isArray(r)?r[t]:r}class h{constructor(e,s){this._datafeedUrl=e,this._requester=s}getQuotes(e){return new Promise(((t,r)=>{this._requester.sendRequest(this._datafeedUrl,"quotes",{symbols:e}).then((e=>{"ok"===e.s?t(e.d):r(e.errmsg)})).catch((e=>{const t=s(e);r(`network error: ${t}`)}))}))}}class l{constructor(e){e&&(this._headers=e)}sendRequest(e,s,t){if(void 0!==t){const e=Object.keys(t);0!==e.length&&(s+="?"),s+=e.map((e=>`${encodeURIComponent(e)}=${encodeURIComponent(t[e].toString())}`)).join("&")}const r={credentials:"same-origin"};return void 0!==this._headers&&(r.headers=this._headers),fetch(`${e}/${s}`,r).then((e=>e.text())).then((e=>JSON.parse(e)))}}e.UDFCompatibleDatafeed=class extends class{constructor(e,s,i,n=1e4){this._configuration={supports_search:!1,supports_group_request:!0,supported_resolutions:["1","5","15","30","60","1D","1W","1M"],supports_marks:!1,supports_timescale_marks:!1},this._symbolsStorage=null,this._datafeedURL=e,this._requester=i,this._historyProvider=new t(e,this._requester),this._quotesProvider=s,this._dataPulseProvider=new r(this._historyProvider,n),this._quotesPulseProvider=new o(this._quotesProvider),this._configurationReadyPromise=this._requestConfiguration().then((e=>{null===e&&(e={supports_search:!1,supports_group_request:!0,supported_resolutions:["1","5","15","30","60","1D","1W","1M"],supports_marks:!1,supports_timescale_marks:!1}),this._setupWithConfiguration(e)}))}onReady(e){this._configurationReadyPromise.then((()=>{e(this._configuration)}))}getQuotes(e,s,t){this._quotesProvider.getQuotes(e).then(s).catch(t)}subscribeQuotes(e,s,t,r){this._quotesPulseProvider.subscribeQuotes(e,s,t,r)}unsubscribeQuotes(e){this._quotesPulseProvider.unsubscribeQuotes(e)}getMarks(e,t,r,o,i){if(!this._configuration.supports_marks)return;const n={symbol:e.ticker||"",from:t,to:r,resolution:i};this._send("marks",n).then((e=>{if(!Array.isArray(e)){const s=[];for(let t=0;t<e.id.length;++t)s.push({id:c(e,"id",t),time:c(e,"time",t),color:c(e,"color",t),text:c(e,"text",t),label:c(e,"label",t),labelFontColor:c(e,"labelFontColor",t),minSize:c(e,"minSize",t)});e=s}o(e)})).catch((e=>{s(e),o([])}))}getTimescaleMarks(e,t,r,o,i){if(!this._configuration.supports_timescale_marks)return;const n={symbol:e.ticker||"",from:t,to:r,resolution:i};this._send("timescale_marks",n).then((e=>{if(!Array.isArray(e)){const s=[];for(let t=0;t<e.id.length;++t)s.push({id:c(e,"id",t),time:c(e,"time",t),color:c(e,"color",t),label:c(e,"label",t),tooltip:c(e,"tooltip",t)});e=s}o(e)})).catch((e=>{s(e),o([])}))}getServerTime(e){this._configuration.supports_time&&this._send("time").then((s=>{const t=parseInt(s);isNaN(t)||e(t)})).catch((e=>{s(e)}))}searchSymbols(e,t,r,o){if(this._configuration.supports_search){const i={limit:30,query:e.toUpperCase(),type:r,exchange:t};this._send("search",i).then((e=>{if(void 0!==e.s)return e.errmsg,void o([]);o(e)})).catch((e=>{s(e),o([])}))}else{if(null===this._symbolsStorage)throw new Error("UdfCompatibleDatafeed: inconsistent configuration (symbols storage)");this._symbolsStorage.searchSymbols(e,t,r,30).then(o).catch(o.bind(null,[]))}}resolveSymbol(e,t,r,o){const i=o&&o.currencyCode,n=o&&o.unitId;function a(e){t(e)}if(this._configuration.supports_group_request){if(null===this._symbolsStorage)throw new Error("UdfCompatibleDatafeed: inconsistent configuration (symbols storage)");this._symbolsStorage.resolveSymbol(e,i,n).then(a).catch(r)}else{const t={symbol:e};void 0!==i&&(t.currencyCode=i),void 0!==n&&(t.unitId=n),this._send("symbols",t).then((e=>{void 0!==e.s?r("unknown_symbol"):a(e)})).catch((e=>{s(e),r("unknown_symbol")}))}}getBars(e,s,t,r,o){this._historyProvider.getBars(e,s,t).then((e=>{r(e.bars,e.meta)})).catch(o)}subscribeBars(e,s,t,r,o){this._dataPulseProvider.subscribeBars(e,s,t,r)}unsubscribeBars(e){this._dataPulseProvider.unsubscribeBars(e)}_requestConfiguration(){return this._send("config").catch((e=>(s(e),null)))}_send(e,s){return this._requester.sendRequest(this._datafeedURL,e,s)}_setupWithConfiguration(e){if(this._configuration=e,void 0===e.exchanges&&(e.exchanges=[]),!e.supports_search&&!e.supports_group_request)throw new Error("Unsupported datafeed configuration. Must either support search, or support group request");!e.supports_group_request&&e.supports_search||(this._symbolsStorage=new a(this._datafeedURL,e.supported_resolutions||[],this._requester)),JSON.stringify(e)}}{constructor(e,s=1e4){const t=new l;super(e,new h(e,t),t,s)}},Object.defineProperty(e,"__esModule",{value:!0})}));
|
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
import { getErrorMessage, logMessage, } from './helpers';
|
|
2
|
-
export class DataPulseProvider {
|
|
3
|
-
constructor(historyProvider, updateFrequency) {
|
|
4
|
-
this._subscribers = {};
|
|
5
|
-
this._requestsPending = 0;
|
|
6
|
-
this._historyProvider = historyProvider;
|
|
7
|
-
setInterval(this._updateData.bind(this), updateFrequency);
|
|
8
|
-
}
|
|
9
|
-
subscribeBars(symbolInfo, resolution, newDataCallback, listenerGuid) {
|
|
10
|
-
if (this._subscribers.hasOwnProperty(listenerGuid)) {
|
|
11
|
-
logMessage(`DataPulseProvider: already has subscriber with id=${listenerGuid}`);
|
|
12
|
-
return;
|
|
13
|
-
}
|
|
14
|
-
this._subscribers[listenerGuid] = {
|
|
15
|
-
lastBarTime: null,
|
|
16
|
-
listener: newDataCallback,
|
|
17
|
-
resolution: resolution,
|
|
18
|
-
symbolInfo: symbolInfo,
|
|
19
|
-
};
|
|
20
|
-
logMessage(`DataPulseProvider: subscribed for #${listenerGuid} - {${symbolInfo.name}, ${resolution}}`);
|
|
21
|
-
}
|
|
22
|
-
unsubscribeBars(listenerGuid) {
|
|
23
|
-
delete this._subscribers[listenerGuid];
|
|
24
|
-
logMessage(`DataPulseProvider: unsubscribed for #${listenerGuid}`);
|
|
25
|
-
}
|
|
26
|
-
_updateData() {
|
|
27
|
-
if (this._requestsPending > 0) {
|
|
28
|
-
return;
|
|
29
|
-
}
|
|
30
|
-
this._requestsPending = 0;
|
|
31
|
-
for (const listenerGuid in this._subscribers) { // tslint:disable-line:forin
|
|
32
|
-
this._requestsPending += 1;
|
|
33
|
-
this._updateDataForSubscriber(listenerGuid)
|
|
34
|
-
.then(() => {
|
|
35
|
-
this._requestsPending -= 1;
|
|
36
|
-
logMessage(`DataPulseProvider: data for #${listenerGuid} updated successfully, pending=${this._requestsPending}`);
|
|
37
|
-
})
|
|
38
|
-
.catch((reason) => {
|
|
39
|
-
this._requestsPending -= 1;
|
|
40
|
-
logMessage(`DataPulseProvider: data for #${listenerGuid} updated with error=${getErrorMessage(reason)}, pending=${this._requestsPending}`);
|
|
41
|
-
});
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
_updateDataForSubscriber(listenerGuid) {
|
|
45
|
-
const subscriptionRecord = this._subscribers[listenerGuid];
|
|
46
|
-
const rangeEndTime = parseInt((Date.now() / 1000).toString());
|
|
47
|
-
// BEWARE: please note we really need 2 bars, not the only last one
|
|
48
|
-
// see the explanation below. `10` is the `large enough` value to work around holidays
|
|
49
|
-
const rangeStartTime = rangeEndTime - periodLengthSeconds(subscriptionRecord.resolution, 10);
|
|
50
|
-
return this._historyProvider.getBars(subscriptionRecord.symbolInfo, subscriptionRecord.resolution, {
|
|
51
|
-
from: rangeStartTime,
|
|
52
|
-
to: rangeEndTime,
|
|
53
|
-
countBack: 2,
|
|
54
|
-
firstDataRequest: false,
|
|
55
|
-
})
|
|
56
|
-
.then((result) => {
|
|
57
|
-
this._onSubscriberDataReceived(listenerGuid, result);
|
|
58
|
-
});
|
|
59
|
-
}
|
|
60
|
-
_onSubscriberDataReceived(listenerGuid, result) {
|
|
61
|
-
// means the subscription was cancelled while waiting for data
|
|
62
|
-
if (!this._subscribers.hasOwnProperty(listenerGuid)) {
|
|
63
|
-
logMessage(`DataPulseProvider: Data comes for already unsubscribed subscription #${listenerGuid}`);
|
|
64
|
-
return;
|
|
65
|
-
}
|
|
66
|
-
const bars = result.bars;
|
|
67
|
-
if (bars.length === 0) {
|
|
68
|
-
return;
|
|
69
|
-
}
|
|
70
|
-
const lastBar = bars[bars.length - 1];
|
|
71
|
-
const subscriptionRecord = this._subscribers[listenerGuid];
|
|
72
|
-
if (subscriptionRecord.lastBarTime !== null && lastBar.time < subscriptionRecord.lastBarTime) {
|
|
73
|
-
return;
|
|
74
|
-
}
|
|
75
|
-
const isNewBar = subscriptionRecord.lastBarTime !== null && lastBar.time > subscriptionRecord.lastBarTime;
|
|
76
|
-
// Pulse updating may miss some trades data (ie, if pulse period = 10 secods and new bar is started 5 seconds later after the last update, the
|
|
77
|
-
// old bar's last 5 seconds trades will be lost). Thus, at fist we should broadcast old bar updates when it's ready.
|
|
78
|
-
if (isNewBar) {
|
|
79
|
-
if (bars.length < 2) {
|
|
80
|
-
throw new Error('Not enough bars in history for proper pulse update. Need at least 2.');
|
|
81
|
-
}
|
|
82
|
-
const previousBar = bars[bars.length - 2];
|
|
83
|
-
subscriptionRecord.listener(previousBar);
|
|
84
|
-
}
|
|
85
|
-
subscriptionRecord.lastBarTime = lastBar.time;
|
|
86
|
-
subscriptionRecord.listener(lastBar);
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
function periodLengthSeconds(resolution, requiredPeriodsCount) {
|
|
90
|
-
let daysCount = 0;
|
|
91
|
-
if (resolution === 'D' || resolution === '1D') {
|
|
92
|
-
daysCount = requiredPeriodsCount;
|
|
93
|
-
}
|
|
94
|
-
else if (resolution === 'M' || resolution === '1M') {
|
|
95
|
-
daysCount = 31 * requiredPeriodsCount;
|
|
96
|
-
}
|
|
97
|
-
else if (resolution === 'W' || resolution === '1W') {
|
|
98
|
-
daysCount = 7 * requiredPeriodsCount;
|
|
99
|
-
}
|
|
100
|
-
else {
|
|
101
|
-
daysCount = requiredPeriodsCount * parseInt(resolution) / (24 * 60);
|
|
102
|
-
}
|
|
103
|
-
return daysCount * 24 * 60 * 60;
|
|
104
|
-
}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* If you want to enable logs from datafeed set it to `true`
|
|
3
|
-
*/
|
|
4
|
-
const isLoggingEnabled = false;
|
|
5
|
-
export function logMessage(message) {
|
|
6
|
-
if (isLoggingEnabled) {
|
|
7
|
-
const now = new Date();
|
|
8
|
-
// tslint:disable-next-line:no-console
|
|
9
|
-
console.log(`${now.toLocaleTimeString()}.${now.getMilliseconds()}> ${message}`);
|
|
10
|
-
}
|
|
11
|
-
}
|
|
12
|
-
export function getErrorMessage(error) {
|
|
13
|
-
if (error === undefined) {
|
|
14
|
-
return '';
|
|
15
|
-
}
|
|
16
|
-
else if (typeof error === 'string') {
|
|
17
|
-
return error;
|
|
18
|
-
}
|
|
19
|
-
return error.message;
|
|
20
|
-
}
|