ccxt 4.2.8 → 4.2.10

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.
Files changed (57) hide show
  1. package/README.md +3 -3
  2. package/build.sh +18 -2
  3. package/dist/ccxt.browser.js +387 -149
  4. package/dist/ccxt.browser.min.js +7 -7
  5. package/dist/cjs/ccxt.js +1 -1
  6. package/dist/cjs/src/base/Exchange.js +37 -8
  7. package/dist/cjs/src/binance.js +6 -1
  8. package/dist/cjs/src/bitmart.js +94 -15
  9. package/dist/cjs/src/bl3p.js +1 -1
  10. package/dist/cjs/src/btcalpha.js +1 -1
  11. package/dist/cjs/src/cryptocom.js +3 -1
  12. package/dist/cjs/src/deribit.js +39 -22
  13. package/dist/cjs/src/kraken.js +1 -1
  14. package/dist/cjs/src/kucoin.js +1 -1
  15. package/dist/cjs/src/lykke.js +1 -1
  16. package/dist/cjs/src/ndax.js +1 -1
  17. package/dist/cjs/src/pro/binance.js +17 -1
  18. package/dist/cjs/src/pro/bitget.js +3 -1
  19. package/dist/cjs/src/pro/bitmart.js +52 -15
  20. package/dist/cjs/src/pro/blockchaincom.js +2 -28
  21. package/dist/cjs/src/pro/bybit.js +3 -1
  22. package/dist/cjs/src/pro/coinbasepro.js +9 -16
  23. package/dist/cjs/src/pro/cryptocom.js +110 -28
  24. package/dist/cjs/src/pro/luno.js +5 -5
  25. package/js/ccxt.d.ts +1 -1
  26. package/js/ccxt.js +1 -1
  27. package/js/src/base/Exchange.d.ts +4 -3
  28. package/js/src/base/Exchange.js +37 -8
  29. package/js/src/binance.js +6 -1
  30. package/js/src/bitmart.js +95 -16
  31. package/js/src/bl3p.d.ts +2 -2
  32. package/js/src/bl3p.js +1 -1
  33. package/js/src/btcalpha.d.ts +2 -2
  34. package/js/src/btcalpha.js +1 -1
  35. package/js/src/cryptocom.js +3 -1
  36. package/js/src/deribit.js +39 -22
  37. package/js/src/kraken.d.ts +2 -2
  38. package/js/src/kraken.js +1 -1
  39. package/js/src/kucoin.js +1 -1
  40. package/js/src/lykke.d.ts +2 -2
  41. package/js/src/lykke.js +1 -1
  42. package/js/src/ndax.d.ts +2 -2
  43. package/js/src/ndax.js +1 -1
  44. package/js/src/pro/binance.js +17 -1
  45. package/js/src/pro/bitget.js +3 -1
  46. package/js/src/pro/bitmart.d.ts +1 -0
  47. package/js/src/pro/bitmart.js +52 -15
  48. package/js/src/pro/blockchaincom.d.ts +1 -11
  49. package/js/src/pro/blockchaincom.js +2 -28
  50. package/js/src/pro/bybit.js +3 -1
  51. package/js/src/pro/coinbasepro.js +9 -16
  52. package/js/src/pro/cryptocom.d.ts +3 -1
  53. package/js/src/pro/cryptocom.js +111 -29
  54. package/js/src/pro/luno.d.ts +4 -4
  55. package/js/src/pro/luno.js +5 -5
  56. package/js/src/static_dependencies/jsencrypt/lib/jsbn/jsbn.d.ts +1 -1
  57. package/package.json +1 -1
@@ -698,7 +698,7 @@ export default class blockchaincom extends blockchaincomRest {
698
698
  return message;
699
699
  }
700
700
  else if (event === 'snapshot') {
701
- const snapshot = this.parseCountedOrderBook(message, symbol, timestamp, 'bids', 'asks', 'px', 'qty', 'num');
701
+ const snapshot = this.parseOrderBook(message, symbol, timestamp, 'bids', 'asks', 'px', 'qty', 'num');
702
702
  storedOrderBook.reset(snapshot);
703
703
  }
704
704
  else if (event === 'updated') {
@@ -714,34 +714,8 @@ export default class blockchaincom extends blockchaincomRest {
714
714
  }
715
715
  client.resolve(storedOrderBook, messageHash);
716
716
  }
