envio 2.32.3 → 2.32.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.
- package/package.json +12 -11
- package/src/Utils.res +9 -0
- package/src/Utils.res.js +18 -0
- package/src/bindings/EventSource.res +20 -0
- package/src/bindings/EventSource.res.js +8 -0
- package/src/sources/HyperSync.res +10 -2
- package/src/sources/HyperSync.res.js +9 -4
- package/src/sources/HyperSyncClient.res +77 -20
- package/src/sources/HyperSyncClient.res.js +22 -6
- package/src/sources/HyperSyncHeightStream.res +105 -0
- package/src/sources/HyperSyncHeightStream.res.js +97 -0
- package/src/sources/HyperSyncSource.res +12 -1
- package/src/sources/HyperSyncSource.res.js +13 -3
- package/src/sources/Source.res +1 -0
- package/src/sources/SourceManager.res +172 -95
- package/src/sources/SourceManager.res.js +150 -87
- package/src/sources/SourceManager.resi +3 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "envio",
|
|
3
|
-
"version": "v2.32.
|
|
3
|
+
"version": "v2.32.7",
|
|
4
4
|
"description": "A latency and sync speed optimized, developer friendly blockchain data indexer.",
|
|
5
5
|
"bin": "./bin.js",
|
|
6
6
|
"main": "./index.js",
|
|
@@ -25,22 +25,23 @@
|
|
|
25
25
|
},
|
|
26
26
|
"homepage": "https://envio.dev",
|
|
27
27
|
"optionalDependencies": {
|
|
28
|
-
"envio-linux-x64": "v2.32.
|
|
29
|
-
"envio-linux-arm64": "v2.32.
|
|
30
|
-
"envio-darwin-x64": "v2.32.
|
|
31
|
-
"envio-darwin-arm64": "v2.32.
|
|
28
|
+
"envio-linux-x64": "v2.32.7",
|
|
29
|
+
"envio-linux-arm64": "v2.32.7",
|
|
30
|
+
"envio-darwin-x64": "v2.32.7",
|
|
31
|
+
"envio-darwin-arm64": "v2.32.7"
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@
|
|
34
|
+
"@elastic/ecs-pino-format": "1.4.0",
|
|
35
|
+
"@envio-dev/hypersync-client": "1.1.0",
|
|
35
36
|
"@envio-dev/hyperfuel-client": "1.2.2",
|
|
36
|
-
"rescript": "11.1.3",
|
|
37
|
-
"rescript-schema": "9.3.0",
|
|
38
|
-
"viem": "2.21.0",
|
|
39
37
|
"bignumber.js": "9.1.2",
|
|
38
|
+
"eventsource": "4.1.0",
|
|
40
39
|
"pino": "8.16.1",
|
|
41
40
|
"pino-pretty": "10.2.3",
|
|
42
|
-
"
|
|
43
|
-
"
|
|
41
|
+
"prom-client": "15.0.0",
|
|
42
|
+
"rescript": "11.1.3",
|
|
43
|
+
"rescript-schema": "9.3.0",
|
|
44
|
+
"viem": "2.21.0"
|
|
44
45
|
},
|
|
45
46
|
"files": [
|
|
46
47
|
"bin.js",
|
package/src/Utils.res
CHANGED
|
@@ -653,3 +653,12 @@ let prettifyExn = exn => {
|
|
|
653
653
|
| exn => exn
|
|
654
654
|
}
|
|
655
655
|
}
|
|
656
|
+
|
|
657
|
+
module EnvioPackage = {
|
|
658
|
+
type t = {version: string}
|
|
659
|
+
|
|
660
|
+
%%private(let getPackageJson = (): t => %raw(`require("../package.json")`))
|
|
661
|
+
let json: t = try getPackageJson() catch {
|
|
662
|
+
| _ => Js.Exn.raiseError("Failed to get package.json in envio package")
|
|
663
|
+
}
|
|
664
|
+
}
|
package/src/Utils.res.js
CHANGED
|
@@ -580,6 +580,23 @@ function prettifyExn(exn) {
|
|
|
580
580
|
}
|
|
581
581
|
}
|
|
582
582
|
|
|
583
|
+
function getPackageJson() {
|
|
584
|
+
return (require("../package.json"));
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
var json;
|
|
588
|
+
|
|
589
|
+
try {
|
|
590
|
+
json = getPackageJson();
|
|
591
|
+
}
|
|
592
|
+
catch (exn){
|
|
593
|
+
json = Js_exn.raiseError("Failed to get package.json in envio package");
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
var EnvioPackage = {
|
|
597
|
+
json: json
|
|
598
|
+
};
|
|
599
|
+
|
|
583
600
|
exports.delay = delay;
|
|
584
601
|
exports.$$Object = $$Object;
|
|
585
602
|
exports.$$Error = $$Error;
|
|
@@ -600,4 +617,5 @@ exports.$$Map = $$Map;
|
|
|
600
617
|
exports.$$Proxy = $$Proxy;
|
|
601
618
|
exports.Hash = Hash;
|
|
602
619
|
exports.prettifyExn = prettifyExn;
|
|
620
|
+
exports.EnvioPackage = EnvioPackage;
|
|
603
621
|
/* variantTag Not a pure module */
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
type t
|
|
2
|
+
|
|
3
|
+
module Fetch = {
|
|
4
|
+
type args = {body?: unknown, headers?: dict<string>, method?: string, path?: string}
|
|
5
|
+
type t = (string, ~args: args) => promise<unknown>
|
|
6
|
+
// NOTE: don't try make the type t. Rescript 11 will curry the args which breaks
|
|
7
|
+
// keet the type inline. This is a workaround for now.
|
|
8
|
+
external fetch: (string, ~args: args) => promise<unknown> = "fetch"
|
|
9
|
+
}
|
|
10
|
+
type options = {fetch?: Fetch.t}
|
|
11
|
+
|
|
12
|
+
@module("eventsource") @new
|
|
13
|
+
external create: (~url: string, ~options: options=?) => t = "EventSource"
|
|
14
|
+
|
|
15
|
+
@set external onopen: (t, unit => unit) => unit = "onopen"
|
|
16
|
+
@set external onerror: (t, Js.Exn.t => unit) => unit = "onerror"
|
|
17
|
+
|
|
18
|
+
type event = {data: string}
|
|
19
|
+
@send external addEventListener: (t, string, event => unit) => unit = "addEventListener"
|
|
20
|
+
@send external close: t => unit = "close"
|
|
@@ -101,12 +101,20 @@ module GetLogs = {
|
|
|
101
101
|
~nonOptionalBlockFieldNames,
|
|
102
102
|
~nonOptionalTransactionFieldNames,
|
|
103
103
|
): logsQueryPageItem => {
|
|
104
|
+
// Remap "type" -> "kind" on the transaction object at runtime before validation.
|
|
105
|
+
// The latest hypersync client renamed "kind" to "type" but v2 consumers expect "kind".
|
|
106
|
+
let transaction: Js.Dict.t<unknown> = event.transaction->Utils.magic
|
|
107
|
+
switch transaction->Js.Dict.get("type") {
|
|
108
|
+
| Some(v) => transaction->Js.Dict.set("kind", v)
|
|
109
|
+
| None => ()
|
|
110
|
+
}
|
|
111
|
+
|
|
104
112
|
let missingParams = []
|
|
105
113
|
missingParams->addMissingParams(Log.fieldNames, event.log, ~prefix="log")
|
|
106
114
|
missingParams->addMissingParams(nonOptionalBlockFieldNames, event.block, ~prefix="block")
|
|
107
115
|
missingParams->addMissingParams(
|
|
108
116
|
nonOptionalTransactionFieldNames,
|
|
109
|
-
|
|
117
|
+
transaction->Utils.magic,
|
|
110
118
|
~prefix="transaction",
|
|
111
119
|
)
|
|
112
120
|
if missingParams->Array.length > 0 {
|
|
@@ -126,7 +134,7 @@ module GetLogs = {
|
|
|
126
134
|
{
|
|
127
135
|
log,
|
|
128
136
|
block: event.block->Utils.magic,
|
|
129
|
-
transaction:
|
|
137
|
+
transaction: transaction->Utils.magic,
|
|
130
138
|
}
|
|
131
139
|
}
|
|
132
140
|
|
|
@@ -6,6 +6,7 @@ var Time = require("../Time.res.js");
|
|
|
6
6
|
var Utils = require("../Utils.res.js");
|
|
7
7
|
var $$BigInt = require("../bindings/BigInt.res.js");
|
|
8
8
|
var Js_exn = require("rescript/lib/js/js_exn.js");
|
|
9
|
+
var Js_dict = require("rescript/lib/js/js_dict.js");
|
|
9
10
|
var Logging = require("../Logging.res.js");
|
|
10
11
|
var Belt_Array = require("rescript/lib/js/belt_Array.js");
|
|
11
12
|
var Belt_Option = require("rescript/lib/js/belt_Option.js");
|
|
@@ -75,6 +76,11 @@ async function query(client, fromBlock, toBlock, logSelections, fieldSelection,
|
|
|
75
76
|
};
|
|
76
77
|
}
|
|
77
78
|
var items = Belt_Array.map(res.data, (function (item) {
|
|
79
|
+
var transaction = item.transaction;
|
|
80
|
+
var v = Js_dict.get(transaction, "type");
|
|
81
|
+
if (v !== undefined) {
|
|
82
|
+
transaction["kind"] = Caml_option.valFromOption(v);
|
|
83
|
+
}
|
|
78
84
|
var missingParams = [];
|
|
79
85
|
var returnedObj = item.log;
|
|
80
86
|
if (Utils.$$Array.notEmpty(fieldNames)) {
|
|
@@ -108,12 +114,11 @@ async function query(client, fromBlock, toBlock, logSelections, fieldSelection,
|
|
|
108
114
|
missingParams.push("block");
|
|
109
115
|
}
|
|
110
116
|
}
|
|
111
|
-
var returnedObj$2 = item.transaction;
|
|
112
117
|
if (Utils.$$Array.notEmpty(nonOptionalTransactionFieldNames)) {
|
|
113
|
-
if (
|
|
118
|
+
if (transaction) {
|
|
114
119
|
for(var idx$2 = 0 ,idx_finish$2 = nonOptionalTransactionFieldNames.length; idx$2 < idx_finish$2; ++idx$2){
|
|
115
120
|
var fieldName$2 = nonOptionalTransactionFieldNames[idx$2];
|
|
116
|
-
var match$2 =
|
|
121
|
+
var match$2 = transaction[fieldName$2];
|
|
117
122
|
if (match$2 !== undefined) {
|
|
118
123
|
|
|
119
124
|
} else {
|
|
@@ -154,7 +159,7 @@ async function query(client, fromBlock, toBlock, logSelections, fieldSelection,
|
|
|
154
159
|
return {
|
|
155
160
|
log: log,
|
|
156
161
|
block: item.block,
|
|
157
|
-
transaction:
|
|
162
|
+
transaction: transaction
|
|
158
163
|
};
|
|
159
164
|
}));
|
|
160
165
|
return {
|
|
@@ -1,12 +1,33 @@
|
|
|
1
|
+
/** Determines query serialization format for HTTP requests. */
|
|
2
|
+
type serializationFormat =
|
|
3
|
+
// Use JSON serialization (default)
|
|
4
|
+
| Json
|
|
5
|
+
// Use Cap'n Proto binary serialization
|
|
6
|
+
| CapnProto
|
|
7
|
+
|
|
8
|
+
let serializationFormatSchema = S.enum([Json, CapnProto])
|
|
9
|
+
|
|
1
10
|
type cfg = {
|
|
2
|
-
|
|
3
|
-
|
|
11
|
+
/** HyperSync server URL. */
|
|
12
|
+
url: string,
|
|
13
|
+
/** HyperSync server api token. */
|
|
14
|
+
apiToken: string,
|
|
15
|
+
/** Milliseconds to wait for a response before timing out. Default: 30000. */
|
|
4
16
|
httpReqTimeoutMillis?: int,
|
|
17
|
+
/** Number of retries to attempt before returning error. Default: 12. */
|
|
5
18
|
maxNumRetries?: int,
|
|
19
|
+
/** Milliseconds that would be used for retry backoff increasing. Default: 500. */
|
|
6
20
|
retryBackoffMs?: int,
|
|
21
|
+
/** Initial wait time for request backoff. Default: 200. */
|
|
7
22
|
retryBaseMs?: int,
|
|
23
|
+
/** Ceiling time for request backoff. Default: 5000. */
|
|
8
24
|
retryCeilingMs?: int,
|
|
25
|
+
/** Enable checksum addresses in responses. */
|
|
9
26
|
enableChecksumAddresses?: bool,
|
|
27
|
+
/** Query serialization format to use for HTTP requests. Default: Json. */
|
|
28
|
+
serializationFormat?: serializationFormat,
|
|
29
|
+
/** Whether to use query caching when using CapnProto serialization format. */
|
|
30
|
+
enableQueryCaching?: bool,
|
|
10
31
|
}
|
|
11
32
|
|
|
12
33
|
module QueryTypes = {
|
|
@@ -67,7 +88,7 @@ module QueryTypes = {
|
|
|
67
88
|
| GasUsed
|
|
68
89
|
| ContractAddress
|
|
69
90
|
| LogsBloom
|
|
70
|
-
|
|
|
91
|
+
| Type
|
|
71
92
|
| Root
|
|
72
93
|
| Status
|
|
73
94
|
| L1Fee
|
|
@@ -111,7 +132,7 @@ module QueryTypes = {
|
|
|
111
132
|
| TraceAddress
|
|
112
133
|
| TransactionHash
|
|
113
134
|
| TransactionPosition
|
|
114
|
-
|
|
|
135
|
+
| Type
|
|
115
136
|
| Error
|
|
116
137
|
|
|
117
138
|
type fieldSelection = {
|
|
@@ -133,7 +154,7 @@ module QueryTypes = {
|
|
|
133
154
|
topic3,
|
|
134
155
|
)
|
|
135
156
|
|
|
136
|
-
type
|
|
157
|
+
type logFilter = {
|
|
137
158
|
/**
|
|
138
159
|
* Address of the contract, any logs that has any of these addresses will be returned.
|
|
139
160
|
* Empty means match all.
|
|
@@ -148,7 +169,7 @@ module QueryTypes = {
|
|
|
148
169
|
|
|
149
170
|
let makeLogSelection = (~address, ~topics) => {address, topics}
|
|
150
171
|
|
|
151
|
-
type
|
|
172
|
+
type transactionFilter = {
|
|
152
173
|
/**
|
|
153
174
|
* Address the transaction should originate from. If transaction.from matches any of these, the transaction
|
|
154
175
|
* will be returned. Keep in mind that this has an and relationship with to filter, so each transaction should
|
|
@@ -167,7 +188,8 @@ module QueryTypes = {
|
|
|
167
188
|
/** If tx.status matches this it will be returned. */
|
|
168
189
|
status?: int,
|
|
169
190
|
/** If transaction.type matches any of these values, the transaction will be returned */
|
|
170
|
-
|
|
191
|
+
@as("type")
|
|
192
|
+
type_?: array<int>,
|
|
171
193
|
contractAddress?: array<Address.t>,
|
|
172
194
|
}
|
|
173
195
|
|
|
@@ -177,7 +199,8 @@ module QueryTypes = {
|
|
|
177
199
|
address?: array<Address.t>,
|
|
178
200
|
callType?: array<string>,
|
|
179
201
|
rewardType?: array<string>,
|
|
180
|
-
|
|
202
|
+
@as("type")
|
|
203
|
+
type_?: array<string>,
|
|
181
204
|
sighash?: array<string>,
|
|
182
205
|
}
|
|
183
206
|
|
|
@@ -214,12 +237,12 @@ module QueryTypes = {
|
|
|
214
237
|
* List of log selections, these have an or relationship between them, so the query will return logs
|
|
215
238
|
* that match any of these selections.
|
|
216
239
|
*/
|
|
217
|
-
logs?: array<
|
|
240
|
+
logs?: array<logFilter>,
|
|
218
241
|
/**
|
|
219
242
|
* List of transaction selections, the query will return transactions that match any of these selections and
|
|
220
243
|
* it will return transactions that are related to the returned logs.
|
|
221
244
|
*/
|
|
222
|
-
transactions?: array<
|
|
245
|
+
transactions?: array<transactionFilter>,
|
|
223
246
|
/**
|
|
224
247
|
* List of trace selections, the query will return traces that match any of these selections and
|
|
225
248
|
* it will re turn traces that are related to the returned logs.
|
|
@@ -360,7 +383,7 @@ module ResponseTypes = {
|
|
|
360
383
|
gasUsed?: bigint,
|
|
361
384
|
contractAddress?: string,
|
|
362
385
|
logsBloom?: string,
|
|
363
|
-
|
|
386
|
+
@as("type") type_?: int,
|
|
364
387
|
root?: string,
|
|
365
388
|
status?: int,
|
|
366
389
|
l1Fee?: bigint,
|
|
@@ -436,6 +459,22 @@ type streamConfig
|
|
|
436
459
|
type queryResponse
|
|
437
460
|
type queryResponseStream
|
|
438
461
|
type eventStream
|
|
462
|
+
|
|
463
|
+
@tag("type")
|
|
464
|
+
type heightStreamEvent =
|
|
465
|
+
| Height({height: int})
|
|
466
|
+
| Connected
|
|
467
|
+
| Reconnecting({delayMillis: int, errorMsg: string})
|
|
468
|
+
|
|
469
|
+
module HeightStream = {
|
|
470
|
+
type t = {
|
|
471
|
+
/** Close the height stream */
|
|
472
|
+
close: unit => promise<unit>,
|
|
473
|
+
/** Receive the next height stream event from the stream */
|
|
474
|
+
recv: unit => promise<heightStreamEvent>,
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
|
|
439
478
|
type t = {
|
|
440
479
|
getHeight: unit => promise<int>,
|
|
441
480
|
collect: (~query: query, ~config: streamConfig) => promise<queryResponse>,
|
|
@@ -445,23 +484,41 @@ type t = {
|
|
|
445
484
|
getEvents: (~query: query) => promise<eventResponse>,
|
|
446
485
|
stream: (~query: query, ~config: streamConfig) => promise<queryResponseStream>,
|
|
447
486
|
streamEvents: (~query: query, ~config: streamConfig) => promise<eventStream>,
|
|
487
|
+
streamHeight: unit => promise<HeightStream.t>,
|
|
448
488
|
}
|
|
449
489
|
|
|
450
|
-
@module("@envio-dev/hypersync-client") @scope("HypersyncClient")
|
|
490
|
+
@module("@envio-dev/hypersync-client") @scope("HypersyncClient")
|
|
491
|
+
external makeWithAgent: (cfg, ~userAgent: string) => t = "newWithAgent"
|
|
492
|
+
|
|
451
493
|
let make = (
|
|
452
494
|
~url,
|
|
453
495
|
~apiToken,
|
|
454
496
|
~httpReqTimeoutMillis,
|
|
455
497
|
~maxNumRetries,
|
|
456
498
|
~enableChecksumAddresses=true,
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
499
|
+
~serializationFormat=?,
|
|
500
|
+
~enableQueryCaching=?,
|
|
501
|
+
~retryBaseMs=?,
|
|
502
|
+
~retryBackoffMs=?,
|
|
503
|
+
~retryCeilingMs=?,
|
|
504
|
+
) => {
|
|
505
|
+
let envioVersion = Utils.EnvioPackage.json.version
|
|
506
|
+
makeWithAgent(
|
|
507
|
+
{
|
|
508
|
+
url,
|
|
509
|
+
enableChecksumAddresses,
|
|
510
|
+
apiToken,
|
|
511
|
+
httpReqTimeoutMillis,
|
|
512
|
+
maxNumRetries,
|
|
513
|
+
?serializationFormat,
|
|
514
|
+
?enableQueryCaching,
|
|
515
|
+
?retryBaseMs,
|
|
516
|
+
?retryBackoffMs,
|
|
517
|
+
?retryCeilingMs,
|
|
518
|
+
},
|
|
519
|
+
~userAgent=`hyperindex/${envioVersion}`,
|
|
520
|
+
)
|
|
521
|
+
}
|
|
465
522
|
|
|
466
523
|
module Decoder = {
|
|
467
524
|
type rec decodedSolType<'a> = {val: 'a}
|
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
+
var Utils = require("../Utils.res.js");
|
|
4
5
|
var Address = require("../Address.res.js");
|
|
5
6
|
var Belt_Array = require("rescript/lib/js/belt_Array.js");
|
|
6
7
|
var S$RescriptSchema = require("rescript-schema/src/S.res.js");
|
|
7
8
|
var HypersyncClient = require("@envio-dev/hypersync-client");
|
|
8
9
|
|
|
10
|
+
var serializationFormatSchema = S$RescriptSchema.$$enum([
|
|
11
|
+
"Json",
|
|
12
|
+
"CapnProto"
|
|
13
|
+
]);
|
|
14
|
+
|
|
9
15
|
function makeTopicSelection(topic0Opt, topic1Opt, topic2Opt, topic3Opt) {
|
|
10
16
|
var topic0 = topic0Opt !== undefined ? topic0Opt : [];
|
|
11
17
|
var topic1 = topic1Opt !== undefined ? topic1Opt : [];
|
|
@@ -57,15 +63,23 @@ var ResponseTypes = {
|
|
|
57
63
|
authorizationListSchema: authorizationListSchema
|
|
58
64
|
};
|
|
59
65
|
|
|
60
|
-
|
|
66
|
+
var HeightStream = {};
|
|
67
|
+
|
|
68
|
+
function make(url, apiToken, httpReqTimeoutMillis, maxNumRetries, enableChecksumAddressesOpt, serializationFormat, enableQueryCaching, retryBaseMs, retryBackoffMs, retryCeilingMs) {
|
|
61
69
|
var enableChecksumAddresses = enableChecksumAddressesOpt !== undefined ? enableChecksumAddressesOpt : true;
|
|
62
|
-
|
|
70
|
+
var envioVersion = Utils.EnvioPackage.json.version;
|
|
71
|
+
return HypersyncClient.HypersyncClient.newWithAgent({
|
|
63
72
|
url: url,
|
|
64
|
-
|
|
73
|
+
apiToken: apiToken,
|
|
65
74
|
httpReqTimeoutMillis: httpReqTimeoutMillis,
|
|
66
75
|
maxNumRetries: maxNumRetries,
|
|
67
|
-
|
|
68
|
-
|
|
76
|
+
retryBackoffMs: retryBackoffMs,
|
|
77
|
+
retryBaseMs: retryBaseMs,
|
|
78
|
+
retryCeilingMs: retryCeilingMs,
|
|
79
|
+
enableChecksumAddresses: enableChecksumAddresses,
|
|
80
|
+
serializationFormat: serializationFormat,
|
|
81
|
+
enableQueryCaching: enableQueryCaching
|
|
82
|
+
}, "hyperindex/" + envioVersion);
|
|
69
83
|
}
|
|
70
84
|
|
|
71
85
|
function toUnderlying(_d) {
|
|
@@ -98,8 +112,10 @@ var Decoder = {
|
|
|
98
112
|
fromSignatures: fromSignatures
|
|
99
113
|
};
|
|
100
114
|
|
|
115
|
+
exports.serializationFormatSchema = serializationFormatSchema;
|
|
101
116
|
exports.QueryTypes = QueryTypes;
|
|
102
117
|
exports.ResponseTypes = ResponseTypes;
|
|
118
|
+
exports.HeightStream = HeightStream;
|
|
103
119
|
exports.make = make;
|
|
104
120
|
exports.Decoder = Decoder;
|
|
105
|
-
/*
|
|
121
|
+
/* serializationFormatSchema Not a pure module */
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Pure subscription-based implementation of the HyperSync height stream.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
let subscribe = (~hyperSyncUrl, ~apiToken, ~onHeight: int => unit): (unit => unit) => {
|
|
6
|
+
let eventsourceRef = ref(None)
|
|
7
|
+
// Timeout doesn't do anything for initialization
|
|
8
|
+
let timeoutIdRef = ref(Js.Global.setTimeout(() => (), 0))
|
|
9
|
+
|
|
10
|
+
// On every successful ping or height event, clear the timeout and set a new one.
|
|
11
|
+
// If the timeout lapses, close and reconnect the EventSource.
|
|
12
|
+
let rec updateTimeoutId = () => {
|
|
13
|
+
timeoutIdRef.contents->Js.Global.clearTimeout
|
|
14
|
+
|
|
15
|
+
// Should receive a ping at least every 5s, so 15s is a safe margin
|
|
16
|
+
// for staleness to restart the EventSource connection
|
|
17
|
+
let staleTimeMillis = 15_000
|
|
18
|
+
let newTimeoutId = Js.Global.setTimeout(() => {
|
|
19
|
+
Logging.trace({
|
|
20
|
+
"msg": "Timeout fired for height stream",
|
|
21
|
+
"url": hyperSyncUrl,
|
|
22
|
+
"staleTimeMillis": staleTimeMillis,
|
|
23
|
+
})
|
|
24
|
+
refreshEventSource()
|
|
25
|
+
}, staleTimeMillis)
|
|
26
|
+
|
|
27
|
+
timeoutIdRef := newTimeoutId
|
|
28
|
+
}
|
|
29
|
+
// Instantiate a new EventSource and set it to the shared refs.
|
|
30
|
+
// Add the necessary event listeners, handle errors
|
|
31
|
+
// and update the timeout.
|
|
32
|
+
and refreshEventSource = () => {
|
|
33
|
+
// Close the old EventSource if it exists (on a new connection after timeout)
|
|
34
|
+
switch eventsourceRef.contents {
|
|
35
|
+
| Some(es) => es->EventSource.close
|
|
36
|
+
| None => ()
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
let userAgent = `hyperindex/${Utils.EnvioPackage.json.version}`
|
|
40
|
+
let es = EventSource.create(
|
|
41
|
+
~url=`${hyperSyncUrl}/height/sse`,
|
|
42
|
+
~options={
|
|
43
|
+
fetch: (url, ~args) => {
|
|
44
|
+
EventSource.Fetch.fetch(
|
|
45
|
+
url,
|
|
46
|
+
~args={
|
|
47
|
+
...args,
|
|
48
|
+
headers: Js.Dict.fromArray([
|
|
49
|
+
("Authorization", `Bearer ${apiToken}`),
|
|
50
|
+
("User-Agent", userAgent),
|
|
51
|
+
]),
|
|
52
|
+
},
|
|
53
|
+
)
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
// Set the new EventSource to the shared ref
|
|
59
|
+
eventsourceRef := Some(es)
|
|
60
|
+
// Update the timeout in case connection goes stale
|
|
61
|
+
updateTimeoutId()
|
|
62
|
+
|
|
63
|
+
es->EventSource.onopen(_ => {
|
|
64
|
+
Logging.trace({"msg": "SSE connection opened for height stream", "url": hyperSyncUrl})
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
es->EventSource.onerror(error => {
|
|
68
|
+
Logging.trace({
|
|
69
|
+
"msg": "EventSource error",
|
|
70
|
+
"error": error->Js.Exn.message,
|
|
71
|
+
})
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
es->EventSource.addEventListener("ping", _event => {
|
|
75
|
+
// ping lets us know from the server that the connection is still alive
|
|
76
|
+
// and that the height hasn't updated for 5 seconds
|
|
77
|
+
// update the timeout on each successful ping received
|
|
78
|
+
updateTimeoutId()
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
es->EventSource.addEventListener("height", event => {
|
|
82
|
+
switch event.data->Belt.Int.fromString {
|
|
83
|
+
| Some(height) =>
|
|
84
|
+
// On a successful height event, update the timeout
|
|
85
|
+
updateTimeoutId()
|
|
86
|
+
// Call the callback with the new height
|
|
87
|
+
onHeight(height)
|
|
88
|
+
| None =>
|
|
89
|
+
Logging.trace({"msg": "Height was not a number in event.data", "data": event.data})
|
|
90
|
+
}
|
|
91
|
+
})
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Start the EventSource connection
|
|
95
|
+
refreshEventSource()
|
|
96
|
+
|
|
97
|
+
// Return unsubscribe function
|
|
98
|
+
() => {
|
|
99
|
+
timeoutIdRef.contents->Js.Global.clearTimeout
|
|
100
|
+
switch eventsourceRef.contents {
|
|
101
|
+
| Some(es) => es->EventSource.close
|
|
102
|
+
| None => ()
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
var Utils = require("../Utils.res.js");
|
|
5
|
+
var Js_dict = require("rescript/lib/js/js_dict.js");
|
|
6
|
+
var Logging = require("../Logging.res.js");
|
|
7
|
+
var Belt_Int = require("rescript/lib/js/belt_Int.js");
|
|
8
|
+
var Caml_obj = require("rescript/lib/js/caml_obj.js");
|
|
9
|
+
var Caml_option = require("rescript/lib/js/caml_option.js");
|
|
10
|
+
var Eventsource = require("eventsource");
|
|
11
|
+
|
|
12
|
+
function subscribe(hyperSyncUrl, apiToken, onHeight) {
|
|
13
|
+
var eventsourceRef = {
|
|
14
|
+
contents: undefined
|
|
15
|
+
};
|
|
16
|
+
var timeoutIdRef = {
|
|
17
|
+
contents: setTimeout((function () {
|
|
18
|
+
|
|
19
|
+
}), 0)
|
|
20
|
+
};
|
|
21
|
+
var updateTimeoutId = function () {
|
|
22
|
+
clearTimeout(timeoutIdRef.contents);
|
|
23
|
+
var newTimeoutId = setTimeout((function () {
|
|
24
|
+
Logging.trace({
|
|
25
|
+
msg: "Timeout fired for height stream",
|
|
26
|
+
url: hyperSyncUrl,
|
|
27
|
+
staleTimeMillis: 15000
|
|
28
|
+
});
|
|
29
|
+
refreshEventSource();
|
|
30
|
+
}), 15000);
|
|
31
|
+
timeoutIdRef.contents = newTimeoutId;
|
|
32
|
+
};
|
|
33
|
+
var refreshEventSource = function () {
|
|
34
|
+
var es = eventsourceRef.contents;
|
|
35
|
+
if (es !== undefined) {
|
|
36
|
+
Caml_option.valFromOption(es).close();
|
|
37
|
+
}
|
|
38
|
+
var userAgent = "hyperindex/" + Utils.EnvioPackage.json.version;
|
|
39
|
+
var es$1 = new Eventsource.EventSource(hyperSyncUrl + "/height/sse", {
|
|
40
|
+
fetch: (function (url, args) {
|
|
41
|
+
var newrecord = Caml_obj.obj_dup(args);
|
|
42
|
+
return fetch(url, (newrecord.headers = Js_dict.fromArray([
|
|
43
|
+
[
|
|
44
|
+
"Authorization",
|
|
45
|
+
"Bearer " + apiToken
|
|
46
|
+
],
|
|
47
|
+
[
|
|
48
|
+
"User-Agent",
|
|
49
|
+
userAgent
|
|
50
|
+
]
|
|
51
|
+
]), newrecord));
|
|
52
|
+
})
|
|
53
|
+
});
|
|
54
|
+
eventsourceRef.contents = Caml_option.some(es$1);
|
|
55
|
+
updateTimeoutId();
|
|
56
|
+
es$1.onopen = (function () {
|
|
57
|
+
Logging.trace({
|
|
58
|
+
msg: "SSE connection opened for height stream",
|
|
59
|
+
url: hyperSyncUrl
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
es$1.onerror = (function (error) {
|
|
63
|
+
Logging.trace({
|
|
64
|
+
msg: "EventSource error",
|
|
65
|
+
error: error.message
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
es$1.addEventListener("ping", (function (_event) {
|
|
69
|
+
updateTimeoutId();
|
|
70
|
+
}));
|
|
71
|
+
es$1.addEventListener("height", (function ($$event) {
|
|
72
|
+
var height = Belt_Int.fromString($$event.data);
|
|
73
|
+
if (height !== undefined) {
|
|
74
|
+
updateTimeoutId();
|
|
75
|
+
return onHeight(height);
|
|
76
|
+
} else {
|
|
77
|
+
return Logging.trace({
|
|
78
|
+
msg: "Height was not a number in event.data",
|
|
79
|
+
data: $$event.data
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
}));
|
|
83
|
+
};
|
|
84
|
+
refreshEventSource();
|
|
85
|
+
return function () {
|
|
86
|
+
clearTimeout(timeoutIdRef.contents);
|
|
87
|
+
var es = eventsourceRef.contents;
|
|
88
|
+
if (es !== undefined) {
|
|
89
|
+
Caml_option.valFromOption(es).close();
|
|
90
|
+
return ;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
exports.subscribe = subscribe;
|
|
97
|
+
/* Utils Not a pure module */
|
|
@@ -73,7 +73,10 @@ let getSelectionConfig = (selection: FetchState.selection, ~chain) => {
|
|
|
73
73
|
->(Utils.magic: array<string> => array<HyperSyncClient.QueryTypes.blockField>),
|
|
74
74
|
transaction: capitalizedTransactionFields
|
|
75
75
|
->Utils.Set.toArray
|
|
76
|
-
->(Utils.magic: array<string> => array<HyperSyncClient.QueryTypes.transactionField>)
|
|
76
|
+
->(Utils.magic: array<string> => array<HyperSyncClient.QueryTypes.transactionField>)
|
|
77
|
+
->// Currently the api for underlying "Type" field is still "Kind"
|
|
78
|
+
// transform this to use the new client
|
|
79
|
+
Array.map(field => (field :> string) !== "Kind" ? field : Type),
|
|
77
80
|
}
|
|
78
81
|
|
|
79
82
|
let noAddressesLogSelection = LogSelection.make(
|
|
@@ -155,6 +158,8 @@ type options = {
|
|
|
155
158
|
clientMaxRetries: int,
|
|
156
159
|
clientTimeoutMillis: int,
|
|
157
160
|
lowercaseAddresses: bool,
|
|
161
|
+
serializationFormat: HyperSyncClient.serializationFormat,
|
|
162
|
+
enableQueryCaching: bool,
|
|
158
163
|
}
|
|
159
164
|
|
|
160
165
|
let make = (
|
|
@@ -169,6 +174,8 @@ let make = (
|
|
|
169
174
|
clientMaxRetries,
|
|
170
175
|
clientTimeoutMillis,
|
|
171
176
|
lowercaseAddresses,
|
|
177
|
+
serializationFormat,
|
|
178
|
+
enableQueryCaching,
|
|
172
179
|
}: options,
|
|
173
180
|
): t => {
|
|
174
181
|
let name = "HyperSync"
|
|
@@ -183,6 +190,8 @@ let make = (
|
|
|
183
190
|
~maxNumRetries=clientMaxRetries,
|
|
184
191
|
~httpReqTimeoutMillis=clientTimeoutMillis,
|
|
185
192
|
~enableChecksumAddresses=!lowercaseAddresses,
|
|
193
|
+
~serializationFormat,
|
|
194
|
+
~enableQueryCaching,
|
|
186
195
|
)
|
|
187
196
|
|
|
188
197
|
let hscDecoder: ref<option<HyperSyncClient.Decoder.t>> = ref(None)
|
|
@@ -568,5 +577,7 @@ let make = (
|
|
|
568
577
|
| ErrorMessage(m) => Js.Exn.raiseError(m)
|
|
569
578
|
},
|
|
570
579
|
getItemsOrThrow,
|
|
580
|
+
createHeightSubscription: (~onHeight) =>
|
|
581
|
+
HyperSyncHeightStream.subscribe(~hyperSyncUrl=endpointUrl, ~apiToken, ~onHeight),
|
|
571
582
|
}
|
|
572
583
|
}
|