envio 3.1.1 → 3.2.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/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 +52 -0
- package/src/EventConfigBuilder.res.mjs +32 -0
- package/src/EventUtils.res +2 -2
- package/src/FetchState.res +126 -71
- package/src/FetchState.res.mjs +73 -51
- 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 +12 -1
- package/src/HandlerLoader.res.mjs +6 -1
- 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 +54 -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 +4 -4
- package/src/LogSelection.res.mjs +5 -7
- 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 +17 -57
- package/src/TestIndexer.res.mjs +14 -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 +10 -9
- package/src/sources/HyperSyncSource.res.mjs +4 -4
- 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,67 +43,32 @@ let toIndexingAddress = (dc: InternalTable.EnvioAddresses.t): Internal.indexingA
|
|
|
43
43
|
registrationBlock: dc.registrationBlock,
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
let
|
|
47
|
-
state: testIndexerState,
|
|
48
|
-
~tableName: string,
|
|
49
|
-
~ids: array<string>,
|
|
50
|
-
): JSON.t => {
|
|
51
|
-
let entityDict = state.entities->Dict.get(tableName)->Option.getOr(Dict.make())
|
|
52
|
-
let entityConfig = state.entityConfigs->Dict.getUnsafe(tableName)
|
|
53
|
-
let results = []
|
|
54
|
-
ids->Array.forEach(id => {
|
|
55
|
-
switch entityDict->Dict.get(id) {
|
|
56
|
-
| Some(entity) =>
|
|
57
|
-
// Serialize entity back to JSON for worker thread
|
|
58
|
-
let jsonEntity = entity->S.reverseConvertToJsonOrThrow(entityConfig.schema)
|
|
59
|
-
results->Array.push(jsonEntity)->ignore
|
|
60
|
-
| None => ()
|
|
61
|
-
}
|
|
62
|
-
})
|
|
63
|
-
results->JSON.Encode.array
|
|
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 => {
|
|
46
|
+
let handleLoad = (state: testIndexerState, ~tableName: string, ~filter: EntityFilter.t): JSON.t => {
|
|
73
47
|
let entityDict = state.entities->Dict.get(tableName)->Option.getOr(Dict.make())
|
|
74
48
|
let entityConfig = state.entityConfigs->Dict.getUnsafe(tableName)
|
|
75
49
|
let results = []
|
|
76
50
|
|
|
77
|
-
//
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
51
|
+
// Field values arrive as JSON from the worker boundary, so parse them
|
|
52
|
+
// with the field's schema before comparing. This properly handles
|
|
53
|
+
// bigint and BigDecimal comparisons
|
|
54
|
+
let parseLeaf = (~fieldName, ~fieldValue: unknown, ~isArray): unknown => {
|
|
55
|
+
let queryField = switch entityConfig.table->Table.queryFields->Dict.get(fieldName) {
|
|
56
|
+
| Some(queryField) => queryField
|
|
57
|
+
| None => JsError.throwWithMessage(`Field ${fieldName} not found in entity ${tableName}`)
|
|
58
|
+
}
|
|
59
|
+
fieldValue->S.convertOrThrow(isArray ? queryField.arrayFieldSchema : queryField.fieldSchema)
|
|
81
60
|
}
|
|
61
|
+
let filter = filter->EntityFilter.mapValues(~mapValue=parseLeaf)
|
|
82
62
|
|
|
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
63
|
entityDict
|
|
89
64
|
->Dict.valuesToArray
|
|
90
65
|
->Array.forEach(entity => {
|
|
91
66
|
// Cast entity to dict of field values (same approach as InMemoryTable)
|
|
92
|
-
let entityAsDict = entity->(Utils.magic: Internal.entity => dict<
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
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
|
-
}
|
|
105
|
-
}
|
|
106
|
-
| None => ()
|
|
67
|
+
let entityAsDict = entity->(Utils.magic: Internal.entity => dict<EntityFilter.FieldValue.t>)
|
|
68
|
+
if filter->EntityFilter.matches(~entity=entityAsDict) {
|
|
69
|
+
// Serialize entity back to JSON for worker thread
|
|
70
|
+
let jsonEntity = entity->S.reverseConvertToJsonOrThrow(entityConfig.schema)
|
|
71
|
+
results->Array.push(jsonEntity)->ignore
|
|
107
72
|
}
|
|
108
73
|
})
|
|
109
74
|
|
|
@@ -777,12 +742,7 @@ let makeCreateTestIndexer = (~config: Config.t, ~workerPath: string): (
|
|
|
777
742
|
)
|
|
778
743
|
|
|
779
744
|
switch msg.payload {
|
|
780
|
-
|
|
|
781
|
-
|
|
782
|
-
| LoadByField({tableName, fieldName, fieldValue, operator}) =>
|
|
783
|
-
state
|
|
784
|
-
->handleLoadByField(~tableName, ~fieldName, ~fieldValue, ~operator)
|
|
785
|
-
->respond
|
|
745
|
+
| Load({tableName, filter}) => state->handleLoad(~tableName, ~filter)->respond
|
|
786
746
|
|
|
787
747
|
| WriteBatch({
|
|
788
748
|
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,18 @@ function toIndexingAddress(dc) {
|
|
|
32
32
|
};
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
function
|
|
35
|
+
function handleLoad(state, tableName, filter) {
|
|
36
36
|
let entityDict = Stdlib_Option.getOr(state.entities[tableName], {});
|
|
37
37
|
let entityConfig = state.entityConfigs[tableName];
|
|
38
38
|
let results = [];
|
|
39
|
-
|
|
40
|
-
let
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
results.push(jsonEntity);
|
|
46
|
-
});
|
|
47
|
-
return results;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
function handleLoadByField(state, tableName, fieldName, fieldValue, operator) {
|
|
51
|
-
let entityDict = Stdlib_Option.getOr(state.entities[tableName], {});
|
|
52
|
-
let entityConfig = state.entityConfigs[tableName];
|
|
53
|
-
let results = [];
|
|
54
|
-
let match = Table.getFieldByDbName(entityConfig.table, fieldName);
|
|
55
|
-
let fieldSchema;
|
|
56
|
-
let exit = 0;
|
|
57
|
-
if (match !== undefined && match.TAG === "Field") {
|
|
58
|
-
fieldSchema = match._0.fieldSchema;
|
|
59
|
-
} else {
|
|
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);
|
|
39
|
+
let parseLeaf = (fieldName, fieldValue, isArray) => {
|
|
40
|
+
let queryField = Table.queryFields(entityConfig.table)[fieldName];
|
|
41
|
+
let queryField$1 = queryField !== undefined ? queryField : Stdlib_JsError.throwWithMessage(`Field ` + fieldName + ` not found in entity ` + tableName);
|
|
42
|
+
return S$RescriptSchema.convertOrThrow(fieldValue, isArray ? queryField$1.arrayFieldSchema : queryField$1.fieldSchema);
|
|
43
|
+
};
|
|
44
|
+
let filter$1 = EntityFilter.mapValues(filter, parseLeaf);
|
|
66
45
|
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) {
|
|
46
|
+
if (!EntityFilter.matches(filter$1, entity)) {
|
|
76
47
|
return;
|
|
77
48
|
}
|
|
78
49
|
let jsonEntity = S$RescriptSchema.reverseConvertToJsonOrThrow(entity, entityConfig.schema);
|
|
@@ -561,15 +532,11 @@ function makeCreateTestIndexer(config, workerPath) {
|
|
|
561
532
|
});
|
|
562
533
|
};
|
|
563
534
|
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);
|
|
535
|
+
if (match.type === "load") {
|
|
536
|
+
return respond(handleLoad(state, match.tableName, match.filter));
|
|
572
537
|
}
|
|
538
|
+
handleWriteBatch(state, match.updatedEntities, match.checkpointIds, match.checkpointChainIds, match.checkpointBlockNumbers, match.checkpointEventsProcessed);
|
|
539
|
+
respond(null);
|
|
573
540
|
});
|
|
574
541
|
worker.on("error", err => {
|
|
575
542
|
worker.terminate();
|
|
@@ -675,8 +642,7 @@ function initTestWorker() {
|
|
|
675
642
|
|
|
676
643
|
export {
|
|
677
644
|
toIndexingAddress,
|
|
678
|
-
|
|
679
|
-
handleLoadByField,
|
|
645
|
+
handleLoad,
|
|
680
646
|
handleWriteBatch,
|
|
681
647
|
makeInitialState,
|
|
682
648
|
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
|
}
|