717
- parseCountedBidAsk(bidAsk, priceKey = 0, amountKey = 1, countKey = 2) {
718
- const price = this.safeNumber(bidAsk, priceKey);
719
- const amount = this.safeNumber(bidAsk, amountKey);
720
- const count = this.safeNumber(bidAsk, countKey);
721
- return [price, amount, count];
722
- }
723
- parseCountedBidsAsks(bidasks, priceKey = 0, amountKey = 1, countKey = 2) {
724
- bidasks = this.toArray(bidasks);
725
- const result = [];
726
- for (let i = 0; i < bidasks.length; i++) {
727
- result.push(this.parseCountedBidAsk(bidasks[i], priceKey, amountKey, countKey));
728
- }
729
- return result;
730
- }
731
- parseCountedOrderBook(orderbook, symbol, timestamp = undefined, bidsKey = 'bids', asksKey = 'asks', priceKey = 0, amountKey = 1, countKey = 2) {
732
- const bids = this.parseCountedBidsAsks(this.safeValue(orderbook, bidsKey, []), priceKey, amountKey, countKey);
733
- const asks = this.parseCountedBidsAsks(this.safeValue(orderbook, asksKey, []), priceKey, amountKey, countKey);
734
- return {
735
- 'symbol': symbol,
736
- 'bids': this.sortBy(bids, 0, true),
737
- 'asks': this.sortBy(asks, 0),
738
- 'timestamp': timestamp,
739
- 'datetime': this.iso8601(timestamp),
740
- 'nonce': undefined,
741
- };
742
- }
743
717
  handleDelta(bookside, delta) {
744
- const bookArray = this.parseCountedBidAsk(delta, 'px', 'qty', 'num');
718
+ const bookArray = this.parseBidAsk(delta, 'px', 'qty', 'num');
745
719
  bookside.storeArray(bookArray);
746
720
  }
747
721
  handleDeltas(bookside, deltas) {
@@ -225,7 +225,9 @@ export default class bybit extends bybitRest {
225
225
  }
226
226
  const ticker = await this.watchTopics(url, messageHashes, topics, params);
227
227
  if (this.newUpdates) {
228
- return ticker;
228
+ const result = {};
229
+ result[ticker['symbol']] = ticker;
230
+ return result;
229
231
  }
230
232
  return this.filterByArray(this.tickers, 'symbol', symbols);
231
233
  }
@@ -92,7 +92,7 @@ export default class coinbasepro extends coinbaseproRest {
92
92
  const symbol = symbols[i];
93
93
  market = this.market(symbol);
94
94
  productIds.push(market['id']);
95
- messageHashes.push(messageHashStart + ':' + market['id']);
95
+ messageHashes.push(messageHashStart + ':' + market['symbol']);
96
96
  }
97
97
  let url = this.urls['api']['ws'];
98
98
  if ('signature' in params) {
@@ -137,10 +137,12 @@ export default class coinbasepro extends coinbaseproRest {
137
137
  throw new BadSymbol(this.id + ' watchTickers requires a non-empty symbols array');
138
138
  }
139
139
  const channel = 'ticker';
140
- const messageHash = 'tickers::';
141
- const newTickers = await this.subscribeMultiple(channel, symbols, messageHash, params);
140
+ const messageHash = 'ticker';
141
+ const ticker = await this.subscribeMultiple(channel, symbols, messageHash, params);
142
142
  if (this.newUpdates) {
143
- return newTickers;
143
+ const result = {};
144
+ result[ticker['symbol']] = ticker;
145
+ return result;
144
146
  }
145
147
  return this.filterByArray(this.tickers, 'symbol', symbols);
146
148
  }
