tardis-dev 12.5.22 → 12.6.7

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 (100) hide show
  1. package/dist/apikeyaccessinfo.js +1 -1
  2. package/dist/apikeyaccessinfo.js.map +1 -1
  3. package/dist/clearcache.js +12 -12
  4. package/dist/clearcache.js.map +1 -1
  5. package/dist/combine.js +1 -1
  6. package/dist/combine.js.map +1 -1
  7. package/dist/computable/booksnapshot.d.ts.map +1 -1
  8. package/dist/computable/booksnapshot.js +1 -1
  9. package/dist/computable/booksnapshot.js.map +1 -1
  10. package/dist/computable/tradebar.d.ts.map +1 -1
  11. package/dist/computable/tradebar.js.map +1 -1
  12. package/dist/debug.js +1 -1
  13. package/dist/debug.js.map +1 -1
  14. package/dist/downloaddatasets.js +13 -13
  15. package/dist/downloaddatasets.js.map +1 -1
  16. package/dist/exchangedetails.js +1 -1
  17. package/dist/exchangedetails.js.map +1 -1
  18. package/dist/filter.js.map +1 -1
  19. package/dist/handy.js +10 -10
  20. package/dist/handy.js.map +1 -1
  21. package/dist/instrumentinfo.js +1 -1
  22. package/dist/instrumentinfo.js.map +1 -1
  23. package/dist/mappers/binance.d.ts.map +1 -1
  24. package/dist/mappers/binance.js +2 -2
  25. package/dist/mappers/binance.js.map +1 -1
  26. package/dist/mappers/binanceoptions.js +10 -10
  27. package/dist/mappers/binanceoptions.js.map +1 -1
  28. package/dist/mappers/bitflyer.js +1 -1
  29. package/dist/mappers/bitflyer.js.map +1 -1
  30. package/dist/mappers/bybit.d.ts +12 -1
  31. package/dist/mappers/bybit.d.ts.map +1 -1
  32. package/dist/mappers/bybit.js +21 -3
  33. package/dist/mappers/bybit.js.map +1 -1
  34. package/dist/mappers/coinbase.js +2 -2
  35. package/dist/mappers/coinbase.js.map +1 -1
  36. package/dist/mappers/dydx.d.ts +6 -14
  37. package/dist/mappers/dydx.d.ts.map +1 -1
  38. package/dist/mappers/dydx.js +1 -1
  39. package/dist/mappers/dydx.js.map +1 -1
  40. package/dist/mappers/ftx.js +2 -2
  41. package/dist/mappers/ftx.js.map +1 -1
  42. package/dist/mappers/huobi.d.ts.map +1 -1
  43. package/dist/mappers/huobi.js +11 -11
  44. package/dist/mappers/huobi.js.map +1 -1
  45. package/dist/mappers/okex.js +14 -14
  46. package/dist/mappers/okex.js.map +1 -1
  47. package/dist/mappers/phemex.d.ts.map +1 -1
  48. package/dist/mappers/phemex.js.map +1 -1
  49. package/dist/realtimefeeds/ascendex.js +2 -2
  50. package/dist/realtimefeeds/ascendex.js.map +1 -1
  51. package/dist/realtimefeeds/binance.js +1 -1
  52. package/dist/realtimefeeds/binance.js.map +1 -1
  53. package/dist/realtimefeeds/bitmex.js +1 -1
  54. package/dist/realtimefeeds/bitmex.js.map +1 -1
  55. package/dist/realtimefeeds/ftx.js +1 -1
  56. package/dist/realtimefeeds/ftx.js.map +1 -1
  57. package/dist/realtimefeeds/huobi.js +7 -7
  58. package/dist/realtimefeeds/huobi.js.map +1 -1
  59. package/dist/realtimefeeds/okex.js +1 -1
  60. package/dist/realtimefeeds/okex.js.map +1 -1
  61. package/dist/realtimefeeds/realtimefeed.js +9 -9
  62. package/dist/realtimefeeds/realtimefeed.js.map +1 -1
  63. package/dist/replay.d.ts.map +1 -1
  64. package/dist/replay.js +75 -40
  65. package/dist/replay.js.map +1 -1
  66. package/dist/stream.js +4 -4
  67. package/dist/stream.js.map +1 -1
  68. package/dist/worker.js +12 -12
  69. package/dist/worker.js.map +1 -1
  70. package/package.json +19 -18
  71. package/src/computable/booksnapshot.ts +4 -2
  72. package/src/computable/tradebar.ts +4 -1
  73. package/src/downloaddatasets.ts +188 -188
  74. package/src/filter.ts +69 -69
  75. package/src/handy.ts +2 -2
  76. package/src/instrumentinfo.ts +1 -1
  77. package/src/mappers/ascendex.ts +156 -156
  78. package/src/mappers/binance.ts +6 -3
  79. package/src/mappers/bybit.ts +28 -4
  80. package/src/mappers/coinflex.ts +159 -159
  81. package/src/mappers/delta.ts +175 -175
  82. package/src/mappers/dydx.ts +303 -306
  83. package/src/mappers/gateio.ts +117 -117
  84. package/src/mappers/gateiofutures.ts +185 -185
  85. package/src/mappers/huobi.ts +4 -2
  86. package/src/mappers/phemex.ts +179 -177
  87. package/src/mappers/poloniex.ts +150 -150
  88. package/src/mappers/serum.ts +103 -103
  89. package/src/mappers/upbit.ts +104 -104
  90. package/src/realtimefeeds/ascendex.ts +65 -65
  91. package/src/realtimefeeds/coinflex.ts +29 -29
  92. package/src/realtimefeeds/delta.ts +27 -27
  93. package/src/realtimefeeds/dydx.ts +40 -40
  94. package/src/realtimefeeds/gateio.ts +41 -41
  95. package/src/realtimefeeds/gateiofutures.ts +90 -90
  96. package/src/realtimefeeds/poloniex.ts +28 -28
  97. package/src/realtimefeeds/realtimefeed.ts +2 -2
  98. package/src/realtimefeeds/upbit.ts +35 -35
  99. package/src/replay.ts +61 -18
  100. package/src/stream.ts +1 -1
