envio 3.1.2 → 3.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/evm.schema.json +83 -11
- package/fuel.schema.json +83 -11
- package/index.d.ts +184 -3
- package/package.json +6 -6
- package/src/Batch.res +2 -2
- package/src/ChainFetcher.res +27 -3
- package/src/ChainFetcher.res.mjs +17 -3
- package/src/ChainManager.res +163 -0
- package/src/ChainManager.res.mjs +136 -0
- package/src/Config.res +213 -30
- package/src/Config.res.mjs +102 -41
- package/src/Core.res +16 -10
- package/src/Ecosystem.res +0 -3
- package/src/Env.res +2 -2
- package/src/Env.res.mjs +2 -2
- package/src/Envio.res +101 -2
- package/src/Envio.res.mjs +2 -3
- package/src/EventConfigBuilder.res +87 -0
- package/src/EventConfigBuilder.res.mjs +53 -0
- package/src/EventUtils.res +2 -2
- package/src/FetchState.res +63 -67
- package/src/FetchState.res.mjs +44 -42
- package/src/GlobalState.res +219 -363
- package/src/GlobalState.res.mjs +314 -491
- package/src/GlobalStateManager.res +49 -59
- package/src/GlobalStateManager.res.mjs +5 -4
- package/src/GlobalStateManager.resi +1 -1
- package/src/HandlerLoader.res +18 -2
- package/src/HandlerLoader.res.mjs +16 -34
- package/src/HandlerRegister.res +9 -9
- package/src/HandlerRegister.res.mjs +9 -9
- package/src/Hasura.res +102 -32
- package/src/Hasura.res.mjs +88 -34
- package/src/InMemoryStore.res +10 -1
- package/src/InMemoryStore.res.mjs +4 -1
- package/src/InMemoryTable.res +83 -136
- package/src/InMemoryTable.res.mjs +57 -86
- package/src/Internal.res +70 -5
- package/src/Internal.res.mjs +2 -8
- package/src/LazyLoader.res +2 -2
- package/src/LazyLoader.res.mjs +3 -3
- package/src/LoadLayer.res +47 -60
- package/src/LoadLayer.res.mjs +28 -50
- package/src/LoadLayer.resi +2 -5
- package/src/LogSelection.res +90 -21
- package/src/LogSelection.res.mjs +72 -21
- package/src/Logging.res +1 -1
- package/src/Main.res +61 -2
- package/src/Main.res.mjs +37 -1
- package/src/Persistence.res +3 -16
- package/src/PgStorage.res +125 -114
- package/src/PgStorage.res.mjs +112 -95
- package/src/Ports.res +5 -0
- package/src/Ports.res.mjs +9 -0
- package/src/Prometheus.res +3 -3
- package/src/Prometheus.res.mjs +4 -4
- package/src/ReorgDetection.res +4 -4
- package/src/ReorgDetection.res.mjs +4 -5
- package/src/SafeCheckpointTracking.res +16 -16
- package/src/SafeCheckpointTracking.res.mjs +2 -2
- package/src/SimulateItems.res +10 -14
- package/src/SimulateItems.res.mjs +5 -2
- package/src/Sink.res +1 -1
- package/src/Sink.res.mjs +1 -2
- package/src/SvmTypes.res +9 -0
- package/src/SvmTypes.res.mjs +14 -0
- package/src/TestIndexer.res +35 -68
- package/src/TestIndexer.res.mjs +17 -48
- package/src/TestIndexerProxyStorage.res +23 -23
- package/src/TestIndexerProxyStorage.res.mjs +12 -15
- package/src/Throttler.res +2 -2
- package/src/Time.res +2 -2
- package/src/Time.res.mjs +2 -2
- package/src/UserContext.res +19 -118
- package/src/UserContext.res.mjs +10 -66
- package/src/Utils.res +15 -15
- package/src/Utils.res.mjs +7 -8
- package/src/adapters/MarkBatchProcessedAdapter.res +5 -0
- package/src/adapters/MarkBatchProcessedAdapter.res.mjs +14 -0
- package/src/bindings/BigDecimal.res +1 -1
- package/src/bindings/BigDecimal.res.mjs +2 -2
- package/src/bindings/ClickHouse.res +8 -6
- package/src/bindings/ClickHouse.res.mjs +5 -5
- package/src/bindings/Hrtime.res +1 -1
- package/src/bindings/Pino.res +2 -2
- package/src/bindings/Pino.res.mjs +3 -4
- package/src/db/EntityFilter.res +410 -0
- package/src/db/EntityFilter.res.mjs +424 -0
- package/src/db/EntityHistory.res +1 -1
- package/src/db/EntityHistory.res.mjs +1 -1
- package/src/db/InternalTable.res +10 -10
- package/src/db/InternalTable.res.mjs +41 -45
- package/src/db/Schema.res +2 -2
- package/src/db/Schema.res.mjs +3 -3
- package/src/db/Table.res +106 -22
- package/src/db/Table.res.mjs +84 -35
- package/src/sources/EventRouter.res +67 -2
- package/src/sources/EventRouter.res.mjs +45 -3
- package/src/sources/Evm.res +0 -7
- package/src/sources/Evm.res.mjs +0 -15
- package/src/sources/EvmChain.res +1 -1
- package/src/sources/EvmChain.res.mjs +1 -2
- package/src/sources/EvmRpcClient.res +42 -0
- package/src/sources/EvmRpcClient.res.mjs +64 -0
- package/src/sources/Fuel.res +0 -7
- package/src/sources/Fuel.res.mjs +0 -15
- package/src/sources/HyperFuelSource.res +5 -4
- package/src/sources/HyperFuelSource.res.mjs +2 -2
- package/src/sources/HyperSyncClient.res +9 -5
- package/src/sources/HyperSyncClient.res.mjs +2 -2
- package/src/sources/HyperSyncHeightStream.res +2 -2
- package/src/sources/HyperSyncHeightStream.res.mjs +2 -2
- package/src/sources/HyperSyncSource.res +12 -11
- package/src/sources/HyperSyncSource.res.mjs +6 -6
- package/src/sources/Rpc.res +1 -5
- package/src/sources/Rpc.res.mjs +1 -9
- package/src/sources/RpcSource.res +57 -21
- package/src/sources/RpcSource.res.mjs +47 -20
- package/src/sources/RpcWebSocketHeightStream.res +1 -1
- package/src/sources/SourceManager.res +3 -2
- package/src/sources/SourceManager.res.mjs +1 -1
- package/src/sources/Svm.res +3 -10
- package/src/sources/Svm.res.mjs +4 -18
- package/src/sources/SvmHyperSyncClient.res +265 -0
- package/src/sources/SvmHyperSyncClient.res.mjs +28 -0
- package/src/sources/SvmHyperSyncSource.res +638 -0
- package/src/sources/SvmHyperSyncSource.res.mjs +557 -0
- package/src/tui/Tui.res +9 -2
- package/src/tui/Tui.res.mjs +18 -3
- package/src/tui/components/BufferedProgressBar.res +2 -2
- package/src/tui/components/TuiData.res +3 -0
- package/svm.schema.json +523 -14
- package/src/TableIndices.res +0 -115
- package/src/TableIndices.res.mjs +0 -144
|
@@ -15,13 +15,13 @@ let make = (
|
|
|
15
15
|
~chainReorgCheckpoints: array<Internal.reorgCheckpoint>,
|
|
16
16
|
) => {
|
|
17
17
|
if maxReorgDepth > 0 && shouldRollbackOnReorg {
|
|
18
|
-
let checkpointIds =
|
|
19
|
-
let checkpointBlockNumbers =
|
|
18
|
+
let checkpointIds = Utils.Array.jsArrayCreate(chainReorgCheckpoints->Array.length)
|
|
19
|
+
let checkpointBlockNumbers = Utils.Array.jsArrayCreate(
|
|
20
20
|
chainReorgCheckpoints->Array.length,
|
|
21
21
|
)
|
|
22
22
|
chainReorgCheckpoints->Array.forEachWithIndex((checkpoint, idx) => {
|
|
23
|
-
checkpointIds->
|
|
24
|
-
checkpointBlockNumbers->
|
|
23
|
+
checkpointIds->Array.setUnsafe(idx, checkpoint.checkpointId)
|
|
24
|
+
checkpointBlockNumbers->Array.setUnsafe(idx, checkpoint.blockNumber)
|
|
25
25
|
})
|
|
26
26
|
Some({
|
|
27
27
|
checkpointIds,
|
|
@@ -39,7 +39,7 @@ let getSafeCheckpointId = (safeCheckpointTracking: t, ~sourceBlockNumber: int) =
|
|
|
39
39
|
switch safeCheckpointTracking.checkpointIds {
|
|
40
40
|
| [] => 0n
|
|
41
41
|
| _
|
|
42
|
-
if safeCheckpointTracking.checkpointBlockNumbers->
|
|
42
|
+
if safeCheckpointTracking.checkpointBlockNumbers->Array.getUnsafe(0) >
|
|
43
43
|
safeBlockNumber => 0n
|
|
44
44
|
| [checkpointId] => checkpointId
|
|
45
45
|
| _ => {
|
|
@@ -49,11 +49,11 @@ let getSafeCheckpointId = (safeCheckpointTracking: t, ~sourceBlockNumber: int) =
|
|
|
49
49
|
|
|
50
50
|
while idx.contents < trackingCheckpointsCount && result.contents === None {
|
|
51
51
|
if (
|
|
52
|
-
safeCheckpointTracking.checkpointBlockNumbers->
|
|
52
|
+
safeCheckpointTracking.checkpointBlockNumbers->Array.getUnsafe(idx.contents) >
|
|
53
53
|
safeBlockNumber
|
|
54
54
|
) {
|
|
55
55
|
result :=
|
|
56
|
-
Some(safeCheckpointTracking.checkpointIds->
|
|
56
|
+
Some(safeCheckpointTracking.checkpointIds->Array.getUnsafe(idx.contents - 1))
|
|
57
57
|
}
|
|
58
58
|
idx := idx.contents + 1
|
|
59
59
|
}
|
|
@@ -61,7 +61,7 @@ let getSafeCheckpointId = (safeCheckpointTracking: t, ~sourceBlockNumber: int) =
|
|
|
61
61
|
switch result.contents {
|
|
62
62
|
| Some(checkpointId) => checkpointId
|
|
63
63
|
| None =>
|
|
64
|
-
safeCheckpointTracking.checkpointIds->
|
|
64
|
+
safeCheckpointTracking.checkpointIds->Array.getUnsafe(trackingCheckpointsCount - 1)
|
|
65
65
|
}
|
|
66
66
|
}
|
|
67
67
|
}
|
|
@@ -82,21 +82,21 @@ let updateOnNewBatch = (
|
|
|
82
82
|
|
|
83
83
|
// Copy + Clean up old checkpoints
|
|
84
84
|
for idx in 0 to safeCheckpointTracking.checkpointIds->Array.length - 1 {
|
|
85
|
-
let checkpointId = safeCheckpointTracking.checkpointIds->
|
|
85
|
+
let checkpointId = safeCheckpointTracking.checkpointIds->Array.getUnsafe(idx)
|
|
86
86
|
if checkpointId >= safeCheckpointId {
|
|
87
87
|
mutCheckpointIds->Array.push(checkpointId)->ignore
|
|
88
88
|
mutCheckpointBlockNumbers
|
|
89
|
-
->Array.push(safeCheckpointTracking.checkpointBlockNumbers->
|
|
89
|
+
->Array.push(safeCheckpointTracking.checkpointBlockNumbers->Array.getUnsafe(idx))
|
|
90
90
|
->ignore
|
|
91
91
|
}
|
|
92
92
|
}
|
|
93
93
|
|
|
94
94
|
// Append new checkpoints
|
|
95
95
|
for idx in 0 to batchCheckpointIds->Array.length - 1 {
|
|
96
|
-
if batchCheckpointChainIds->
|
|
97
|
-
mutCheckpointIds->Array.push(batchCheckpointIds->
|
|
96
|
+
if batchCheckpointChainIds->Array.getUnsafe(idx) === chainId {
|
|
97
|
+
mutCheckpointIds->Array.push(batchCheckpointIds->Array.getUnsafe(idx))->ignore
|
|
98
98
|
mutCheckpointBlockNumbers
|
|
99
|
-
->Array.push(batchCheckpointBlockNumbers->
|
|
99
|
+
->Array.push(batchCheckpointBlockNumbers->Array.getUnsafe(idx))
|
|
100
100
|
->ignore
|
|
101
101
|
}
|
|
102
102
|
}
|
|
@@ -113,13 +113,13 @@ let rollback = (safeCheckpointTracking: t, ~targetBlockNumber: int) => {
|
|
|
113
113
|
let mutCheckpointBlockNumbers = []
|
|
114
114
|
|
|
115
115
|
for idx in 0 to safeCheckpointTracking.checkpointIds->Array.length - 1 {
|
|
116
|
-
let blockNumber = safeCheckpointTracking.checkpointBlockNumbers->
|
|
116
|
+
let blockNumber = safeCheckpointTracking.checkpointBlockNumbers->Array.getUnsafe(idx)
|
|
117
117
|
if blockNumber <= targetBlockNumber {
|
|
118
118
|
mutCheckpointIds
|
|
119
|
-
->Array.push(safeCheckpointTracking.checkpointIds->
|
|
119
|
+
->Array.push(safeCheckpointTracking.checkpointIds->Array.getUnsafe(idx))
|
|
120
120
|
->ignore
|
|
121
121
|
mutCheckpointBlockNumbers
|
|
122
|
-
->Array.push(safeCheckpointTracking.checkpointBlockNumbers->
|
|
122
|
+
->Array.push(safeCheckpointTracking.checkpointBlockNumbers->Array.getUnsafe(idx))
|
|
123
123
|
->ignore
|
|
124
124
|
}
|
|
125
125
|
}
|
|
@@ -5,8 +5,8 @@ function make(maxReorgDepth, shouldRollbackOnReorg, chainReorgCheckpoints) {
|
|
|
5
5
|
if (!(maxReorgDepth > 0 && shouldRollbackOnReorg)) {
|
|
6
6
|
return;
|
|
7
7
|
}
|
|
8
|
-
let checkpointIds =
|
|
9
|
-
let checkpointBlockNumbers =
|
|
8
|
+
let checkpointIds = Array(chainReorgCheckpoints.length);
|
|
9
|
+
let checkpointBlockNumbers = Array(chainReorgCheckpoints.length);
|
|
10
10
|
chainReorgCheckpoints.forEach((checkpoint, idx) => {
|
|
11
11
|
checkpointIds[idx] = checkpoint.id;
|
|
12
12
|
checkpointBlockNumbers[idx] = checkpoint.block_number;
|
package/src/SimulateItems.res
CHANGED
|
@@ -38,7 +38,7 @@ let evmSimulateBlockSchema = S.schema(s =>
|
|
|
38
38
|
}
|
|
39
39
|
)
|
|
40
40
|
|
|
41
|
-
type evmSimulateBlock = {number: int, timestamp: int}
|
|
41
|
+
type evmSimulateBlock = {number: int, timestamp: int, hash: string}
|
|
42
42
|
|
|
43
43
|
let parseEvmSimulateBlock = (
|
|
44
44
|
~defaultBlockNumber: int,
|
|
@@ -125,7 +125,7 @@ let fuelSimulateBlockSchema = S.schema(s =>
|
|
|
125
125
|
}
|
|
126
126
|
)
|
|
127
127
|
|
|
128
|
-
type fuelSimulateBlock = {height: int, time: int}
|
|
128
|
+
type fuelSimulateBlock = {height: int, time: int, id: string}
|
|
129
129
|
|
|
130
130
|
let parseFuelSimulateBlock = (
|
|
131
131
|
~defaultBlockNumber: int,
|
|
@@ -259,15 +259,15 @@ let parse = (~simulateItems: array<JSON.t>, ~config: Config.t, ~chainConfig: Con
|
|
|
259
259
|
rawItem["block"]->(Utils.magic: 'a => Nullable.t<JSON.t>)->Nullable.toOption
|
|
260
260
|
let transactionJson: option<JSON.t> =
|
|
261
261
|
rawItem["transaction"]->(Utils.magic: 'a => Nullable.t<JSON.t>)->Nullable.toOption
|
|
262
|
-
let (block, blockNumber, timestamp) = switch config.ecosystem.name {
|
|
262
|
+
let (block, blockNumber, timestamp, blockHash) = switch config.ecosystem.name {
|
|
263
263
|
| Fuel =>
|
|
264
264
|
let block = parseFuelSimulateBlock(~defaultBlockNumber=currentBlock.contents, ~blockJson)
|
|
265
265
|
let blockFields = block->(Utils.magic: Internal.eventBlock => fuelSimulateBlock)
|
|
266
|
-
(block, blockFields.height, blockFields.time)
|
|
266
|
+
(block, blockFields.height, blockFields.time, blockFields.id)
|
|
267
267
|
| Evm =>
|
|
268
268
|
let block = parseEvmSimulateBlock(~defaultBlockNumber=currentBlock.contents, ~blockJson)
|
|
269
269
|
let blockFields = block->(Utils.magic: Internal.eventBlock => evmSimulateBlock)
|
|
270
|
-
(block, blockFields.number, blockFields.timestamp)
|
|
270
|
+
(block, blockFields.number, blockFields.timestamp, blockFields.hash)
|
|
271
271
|
| Svm => JsError.throwWithMessage("simulate is not supported for SVM ecosystem")
|
|
272
272
|
}
|
|
273
273
|
let transaction = switch config.ecosystem.name {
|
|
@@ -286,6 +286,7 @@ let parse = (~simulateItems: array<JSON.t>, ~config: Config.t, ~chainConfig: Con
|
|
|
286
286
|
timestamp,
|
|
287
287
|
chain,
|
|
288
288
|
blockNumber,
|
|
289
|
+
blockHash,
|
|
289
290
|
logIndex,
|
|
290
291
|
event: {
|
|
291
292
|
contractName: eventConfig.contractName,
|
|
@@ -320,18 +321,13 @@ let patchConfig = (~config: Config.t, ~processConfig: JSON.t): Config.t => {
|
|
|
320
321
|
let chainIdStr = chain->ChainMap.Chain.toChainId->Int.toString
|
|
321
322
|
switch chainsDict->Dict.get(chainIdStr) {
|
|
322
323
|
| Some(processChainJson) =>
|
|
323
|
-
let
|
|
324
|
-
|
|
324
|
+
let raw = processChainJson->(Utils.magic: JSON.t => {..})
|
|
325
|
+
let simulateRaw: option<array<JSON.t>> = raw["simulate"]->Nullable.toOption
|
|
325
326
|
switch simulateRaw {
|
|
326
327
|
| Some(simulateItems) =>
|
|
327
328
|
let items = parse(~simulateItems, ~config, ~chainConfig)
|
|
328
|
-
|
|
329
|
-
let
|
|
330
|
-
(processChainJson->(Utils.magic: JSON.t => {..}))["startBlock"]->(
|
|
331
|
-
Utils.magic: 'a => int
|
|
332
|
-
)
|
|
333
|
-
let endBlock: int =
|
|
334
|
-
(processChainJson->(Utils.magic: JSON.t => {..}))["endBlock"]->(Utils.magic: 'a => int)
|
|
329
|
+
let startBlock: int = raw["startBlock"]->(Utils.magic: 'a => int)
|
|
330
|
+
let endBlock: int = raw["endBlock"]->(Utils.magic: 'a => int)
|
|
335
331
|
let source = SimulateSource.make(~items, ~endBlock, ~chain)
|
|
336
332
|
{...chainConfig, startBlock, endBlock, sourceConfig: Config.CustomSources([source])}
|
|
337
333
|
| None => chainConfig
|
|
@@ -201,7 +201,8 @@ function parse(simulateItems, config, chainConfig) {
|
|
|
201
201
|
match$3 = [
|
|
202
202
|
block,
|
|
203
203
|
block.number,
|
|
204
|
-
block.timestamp
|
|
204
|
+
block.timestamp,
|
|
205
|
+
block.hash
|
|
205
206
|
];
|
|
206
207
|
break;
|
|
207
208
|
case "fuel" :
|
|
@@ -209,7 +210,8 @@ function parse(simulateItems, config, chainConfig) {
|
|
|
209
210
|
match$3 = [
|
|
210
211
|
block$1,
|
|
211
212
|
block$1.height,
|
|
212
|
-
block$1.time
|
|
213
|
+
block$1.time,
|
|
214
|
+
block$1.id
|
|
213
215
|
];
|
|
214
216
|
break;
|
|
215
217
|
case "svm" :
|
|
@@ -237,6 +239,7 @@ function parse(simulateItems, config, chainConfig) {
|
|
|
237
239
|
timestamp: match$3[2],
|
|
238
240
|
chain: chain,
|
|
239
241
|
blockNumber: blockNumber,
|
|
242
|
+
blockHash: match$3[3],
|
|
240
243
|
logIndex: logIndex,
|
|
241
244
|
event: {
|
|
242
245
|
contractName: eventConfig.contractName,
|
package/src/Sink.res
CHANGED
|
@@ -34,7 +34,7 @@ let makeClickHouse = (~host, ~database, ~username, ~password): t => {
|
|
|
34
34
|
},
|
|
35
35
|
writeBatch: async (~batch, ~updatedEntities) => {
|
|
36
36
|
await Promise.all(
|
|
37
|
-
updatedEntities->
|
|
37
|
+
updatedEntities->Array.map(({entityConfig, changes}) => {
|
|
38
38
|
ClickHouse.setUpdatesOrThrow(client, ~cache, ~changes, ~entityConfig, ~database)
|
|
39
39
|
}),
|
|
40
40
|
)->Utils.Promise.ignoreValue
|
package/src/Sink.res.mjs
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
2
|
|
|
3
|
-
import * as Belt_Array from "@rescript/runtime/lib/es6/Belt_Array.js";
|
|
4
3
|
import * as ClickHouse from "./bindings/ClickHouse.res.mjs";
|
|
5
4
|
import * as Client from "@clickhouse/client";
|
|
6
5
|
|
|
@@ -21,7 +20,7 @@ function makeClickHouse(host, database, username, password) {
|
|
|
21
20
|
},
|
|
22
21
|
resume: checkpointId => ClickHouse.resume(client, database, checkpointId),
|
|
23
22
|
writeBatch: async (batch, updatedEntities) => {
|
|
24
|
-
await Promise.all(
|
|
23
|
+
await Promise.all(updatedEntities.map(param => ClickHouse.setUpdatesOrThrow(client, cache, param.changes, param.entityConfig, database)));
|
|
25
24
|
return await ClickHouse.setCheckpointsOrThrow(client, batch, database);
|
|
26
25
|
}
|
|
27
26
|
};
|
package/src/SvmTypes.res
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
module Pubkey = {
|
|
2
|
+
type t
|
|
3
|
+
let schema =
|
|
4
|
+
S.string->S.setName("SVM.Pubkey")->(Utils.magic: S.t<string> => S.t<t>)
|
|
5
|
+
external fromStringUnsafe: string => t = "%identity"
|
|
6
|
+
external fromStringsUnsafe: array<string> => array<t> = "%identity"
|
|
7
|
+
external toString: t => string = "%identity"
|
|
8
|
+
external toStrings: array<t> => array<string> = "%identity"
|
|
9
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as S$RescriptSchema from "rescript-schema/src/S.res.mjs";
|
|
4
|
+
|
|
5
|
+
let schema = S$RescriptSchema.setName(S$RescriptSchema.string, "SVM.Pubkey");
|
|
6
|
+
|
|
7
|
+
let Pubkey = {
|
|
8
|
+
schema: schema
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export {
|
|
12
|
+
Pubkey,
|
|
13
|
+
}
|
|
14
|
+
/* schema Not a pure module */
|
package/src/TestIndexer.res
CHANGED
|
@@ -43,71 +43,43 @@ let toIndexingAddress = (dc: InternalTable.EnvioAddresses.t): Internal.indexingA
|
|
|
43
43
|
registrationBlock: dc.registrationBlock,
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
let
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
let handleLoadByField = (
|
|
67
|
-
state: testIndexerState,
|
|
68
|
-
~tableName: string,
|
|
69
|
-
~fieldName: string,
|
|
70
|
-
~fieldValue: JSON.t,
|
|
71
|
-
~operator: Persistence.operator,
|
|
72
|
-
): JSON.t => {
|
|
73
|
-
let entityDict = state.entities->Dict.get(tableName)->Option.getOr(Dict.make())
|
|
74
|
-
let entityConfig = state.entityConfigs->Dict.getUnsafe(tableName)
|
|
75
|
-
let results = []
|
|
76
|
-
|
|
77
|
-
// Get the field schema from the entity's table to properly parse the JSON field value
|
|
78
|
-
let fieldSchema = switch entityConfig.table->Table.getFieldByDbName(fieldName) {
|
|
79
|
-
| Some(Table.Field({fieldSchema})) => fieldSchema
|
|
80
|
-
| _ => JsError.throwWithMessage(`Field ${fieldName} not found in entity ${tableName}`)
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// Parse JSON field value to typed value using the field's schema
|
|
84
|
-
let parsedFieldValue = fieldValue->S.convertOrThrow(fieldSchema)->TableIndices.FieldValue.castFrom
|
|
85
|
-
|
|
86
|
-
// Compare using TableIndices.FieldValue logic (same approach as InMemoryTable)
|
|
87
|
-
// This properly handles bigint and BigDecimal comparisons
|
|
88
|
-
entityDict
|
|
89
|
-
->Dict.valuesToArray
|
|
90
|
-
->Array.forEach(entity => {
|
|
91
|
-
// Cast entity to dict of field values (same approach as InMemoryTable)
|
|
92
|
-
let entityAsDict = entity->(Utils.magic: Internal.entity => dict<TableIndices.FieldValue.t>)
|
|
93
|
-
switch entityAsDict->Dict.get(fieldName) {
|
|
94
|
-
| Some(entityFieldValue) => {
|
|
95
|
-
let matches = switch operator {
|
|
96
|
-
| #"=" => entityFieldValue->TableIndices.FieldValue.eq(parsedFieldValue)
|
|
97
|
-
| #">" => entityFieldValue->TableIndices.FieldValue.gt(parsedFieldValue)
|
|
98
|
-
| #"<" => entityFieldValue->TableIndices.FieldValue.lt(parsedFieldValue)
|
|
99
|
-
}
|
|
100
|
-
if matches {
|
|
101
|
-
// Serialize entity back to JSON for worker thread
|
|
102
|
-
let jsonEntity = entity->S.reverseConvertToJsonOrThrow(entityConfig.schema)
|
|
103
|
-
results->Array.push(jsonEntity)->ignore
|
|
104
|
-
}
|
|
46
|
+
let handleLoad = (state: testIndexerState, ~tableName: string, ~filter: EntityFilter.t): JSON.t => {
|
|
47
|
+
// Loads for non-entity tables (e.g. effect caches `envio_effect_<name>`) reach
|
|
48
|
+
// here too. TestIndexer never persists those, so there's nothing to return —
|
|
49
|
+
// an empty result makes the effect recompute instead of crashing on a missing
|
|
50
|
+
// entityConfig.
|
|
51
|
+
switch state.entityConfigs->Dict.get(tableName) {
|
|
52
|
+
| None => []->JSON.Encode.array
|
|
53
|
+
| Some(entityConfig) =>
|
|
54
|
+
let entityDict = state.entities->Dict.get(tableName)->Option.getOr(Dict.make())
|
|
55
|
+
let results = []
|
|
56
|
+
|
|
57
|
+
// Field values arrive as JSON from the worker boundary, so parse them
|
|
58
|
+
// with the field's schema before comparing. This properly handles
|
|
59
|
+
// bigint and BigDecimal comparisons
|
|
60
|
+
let parseLeaf = (~fieldName, ~fieldValue: unknown, ~isArray): unknown => {
|
|
61
|
+
let queryField = switch entityConfig.table->Table.queryFields->Dict.get(fieldName) {
|
|
62
|
+
| Some(queryField) => queryField
|
|
63
|
+
| None => JsError.throwWithMessage(`Field ${fieldName} not found in entity ${tableName}`)
|
|
105
64
|
}
|
|
106
|
-
|
|
65
|
+
fieldValue->S.convertOrThrow(isArray ? queryField.arrayFieldSchema : queryField.fieldSchema)
|
|
107
66
|
}
|
|
108
|
-
|
|
67
|
+
let filter = filter->EntityFilter.mapValues(~mapValue=parseLeaf)
|
|
68
|
+
|
|
69
|
+
entityDict
|
|
70
|
+
->Dict.valuesToArray
|
|
71
|
+
->Array.forEach(entity => {
|
|
72
|
+
// Cast entity to dict of field values (same approach as InMemoryTable)
|
|
73
|
+
let entityAsDict = entity->(Utils.magic: Internal.entity => dict<EntityFilter.FieldValue.t>)
|
|
74
|
+
if filter->EntityFilter.matches(~entity=entityAsDict) {
|
|
75
|
+
// Serialize entity back to JSON for worker thread
|
|
76
|
+
let jsonEntity = entity->S.reverseConvertToJsonOrThrow(entityConfig.schema)
|
|
77
|
+
results->Array.push(jsonEntity)->ignore
|
|
78
|
+
}
|
|
79
|
+
})
|
|
109
80
|
|
|
110
|
-
|
|
81
|
+
results->JSON.Encode.array
|
|
82
|
+
}
|
|
111
83
|
}
|
|
112
84
|
|
|
113
85
|
let handleWriteBatch = (
|
|
@@ -777,12 +749,7 @@ let makeCreateTestIndexer = (~config: Config.t, ~workerPath: string): (
|
|
|
777
749
|
)
|
|
778
750
|
|
|
779
751
|
switch msg.payload {
|
|
780
|
-
|
|
|
781
|
-
|
|
782
|
-
| LoadByField({tableName, fieldName, fieldValue, operator}) =>
|
|
783
|
-
state
|
|
784
|
-
->handleLoadByField(~tableName, ~fieldName, ~fieldValue, ~operator)
|
|
785
|
-
->respond
|
|
752
|
+
| Load({tableName, filter}) => state->handleLoad(~tableName, ~filter)->respond
|
|
786
753
|
|
|
787
754
|
| WriteBatch({
|
|
788
755
|
updatedEntities,
|
package/src/TestIndexer.res.mjs
CHANGED
|
@@ -11,7 +11,7 @@ import * as ChainMap from "./ChainMap.res.mjs";
|
|
|
11
11
|
import * as Stdlib_Int from "@rescript/runtime/lib/es6/Stdlib_Int.js";
|
|
12
12
|
import * as Persistence from "./Persistence.res.mjs";
|
|
13
13
|
import * as Stdlib_Dict from "@rescript/runtime/lib/es6/Stdlib_Dict.js";
|
|
14
|
-
import * as
|
|
14
|
+
import * as EntityFilter from "./db/EntityFilter.res.mjs";
|
|
15
15
|
import * as InternalTable from "./db/InternalTable.res.mjs";
|
|
16
16
|
import * as Primitive_int from "@rescript/runtime/lib/es6/Primitive_int.js";
|
|
17
17
|
import * as SimulateItems from "./SimulateItems.res.mjs";
|
|
@@ -32,47 +32,21 @@ function toIndexingAddress(dc) {
|
|
|
32
32
|
};
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
function
|
|
36
|
-
let entityDict = Stdlib_Option.getOr(state.entities[tableName], {});
|
|
35
|
+
function handleLoad(state, tableName, filter) {
|
|
37
36
|
let entityConfig = state.entityConfigs[tableName];
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
if (entity === undefined) {
|
|
42
|
-
return;
|
|
43
|
-
}
|
|
44
|
-
let jsonEntity = S$RescriptSchema.reverseConvertToJsonOrThrow(entity, entityConfig.schema);
|
|
45
|
-
results.push(jsonEntity);
|
|
46
|
-
});
|
|
47
|
-
return results;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
function handleLoadByField(state, tableName, fieldName, fieldValue, operator) {
|
|
37
|
+
if (entityConfig === undefined) {
|
|
38
|
+
return [];
|
|
39
|
+
}
|
|
51
40
|
let entityDict = Stdlib_Option.getOr(state.entities[tableName], {});
|
|
52
|
-
let entityConfig = state.entityConfigs[tableName];
|
|
53
41
|
let results = [];
|
|
54
|
-
let
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
exit = 1;
|
|
61
|
-
}
|
|
62
|
-
if (exit === 1) {
|
|
63
|
-
fieldSchema = Stdlib_JsError.throwWithMessage(`Field ` + fieldName + ` not found in entity ` + tableName);
|
|
64
|
-
}
|
|
65
|
-
let parsedFieldValue = S$RescriptSchema.convertOrThrow(fieldValue, fieldSchema);
|
|
42
|
+
let parseLeaf = (fieldName, fieldValue, isArray) => {
|
|
43
|
+
let queryField = Table.queryFields(entityConfig.table)[fieldName];
|
|
44
|
+
let queryField$1 = queryField !== undefined ? queryField : Stdlib_JsError.throwWithMessage(`Field ` + fieldName + ` not found in entity ` + tableName);
|
|
45
|
+
return S$RescriptSchema.convertOrThrow(fieldValue, isArray ? queryField$1.arrayFieldSchema : queryField$1.fieldSchema);
|
|
46
|
+
};
|
|
47
|
+
let filter$1 = EntityFilter.mapValues(filter, parseLeaf);
|
|
66
48
|
Object.values(entityDict).forEach(entity => {
|
|
67
|
-
|
|
68
|
-
if (entityFieldValue === undefined) {
|
|
69
|
-
return;
|
|
70
|
-
}
|
|
71
|
-
let entityFieldValue$1 = Primitive_option.valFromOption(entityFieldValue);
|
|
72
|
-
let matches = operator === "=" ? TableIndices.FieldValue.eq(entityFieldValue$1, parsedFieldValue) : (
|
|
73
|
-
operator === ">" ? TableIndices.FieldValue.gt(entityFieldValue$1, parsedFieldValue) : TableIndices.FieldValue.lt(entityFieldValue$1, parsedFieldValue)
|
|
74
|
-
);
|
|
75
|
-
if (!matches) {
|
|
49
|
+
if (!EntityFilter.matches(filter$1, entity)) {
|
|
76
50
|
return;
|
|
77
51
|
}
|
|
78
52
|
let jsonEntity = S$RescriptSchema.reverseConvertToJsonOrThrow(entity, entityConfig.schema);
|
|
@@ -561,15 +535,11 @@ function makeCreateTestIndexer(config, workerPath) {
|
|
|
561
535
|
});
|
|
562
536
|
};
|
|
563
537
|
let match = msg.payload;
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
return respond(handleLoadByIds(state, match.tableName, match.ids));
|
|
567
|
-
case "loadByField" :
|
|
568
|
-
return respond(handleLoadByField(state, match.tableName, match.fieldName, match.fieldValue, match.operator));
|
|
569
|
-
case "writeBatch" :
|
|
570
|
-
handleWriteBatch(state, match.updatedEntities, match.checkpointIds, match.checkpointChainIds, match.checkpointBlockNumbers, match.checkpointEventsProcessed);
|
|
571
|
-
return respond(null);
|
|
538
|
+
if (match.type === "load") {
|
|
539
|
+
return respond(handleLoad(state, match.tableName, match.filter));
|
|
572
540
|
}
|
|
541
|
+
handleWriteBatch(state, match.updatedEntities, match.checkpointIds, match.checkpointChainIds, match.checkpointBlockNumbers, match.checkpointEventsProcessed);
|
|
542
|
+
respond(null);
|
|
573
543
|
});
|
|
574
544
|
worker.on("error", err => {
|
|
575
545
|
worker.terminate();
|
|
@@ -675,8 +645,7 @@ function initTestWorker() {
|
|
|
675
645
|
|
|
676
646
|
export {
|
|
677
647
|
toIndexingAddress,
|
|
678
|
-
|
|
679
|
-
handleLoadByField,
|
|
648
|
+
handleLoad,
|
|
680
649
|
handleWriteBatch,
|
|
681
650
|
makeInitialState,
|
|
682
651
|
rawChainConfigSchema,
|
|
@@ -15,13 +15,11 @@ type serializableUpdatedEntity = {
|
|
|
15
15
|
// Worker -> Main thread payloads
|
|
16
16
|
@tag("type")
|
|
17
17
|
type workerPayload =
|
|
18
|
-
| @as("
|
|
19
|
-
|
|
20
|
-
LoadByField({
|
|
18
|
+
| @as("load")
|
|
19
|
+
Load({
|
|
21
20
|
tableName: string,
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
operator: Persistence.operator,
|
|
21
|
+
// Leaf field values are JSON-serialized with the table's field schemas
|
|
22
|
+
filter: EntityFilter.t,
|
|
25
23
|
})
|
|
26
24
|
| @as("writeBatch")
|
|
27
25
|
WriteBatch({
|
|
@@ -107,27 +105,29 @@ let makeStorage = (proxy: t): Persistence.storage => {
|
|
|
107
105
|
)
|
|
108
106
|
},
|
|
109
107
|
resumeInitialState: async () => proxy.initialState,
|
|
110
|
-
|
|
111
|
-
let
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
108
|
+
loadOrThrow: async (~filter, ~table: Table.table) => {
|
|
109
|
+
let serializeLeafOrThrow = (~fieldName, ~fieldValue: unknown, ~isArray) => {
|
|
110
|
+
let queryField = switch table->Table.queryFields->Dict.get(fieldName) {
|
|
111
|
+
| Some(queryField) => queryField
|
|
112
|
+
| None =>
|
|
113
|
+
JsError.throwWithMessage(
|
|
114
|
+
`TestIndexer: The table "${table.tableName}" doesn't have the field "${fieldName}"`,
|
|
115
|
+
)
|
|
116
|
+
}
|
|
117
|
+
fieldValue
|
|
118
|
+
->S.reverseConvertToJsonOrThrow(
|
|
119
|
+
isArray ? queryField.arrayFieldSchema : queryField.fieldSchema,
|
|
120
|
+
)
|
|
121
|
+
->(Utils.magic: JSON.t => unknown)
|
|
122
|
+
}
|
|
122
123
|
let response = await proxy->sendRequest(
|
|
123
|
-
~payload=
|
|
124
|
+
~payload=Load({
|
|
124
125
|
tableName: table.tableName,
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
operator,
|
|
126
|
+
// Field values must be JSON-safe to survive the worker thread boundary
|
|
127
|
+
filter: filter->EntityFilter.mapValues(~mapValue=serializeLeafOrThrow),
|
|
128
128
|
}),
|
|
129
129
|
)
|
|
130
|
-
response->S.parseOrThrow(rowsSchema)
|
|
130
|
+
response->S.parseOrThrow(table->Table.rowsSchema)
|
|
131
131
|
},
|
|
132
132
|
writeBatch: async (
|
|
133
133
|
~batch,
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
2
|
|
|
3
|
+
import * as Table from "./db/Table.res.mjs";
|
|
3
4
|
import * as Stdlib_Dict from "@rescript/runtime/lib/es6/Stdlib_Dict.js";
|
|
5
|
+
import * as EntityFilter from "./db/EntityFilter.res.mjs";
|
|
4
6
|
import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
|
|
5
7
|
import * as S$RescriptSchema from "rescript-schema/src/S.res.mjs";
|
|
6
8
|
|
|
@@ -51,23 +53,18 @@ function makeStorage(proxy) {
|
|
|
51
53
|
isInitialized: async () => true,
|
|
52
54
|
initialize: async (param, param$1, param$2, param$3) => Stdlib_JsError.throwWithMessage("TestIndexer: initialize should not be called. Use resumeInitialState instead."),
|
|
53
55
|
resumeInitialState: async () => proxy.initialState,
|
|
54
|
-
|
|
56
|
+
loadOrThrow: async (filter, table) => {
|
|
57
|
+
let serializeLeafOrThrow = (fieldName, fieldValue, isArray) => {
|
|
58
|
+
let queryField = Table.queryFields(table)[fieldName];
|
|
59
|
+
let queryField$1 = queryField !== undefined ? queryField : Stdlib_JsError.throwWithMessage(`TestIndexer: The table "` + table.tableName + `" doesn't have the field "` + fieldName + `"`);
|
|
60
|
+
return S$RescriptSchema.reverseConvertToJsonOrThrow(fieldValue, isArray ? queryField$1.arrayFieldSchema : queryField$1.fieldSchema);
|
|
61
|
+
};
|
|
55
62
|
let response = await sendRequest(proxy, {
|
|
56
|
-
type: "
|
|
63
|
+
type: "load",
|
|
57
64
|
tableName: table.tableName,
|
|
58
|
-
|
|
65
|
+
filter: EntityFilter.mapValues(filter, serializeLeafOrThrow)
|
|
59
66
|
});
|
|
60
|
-
return S$RescriptSchema.parseOrThrow(response, rowsSchema);
|
|
61
|
-
},
|
|
62
|
-
loadByFieldOrThrow: async (fieldName, fieldSchema, fieldValue, operator, table, rowsSchema) => {
|
|
63
|
-
let response = await sendRequest(proxy, {
|
|
64
|
-
type: "loadByField",
|
|
65
|
-
tableName: table.tableName,
|
|
66
|
-
fieldName: fieldName,
|
|
67
|
-
fieldValue: S$RescriptSchema.reverseConvertToJsonOrThrow(fieldValue, fieldSchema),
|
|
68
|
-
operator: operator
|
|
69
|
-
});
|
|
70
|
-
return S$RescriptSchema.parseOrThrow(response, rowsSchema);
|
|
67
|
+
return S$RescriptSchema.parseOrThrow(response, Table.rowsSchema(table));
|
|
71
68
|
},
|
|
72
69
|
dumpEffectCache: async () => {},
|
|
73
70
|
reset: async () => {},
|
|
@@ -121,4 +118,4 @@ export {
|
|
|
121
118
|
sendRequest,
|
|
122
119
|
makeStorage,
|
|
123
120
|
}
|
|
124
|
-
/*
|
|
121
|
+
/* Table Not a pure module */
|
package/src/Throttler.res
CHANGED
|
@@ -12,7 +12,7 @@ let make = (~intervalMillis: int, ~logger) => {
|
|
|
12
12
|
isRunning: false,
|
|
13
13
|
isAwaitingInterval: false,
|
|
14
14
|
scheduled: None,
|
|
15
|
-
intervalMillis: intervalMillis->
|
|
15
|
+
intervalMillis: intervalMillis->Int.toFloat,
|
|
16
16
|
logger,
|
|
17
17
|
}
|
|
18
18
|
|
|
@@ -53,7 +53,7 @@ let rec startInternal = (throttler: t) => {
|
|
|
53
53
|
let _ = setTimeout(() => {
|
|
54
54
|
throttler.isAwaitingInterval = false
|
|
55
55
|
throttler->startInternal
|
|
56
|
-
},
|
|
56
|
+
}, Int.fromFloat(throttler.intervalMillis -. timeSinceLastRun))
|
|
57
57
|
}
|
|
58
58
|
| _ => ()
|
|
59
59
|
}
|
package/src/Time.res
CHANGED
|
@@ -16,7 +16,7 @@ let rec retryAsyncWithExponentialBackOff = async (
|
|
|
16
16
|
let nextRetryCount = retryCount + 1
|
|
17
17
|
let log = retryCount === 0 ? Logging.childTrace : Logging.childWarn
|
|
18
18
|
logger->log({
|
|
19
|
-
"msg": `Retrying query ${nextRetryCount->
|
|
19
|
+
"msg": `Retrying query ${nextRetryCount->Int.toString}/${maxRetries->Int.toString} in ${backOffMillis->Int.toString}ms - waiting for correct result.`,
|
|
20
20
|
"err": exn->Utils.prettifyExn,
|
|
21
21
|
})
|
|
22
22
|
await resolvePromiseAfterDelay(~delayMilliseconds=backOffMillis)
|
|
@@ -32,7 +32,7 @@ let rec retryAsyncWithExponentialBackOff = async (
|
|
|
32
32
|
exn
|
|
33
33
|
->ErrorHandling.make(
|
|
34
34
|
~logger,
|
|
35
|
-
~msg=`Failure. Max retries ${retryCount->
|
|
35
|
+
~msg=`Failure. Max retries ${retryCount->Int.toString}/${maxRetries->Int.toString} exceeded`,
|
|
36
36
|
)
|
|
37
37
|
->ErrorHandling.log
|
|
38
38
|
await Promise.reject(exn->JsExn.anyToExnInternal)
|
package/src/Time.res.mjs
CHANGED
|
@@ -20,13 +20,13 @@ async function retryAsyncWithExponentialBackOff(backOffMillisOpt, multiplicative
|
|
|
20
20
|
let nextRetryCount = retryCount + 1 | 0;
|
|
21
21
|
let log = retryCount === 0 ? Logging.childTrace : Logging.childWarn;
|
|
22
22
|
log(logger, {
|
|
23
|
-
msg: `Retrying query ` +
|
|
23
|
+
msg: `Retrying query ` + nextRetryCount.toString() + `/` + maxRetries.toString() + ` in ` + backOffMillis.toString() + `ms - waiting for correct result.`,
|
|
24
24
|
err: Utils.prettifyExn(exn)
|
|
25
25
|
});
|
|
26
26
|
await Utils.delay(backOffMillis);
|
|
27
27
|
return await retryAsyncWithExponentialBackOff(backOffMillis * multiplicative | 0, multiplicative, nextRetryCount, maxRetries, logger, f);
|
|
28
28
|
}
|
|
29
|
-
ErrorHandling.log(ErrorHandling.make(exn, logger, `Failure. Max retries ` +
|
|
29
|
+
ErrorHandling.log(ErrorHandling.make(exn, logger, `Failure. Max retries ` + retryCount.toString() + `/` + maxRetries.toString() + ` exceeded`));
|
|
30
30
|
return await Promise.reject(Primitive_exceptions.internalToException(exn));
|
|
31
31
|
}
|
|
32
32
|
}
|