@@ -752,19 +754,10 @@ export default class coinbasepro extends coinbaseproRest {
752
754
  const ticker = this.parseTicker(message);
753
755
  const symbol = ticker['symbol'];
754
756
  this.tickers[symbol] = ticker;
755
- const type = this.safeString(message, 'type');
756
- const messageHash = type + ':' + marketId;
757
+ const messageHash = 'ticker:' + symbol;
758
+ const idMessageHash = 'ticker:' + marketId;
757
759
  client.resolve(ticker, messageHash);
758
- const messageHashes = this.findMessageHashes(client, 'tickers::');
759
- for (let i = 0; i < messageHashes.length; i++) {
760
- const currentMessageHash = messageHashes[i];
761
- const parts = currentMessageHash.split('::');
762
- const symbolsString = parts[1];
763
- const symbols = symbolsString.split(',');
764
- if (this.inArray(symbol, symbols)) {
765
- client.resolve(ticker, currentMessageHash);
766
- }
767
- }
760
+ client.resolve(ticker, idMessageHash);
768
761
  }
769
762
  return message;
770
763
  }
@@ -6,7 +6,9 @@ export default class cryptocom extends cryptocomRest {
6
6
  pong(client: any, message: any): Promise<void>;
7
7
  watchOrderBook(symbol: string, limit?: Int, params?: {}): Promise<OrderBook>;
8
8
  watchOrderBookForSymbols(symbols: string[], limit?: Int, params?: {}): Promise<OrderBook>;
9
- handleOrderBookSnapshot(client: Client, message: any): void;
9
+ handleDelta(bookside: any, delta: any): void;
10
+ handleDeltas(bookside: any, deltas: any): void;
11
+ handleOrderBook(client: Client, message: any): void;
10
12
  watchTrades(symbol: string, since?: Int, limit?: Int, params?: {}): Promise<Trade[]>;
11
13
  watchTradesForSymbols(symbols: string[], since?: Int, limit?: Int, params?: {}): Promise<Trade[]>;
12
14
  handleTrades(client: Client, message: any): void;
@@ -6,7 +6,7 @@
6
6
 
7
7
  // ---------------------------------------------------------------------------
8
8
  import cryptocomRest from '../cryptocom.js';
9
- import { AuthenticationError, NetworkError } from '../base/errors.js';
9
+ import { AuthenticationError, InvalidNonce, NetworkError } from '../base/errors.js';
10
10
  import { ArrayCache, ArrayCacheByTimestamp, ArrayCacheBySymbolById, ArrayCacheBySymbolBySide } from '../base/ws/Cache.js';
11
11
  import { sha256 } from '../static_dependencies/noble-hashes/sha256.js';
12
12
  // ---------------------------------------------------------------------------
@@ -74,6 +74,8 @@ export default class cryptocom extends cryptocomRest {
74
74
  * @param {string} symbol unified symbol of the market to fetch the order book for
75
75
  * @param {int} [limit] the maximum amount of order book entries to return
76
76
  * @param {object} [params] extra parameters specific to the exchange API endpoint
77
+ * @param {string} [params.bookSubscriptionType] The subscription type. Allowed values: SNAPSHOT full snapshot. This is the default if not specified. SNAPSHOT_AND_UPDATE delta updates
78
+ * @param {int} [params.bookUpdateFrequency] Book update interval in ms. Allowed values: 100 for snapshot subscription 10 for delta subscription
77
79
  * @returns {object} A dictionary of [order book structures]{@link https://docs.ccxt.com/#/?id=order-book-structure} indexed by market symbols
78
80
  */
79
81
  return await this.watchOrderBookForSymbols([symbol], limit, params);
@@ -87,6 +89,8 @@ export default class cryptocom extends cryptocomRest {
87
89
  * @param {string[]} symbols unified array of symbols
88
90
  * @param {int} [limit] the maximum amount of order book entries to return
89
91
  * @param {object} [params] extra parameters specific to the exchange API endpoint
92
+ * @param {string} [params.bookSubscriptionType] The subscription type. Allowed values: SNAPSHOT full snapshot. This is the default if not specified. SNAPSHOT_AND_UPDATE delta updates
93
+ * @param {int} [params.bookUpdateFrequency] Book update interval in ms. Allowed values: 100 for snapshot subscription 10 for delta subscription
90
94
  * @returns {object} A dictionary of [order book structures]{@link https://docs.ccxt.com/#/?id=order-book-structure} indexed by market symbols
91
95
  */
92
96
  await this.loadMarkets();
@@ -94,12 +98,26 @@ export default class cryptocom extends cryptocomRest {
94
98
  const topics = [];
95
99
  const messageHashes = [];
96
100
  if (!limit) {
97
- limit = 150;
101
+ limit = 50;
102
+ }
103
+ const topicParams = this.safeValue(params, 'params');
104
+ if (topicParams === undefined) {
105
+ params['params'] = {};
106
+ }
107
+ let bookSubscriptionType = undefined;
108
+ [bookSubscriptionType, params] = this.handleOptionAndParams2(params, 'watchOrderBook', 'watchOrderBookForSymbols', 'bookSubscriptionType', 'SNAPSHOT_AND_UPDATE');
109
+ if (bookSubscriptionType !== undefined) {
110
+ params['params']['bookSubscriptionType'] = bookSubscriptionType;
111
+ }
112
+ let bookUpdateFrequency = undefined;
113
+ [bookUpdateFrequency, params] = this.handleOptionAndParams2(params, 'watchOrderBook', 'watchOrderBookForSymbols', 'bookUpdateFrequency');
114
+ if (bookUpdateFrequency !== undefined) {
115
+ params['params']['bookSubscriptionType'] = bookSubscriptionType;
98
116
  }
99
117
  for (let i = 0; i < symbols.length; i++) {
100
118
  const symbol = symbols[i];
101
119
  const market = this.market(symbol);
102
- const currentTopic = 'book' + '.' + market['id'] + '.' + limit;
120
+ const currentTopic = 'book' + '.' + market['id'] + '.' + limit.toString();
103
121
  const messageHash = 'orderbook:' + market['symbol'];
104
122
  messageHashes.push(messageHash);
105
123
  topics.push(currentTopic);
@@ -107,27 +125,72 @@ export default class cryptocom extends cryptocomRest {
107
125
  const orderbook = await this.watchPublicMultiple(messageHashes, topics, params);
108
126
  return orderbook.limit();
109
127
  }
110
- handleOrderBookSnapshot(client, message) {
111
- // full snapshot
128
+ handleDelta(bookside, delta) {
129
+ const price = this.safeFloat(delta, 0);
130
+ const amount = this.safeFloat(delta, 1);
131
+ const count = this.safeInteger(delta, 2);
132
+ bookside.store(price, amount, count);
133
+ }
134
+ handleDeltas(bookside, deltas) {
135
+ for (let i = 0; i < deltas.length; i++) {
136
+ this.handleDelta(bookside, deltas[i]);
137
+ }
138
+ }
139
+ handleOrderBook(client, message) {
112
140
  //
113
- // {
114
- // "instrument_name":"LTC_USDT",
115
- // "subscription":"book.LTC_USDT.150",
116
- // "channel":"book",
117
- // "depth":150,
118
- // "data": [
119
- // {
120
- // "bids": [
121
- // [122.21, 0.74041, 4]
122
- // ],
123
- // "asks": [
124
- // [122.29, 0.00002, 1]
125
- // ]
126
- // "t": 1648123943803,
127
- // "s":754560122
128
- // }
129
- // ]
130
- // }
141
+ // snapshot
142
+ // {
143
+ // "instrument_name":"LTC_USDT",
144
+ // "subscription":"book.LTC_USDT.150",
145
+ // "channel":"book",
146
+ // "depth":150,
147
+ // "data": [
148
+ // {
149
+ // "bids": [
150
+ // [122.21, 0.74041, 4]
151
+ // ],
152
+ // "asks": [
153
+ // [122.29, 0.00002, 1]
154
+ // ]
155
+ // "t": 1648123943803,
156
+ // "s":754560122
157
+ // }
158
+ // ]
159
+ // }
160
+ // update
161
+ // {
162
+ // "instrument_name":"BTC_USDT",
163
+ // "subscription":"book.BTC_USDT.50",
164
+ // "channel":"book.update",
165
+ // "depth":50,
166
+ // "data":[
167
+ // {
168
+ // "update":{
169
+ // "asks":[
170
+ // [
171
+ // "43755.46",
172
+ // "0.10000",
173
+ // "1"
174
+ // ],
175
+ // ...
176
+ // ],
177
+ // "bids":[
178
+ // [
179
+ // "43737.46",
180
+ // "0.14096",
181
+ // "1"
182
+ // ],
183
+ // ...
184
+ // ]
185
+ // },
186
+ // "t":1704484068898,
187
+ // "tt":1704484068892,
188
+ // "u":78795598253024,
189
+ // "pu":78795598162080,
190
+ // "cs":-781431132
191
+ // }
192
+ // ]
193
+ // }
131
194
  //
132
195
  const marketId = this.safeString(message, 'instrument_name');
133
196
  const market = this.safeMarket(marketId);
@@ -135,14 +198,32 @@ export default class cryptocom extends cryptocomRest {
135
198
  let data = this.safeValue(message, 'data');
136
199
  data = this.safeValue(data, 0);
137
200
  const timestamp = this.safeInteger(data, 't');
138
- const snapshot = this.parseOrderBook(data, symbol, timestamp);
139
- snapshot['nonce'] = this.safeInteger(data, 's');
140
201
  let orderbook = this.safeValue(this.orderbooks, symbol);
141
202
  if (orderbook === undefined) {
142
203
  const limit = this.safeInteger(message, 'depth');
143
- orderbook = this.orderBook({}, limit);
204
+ orderbook = this.countedOrderBook({}, limit);
144
205
  }
145
- orderbook.reset(snapshot);
206
+ const channel = this.safeString(message, 'channel');
207
+ const nonce = this.safeInteger2(data, 'u', 's');
208
+ let books = data;
209
+ if (channel === 'book') { // snapshot
210
+ orderbook.reset({});
211
+ orderbook['symbol'] = symbol;
212
+ orderbook['timestamp'] = timestamp;
213
+ orderbook['datetime'] = this.iso8601(timestamp);
214
+ orderbook['nonce'] = nonce;
215
+ }
216
+ else {
217
+ books = this.safeValue(data, 'update', {});
218
+ const previousNonce = this.safeInteger(data, 'pu');
219
+ const currentNonce = orderbook['nonce'];
220
+ if (currentNonce !== previousNonce) {
221
+ throw new InvalidNonce(this.id + ' watchOrderBook() ' + symbol + ' ' + previousNonce + ' != ' + nonce);
222
+ }
223
+ }
224
+ this.handleDeltas(orderbook['asks'], this.safeValue(books, 'asks', []));
225
+ this.handleDeltas(orderbook['bids'], this.safeValue(books, 'bids', []));
226
+ orderbook['nonce'] = nonce;
146
227
  this.orderbooks[symbol] = orderbook;
147
228
  const messageHash = 'orderbook:' + symbol;
148
229
  client.resolve(orderbook, messageHash);
@@ -764,7 +845,7 @@ export default class cryptocom extends cryptocomRest {
764
845
  },
765
846
  'nonce': id,
766
847
  };
767
- const message = this.extend(request, params);
848
+ const message = this.deepExtend(request, params);
768
849
  return await this.watchMultiple(url, messageHashes, message, messageHashes);
769
850
  }
770
851
  async watchPrivateRequest(nonce, params = {}) {
@@ -831,7 +912,8 @@ export default class cryptocom extends cryptocomRest {
831
912
  'candlestick': this.handleOHLCV,
832
913
  'ticker': this.handleTicker,
833
914
  'trade': this.handleTrades,
834
- 'book': this.handleOrderBookSnapshot,
915
+ 'book': this.handleOrderBook,
916
+ 'book.update': this.handleOrderBook,
835
917
  'user.order': this.handleOrders,
836
918
  'user.trade': this.handleTrades,
837
919
  'user.balance': this.handleBalance,
@@ -1,5 +1,5 @@
1
1
  import lunoRest from '../luno.js';
2
- import type { Int, Trade, OrderBook } from '../base/types.js';
2
+ import type { Int, Trade, OrderBook, IndexType } from '../base/types.js';
3
3
  import Client from '../base/ws/Client.js';
4
4
  export default class luno extends lunoRest {
5
5
  describe(): any;
@@ -8,7 +8,7 @@ export default class luno extends lunoRest {
8
8
  parseTrade(trade: any, market?: any): Trade;
9
9
  watchOrderBook(symbol: string, limit?: Int, params?: {}): Promise<OrderBook>;
10
10
  handleOrderBook(client: Client, message: any, subscription: any): void;
11
- customParseOrderBook(orderbook: any, symbol: any, timestamp?: any, bidsKey?: string, asksKey?: string, priceKey?: string, amountKey?: string, thirdKey?: any): {
11
+ customParseOrderBook(orderbook: any, symbol: any, timestamp?: any, bidsKey?: string, asksKey?: IndexType, priceKey?: IndexType, amountKey?: IndexType, countOrIdKey?: IndexType): {
12
12
  symbol: any;
13
13
  bids: any;
14
14
  asks: any;
@@ -16,8 +16,8 @@ export default class luno extends lunoRest {
16
16
  datetime: string;
17
17
  nonce: any;
18
18
  };
19
- parseBidsAsks(bidasks: any, priceKey?: string, amountKey?: string, thirdKey?: any): any[];
20
- customParseBidAsk(bidask: any, priceKey?: string, amountKey?: string, thirdKey?: any): number[];
19
+ parseBidsAsks(bidasks: any, priceKey?: IndexType, amountKey?: IndexType, thirdKey?: IndexType): any[];
20
+ customParseBidAsk(bidask: any, priceKey?: IndexType, amountKey?: IndexType, thirdKey?: IndexType): number[];
21
21
  handleDelta(orderbook: any, message: any): any;
22
22
  handleMessage(client: Client, message: any): any;
23
23
  }
@@ -213,9 +213,9 @@ export default class luno extends lunoRest {
213
213
  storedOrderBook['nonce'] = nonce;
214
214
  client.resolve(storedOrderBook, messageHash);
215
215
  }
216
- customParseOrderBook(orderbook, symbol, timestamp = undefined, bidsKey = 'bids', asksKey = 'asks', priceKey = 'price', amountKey = 'volume', thirdKey = undefined) {
217
- const bids = this.parseBidsAsks(this.safeValue(orderbook, bidsKey, []), priceKey, amountKey, thirdKey);
218
- const asks = this.parseBidsAsks(this.safeValue(orderbook, asksKey, []), priceKey, amountKey, thirdKey);
216
+ customParseOrderBook(orderbook, symbol, timestamp = undefined, bidsKey = 'bids', asksKey = 'asks', priceKey = 'price', amountKey = 'volume', countOrIdKey = 2) {
217
+ const bids = this.parseBidsAsks(this.safeValue(orderbook, bidsKey, []), priceKey, amountKey, countOrIdKey);
218
+ const asks = this.parseBidsAsks(this.safeValue(orderbook, asksKey, []), priceKey, amountKey, countOrIdKey);
219
219
  return {
220
220
  'symbol': symbol,
221
221
  'bids': this.sortBy(bids, 0, true),
@@ -225,7 +225,7 @@ export default class luno extends lunoRest {
225
225
  'nonce': undefined,
226
226
  };
227
227
  }
228
- parseBidsAsks(bidasks, priceKey = 'price', amountKey = 'volume', thirdKey = undefined) {
228
+ parseBidsAsks(bidasks, priceKey = 'price', amountKey = 'volume', thirdKey = 2) {
229
229
  bidasks = this.toArray(bidasks);
230
230
  const result = [];
231
231
  for (let i = 0; i < bidasks.length; i++) {
@@ -233,7 +233,7 @@ export default class luno extends lunoRest {
233
233
  }
234
234
  return result;
235
235
  }
236
- customParseBidAsk(bidask, priceKey = 'price', amountKey = 'volume', thirdKey = undefined) {
236
+ customParseBidAsk(bidask, priceKey = 'price', amountKey = 'volume', thirdKey = 2) {
237
237
  const price = this.safeNumber(bidask, priceKey);
238
238
  const amount = this.safeNumber(bidask, amountKey);
239
239
  const result = [price, amount];
@@ -15,7 +15,7 @@ export declare class BigInteger {
15
15
  protected intValue(): number;
16
16
  protected byteValue(): number;
17
17
  protected shortValue(): number;
18
- protected signum(): 0 | 1 | -1;
18
+ protected signum(): 1 | 0 | -1;
19
19
  toByteArray(): number[];
20
20
  protected equals(a: BigInteger): boolean;
21
21
  protected min(a: BigInteger): BigInteger;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ccxt",
3
- "version": "4.2.8",
3
+ "version": "4.2.10",
4
4
  "description": "A JavaScript / TypeScript / Python / C# / PHP cryptocurrency trading library with support for 100+ exchanges",
5
5
  "unpkg": "dist/ccxt.browser.js",
6
6
  "type": "module",