@@ -1,40 +1,40 @@
1
- import { Filter } from '../types'
2
- import { RealTimeFeedBase } from './realtimefeed'
3
-
4
- export class DydxRealTimeFeed extends RealTimeFeedBase {
5
- protected readonly wssURL = 'wss://api.dydx.exchange/v3/ws'
6
-
7
- protected mapToSubscribeMessages(filters: Filter<string>[]): any[] {
8
- const subs = filters
9
- .map((filter) => {
10
- if (filter.channel === 'v3_markets') {
11
- return [
12
- {
13
- type: 'subscribe',
14
- channel: 'v3_markets'
15
- }
16
- ]
17
- }
18
-
19
- if (!filter.symbols || filter.symbols.length === 0) {
20
- throw new Error('DydxRealTimeFeed requires explicitly specified symbols when subscribing to live feed')
21
- }
22
-
23
- return filter.symbols.map((symbol) => {
24
- return {
25
- type: 'subscribe',
26
- channel: filter.channel,
27
- id: symbol,
28
- includeOffsets: filter.channel === 'v3_orderbook' ? true : undefined
29
- }
30
- })
31
- })
32
- .flatMap((f) => f)
33
-
34
- return subs
35
- }
36
-
37
- protected messageIsError(message: any): boolean {
38
- return message.type === 'error'
39
- }
40
- }
1
+ import { Filter } from '../types'
2
+ import { RealTimeFeedBase } from './realtimefeed'
3
+
4
+ export class DydxRealTimeFeed extends RealTimeFeedBase {
5
+ protected readonly wssURL = 'wss://api.dydx.exchange/v3/ws'
6
+
7
+ protected mapToSubscribeMessages(filters: Filter<string>[]): any[] {
8
+ const subs = filters
9
+ .map((filter) => {
10
+ if (filter.channel === 'v3_markets') {
11
+ return [
12
+ {
13
+ type: 'subscribe',
14
+ channel: 'v3_markets'
15
+ }
16
+ ]
17
+ }
18
+
19
+ if (!filter.symbols || filter.symbols.length === 0) {
20
+ throw new Error('DydxRealTimeFeed requires explicitly specified symbols when subscribing to live feed')
21
+ }
22
+
23
+ return filter.symbols.map((symbol) => {
24
+ return {
25
+ type: 'subscribe',
26
+ channel: filter.channel,
27
+ id: symbol,
28
+ includeOffsets: filter.channel === 'v3_orderbook' ? true : undefined
29
+ }
30
+ })
31
+ })
32
+ .flatMap((f) => f)
33
+
34
+ return subs
35
+ }
36
+
37
+ protected messageIsError(message: any): boolean {
38
+ return message.type === 'error'
39
+ }
40
+ }
@@ -1,41 +1,41 @@
1
- import { Filter } from '../types'
2
- import { RealTimeFeedBase } from './realtimefeed'
3
-
4
- export class GateIORealTimeFeed extends RealTimeFeedBase {
5
- protected readonly wssURL = 'wss://ws.gate.io/v3/'
6
-
7
- protected mapToSubscribeMessages(filters: Filter<string>[]): any[] {
8
- const id = 1
9
- const payload = filters.map((filter) => {
10
- if (!filter.symbols || filter.symbols.length === 0) {
11
- throw new Error('GateIORealTimeFeed requires explicitly specified symbols when subscribing to live feed')
12
- }
13
-
14
- if (filter.channel === 'depth') {
15
- return {
16
- id,
17
- method: `${filter.channel}.subscribe`,
18
- params: filter.symbols.map((s) => {
19
- return [s, 30, '0']
20
- })
21
- }
22
- } else {
23
- return {
24
- id,
25
- method: `${filter.channel}.subscribe`,
26
- params: filter.symbols
27
- }
28
- }
29
- })
30
-
31
- return payload
32
- }
33
-
34
- protected messageIsError(message: any): boolean {
35
- if (message.error !== null && message.error !== undefined) {
36
- return true
37
- }
38
-
39
- return false
40
- }
41
- }
1
+ import { Filter } from '../types'
2
+ import { RealTimeFeedBase } from './realtimefeed'
3
+
4
+ export class GateIORealTimeFeed extends RealTimeFeedBase {
5
+ protected readonly wssURL = 'wss://ws.gate.io/v3/'
6
+
7
+ protected mapToSubscribeMessages(filters: Filter<string>[]): any[] {
8
+ const id = 1
9
+ const payload = filters.map((filter) => {
10
+ if (!filter.symbols || filter.symbols.length === 0) {
11
+ throw new Error('GateIORealTimeFeed requires explicitly specified symbols when subscribing to live feed')
12
+ }
13
+
14
+ if (filter.channel === 'depth') {
15
+ return {
16
+ id,
17
+ method: `${filter.channel}.subscribe`,
18
+ params: filter.symbols.map((s) => {
19
+ return [s, 30, '0']
20
+ })
21
+ }
22
+ } else {
23
+ return {
24
+ id,
25
+ method: `${filter.channel}.subscribe`,
26
+ params: filter.symbols
27
+ }
28
+ }
29
+ })
30
+
31
+ return payload
32
+ }
33
+
34
+ protected messageIsError(message: any): boolean {
35
+ if (message.error !== null && message.error !== undefined) {
36
+ return true
37
+ }
38
+
39
+ return false
40
+ }
41
+ }
@@ -1,90 +1,90 @@
1
- import { Filter } from '../types'
2
- import { RealTimeFeedBase, MultiConnectionRealTimeFeedBase } from './realtimefeed'
3
-
4
- export class GateIOFuturesRealTimeFeed extends MultiConnectionRealTimeFeedBase {
5
- protected *_getRealTimeFeeds(exchange: string, filters: Filter<string>[], timeoutIntervalMS?: number, onError?: (error: Error) => void) {
6
- const linearContractsFilters = filters.reduce(
7
- this._only((s) => s.endsWith('_USDT')),
8
- [] as Filter<string>[]
9
- )
10
-
11
- const inverseContractsFilters = filters.reduce(
12
- this._only((s) => s.endsWith('_USDT') === false),
13
- [] as Filter<string>[]
14
- )
15
-
16
- if (linearContractsFilters.length > 0) {
17
- yield new GateIOFuturesSingleConnectionRealTimeFeed('usdt', exchange, linearContractsFilters, timeoutIntervalMS, onError)
18
- }
19
-
20
- if (inverseContractsFilters.length > 0) {
21
- yield new GateIOFuturesSingleConnectionRealTimeFeed('btc', exchange, inverseContractsFilters, timeoutIntervalMS, onError)
22
- }
23
- }
24
-
25
- private _only(filter: (symbol: string) => boolean) {
26
- return (prev: Filter<string>[], current: Filter<string>) => {
27
- if (!current.symbols || current.symbols.length === 0) {
28
- throw new Error('GateIOFuturesRealTimeFeed requires explicitly specified symbols when subscribing to live feed')
29
- }
30
-
31
- const symbols = current.symbols.filter(filter)
32
- if (symbols.length > 0) {
33
- prev.push({
34
- channel: current.channel,
35
- symbols
36
- })
37
- }
38
- return prev
39
- }
40
- }
41
- }
42
-
43
- class GateIOFuturesSingleConnectionRealTimeFeed extends RealTimeFeedBase {
44
- protected readonly wssURL: string
45
-
46
- constructor(
47
- wsURLSuffix: string,
48
- exchange: string,
49
- filters: Filter<string>[],
50
- timeoutIntervalMS?: number,
51
- onError?: (error: Error) => void
52
- ) {
53
- super(exchange, filters, timeoutIntervalMS, onError)
54
- this.wssURL = `wss://fx-ws.gateio.ws/v4/ws/${wsURLSuffix}`
55
- }
56
-
57
- protected mapToSubscribeMessages(filters: Filter<string>[]): any[] {
58
- const payload = filters.flatMap((filter) => {
59
- if (filter.channel === 'order_book') {
60
- return filter.symbols!.map((symbol) => {
61
- return {
62
- event: 'subscribe',
63
- channel: `futures.${filter.channel}`,
64
- payload: [symbol, '20', '0'],
65
- time: Math.floor(new Date().valueOf() / 1000)
66
- }
67
- })
68
- } else {
69
- return [
70
- {
71
- event: 'subscribe',
72
- channel: `futures.${filter.channel}`,
73
- payload: filter.symbols,
74
- time: Math.floor(new Date().valueOf() / 1000)
75
- }
76
- ]
77
- }
78
- })
79
-
80
- return payload
81
- }
82
-
83
- protected messageIsError(message: any): boolean {
84
- if (message.error !== null && message.error !== undefined) {
85
- return true
86
- }
87
-
88
- return false
89
- }
90
- }
1
+ import { Filter } from '../types'
2
+ import { RealTimeFeedBase, MultiConnectionRealTimeFeedBase } from './realtimefeed'
3
+
4
+ export class GateIOFuturesRealTimeFeed extends MultiConnectionRealTimeFeedBase {
5
+ protected *_getRealTimeFeeds(exchange: string, filters: Filter<string>[], timeoutIntervalMS?: number, onError?: (error: Error) => void) {
6
+ const linearContractsFilters = filters.reduce(
7
+ this._only((s) => s.endsWith('_USDT')),
8
+ [] as Filter<string>[]
9
+ )
10
+
11
+ const inverseContractsFilters = filters.reduce(
12
+ this._only((s) => s.endsWith('_USDT') === false),
13
+ [] as Filter<string>[]
14
+ )
15
+
16
+ if (linearContractsFilters.length > 0) {
17
+ yield new GateIOFuturesSingleConnectionRealTimeFeed('usdt', exchange, linearContractsFilters, timeoutIntervalMS, onError)
18
+ }
19
+
20
+ if (inverseContractsFilters.length > 0) {
21
+ yield new GateIOFuturesSingleConnectionRealTimeFeed('btc', exchange, inverseContractsFilters, timeoutIntervalMS, onError)
22
+ }
23
+ }
24
+
25
+ private _only(filter: (symbol: string) => boolean) {
26
+ return (prev: Filter<string>[], current: Filter<string>) => {
27
+ if (!current.symbols || current.symbols.length === 0) {
28
+ throw new Error('GateIOFuturesRealTimeFeed requires explicitly specified symbols when subscribing to live feed')
29
+ }
30
+
31
+ const symbols = current.symbols.filter(filter)
32
+ if (symbols.length > 0) {
33
+ prev.push({
34
+ channel: current.channel,
35
+ symbols
36
+ })
37
+ }
38
+ return prev
39
+ }
40
+ }
41
+ }
42
+
43
+ class GateIOFuturesSingleConnectionRealTimeFeed extends RealTimeFeedBase {
44
+ protected readonly wssURL: string
45
+
46
+ constructor(
47
+ wsURLSuffix: string,
48
+ exchange: string,
49
+ filters: Filter<string>[],
50
+ timeoutIntervalMS?: number,
51
+ onError?: (error: Error) => void
52
+ ) {
53
+ super(exchange, filters, timeoutIntervalMS, onError)
54
+ this.wssURL = `wss://fx-ws.gateio.ws/v4/ws/${wsURLSuffix}`
55
+ }
56
+
57
+ protected mapToSubscribeMessages(filters: Filter<string>[]): any[] {
58
+ const payload = filters.flatMap((filter) => {
59
+ if (filter.channel === 'order_book') {
60
+ return filter.symbols!.map((symbol) => {
61
+ return {
62
+ event: 'subscribe',
63
+ channel: `futures.${filter.channel}`,
64
+ payload: [symbol, '20', '0'],
65
+ time: Math.floor(new Date().valueOf() / 1000)
66
+ }
67
+ })
68
+ } else {
69
+ return [
70
+ {
71
+ event: 'subscribe',
72
+ channel: `futures.${filter.channel}`,
73
+ payload: filter.symbols,
74
+ time: Math.floor(new Date().valueOf() / 1000)
75
+ }
76
+ ]
77
+ }
78
+ })
79
+
80
+ return payload
81
+ }
82
+
83
+ protected messageIsError(message: any): boolean {
84
+ if (message.error !== null && message.error !== undefined) {
85
+ return true
86
+ }
87
+
88
+ return false
89
+ }
90
+ }
@@ -1,28 +1,28 @@
1
- import { Filter } from '../types'
2
- import { RealTimeFeedBase } from './realtimefeed'
3
-
4
- export class PoloniexRealTimeFeed extends RealTimeFeedBase {
5
- protected readonly wssURL = 'wss://api2.poloniex.com'
6
-
7
- protected mapToSubscribeMessages(filters: Filter<string>[]) {
8
- const allSymbols = filters.flatMap((filter) => {
9
- if (!filter.symbols || filter.symbols.length === 0) {
10
- throw new Error('PoloniexRealTimeFeed requires explicitly specified symbols when subscribing to live feed')
11
- }
12
- return filter.symbols
13
- })
14
-
15
- const uniqueSymbols = [...new Set(allSymbols)]
16
-
17
- return uniqueSymbols.map((symbol) => {
18
- return {
19
- command: 'subscribe',
20
- channel: symbol
21
- }
22
- })
23
- }
24
-
25
- protected messageIsError(message: any): boolean {
26
- return message.error !== undefined && message.error !== null
27
- }
28
- }
1
+ import { Filter } from '../types'
2
+ import { RealTimeFeedBase } from './realtimefeed'
3
+
4
+ export class PoloniexRealTimeFeed extends RealTimeFeedBase {
5
+ protected readonly wssURL = 'wss://api2.poloniex.com'
6
+
7
+ protected mapToSubscribeMessages(filters: Filter<string>[]) {
8
+ const allSymbols = filters.flatMap((filter) => {
9
+ if (!filter.symbols || filter.symbols.length === 0) {
10
+ throw new Error('PoloniexRealTimeFeed requires explicitly specified symbols when subscribing to live feed')
11
+ }
12
+ return filter.symbols
13
+ })
14
+
15
+ const uniqueSymbols = [...new Set(allSymbols)]
16
+
17
+ return uniqueSymbols.map((symbol) => {
18
+ return {
19
+ command: 'subscribe',
20
+ channel: symbol
21
+ }
22
+ })
23
+ }
24
+
25
+ protected messageIsError(message: any): boolean {
26
+ return message.error !== undefined && message.error !== null
27
+ }
28
+ }
@@ -43,7 +43,7 @@ export abstract class RealTimeFeedBase implements RealTimeFeedIterable {
43
43
  this._filters = optimizeFilters(filters)
44
44
  this.debug = dbg(`tardis-dev:realtime:${_exchange}`)
45
45
 
46
- this._wsClientOptions = { perMessageDeflate: false, handshakeTimeout: 10 * ONE_SEC_IN_MS }
46
+ this._wsClientOptions = { perMessageDeflate: false, handshakeTimeout: 10 * ONE_SEC_IN_MS, skipUTF8Validation: true } as any
47
47
 
48
48
  if (httpsProxyAgent !== undefined) {
49
49
  this._wsClientOptions.agent = httpsProxyAgent
@@ -123,7 +123,7 @@ export abstract class RealTimeFeedBase implements RealTimeFeedIterable {
123
123
  clearInterval(staleConnectionTimerId)
124
124
  }
125
125
  yield { __disconnect__: true }
126
- } catch (error) {
126
+ } catch (error: any) {
127
127
  if (this._onError !== undefined) {
128
128
  this._onError(error)
129
129
  }
@@ -1,35 +1,35 @@
1
- import { Filter } from '../types'
2
- import { RealTimeFeedBase } from './realtimefeed'
3
-
4
- export class UpbitRealTimeFeed extends RealTimeFeedBase {
5
- protected readonly wssURL = 'wss://api.upbit.com/websocket/v1'
6
-
7
- protected mapToSubscribeMessages(filters: Filter<string>[]): any[] {
8
- const subs = filters.map((filter) => {
9
- if (!filter.symbols || filter.symbols.length === 0) {
10
- throw new Error('UpbitRealTimeFeed requires explicitly specified symbols when subscribing to live feed')
11
- }
12
-
13
- return {
14
- type: filter.channel,
15
- codes: filter.symbols,
16
- isOnlyRealtime: true
17
- }
18
- })
19
-
20
- const payload = [
21
- [
22
- {
23
- ticket: new Date().valueOf().toString()
24
- },
25
- ...subs
26
- ]
27
- ]
28
-
29
- return payload
30
- }
31
-
32
- protected messageIsError(message: any): boolean {
33
- return message.type === 'error'
34
- }
35
- }
1
+ import { Filter } from '../types'
2
+ import { RealTimeFeedBase } from './realtimefeed'
3
+
4
+ export class UpbitRealTimeFeed extends RealTimeFeedBase {
5
+ protected readonly wssURL = 'wss://api.upbit.com/websocket/v1'
6
+
7
+ protected mapToSubscribeMessages(filters: Filter<string>[]): any[] {
8
+ const subs = filters.map((filter) => {
9
+ if (!filter.symbols || filter.symbols.length === 0) {
10
+ throw new Error('UpbitRealTimeFeed requires explicitly specified symbols when subscribing to live feed')
11
+ }
12
+
13
+ return {
14
+ type: filter.channel,
15
+ codes: filter.symbols,
16
+ isOnlyRealtime: true
17
+ }
18
+ })
19
+
20
+ const payload = [
21
+ [
22
+ {
23
+ ticket: new Date().valueOf().toString()
24
+ },
25
+ ...subs
26
+ ]
27
+ ]
28
+
29
+ return payload
30
+ }
31
+
32
+ protected messageIsError(message: any): boolean {
33
+ return message.type === 'error'
34
+ }
35
+ }
package/src/replay.ts CHANGED
@@ -1,16 +1,17 @@
1
1
  import { createReadStream } from 'fs-extra'
2
2
  import path from 'path'
3
+ import { EventEmitter } from 'stream'
3
4
  import { Worker } from 'worker_threads'
4
5
  import { constants, createGunzip } from 'zlib'
5
6
  import { BinarySplitStream } from './binarysplit'
7
+ import { clearCacheSync } from './clearcache'
6
8
  import { EXCHANGES, EXCHANGE_CHANNELS_INFO } from './consts'
7
9
  import { debug } from './debug'
8
- import { getFilters, normalizeMessages, parseAsUTCDate, parseμs, wait, addDays } from './handy'
10
+ import { addDays, getFilters, normalizeMessages, parseAsUTCDate, parseμs, wait } from './handy'
9
11
  import { MapperFactory, normalizeBookChanges } from './mappers'
10
12
  import { getOptions } from './options'
11
13
  import { Disconnect, Exchange, FilterForExchange } from './types'
12
14
  import { WorkerJobPayload, WorkerMessage, WorkerSignal } from './worker'
13
- import { clearCacheSync } from './clearcache'
14
15
 
15
16
  export async function* replay<T extends Exchange, U extends boolean = false, Z extends boolean = false>({
16
17
  exchange,
@@ -55,9 +56,7 @@ export async function* replay<T extends Exchange, U extends boolean = false, Z e
55
56
  waitWhenDataNotYetAvailable
56
57
  }
57
58
 
58
- const worker = new Worker(path.resolve(__dirname, 'worker.js'), {
59
- workerData: payload
60
- })
59
+ const worker = new ReliableWorker(payload)
61
60
 
62
61
  worker.on('message', (message: WorkerMessage) => {
63
62
  cachedSlicePaths.set(message.sliceKey, message.slicePath)
@@ -69,25 +68,20 @@ export async function* replay<T extends Exchange, U extends boolean = false, Z e
69
68
  replayError = err
70
69
  })
71
70
 
72
- worker.on('exit', (code) => {
73
- debug('worker finished with code: %d', code)
74
- })
75
-
76
71
  try {
77
72
  // date is always formatted to have length of 28 so we can skip looking for first space in line and use it
78
73
  // as hardcoded value
79
74
  const DATE_MESSAGE_SPLIT_INDEX = 28
80
75
 
81
- // experimental more lenient gzip decompression, behind env flag for now
76
+ // more lenient gzip decompression
82
77
  // see https://github.com/request/request/pull/2492 and https://github.com/node-fetch/node-fetch/pull/239
83
78
 
84
- const ZLIB_OPTIONS = process.env.TARDIS_LENIENT_GZIP_DECOMPRESS
85
- ? {
86
- chunkSize: 128 * 1024,
87
- flush: constants.Z_SYNC_FLUSH,
88
- finishFlush: constants.Z_SYNC_FLUSH
89
- }
90
- : { chunkSize: 128 * 1024 }
79
+ const ZLIB_OPTIONS = {
80
+ chunkSize: 128 * 1024,
81
+ flush: constants.Z_SYNC_FLUSH,
82
+ finishFlush: constants.Z_SYNC_FLUSH
83
+ }
84
+
91
85
  // helper flag that helps us not yielding two subsequent undefined/disconnect messages
92
86
  let lastMessageWasUndefined = false
93
87
 
@@ -208,7 +202,10 @@ export async function* replay<T extends Exchange, U extends boolean = false, Z e
208
202
  )
209
203
  }
210
204
 
211
- await terminateWorker(worker, 500)
205
+ const underlyingWorker = worker.getUnderlyingWorker()
206
+ if (underlyingWorker !== undefined) {
207
+ await terminateWorker(underlyingWorker, 500)
208
+ }
212
209
  }
213
210
  }
