tardis-machine 18.0.0 → 18.1.0
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/benchmark.js +35 -14
- package/bin/tardis-machine.js +0 -1
- package/dist/tardismachine.d.ts.map +1 -1
- package/dist/tardismachine.js +20 -55
- package/dist/tardismachine.js.map +1 -1
- package/dist/ws/replay.d.ts +2 -2
- package/dist/ws/replay.d.ts.map +1 -1
- package/dist/ws/replay.js +11 -5
- package/dist/ws/replay.js.map +1 -1
- package/dist/ws/replaynormalized.d.ts +2 -2
- package/dist/ws/replaynormalized.d.ts.map +1 -1
- package/dist/ws/replaynormalized.js +7 -5
- package/dist/ws/replaynormalized.js.map +1 -1
- package/dist/ws/server.d.ts +20 -0
- package/dist/ws/server.d.ts.map +1 -0
- package/dist/ws/server.js +154 -0
- package/dist/ws/server.js.map +1 -0
- package/dist/ws/streamnormalized.d.ts +2 -2
- package/dist/ws/streamnormalized.d.ts.map +1 -1
- package/dist/ws/streamnormalized.js +7 -5
- package/dist/ws/streamnormalized.js.map +1 -1
- package/dist/ws/subscriptionsmappers.d.ts +1 -1
- package/dist/ws/subscriptionsmappers.d.ts.map +1 -1
- package/node_modules/tardis-dev/README.md +1 -1
- package/node_modules/tardis-dev/dist/debug.d.ts +12 -2
- package/node_modules/tardis-dev/dist/debug.d.ts.map +1 -1
- package/node_modules/tardis-dev/dist/debug.js +2 -1
- package/node_modules/tardis-dev/dist/debug.js.map +1 -1
- package/node_modules/tardis-dev/dist/downloaddatasets.d.ts.map +1 -1
- package/node_modules/tardis-dev/dist/downloaddatasets.js +3 -1
- package/node_modules/tardis-dev/dist/downloaddatasets.js.map +1 -1
- package/node_modules/tardis-dev/dist/handy.d.ts +5 -2
- package/node_modules/tardis-dev/dist/handy.d.ts.map +1 -1
- package/node_modules/tardis-dev/dist/handy.js +77 -90
- package/node_modules/tardis-dev/dist/handy.js.map +1 -1
- package/node_modules/tardis-dev/dist/mappers/hyperliquid.d.ts +1 -1
- package/node_modules/tardis-dev/dist/mappers/hyperliquid.d.ts.map +1 -1
- package/node_modules/tardis-dev/dist/mappers/hyperliquid.js +15 -4
- package/node_modules/tardis-dev/dist/mappers/hyperliquid.js.map +1 -1
- package/node_modules/tardis-dev/dist/mappers/index.d.ts +1 -1
- package/node_modules/tardis-dev/dist/realtimefeeds/mexc.d.ts +0 -7
- package/node_modules/tardis-dev/dist/realtimefeeds/mexc.d.ts.map +1 -1
- package/node_modules/tardis-dev/dist/realtimefeeds/mexc.js +2 -70
- package/node_modules/tardis-dev/dist/realtimefeeds/mexc.js.map +1 -1
- package/node_modules/tardis-dev/dist/realtimefeeds/mexcprotobuf.d.ts +43 -0
- package/node_modules/tardis-dev/dist/realtimefeeds/mexcprotobuf.d.ts.map +1 -0
- package/node_modules/tardis-dev/dist/realtimefeeds/mexcprotobuf.js +294 -0
- package/node_modules/tardis-dev/dist/realtimefeeds/mexcprotobuf.js.map +1 -0
- package/node_modules/tardis-dev/dist/realtimefeeds/realtimefeed.d.ts +3 -3
- package/node_modules/tardis-dev/dist/realtimefeeds/realtimefeed.d.ts.map +1 -1
- package/node_modules/tardis-dev/dist/realtimefeeds/realtimefeed.js +3 -3
- package/node_modules/tardis-dev/dist/realtimefeeds/realtimefeed.js.map +1 -1
- package/node_modules/tardis-dev/dist/replay.d.ts.map +1 -1
- package/node_modules/tardis-dev/dist/replay.js +6 -4
- package/node_modules/tardis-dev/dist/replay.js.map +1 -1
- package/node_modules/tardis-dev/dist/worker.d.ts.map +1 -1
- package/node_modules/tardis-dev/dist/worker.js +7 -4
- package/node_modules/tardis-dev/dist/worker.js.map +1 -1
- package/node_modules/tardis-dev/dist/zstd.d.ts +3 -0
- package/node_modules/tardis-dev/dist/zstd.d.ts.map +1 -0
- package/node_modules/tardis-dev/dist/zstd.js +122 -0
- package/node_modules/tardis-dev/dist/zstd.js.map +1 -0
- package/node_modules/tardis-dev/package.json +3 -7
- package/node_modules/tardis-dev/src/debug.ts +16 -1
- package/node_modules/tardis-dev/src/downloaddatasets.ts +3 -1
- package/node_modules/tardis-dev/src/external-modules.d.ts +5 -0
- package/node_modules/tardis-dev/src/handy.ts +100 -93
- package/node_modules/tardis-dev/src/mappers/hyperliquid.ts +19 -5
- package/node_modules/tardis-dev/src/realtimefeeds/mexc.ts +2 -72
- package/node_modules/tardis-dev/src/realtimefeeds/mexcprotobuf.ts +364 -0
- package/node_modules/tardis-dev/src/realtimefeeds/realtimefeed.ts +5 -5
- package/node_modules/tardis-dev/src/replay.ts +6 -4
- package/node_modules/tardis-dev/src/worker.ts +7 -4
- package/node_modules/tardis-dev/src/zstd.ts +139 -0
- package/package.json +9 -35
- package/src/tardismachine.ts +25 -66
- package/src/ws/replay.ts +13 -8
- package/src/ws/replaynormalized.ts +8 -6
- package/src/ws/server.ts +173 -0
- package/src/ws/streamnormalized.ts +8 -6
- package/src/ws/subscriptionsmappers.ts +1 -1
- package/node_modules/follow-redirects/LICENSE +0 -18
- package/node_modules/follow-redirects/README.md +0 -157
- package/node_modules/follow-redirects/debug.js +0 -15
- package/node_modules/follow-redirects/http.js +0 -1
- package/node_modules/follow-redirects/https.js +0 -1
- package/node_modules/follow-redirects/index.js +0 -709
- package/node_modules/follow-redirects/package.json +0 -58
- package/node_modules/long/LICENSE +0 -202
- package/node_modules/long/README.md +0 -286
- package/node_modules/long/index.d.ts +0 -2
- package/node_modules/long/index.js +0 -1581
- package/node_modules/long/package.json +0 -58
- package/node_modules/long/types.d.ts +0 -474
- package/node_modules/long/umd/index.d.ts +0 -3
- package/node_modules/long/umd/index.js +0 -1622
- package/node_modules/long/umd/package.json +0 -3
- package/node_modules/long/umd/types.d.ts +0 -474
- package/node_modules/protobufjs/LICENSE +0 -39
- package/node_modules/protobufjs/README.md +0 -440
- package/node_modules/protobufjs/dist/light/protobuf.js +0 -9276
- package/node_modules/protobufjs/dist/light/protobuf.js.map +0 -1
- package/node_modules/protobufjs/dist/light/protobuf.min.js +0 -8
- package/node_modules/protobufjs/dist/light/protobuf.min.js.map +0 -1
- package/node_modules/protobufjs/dist/minimal/protobuf.js +0 -3556
- package/node_modules/protobufjs/dist/minimal/protobuf.js.map +0 -1
- package/node_modules/protobufjs/dist/minimal/protobuf.min.js +0 -8
- package/node_modules/protobufjs/dist/minimal/protobuf.min.js.map +0 -1
- package/node_modules/protobufjs/dist/protobuf.js +0 -11233
- package/node_modules/protobufjs/dist/protobuf.js.map +0 -1
- package/node_modules/protobufjs/dist/protobuf.min.js +0 -8
- package/node_modules/protobufjs/dist/protobuf.min.js.map +0 -1
- package/node_modules/protobufjs/ext/README.md +0 -70
- package/node_modules/protobufjs/ext/debug/README.md +0 -4
- package/node_modules/protobufjs/ext/debug/index.js +0 -71
- package/node_modules/protobufjs/ext/descriptor/README.md +0 -5
- package/node_modules/protobufjs/ext/descriptor/index.d.ts +0 -2
- package/node_modules/protobufjs/ext/descriptor/index.js +0 -2
- package/node_modules/protobufjs/ext/descriptor.d.ts +0 -87
- package/node_modules/protobufjs/ext/descriptor.generated.d.ts +0 -417
- package/node_modules/protobufjs/ext/descriptor.js +0 -1356
- package/node_modules/protobufjs/ext/protojson.LICENSE +0 -201
- package/node_modules/protobufjs/ext/protojson.d.ts +0 -20
- package/node_modules/protobufjs/ext/protojson.generated.d.ts +0 -49
- package/node_modules/protobufjs/ext/protojson.js +0 -951
- package/node_modules/protobufjs/ext/textformat.d.ts +0 -19
- package/node_modules/protobufjs/ext/textformat.generated.d.ts +0 -33
- package/node_modules/protobufjs/ext/textformat.js +0 -1276
- package/node_modules/protobufjs/google/LICENSE +0 -27
- package/node_modules/protobufjs/google/README.md +0 -1
- package/node_modules/protobufjs/google/api/annotations.json +0 -85
- package/node_modules/protobufjs/google/api/annotations.proto +0 -11
- package/node_modules/protobufjs/google/api/http.json +0 -87
- package/node_modules/protobufjs/google/api/http.proto +0 -31
- package/node_modules/protobufjs/google/protobuf/api.json +0 -124
- package/node_modules/protobufjs/google/protobuf/api.proto +0 -34
- package/node_modules/protobufjs/google/protobuf/compiler/plugin.json +0 -126
- package/node_modules/protobufjs/google/protobuf/compiler/plugin.proto +0 -47
- package/node_modules/protobufjs/google/protobuf/descriptor.json +0 -1465
- package/node_modules/protobufjs/google/protobuf/descriptor.proto +0 -527
- package/node_modules/protobufjs/google/protobuf/source_context.json +0 -21
- package/node_modules/protobufjs/google/protobuf/source_context.proto +0 -7
- package/node_modules/protobufjs/google/protobuf/type.json +0 -209
- package/node_modules/protobufjs/google/protobuf/type.proto +0 -89
- package/node_modules/protobufjs/index.d.ts +0 -3203
- package/node_modules/protobufjs/index.js +0 -4
- package/node_modules/protobufjs/light.d.ts +0 -2
- package/node_modules/protobufjs/light.js +0 -4
- package/node_modules/protobufjs/minimal.d.ts +0 -2
- package/node_modules/protobufjs/minimal.js +0 -4
- package/node_modules/protobufjs/package.json +0 -93
- package/node_modules/protobufjs/src/common.js +0 -405
- package/node_modules/protobufjs/src/converter.js +0 -352
- package/node_modules/protobufjs/src/decoder.js +0 -279
- package/node_modules/protobufjs/src/encoder.js +0 -123
- package/node_modules/protobufjs/src/enum.js +0 -239
- package/node_modules/protobufjs/src/field.js +0 -505
- package/node_modules/protobufjs/src/index-light.js +0 -104
- package/node_modules/protobufjs/src/index-minimal.js +0 -35
- package/node_modules/protobufjs/src/index.js +0 -12
- package/node_modules/protobufjs/src/mapfield.js +0 -144
- package/node_modules/protobufjs/src/message.js +0 -137
- package/node_modules/protobufjs/src/method.js +0 -183
- package/node_modules/protobufjs/src/namespace.js +0 -574
- package/node_modules/protobufjs/src/object.js +0 -392
- package/node_modules/protobufjs/src/oneof.js +0 -233
- package/node_modules/protobufjs/src/parse.js +0 -1111
- package/node_modules/protobufjs/src/reader.js +0 -788
- package/node_modules/protobufjs/src/reader_buffer.js +0 -77
- package/node_modules/protobufjs/src/root.js +0 -424
- package/node_modules/protobufjs/src/roots.js +0 -18
- package/node_modules/protobufjs/src/rpc/service.js +0 -155
- package/node_modules/protobufjs/src/rpc.js +0 -36
- package/node_modules/protobufjs/src/service.js +0 -206
- package/node_modules/protobufjs/src/tokenize.js +0 -421
- package/node_modules/protobufjs/src/type.js +0 -676
- package/node_modules/protobufjs/src/types.js +0 -196
- package/node_modules/protobufjs/src/typescript.js +0 -25
- package/node_modules/protobufjs/src/util/aspromise.d.ts +0 -13
- package/node_modules/protobufjs/src/util/aspromise.js +0 -52
- package/node_modules/protobufjs/src/util/base64.d.ts +0 -32
- package/node_modules/protobufjs/src/util/base64.js +0 -146
- package/node_modules/protobufjs/src/util/codegen.d.ts +0 -31
- package/node_modules/protobufjs/src/util/codegen.js +0 -118
- package/node_modules/protobufjs/src/util/eventemitter.d.ts +0 -45
- package/node_modules/protobufjs/src/util/eventemitter.js +0 -86
- package/node_modules/protobufjs/src/util/fetch.d.ts +0 -56
- package/node_modules/protobufjs/src/util/fetch.js +0 -112
- package/node_modules/protobufjs/src/util/float.d.ts +0 -83
- package/node_modules/protobufjs/src/util/float.js +0 -335
- package/node_modules/protobufjs/src/util/fs.js +0 -11
- package/node_modules/protobufjs/src/util/longbits.js +0 -204
- package/node_modules/protobufjs/src/util/minimal.js +0 -511
- package/node_modules/protobufjs/src/util/path.d.ts +0 -22
- package/node_modules/protobufjs/src/util/path.js +0 -100
- package/node_modules/protobufjs/src/util/patterns.js +0 -7
- package/node_modules/protobufjs/src/util/pool.d.ts +0 -32
- package/node_modules/protobufjs/src/util/pool.js +0 -48
- package/node_modules/protobufjs/src/util/utf8.d.ts +0 -24
- package/node_modules/protobufjs/src/util/utf8.js +0 -190
- package/node_modules/protobufjs/src/util.js +0 -264
- package/node_modules/protobufjs/src/verifier.js +0 -185
- package/node_modules/protobufjs/src/wrappers.js +0 -106
- package/node_modules/protobufjs/src/writer.js +0 -700
- package/node_modules/protobufjs/src/writer_buffer.js +0 -100
- package/node_modules/protobufjs/tsconfig.json +0 -6
- package/node_modules/uWebSockets.js/ESM_wrapper.mjs +0 -53
- package/node_modules/uWebSockets.js/LICENSE +0 -201
- package/node_modules/uWebSockets.js/README.md +0 -6
- package/node_modules/uWebSockets.js/index.d.ts +0 -468
- package/node_modules/uWebSockets.js/package.json +0 -12
- package/node_modules/uWebSockets.js/source_commit +0 -1
- package/node_modules/uWebSockets.js/uws.js +0 -76
- package/node_modules/uWebSockets.js/uws_darwin_arm64_127.node +0 -0
- package/node_modules/uWebSockets.js/uws_darwin_arm64_137.node +0 -0
- package/node_modules/uWebSockets.js/uws_darwin_arm64_147.node +0 -0
- package/node_modules/uWebSockets.js/uws_darwin_x64_127.node +0 -0
- package/node_modules/uWebSockets.js/uws_darwin_x64_137.node +0 -0
- package/node_modules/uWebSockets.js/uws_darwin_x64_147.node +0 -0
- package/node_modules/uWebSockets.js/uws_linux_arm64_127.node +0 -0
- package/node_modules/uWebSockets.js/uws_linux_arm64_137.node +0 -0
- package/node_modules/uWebSockets.js/uws_linux_arm64_147.node +0 -0
- package/node_modules/uWebSockets.js/uws_linux_x64_127.node +0 -0
- package/node_modules/uWebSockets.js/uws_linux_x64_137.node +0 -0
- package/node_modules/uWebSockets.js/uws_linux_x64_147.node +0 -0
- package/node_modules/uWebSockets.js/uws_win32_x64_127.node +0 -0
- package/node_modules/uWebSockets.js/uws_win32_x64_137.node +0 -0
- package/node_modules/uWebSockets.js/uws_win32_x64_147.node +0 -0
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
import crypto, { createHash } from 'crypto'
|
|
2
2
|
import { createWriteStream, mkdirSync, rmSync } from 'node:fs'
|
|
3
3
|
import { rename } from 'node:fs/promises'
|
|
4
|
-
import { Agent as HttpAgent } from 'node:http'
|
|
5
|
-
import { Agent as HttpsAgent, type RequestOptions } from 'node:https'
|
|
6
|
-
import
|
|
4
|
+
import { Agent as HttpAgent, request as httpRequest, type IncomingMessage } from 'node:http'
|
|
5
|
+
import { Agent as HttpsAgent, request as httpsRequest, type RequestOptions } from 'node:https'
|
|
6
|
+
import { pipeline } from 'node:stream/promises'
|
|
7
7
|
import path from 'path'
|
|
8
8
|
import { debug } from './debug.ts'
|
|
9
9
|
import { Mapper } from './mappers/index.ts'
|
|
10
10
|
import { Disconnect, Exchange, Filter, FilterForExchange } from './types.ts'
|
|
11
|
-
const { http: followRedirectsHttp, https: followRedirectsHttps } = followRedirects
|
|
12
11
|
|
|
13
12
|
export function parseAsUTCDate(val: string) {
|
|
14
13
|
// Treat date-only and minute-level strings as UTC instead of local time.
|
|
@@ -456,44 +455,75 @@ async function requestViaFetch(method: string, url: string, options: HttpRequest
|
|
|
456
455
|
}
|
|
457
456
|
}
|
|
458
457
|
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
458
|
+
type DownloadRequestOptions = {
|
|
459
|
+
headers: Record<string, string>
|
|
460
|
+
timeout: number
|
|
461
|
+
}
|
|
462
462
|
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
headers: preparedRequest.headers,
|
|
471
|
-
timeout: options.timeout
|
|
472
|
-
},
|
|
473
|
-
(response) => {
|
|
474
|
-
response.setEncoding('utf8')
|
|
475
|
-
let body = ''
|
|
476
|
-
response.on('error', reject)
|
|
477
|
-
response.on('data', (chunk) => (body += chunk))
|
|
478
|
-
response.on('end', () => {
|
|
479
|
-
resolve(createHttpResponse(response.statusCode ?? 0, parseNodeResponseHeaders(response.headers), body))
|
|
480
|
-
})
|
|
481
|
-
}
|
|
482
|
-
)
|
|
483
|
-
.on('error', reject)
|
|
484
|
-
.on('timeout', () => {
|
|
485
|
-
reject(new Error('Request timed out'))
|
|
486
|
-
request.destroy()
|
|
487
|
-
})
|
|
488
|
-
|
|
489
|
-
if (preparedRequest.body !== undefined) {
|
|
490
|
-
request.write(preparedRequest.body)
|
|
491
|
-
}
|
|
463
|
+
function sendHttpRequest(url: string | URL, options: RequestOptions, body?: string): Promise<IncomingMessage> {
|
|
464
|
+
const requestUrl = new URL(url)
|
|
465
|
+
const requestClient = requestUrl.protocol === 'http:' ? httpRequest : httpsRequest
|
|
466
|
+
const agent = getProxyAgent(requestUrl) ?? (requestUrl.protocol === 'https:' ? httpsAgent : undefined)
|
|
467
|
+
|
|
468
|
+
return new Promise((resolve, reject) => {
|
|
469
|
+
const request = requestClient(requestUrl, { ...options, agent }, resolve)
|
|
492
470
|
|
|
471
|
+
request.once('error', reject)
|
|
472
|
+
request.once('timeout', () => request.destroy(new Error('Request timed out')))
|
|
473
|
+
if (body !== undefined) {
|
|
474
|
+
request.write(body)
|
|
475
|
+
}
|
|
493
476
|
request.end()
|
|
494
477
|
})
|
|
495
478
|
}
|
|
496
479
|
|
|
480
|
+
async function openDownloadResponse(url: string, options: DownloadRequestOptions) {
|
|
481
|
+
const response = await sendHttpRequest(url, options)
|
|
482
|
+
const statusCode = response.statusCode ?? 0
|
|
483
|
+
const location = response.headers.location
|
|
484
|
+
if (location === undefined || statusCode < 300 || statusCode >= 400) {
|
|
485
|
+
return response
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
response.destroy()
|
|
489
|
+
const sourceUrl = new URL(url)
|
|
490
|
+
const redirectUrl = new URL(location, sourceUrl)
|
|
491
|
+
const headers: Record<string, string> = { ...options.headers }
|
|
492
|
+
if (sourceUrl.origin !== redirectUrl.origin) {
|
|
493
|
+
delete headers.Authorization
|
|
494
|
+
delete headers.authorization
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
return sendHttpRequest(redirectUrl, {
|
|
498
|
+
...options,
|
|
499
|
+
headers
|
|
500
|
+
})
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
async function readResponseText(response: IncomingMessage) {
|
|
504
|
+
response.setEncoding('utf8')
|
|
505
|
+
let body = ''
|
|
506
|
+
for await (const chunk of response) {
|
|
507
|
+
body += chunk
|
|
508
|
+
}
|
|
509
|
+
return body
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
async function requestViaProxy(method: string, url: string, options: HttpRequestOptions): Promise<HttpResponse> {
|
|
513
|
+
const preparedRequest = prepareRequest(method, options)
|
|
514
|
+
const response = await sendHttpRequest(
|
|
515
|
+
url,
|
|
516
|
+
{
|
|
517
|
+
method,
|
|
518
|
+
headers: preparedRequest.headers,
|
|
519
|
+
timeout: options.timeout
|
|
520
|
+
},
|
|
521
|
+
preparedRequest.body
|
|
522
|
+
)
|
|
523
|
+
|
|
524
|
+
return createHttpResponse(response.statusCode ?? 0, parseNodeResponseHeaders(response.headers), await readResponseText(response))
|
|
525
|
+
}
|
|
526
|
+
|
|
497
527
|
async function request(method: string, url: string, options: HttpRequestOptions = {}) {
|
|
498
528
|
const retrySettings = getRetrySettings(method, options.retry)
|
|
499
529
|
|
|
@@ -551,7 +581,8 @@ export async function download({
|
|
|
551
581
|
url,
|
|
552
582
|
userAgent,
|
|
553
583
|
appendContentEncodingExtension = false,
|
|
554
|
-
acceptEncoding = 'gzip'
|
|
584
|
+
acceptEncoding = 'gzip',
|
|
585
|
+
attemptTimeoutMS = 90 * ONE_SEC_IN_MS
|
|
555
586
|
}: {
|
|
556
587
|
url: string
|
|
557
588
|
downloadPath: string
|
|
@@ -559,9 +590,10 @@ export async function download({
|
|
|
559
590
|
apiKey: string
|
|
560
591
|
appendContentEncodingExtension?: boolean
|
|
561
592
|
acceptEncoding?: string
|
|
593
|
+
attemptTimeoutMS?: number
|
|
562
594
|
}) {
|
|
563
595
|
const httpRequestOptions = {
|
|
564
|
-
timeout:
|
|
596
|
+
timeout: attemptTimeoutMS,
|
|
565
597
|
headers: {
|
|
566
598
|
'Accept-Encoding': acceptEncoding,
|
|
567
599
|
'User-Agent': userAgent,
|
|
@@ -622,17 +654,22 @@ export function cleanTempFiles() {
|
|
|
622
654
|
tmpFileCleanups.forEach((cleanup) => cleanup())
|
|
623
655
|
}
|
|
624
656
|
|
|
625
|
-
async function _downloadFile(
|
|
657
|
+
async function _downloadFile(
|
|
658
|
+
requestOptions: DownloadRequestOptions,
|
|
659
|
+
url: string,
|
|
660
|
+
downloadPath: string,
|
|
661
|
+
appendContentEncodingExtension: boolean
|
|
662
|
+
) {
|
|
626
663
|
// first ensure that directory where we want to download file exists
|
|
627
664
|
mkdirSync(path.dirname(downloadPath), { recursive: true })
|
|
628
665
|
|
|
629
666
|
// create write file stream that we'll write data into - first as unconfirmed temp file
|
|
630
667
|
|
|
631
668
|
const tmpFilePath = `${downloadPath}${crypto.randomBytes(8).toString('hex')}.unconfirmed`
|
|
632
|
-
|
|
669
|
+
let fileWriteStream: ReturnType<typeof createWriteStream> | undefined
|
|
633
670
|
const cleanup = () => {
|
|
634
671
|
try {
|
|
635
|
-
fileWriteStream
|
|
672
|
+
fileWriteStream?.destroy()
|
|
636
673
|
rmSync(tmpFilePath, { force: true })
|
|
637
674
|
} catch {}
|
|
638
675
|
}
|
|
@@ -642,59 +679,29 @@ async function _downloadFile(requestOptions: RequestOptions, url: string, downlo
|
|
|
642
679
|
|
|
643
680
|
try {
|
|
644
681
|
// based on https://github.com/nodejs/node/issues/28172 - only reliable way to consume response stream and avoiding all the 'gotchas'
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
const req = requestClient
|
|
651
|
-
.get(url, { ...requestOptions, agent }, (res) => {
|
|
652
|
-
const { statusCode } = res
|
|
653
|
-
if (statusCode !== 200) {
|
|
654
|
-
// read the error response text and throw it as an HttpError
|
|
655
|
-
res.setEncoding('utf8')
|
|
656
|
-
let body = ''
|
|
657
|
-
res.on('error', reject)
|
|
658
|
-
res.on('data', (chunk) => (body += chunk))
|
|
659
|
-
res.on('end', () => {
|
|
660
|
-
reject(new HttpError(statusCode!, body, url))
|
|
661
|
-
})
|
|
662
|
-
} else {
|
|
663
|
-
responseHeaders = parseNodeResponseHeaders(res.headers)
|
|
664
|
-
if (appendContentEncodingExtension) {
|
|
665
|
-
const contentEncoding = asSingleHeaderValue(res.headers['content-encoding'])
|
|
666
|
-
if (contentEncoding === 'zstd') {
|
|
667
|
-
finalDownloadPath = `${downloadPath}.zst`
|
|
668
|
-
} else if (contentEncoding === undefined || contentEncoding === 'gzip') {
|
|
669
|
-
finalDownloadPath = `${downloadPath}.gz`
|
|
670
|
-
} else {
|
|
671
|
-
reject(new Error(`Unsupported data feed content encoding: ${contentEncoding}`))
|
|
672
|
-
return
|
|
673
|
-
}
|
|
674
|
-
}
|
|
682
|
+
const response = await openDownloadResponse(url, requestOptions)
|
|
683
|
+
const { statusCode } = response
|
|
684
|
+
if (statusCode !== 200) {
|
|
685
|
+
throw new HttpError(statusCode ?? 0, await readResponseText(response), url)
|
|
686
|
+
}
|
|
675
687
|
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
debug('download file request timeout, %s', url)
|
|
694
|
-
reject(new Error('Request timed out'))
|
|
695
|
-
req.destroy()
|
|
696
|
-
})
|
|
697
|
-
})
|
|
688
|
+
const responseHeaders = parseNodeResponseHeaders(response.headers)
|
|
689
|
+
if (appendContentEncodingExtension) {
|
|
690
|
+
const contentEncoding = asSingleHeaderValue(response.headers['content-encoding'])
|
|
691
|
+
if (contentEncoding === 'zstd') {
|
|
692
|
+
finalDownloadPath = `${downloadPath}.zst`
|
|
693
|
+
} else if (contentEncoding === undefined || contentEncoding === 'gzip') {
|
|
694
|
+
finalDownloadPath = `${downloadPath}.gz`
|
|
695
|
+
} else {
|
|
696
|
+
throw new Error(`Unsupported data feed content encoding: ${contentEncoding}`)
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
fileWriteStream = createWriteStream(tmpFilePath)
|
|
701
|
+
await pipeline(response, fileWriteStream)
|
|
702
|
+
if (!response.complete) {
|
|
703
|
+
throw new Error('The connection was terminated while the message was still being sent')
|
|
704
|
+
}
|
|
698
705
|
|
|
699
706
|
// finally when saving from the network to file has succeded, rename tmp file to normal name
|
|
700
707
|
// then we're sure that responses is 100% saved and also even if different process was doing the same we're good
|
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
import { BookChange, BookTicker, DerivativeTicker, Trade } from '../types.ts'
|
|
2
2
|
import { Mapper, PendingTickerInfoHelper } from './mapper.ts'
|
|
3
|
-
import { exchangeMappers } from './registry.ts'
|
|
3
|
+
import { exchangeMappers, mapper } from './registry.ts'
|
|
4
|
+
|
|
5
|
+
const HYPERLIQUID_FAST_BOOK_SWITCH_DATE = new Date('2026-06-17T00:00:00.000Z')
|
|
4
6
|
|
|
5
7
|
export const hyperliquidMappers = exchangeMappers({
|
|
6
8
|
hyperliquid: {
|
|
7
9
|
trades: () => new HyperliquidTradesMapper(),
|
|
8
|
-
bookChanges: (
|
|
10
|
+
bookChanges: mapper([
|
|
11
|
+
{ until: HYPERLIQUID_FAST_BOOK_SWITCH_DATE, use: () => new HyperliquidBookChangeMapper('l2Book') },
|
|
12
|
+
{ use: () => new HyperliquidBookChangeMapper('fastBook') }
|
|
13
|
+
]),
|
|
9
14
|
derivativeTickers: () => new HyperliquidDerivativeTickerMapper(),
|
|
10
15
|
bookTickers: () => new HyperliquidBookTickerMapper()
|
|
11
16
|
}
|
|
@@ -78,8 +83,14 @@ function mapHyperliquidLevel(level: HyperliquidWsLevel) {
|
|
|
78
83
|
}
|
|
79
84
|
}
|
|
80
85
|
class HyperliquidBookChangeMapper implements Mapper<'hyperliquid', BookChange> {
|
|
86
|
+
constructor(private readonly filterChannel: HyperliquidBookChannel) {}
|
|
87
|
+
|
|
81
88
|
canHandle(message: HyperliquidWsBookMessage) {
|
|
82
|
-
|
|
89
|
+
// Hyperliquid returns channel=l2Book for both live feeds and marks only the fast feed with data.fast=true.
|
|
90
|
+
// The recorder rewrites that channel to fastBook so historical data can distinguish the two feeds.
|
|
91
|
+
const isFastBook = message.channel === 'fastBook' || (message.channel === 'l2Book' && message.data.fast === true)
|
|
92
|
+
|
|
93
|
+
return this.filterChannel === 'fastBook' ? isFastBook : message.channel === 'l2Book' && isFastBook === false
|
|
83
94
|
}
|
|
84
95
|
|
|
85
96
|
getFilters(symbols?: string[]) {
|
|
@@ -87,7 +98,7 @@ class HyperliquidBookChangeMapper implements Mapper<'hyperliquid', BookChange> {
|
|
|
87
98
|
|
|
88
99
|
return [
|
|
89
100
|
{
|
|
90
|
-
channel:
|
|
101
|
+
channel: this.filterChannel,
|
|
91
102
|
symbols
|
|
92
103
|
}
|
|
93
104
|
]
|
|
@@ -206,14 +217,17 @@ type HyperliquidTradeMessage = {
|
|
|
206
217
|
}
|
|
207
218
|
|
|
208
219
|
type HyperliquidWsBookMessage = {
|
|
209
|
-
channel:
|
|
220
|
+
channel: HyperliquidBookChannel
|
|
210
221
|
data: {
|
|
211
222
|
coin: 'ATOM'
|
|
212
223
|
time: 1730160007687
|
|
213
224
|
levels: [HyperliquidWsLevel[], HyperliquidWsLevel[]]
|
|
225
|
+
fast?: boolean
|
|
214
226
|
}
|
|
215
227
|
}
|
|
216
228
|
|
|
229
|
+
type HyperliquidBookChannel = 'l2Book' | 'fastBook'
|
|
230
|
+
|
|
217
231
|
type HyperliquidWsLevel = {
|
|
218
232
|
px: string // price
|
|
219
233
|
sz: string // size
|
|
@@ -1,71 +1,13 @@
|
|
|
1
|
-
import protobuf from 'protobufjs'
|
|
2
1
|
import { CircularBuffer, getJSON, wait } from '../handy.ts'
|
|
3
2
|
import type { MexcDepthSnapshotMessage } from '../mappers/mexc.ts'
|
|
4
3
|
import { Filter } from '../types.ts'
|
|
5
4
|
import { RealTimeFeedBase } from './realtimefeed.ts'
|
|
5
|
+
import { decodeMexcProtobufMessage } from './mexcprotobuf.ts'
|
|
6
6
|
|
|
7
7
|
export class MexcRealTimeFeed extends RealTimeFeedBase {
|
|
8
8
|
private static readonly depthChannel = 'spot@public.aggre.depth.v3.api.pb@10ms'
|
|
9
9
|
private static readonly depthSnapshotChannel = 'spot@public.aggre.depth.snapshot.v3.api.pb@10ms'
|
|
10
10
|
private static readonly jsonObjectStart = '{'.charCodeAt(0)
|
|
11
|
-
private static pushDataV3ApiWrapper: protobuf.Type | undefined
|
|
12
|
-
/**
|
|
13
|
-
* MEXC protobuf docs at @see https://www.mexc.com/api-docs/spot-v3/websocket-market-streams/protocol-buffers-integration
|
|
14
|
-
* and schemas from @see https://github.com/mexcdevelop/websocket-proto, mirrored in tardis-recorder/datafeeds/src/mexc/proto.
|
|
15
|
-
*/
|
|
16
|
-
private static readonly pushDataV3ApiWrapperSchema = `
|
|
17
|
-
syntax = "proto3";
|
|
18
|
-
|
|
19
|
-
message PushDataV3ApiWrapper {
|
|
20
|
-
string channel = 1;
|
|
21
|
-
oneof body {
|
|
22
|
-
PublicAggreDepthsV3Api publicAggreDepths = 313;
|
|
23
|
-
PublicAggreDealsV3Api publicAggreDeals = 314;
|
|
24
|
-
PublicAggreBookTickerV3Api publicAggreBookTicker = 315;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
optional string symbol = 3;
|
|
28
|
-
optional string symbolId = 4;
|
|
29
|
-
optional int64 createTime = 5;
|
|
30
|
-
optional int64 sendTime = 6;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
message PublicAggreDealsV3Api {
|
|
34
|
-
repeated PublicAggreDealsV3ApiItem deals = 1;
|
|
35
|
-
string eventType = 2;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
message PublicAggreDealsV3ApiItem {
|
|
39
|
-
string price = 1;
|
|
40
|
-
string quantity = 2;
|
|
41
|
-
int32 tradeType = 3;
|
|
42
|
-
int64 time = 4;
|
|
43
|
-
string tradeId = 5;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
message PublicAggreDepthsV3Api {
|
|
47
|
-
repeated PublicAggreDepthV3ApiItem asks = 1;
|
|
48
|
-
repeated PublicAggreDepthV3ApiItem bids = 2;
|
|
49
|
-
string eventType = 3;
|
|
50
|
-
string fromVersion = 4;
|
|
51
|
-
string toVersion = 5;
|
|
52
|
-
int64 lastOrderCreateTime = 6;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
message PublicAggreDepthV3ApiItem {
|
|
56
|
-
string price = 1;
|
|
57
|
-
string quantity = 2;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
message PublicAggreBookTickerV3Api {
|
|
61
|
-
string bidPrice = 1;
|
|
62
|
-
string bidQuantity = 2;
|
|
63
|
-
string askPrice = 3;
|
|
64
|
-
string askQuantity = 4;
|
|
65
|
-
string version = 5;
|
|
66
|
-
int64 lastOrderCreateTime = 6;
|
|
67
|
-
}
|
|
68
|
-
`
|
|
69
11
|
|
|
70
12
|
private readonly pendingDepthSnapshotSymbols = new Set<string>()
|
|
71
13
|
private readonly bufferedDepthUpdates = new Map<string, CircularBuffer<MexcDepthUpdateData>>()
|
|
@@ -114,11 +56,7 @@ export class MexcRealTimeFeed extends RealTimeFeedBase {
|
|
|
114
56
|
return JSON.parse(buffer.toString())
|
|
115
57
|
}
|
|
116
58
|
|
|
117
|
-
|
|
118
|
-
return pushDataV3ApiWrapper.toObject(pushDataV3ApiWrapper.decode(buffer), {
|
|
119
|
-
longs: String,
|
|
120
|
-
arrays: true
|
|
121
|
-
})
|
|
59
|
+
return decodeMexcProtobufMessage(buffer)
|
|
122
60
|
}
|
|
123
61
|
|
|
124
62
|
protected override messageIsError(message: any): boolean {
|
|
@@ -168,14 +106,6 @@ export class MexcRealTimeFeed extends RealTimeFeedBase {
|
|
|
168
106
|
this.send({ method: 'PING' })
|
|
169
107
|
}
|
|
170
108
|
|
|
171
|
-
private getPushDataV3ApiWrapper() {
|
|
172
|
-
MexcRealTimeFeed.pushDataV3ApiWrapper ??= protobuf
|
|
173
|
-
.parse(MexcRealTimeFeed.pushDataV3ApiWrapperSchema)
|
|
174
|
-
.root.lookupType('PushDataV3ApiWrapper')
|
|
175
|
-
|
|
176
|
-
return MexcRealTimeFeed.pushDataV3ApiWrapper
|
|
177
|
-
}
|
|
178
|
-
|
|
179
109
|
private resetDepthSnapshotTracking(filters: Required<Filter<string>>[]) {
|
|
180
110
|
this.pendingDepthSnapshotSymbols.clear()
|
|
181
111
|
this.bufferedDepthUpdates.clear()
|