214
211
 
@@ -330,6 +327,52 @@ function validateReplayNormalizedOptions(fromDate: Date, normalizers: MapperFact
330
327
  }
331
328
  }
332
329
 
330
+ class ReliableWorker extends EventEmitter {
331
+ private _errorsCount = 0
332
+ private _worker: Worker | undefined = undefined
333
+
334
+ constructor(private readonly _payload: WorkerJobPayload) {
335
+ super()
336
+
337
+ this._initWorker()
338
+ }
339
+
340
+ private _initWorker() {
341
+ this._worker = new Worker(path.resolve(__dirname, 'worker.js'), {
342
+ workerData: this._payload
343
+ })
344
+
345
+ this._worker.on('message', (message: WorkerMessage) => {
346
+ this.emit('message', message)
347
+ })
348
+
349
+ this._worker.on('error', this._handleError)
350
+
351
+ this._worker.on('exit', (code) => {
352
+ debug('worker finished with code: %d', code)
353
+ })
354
+ }
355
+
356
+ private _handleError = async (err: Error) => {
357
+ debug('underlying worker error %o', err)
358
+
359
+ if (err.message.includes('HttpError') === false && this._errorsCount < 30) {
360
+ this._errorsCount++
361
+ const delayMS = Math.min(Math.pow(2, this._errorsCount) * 1000, 120 * 1000)
362
+ debug('re-init worker after: %d ms', delayMS)
363
+ await wait(delayMS)
364
+ // it was most likely unhandled socket hang up error, let's retry first with new worker and don't emit error right away
365
+ this._initWorker()
366
+ } else {
367
+ this.emit('error', err)
368
+ }
369
+ }
370
+
371
+ public getUnderlyingWorker() {
372
+ return this._worker
373
+ }
374
+ }
375
+
333
376
  export type ReplayOptions<T extends Exchange, U extends boolean = false, Z extends boolean = false> = {
334
377
  readonly exchange: T
335
378
  readonly from: string
package/src/stream.ts CHANGED
@@ -92,7 +92,7 @@ async function* _streamNormalized<T extends Exchange, U extends MapperFactory<T,
92
92
  for await (const message of normalizedMessages) {
93
93
  yield message
94
94
  }
95
- } catch (error) {
95
+ } catch (error: any) {
96
96
  if (onError !== undefined) {
97
97
  onError(error)
98
98
  }