envio 3.0.0-alpha.0 → 3.0.0-alpha.10
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/README.md +2 -2
- package/evm.schema.json +53 -57
- package/fuel.schema.json +35 -31
- package/index.d.ts +446 -1
- package/index.js +4 -0
- package/package.json +23 -11
- package/rescript.json +4 -1
- package/src/Batch.res.mjs +1 -1
- package/src/Benchmark.res +394 -0
- package/src/Benchmark.res.mjs +398 -0
- package/src/ChainFetcher.res +526 -0
- package/src/ChainFetcher.res.mjs +343 -0
- package/src/ChainManager.res +182 -0
- package/src/ChainManager.res.mjs +150 -0
- package/src/Config.res +367 -27
- package/src/Config.res.mjs +338 -28
- package/src/{Indexer.res → Ctx.res} +1 -1
- package/src/Ecosystem.res +25 -0
- package/src/Ecosystem.res.mjs +29 -0
- package/src/Env.res +252 -0
- package/src/Env.res.mjs +271 -0
- package/src/Envio.gen.ts +9 -1
- package/src/Envio.res +12 -9
- package/src/EventProcessing.res +476 -0
- package/src/EventProcessing.res.mjs +341 -0
- package/src/EventRegister.res +4 -15
- package/src/EventRegister.res.mjs +3 -9
- package/src/EventRegister.resi +2 -8
- package/src/FetchState.res +54 -29
- package/src/FetchState.res.mjs +62 -35
- package/src/GlobalState.res +1163 -0
- package/src/GlobalState.res.mjs +1196 -0
- package/src/GlobalStateManager.res +68 -0
- package/src/GlobalStateManager.res.mjs +75 -0
- package/src/GlobalStateManager.resi +7 -0
- package/src/HandlerLoader.res +89 -0
- package/src/HandlerLoader.res.mjs +79 -0
- package/src/Internal.gen.ts +3 -14
- package/src/Internal.res +45 -13
- package/src/LoadLayer.res +444 -0
- package/src/LoadLayer.res.mjs +296 -0
- package/src/LoadLayer.resi +32 -0
- package/src/LogSelection.res +33 -27
- package/src/LogSelection.res.mjs +6 -0
- package/src/Logging.res +11 -4
- package/src/Logging.res.mjs +14 -6
- package/src/Main.res +351 -0
- package/src/Main.res.mjs +312 -0
- package/src/Persistence.res +1 -2
- package/src/PgStorage.gen.ts +10 -0
- package/src/PgStorage.res +28 -34
- package/src/PgStorage.res.d.mts +5 -0
- package/src/PgStorage.res.mjs +27 -27
- package/src/Prometheus.res +8 -8
- package/src/Prometheus.res.mjs +10 -10
- package/src/ReorgDetection.res +6 -10
- package/src/ReorgDetection.res.mjs +6 -6
- package/src/TestIndexer.res +536 -0
- package/src/TestIndexer.res.mjs +412 -0
- package/src/TestIndexerProxyStorage.res +210 -0
- package/src/TestIndexerProxyStorage.res.mjs +157 -0
- package/src/Types.ts +1 -1
- package/src/UserContext.res +355 -0
- package/src/UserContext.res.mjs +236 -0
- package/src/Utils.res +28 -0
- package/src/Utils.res.mjs +18 -0
- package/src/bindings/ClickHouse.res +31 -1
- package/src/bindings/ClickHouse.res.mjs +27 -1
- package/src/bindings/DateFns.res +71 -0
- package/src/bindings/DateFns.res.mjs +22 -0
- package/src/bindings/Ethers.res +27 -67
- package/src/bindings/Ethers.res.mjs +18 -70
- package/src/bindings/EventSource.res +13 -0
- package/src/bindings/EventSource.res.mjs +2 -0
- package/src/bindings/NodeJs.res +44 -3
- package/src/bindings/NodeJs.res.mjs +11 -3
- package/src/bindings/Pino.res +21 -7
- package/src/bindings/Pino.res.mjs +11 -5
- package/src/bindings/Postgres.gen.ts +8 -0
- package/src/bindings/Postgres.res +3 -0
- package/src/bindings/Postgres.res.d.mts +5 -0
- package/src/bindings/RescriptMocha.res +123 -0
- package/src/bindings/RescriptMocha.res.mjs +18 -0
- package/src/bindings/Vitest.res +134 -0
- package/src/bindings/Vitest.res.mjs +9 -0
- package/src/bindings/Yargs.res +8 -0
- package/src/bindings/Yargs.res.mjs +2 -0
- package/src/db/InternalTable.res +2 -0
- package/src/db/InternalTable.res.mjs +1 -1
- package/src/sources/Evm.res +87 -0
- package/src/sources/Evm.res.mjs +105 -0
- package/src/sources/EvmChain.res +90 -0
- package/src/sources/EvmChain.res.mjs +57 -0
- package/src/sources/Fuel.res +19 -34
- package/src/sources/Fuel.res.mjs +34 -16
- package/src/sources/FuelSDK.res +38 -0
- package/src/sources/FuelSDK.res.mjs +29 -0
- package/src/sources/HyperFuel.res +2 -2
- package/src/sources/HyperFuel.resi +1 -1
- package/src/sources/HyperFuelClient.res +2 -2
- package/src/sources/HyperFuelSource.res +8 -8
- package/src/sources/HyperFuelSource.res.mjs +5 -5
- package/src/sources/HyperSyncHeightStream.res +97 -0
- package/src/sources/HyperSyncHeightStream.res.mjs +94 -0
- package/src/sources/HyperSyncSource.res +64 -172
- package/src/sources/HyperSyncSource.res.mjs +73 -155
- package/src/sources/Rpc.res +43 -0
- package/src/sources/Rpc.res.mjs +31 -0
- package/src/sources/RpcSource.res +32 -130
- package/src/sources/RpcSource.res.mjs +47 -121
- package/src/sources/Source.res +3 -2
- package/src/sources/SourceManager.res +183 -108
- package/src/sources/SourceManager.res.mjs +162 -99
- package/src/sources/SourceManager.resi +4 -5
- package/src/sources/Svm.res +59 -0
- package/src/sources/Svm.res.mjs +79 -0
- package/src/tui/Tui.res +266 -0
- package/src/tui/Tui.res.mjs +342 -0
- package/src/tui/bindings/Ink.res +376 -0
- package/src/tui/bindings/Ink.res.mjs +75 -0
- package/src/tui/bindings/Style.res +123 -0
- package/src/tui/bindings/Style.res.mjs +2 -0
- package/src/tui/components/BufferedProgressBar.res +40 -0
- package/src/tui/components/BufferedProgressBar.res.mjs +57 -0
- package/src/tui/components/CustomHooks.res +122 -0
- package/src/tui/components/CustomHooks.res.mjs +179 -0
- package/src/tui/components/Messages.res +41 -0
- package/src/tui/components/Messages.res.mjs +75 -0
- package/src/tui/components/SyncETA.res +193 -0
- package/src/tui/components/SyncETA.res.mjs +269 -0
- package/src/tui/components/TuiData.res +46 -0
- package/src/tui/components/TuiData.res.mjs +29 -0
- package/src/Platform.res +0 -140
- package/src/Platform.res.mjs +0 -170
- package/src/bindings/Ethers.gen.ts +0 -14
- /package/src/{Indexer.res.mjs → Ctx.res.mjs} +0 -0
|
@@ -0,0 +1,412 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as Env from "./Env.res.mjs";
|
|
4
|
+
import * as Main from "./Main.res.mjs";
|
|
5
|
+
import * as Table from "./db/Table.res.mjs";
|
|
6
|
+
import * as Js_exn from "rescript/lib/es6/js_exn.js";
|
|
7
|
+
import * as Js_dict from "rescript/lib/es6/js_dict.js";
|
|
8
|
+
import * as Js_json from "rescript/lib/es6/js_json.js";
|
|
9
|
+
import * as Logging from "./Logging.res.mjs";
|
|
10
|
+
import * as Process from "process";
|
|
11
|
+
import * as Belt_Int from "rescript/lib/es6/belt_Int.js";
|
|
12
|
+
import * as ChainMap from "./ChainMap.res.mjs";
|
|
13
|
+
import * as Belt_Array from "rescript/lib/es6/belt_Array.js";
|
|
14
|
+
import * as Belt_Option from "rescript/lib/es6/belt_Option.js";
|
|
15
|
+
import * as Caml_option from "rescript/lib/es6/caml_option.js";
|
|
16
|
+
import * as TableIndices from "./TableIndices.res.mjs";
|
|
17
|
+
import * as InternalTable from "./db/InternalTable.res.mjs";
|
|
18
|
+
import * as Worker_threads from "worker_threads";
|
|
19
|
+
import * as S$RescriptSchema from "rescript-schema/src/S.res.mjs";
|
|
20
|
+
import * as Caml_js_exceptions from "rescript/lib/es6/caml_js_exceptions.js";
|
|
21
|
+
import * as TestIndexerProxyStorage from "./TestIndexerProxyStorage.res.mjs";
|
|
22
|
+
|
|
23
|
+
function toIndexingContract(dc) {
|
|
24
|
+
return {
|
|
25
|
+
address: dc.contract_address,
|
|
26
|
+
contractName: dc.contract_name,
|
|
27
|
+
startBlock: dc.registering_event_block_number,
|
|
28
|
+
registrationBlock: dc.registering_event_block_number
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function handleLoadByIds(state, tableName, ids) {
|
|
33
|
+
var entityDict = Belt_Option.getWithDefault(Js_dict.get(state.entities, tableName), {});
|
|
34
|
+
var entityConfig = state.entityConfigs[tableName];
|
|
35
|
+
var results = [];
|
|
36
|
+
Belt_Array.forEach(ids, (function (id) {
|
|
37
|
+
var entity = Js_dict.get(entityDict, id);
|
|
38
|
+
if (entity === undefined) {
|
|
39
|
+
return ;
|
|
40
|
+
}
|
|
41
|
+
var jsonEntity = S$RescriptSchema.reverseConvertToJsonOrThrow(entity, entityConfig.schema);
|
|
42
|
+
results.push(jsonEntity);
|
|
43
|
+
}));
|
|
44
|
+
return results;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function handleLoadByField(state, tableName, fieldName, fieldValue, operator) {
|
|
48
|
+
var entityDict = Belt_Option.getWithDefault(Js_dict.get(state.entities, tableName), {});
|
|
49
|
+
var entityConfig = state.entityConfigs[tableName];
|
|
50
|
+
var results = [];
|
|
51
|
+
var match = Table.getFieldByName(entityConfig.table, fieldName);
|
|
52
|
+
var fieldSchema;
|
|
53
|
+
var exit = 0;
|
|
54
|
+
if (match !== undefined && match.TAG === "Field") {
|
|
55
|
+
fieldSchema = match._0.fieldSchema;
|
|
56
|
+
} else {
|
|
57
|
+
exit = 1;
|
|
58
|
+
}
|
|
59
|
+
if (exit === 1) {
|
|
60
|
+
fieldSchema = Js_exn.raiseError("Field " + fieldName + " not found in entity " + tableName);
|
|
61
|
+
}
|
|
62
|
+
var parsedFieldValue = S$RescriptSchema.convertOrThrow(fieldValue, fieldSchema);
|
|
63
|
+
Belt_Array.forEach(Js_dict.values(entityDict), (function (entity) {
|
|
64
|
+
var entityFieldValue = Js_dict.get(entity, fieldName);
|
|
65
|
+
if (entityFieldValue === undefined) {
|
|
66
|
+
return ;
|
|
67
|
+
}
|
|
68
|
+
var entityFieldValue$1 = Caml_option.valFromOption(entityFieldValue);
|
|
69
|
+
var matches = operator === "=" ? TableIndices.FieldValue.eq(entityFieldValue$1, parsedFieldValue) : (
|
|
70
|
+
operator === ">" ? TableIndices.FieldValue.gt(entityFieldValue$1, parsedFieldValue) : TableIndices.FieldValue.lt(entityFieldValue$1, parsedFieldValue)
|
|
71
|
+
);
|
|
72
|
+
if (!matches) {
|
|
73
|
+
return ;
|
|
74
|
+
}
|
|
75
|
+
var jsonEntity = S$RescriptSchema.reverseConvertToJsonOrThrow(entity, entityConfig.schema);
|
|
76
|
+
results.push(jsonEntity);
|
|
77
|
+
}));
|
|
78
|
+
return results;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function handleWriteBatch(state, updatedEntities, checkpointIds, checkpointChainIds, checkpointBlockNumbers, checkpointBlockHashes, checkpointEventsProcessed) {
|
|
82
|
+
var changesByCheckpoint = {};
|
|
83
|
+
Belt_Array.forEach(updatedEntities, (function (param) {
|
|
84
|
+
var entityName = param.entityName;
|
|
85
|
+
var dict = Js_dict.get(state.entities, entityName);
|
|
86
|
+
var entityDict;
|
|
87
|
+
if (dict !== undefined) {
|
|
88
|
+
entityDict = dict;
|
|
89
|
+
} else {
|
|
90
|
+
var dict$1 = {};
|
|
91
|
+
state.entities[entityName] = dict$1;
|
|
92
|
+
entityDict = dict$1;
|
|
93
|
+
}
|
|
94
|
+
var entityConfig = state.entityConfigs[entityName];
|
|
95
|
+
Belt_Array.forEach(param.updates, (function (update) {
|
|
96
|
+
var processChange = function (change) {
|
|
97
|
+
if (change.type === "SET") {
|
|
98
|
+
var parsedEntity = S$RescriptSchema.parseOrThrow(change.entity, entityConfig.schema);
|
|
99
|
+
entityDict[change.entityId] = parsedEntity;
|
|
100
|
+
var checkpointKey = String(change.checkpointId);
|
|
101
|
+
var changes = Js_dict.get(changesByCheckpoint, checkpointKey);
|
|
102
|
+
var entityChanges;
|
|
103
|
+
if (changes !== undefined) {
|
|
104
|
+
entityChanges = changes;
|
|
105
|
+
} else {
|
|
106
|
+
var changes$1 = {};
|
|
107
|
+
changesByCheckpoint[checkpointKey] = changes$1;
|
|
108
|
+
entityChanges = changes$1;
|
|
109
|
+
}
|
|
110
|
+
var change$1 = Js_dict.get(entityChanges, entityName);
|
|
111
|
+
var entityChange;
|
|
112
|
+
if (change$1 !== undefined) {
|
|
113
|
+
entityChange = change$1;
|
|
114
|
+
} else {
|
|
115
|
+
var change_sets = [];
|
|
116
|
+
var change_deleted = [];
|
|
117
|
+
var change$2 = {
|
|
118
|
+
sets: change_sets,
|
|
119
|
+
deleted: change_deleted
|
|
120
|
+
};
|
|
121
|
+
entityChanges[entityName] = change$2;
|
|
122
|
+
entityChange = change$2;
|
|
123
|
+
}
|
|
124
|
+
entityChange.sets.push(parsedEntity);
|
|
125
|
+
return ;
|
|
126
|
+
}
|
|
127
|
+
var entityId = change.entityId;
|
|
128
|
+
Js_dict.unsafeDeleteKey(entityDict, entityId);
|
|
129
|
+
var checkpointKey$1 = String(change.checkpointId);
|
|
130
|
+
var changes$2 = Js_dict.get(changesByCheckpoint, checkpointKey$1);
|
|
131
|
+
var entityChanges$1;
|
|
132
|
+
if (changes$2 !== undefined) {
|
|
133
|
+
entityChanges$1 = changes$2;
|
|
134
|
+
} else {
|
|
135
|
+
var changes$3 = {};
|
|
136
|
+
changesByCheckpoint[checkpointKey$1] = changes$3;
|
|
137
|
+
entityChanges$1 = changes$3;
|
|
138
|
+
}
|
|
139
|
+
var change$3 = Js_dict.get(entityChanges$1, entityName);
|
|
140
|
+
var entityChange$1;
|
|
141
|
+
if (change$3 !== undefined) {
|
|
142
|
+
entityChange$1 = change$3;
|
|
143
|
+
} else {
|
|
144
|
+
var change_sets$1 = [];
|
|
145
|
+
var change_deleted$1 = [];
|
|
146
|
+
var change$4 = {
|
|
147
|
+
sets: change_sets$1,
|
|
148
|
+
deleted: change_deleted$1
|
|
149
|
+
};
|
|
150
|
+
entityChanges$1[entityName] = change$4;
|
|
151
|
+
entityChange$1 = change$4;
|
|
152
|
+
}
|
|
153
|
+
entityChange$1.deleted.push(entityId);
|
|
154
|
+
};
|
|
155
|
+
Belt_Array.forEach(update.history, processChange);
|
|
156
|
+
if (update.history.length === 0) {
|
|
157
|
+
return processChange(update.latestChange);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
}));
|
|
161
|
+
}));
|
|
162
|
+
for(var i = 0 ,i_finish = checkpointIds.length; i < i_finish; ++i){
|
|
163
|
+
var checkpointId = checkpointIds[i];
|
|
164
|
+
var change = {};
|
|
165
|
+
change["block"] = checkpointBlockNumbers[i];
|
|
166
|
+
var hash = checkpointBlockHashes[i];
|
|
167
|
+
if (hash !== null) {
|
|
168
|
+
change["blockHash"] = hash;
|
|
169
|
+
}
|
|
170
|
+
change["chainId"] = checkpointChainIds[i];
|
|
171
|
+
change["eventsProcessed"] = checkpointEventsProcessed[i];
|
|
172
|
+
var checkpointKey = String(checkpointId);
|
|
173
|
+
var entityChanges = Js_dict.get(changesByCheckpoint, checkpointKey);
|
|
174
|
+
if (entityChanges !== undefined) {
|
|
175
|
+
Belt_Array.forEach(Js_dict.entries(entityChanges), (function(change){
|
|
176
|
+
return function (param) {
|
|
177
|
+
var match = param[1];
|
|
178
|
+
var deleted = match.deleted;
|
|
179
|
+
var sets = match.sets;
|
|
180
|
+
var entityName = param[0];
|
|
181
|
+
if (entityName === InternalTable.DynamicContractRegistry.name) {
|
|
182
|
+
var entityObj = {};
|
|
183
|
+
if (sets.length !== 0) {
|
|
184
|
+
var simplifiedSets = Belt_Array.map(sets, (function (entity) {
|
|
185
|
+
return {
|
|
186
|
+
address: entity.contract_address,
|
|
187
|
+
contract: entity.contract_name
|
|
188
|
+
};
|
|
189
|
+
}));
|
|
190
|
+
entityObj["sets"] = simplifiedSets;
|
|
191
|
+
}
|
|
192
|
+
change["addresses"] = entityObj;
|
|
193
|
+
return ;
|
|
194
|
+
}
|
|
195
|
+
var entityObj$1 = {};
|
|
196
|
+
if (sets.length !== 0) {
|
|
197
|
+
entityObj$1["sets"] = sets;
|
|
198
|
+
}
|
|
199
|
+
if (deleted.length !== 0) {
|
|
200
|
+
entityObj$1["deleted"] = deleted;
|
|
201
|
+
}
|
|
202
|
+
change[entityName] = entityObj$1;
|
|
203
|
+
}
|
|
204
|
+
}(change)));
|
|
205
|
+
}
|
|
206
|
+
state.processChanges.push(change);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function makeInitialState(config, processConfigChains, dynamicContractsByChain) {
|
|
211
|
+
var chainKeys = Object.keys(processConfigChains);
|
|
212
|
+
var chains = Belt_Array.map(chainKeys, (function (chainIdStr) {
|
|
213
|
+
var chainId = Belt_Option.getWithDefault(Belt_Int.fromString(chainIdStr), 0);
|
|
214
|
+
var chain = ChainMap.Chain.makeUnsafe(chainId);
|
|
215
|
+
if (!ChainMap.has(config.chainMap, chain)) {
|
|
216
|
+
Js_exn.raiseError("Chain " + chainIdStr + " is not configured in config.yaml");
|
|
217
|
+
}
|
|
218
|
+
var processChainConfig = processConfigChains[chainIdStr];
|
|
219
|
+
var dynamicContracts = Belt_Option.getWithDefault(Js_dict.get(dynamicContractsByChain, chainIdStr), []);
|
|
220
|
+
return {
|
|
221
|
+
id: chainId,
|
|
222
|
+
startBlock: processChainConfig.startBlock,
|
|
223
|
+
endBlock: processChainConfig.endBlock,
|
|
224
|
+
maxReorgDepth: 0,
|
|
225
|
+
progressBlockNumber: -1,
|
|
226
|
+
numEventsProcessed: 0,
|
|
227
|
+
firstEventBlockNumber: undefined,
|
|
228
|
+
timestampCaughtUpToHeadOrEndblock: undefined,
|
|
229
|
+
dynamicContracts: dynamicContracts,
|
|
230
|
+
sourceBlockNumber: processChainConfig.endBlock
|
|
231
|
+
};
|
|
232
|
+
}));
|
|
233
|
+
return {
|
|
234
|
+
cleanRun: true,
|
|
235
|
+
cache: {},
|
|
236
|
+
chains: chains,
|
|
237
|
+
checkpointId: InternalTable.Checkpoints.initialCheckpointId,
|
|
238
|
+
reorgCheckpoints: []
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
function validateBlockRange(chainId, configChain, processChainConfig, progressBlock) {
|
|
243
|
+
if (processChainConfig.startBlock < configChain.startBlock) {
|
|
244
|
+
Js_exn.raiseError("Invalid block range for chain " + chainId + ": startBlock (" + String(processChainConfig.startBlock) + ") is less than config.startBlock (" + String(configChain.startBlock) + "). " + ("Either use startBlock >= " + String(configChain.startBlock) + " or create a new test indexer with createTestIndexer()."));
|
|
245
|
+
}
|
|
246
|
+
var configEndBlock = configChain.endBlock;
|
|
247
|
+
if (configEndBlock !== undefined && processChainConfig.endBlock > configEndBlock) {
|
|
248
|
+
Js_exn.raiseError("Invalid block range for chain " + chainId + ": endBlock (" + String(processChainConfig.endBlock) + ") exceeds config.endBlock (" + String(configEndBlock) + "). " + ("Either use endBlock <= " + String(configEndBlock) + " or create a new test indexer with createTestIndexer()."));
|
|
249
|
+
}
|
|
250
|
+
if (progressBlock !== undefined && processChainConfig.startBlock <= progressBlock) {
|
|
251
|
+
return Js_exn.raiseError("Invalid block range for chain " + chainId + ": startBlock (" + String(processChainConfig.startBlock) + ") must be greater than previously processed endBlock (" + String(progressBlock) + "). " + ("Either use startBlock > " + String(progressBlock) + " or create a new test indexer with createTestIndexer()."));
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
function makeCreateTestIndexer(config, workerPath, allEntities) {
|
|
257
|
+
return function () {
|
|
258
|
+
var entities = {};
|
|
259
|
+
var entityConfigs = {};
|
|
260
|
+
Belt_Array.forEach(allEntities, (function (entityConfig) {
|
|
261
|
+
entities[entityConfig.name] = {};
|
|
262
|
+
entityConfigs[entityConfig.name] = entityConfig;
|
|
263
|
+
}));
|
|
264
|
+
var state = {
|
|
265
|
+
processInProgress: false,
|
|
266
|
+
progressBlockByChain: {},
|
|
267
|
+
entities: entities,
|
|
268
|
+
entityConfigs: entityConfigs,
|
|
269
|
+
processChanges: []
|
|
270
|
+
};
|
|
271
|
+
return {
|
|
272
|
+
process: (function (processConfig) {
|
|
273
|
+
if (state.processInProgress) {
|
|
274
|
+
Js_exn.raiseError("createTestIndexer process is already running. Only one process call is allowed at a time");
|
|
275
|
+
}
|
|
276
|
+
var chains = processConfig.chains;
|
|
277
|
+
var chainKeys = Object.keys(chains);
|
|
278
|
+
var n = chainKeys.length;
|
|
279
|
+
if (n !== 0) {
|
|
280
|
+
if (n !== 1) {
|
|
281
|
+
Js_exn.raiseError("createTestIndexer does not support processing multiple chains at once. Found " + String(n) + " chains defined");
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
} else {
|
|
285
|
+
Js_exn.raiseError("createTestIndexer requires exactly one chain to be defined");
|
|
286
|
+
}
|
|
287
|
+
Belt_Array.forEach(chainKeys, (function (chainIdStr) {
|
|
288
|
+
var chainId = Belt_Option.getWithDefault(Belt_Int.fromString(chainIdStr), 0);
|
|
289
|
+
var chain = ChainMap.Chain.makeUnsafe(chainId);
|
|
290
|
+
var configChain = ChainMap.get(config.chainMap, chain);
|
|
291
|
+
var processChainConfig = chains[chainIdStr];
|
|
292
|
+
var progressBlock = Js_dict.get(state.progressBlockByChain, chainIdStr);
|
|
293
|
+
validateBlockRange(chainIdStr, configChain, processChainConfig, progressBlock);
|
|
294
|
+
}));
|
|
295
|
+
state.processChanges = [];
|
|
296
|
+
var dynamicContractsByChain = {};
|
|
297
|
+
var dcDict = Js_dict.get(state.entities, InternalTable.DynamicContractRegistry.name);
|
|
298
|
+
if (dcDict !== undefined) {
|
|
299
|
+
Belt_Array.forEach(Js_dict.values(dcDict), (function (entity) {
|
|
300
|
+
var chainIdStr = String(entity.chain_id);
|
|
301
|
+
var arr = Js_dict.get(dynamicContractsByChain, chainIdStr);
|
|
302
|
+
var contracts;
|
|
303
|
+
if (arr !== undefined) {
|
|
304
|
+
contracts = arr;
|
|
305
|
+
} else {
|
|
306
|
+
var arr$1 = [];
|
|
307
|
+
dynamicContractsByChain[chainIdStr] = arr$1;
|
|
308
|
+
contracts = arr$1;
|
|
309
|
+
}
|
|
310
|
+
contracts.push(toIndexingContract(entity));
|
|
311
|
+
}));
|
|
312
|
+
}
|
|
313
|
+
var initialState = makeInitialState(config, chains, dynamicContractsByChain);
|
|
314
|
+
return new Promise((function (resolve, reject) {
|
|
315
|
+
var workerDataObj = {
|
|
316
|
+
processConfig: JSON.parse(Js_json.serializeExn(processConfig)),
|
|
317
|
+
initialState: initialState
|
|
318
|
+
};
|
|
319
|
+
var workerData = JSON.parse(Js_json.serializeExn(workerDataObj));
|
|
320
|
+
var worker;
|
|
321
|
+
try {
|
|
322
|
+
worker = new Worker_threads.Worker(workerPath, {
|
|
323
|
+
workerData: workerData
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
catch (raw_exn){
|
|
327
|
+
var exn = Caml_js_exceptions.internalToOCamlException(raw_exn);
|
|
328
|
+
reject(exn);
|
|
329
|
+
throw exn;
|
|
330
|
+
}
|
|
331
|
+
state.processInProgress = true;
|
|
332
|
+
worker.on("message", (function (msg) {
|
|
333
|
+
var respond = function (data) {
|
|
334
|
+
worker.postMessage({
|
|
335
|
+
id: msg.id,
|
|
336
|
+
payload: {
|
|
337
|
+
type: "response",
|
|
338
|
+
data: data
|
|
339
|
+
}
|
|
340
|
+
});
|
|
341
|
+
};
|
|
342
|
+
var match = msg.payload;
|
|
343
|
+
switch (match.type) {
|
|
344
|
+
case "loadByIds" :
|
|
345
|
+
return respond(handleLoadByIds(state, match.tableName, match.ids));
|
|
346
|
+
case "loadByField" :
|
|
347
|
+
return respond(handleLoadByField(state, match.tableName, match.fieldName, match.fieldValue, match.operator));
|
|
348
|
+
case "writeBatch" :
|
|
349
|
+
handleWriteBatch(state, match.updatedEntities, match.checkpointIds, match.checkpointChainIds, match.checkpointBlockNumbers, match.checkpointBlockHashes, match.checkpointEventsProcessed);
|
|
350
|
+
return respond(null);
|
|
351
|
+
|
|
352
|
+
}
|
|
353
|
+
}));
|
|
354
|
+
worker.on("error", (function (err) {
|
|
355
|
+
state.processInProgress = false;
|
|
356
|
+
worker.terminate();
|
|
357
|
+
reject(err);
|
|
358
|
+
}));
|
|
359
|
+
worker.on("exit", (function (code) {
|
|
360
|
+
state.processInProgress = false;
|
|
361
|
+
if (code !== 0) {
|
|
362
|
+
return reject(new Error("Worker exited with code " + String(code)));
|
|
363
|
+
} else {
|
|
364
|
+
Belt_Array.forEach(chainKeys, (function (chainIdStr) {
|
|
365
|
+
var processChainConfig = chains[chainIdStr];
|
|
366
|
+
state.progressBlockByChain[chainIdStr] = processChainConfig.endBlock;
|
|
367
|
+
}));
|
|
368
|
+
return resolve({
|
|
369
|
+
changes: state.processChanges
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
}));
|
|
373
|
+
}));
|
|
374
|
+
})
|
|
375
|
+
};
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
function initTestWorker(makeGeneratedConfig, makePersistence) {
|
|
380
|
+
if (Worker_threads.isMainThread) {
|
|
381
|
+
Js_exn.raiseError("initTestWorker must be called from a worker thread");
|
|
382
|
+
}
|
|
383
|
+
var port = Worker_threads.parentPort;
|
|
384
|
+
var parentPort = (port == null) ? Js_exn.raiseError("initTestWorker: No parent port available") : port;
|
|
385
|
+
var workerData = Worker_threads.workerData;
|
|
386
|
+
if (workerData == null) {
|
|
387
|
+
Logging.error("TestIndexerWorker: No worker data provided");
|
|
388
|
+
Process.exit(1);
|
|
389
|
+
return ;
|
|
390
|
+
}
|
|
391
|
+
var proxy = TestIndexerProxyStorage.make(parentPort, workerData.initialState);
|
|
392
|
+
var storage = TestIndexerProxyStorage.makeStorage(proxy);
|
|
393
|
+
var persistence = makePersistence(storage);
|
|
394
|
+
if (Env.userLogLevel !== undefined) {
|
|
395
|
+
|
|
396
|
+
} else {
|
|
397
|
+
Logging.setLogLevel("silent");
|
|
398
|
+
}
|
|
399
|
+
Main.start(makeGeneratedConfig, persistence, true);
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
export {
|
|
403
|
+
toIndexingContract ,
|
|
404
|
+
handleLoadByIds ,
|
|
405
|
+
handleLoadByField ,
|
|
406
|
+
handleWriteBatch ,
|
|
407
|
+
makeInitialState ,
|
|
408
|
+
validateBlockRange ,
|
|
409
|
+
makeCreateTestIndexer ,
|
|
410
|
+
initTestWorker ,
|
|
411
|
+
}
|
|
412
|
+
/* Env Not a pure module */
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
open Belt
|
|
2
|
+
|
|
3
|
+
// Message types for communication between worker and main thread
|
|
4
|
+
type requestId = int
|
|
5
|
+
|
|
6
|
+
// Serializable change with entity as JSON (for worker thread messaging)
|
|
7
|
+
@tag("type")
|
|
8
|
+
type serializableChange =
|
|
9
|
+
| @as("SET") Set({entityId: string, entity: Js.Json.t, checkpointId: float})
|
|
10
|
+
| @as("DELETE") Delete({entityId: string, checkpointId: float})
|
|
11
|
+
|
|
12
|
+
type serializableEntityUpdate = {
|
|
13
|
+
latestChange: serializableChange,
|
|
14
|
+
history: array<serializableChange>,
|
|
15
|
+
containsRollbackDiffChange: bool,
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
type serializableUpdatedEntity = {
|
|
19
|
+
entityName: string,
|
|
20
|
+
updates: array<serializableEntityUpdate>,
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Worker -> Main thread payloads
|
|
24
|
+
@tag("type")
|
|
25
|
+
type workerPayload =
|
|
26
|
+
| @as("loadByIds") LoadByIds({tableName: string, ids: array<string>})
|
|
27
|
+
| @as("loadByField")
|
|
28
|
+
LoadByField({
|
|
29
|
+
tableName: string,
|
|
30
|
+
fieldName: string,
|
|
31
|
+
fieldValue: Js.Json.t,
|
|
32
|
+
operator: Persistence.operator,
|
|
33
|
+
})
|
|
34
|
+
| @as("writeBatch")
|
|
35
|
+
WriteBatch({
|
|
36
|
+
updatedEntities: array<serializableUpdatedEntity>,
|
|
37
|
+
checkpointIds: array<float>,
|
|
38
|
+
checkpointChainIds: array<int>,
|
|
39
|
+
checkpointBlockNumbers: array<int>,
|
|
40
|
+
checkpointBlockHashes: array<Js.Null.t<string>>,
|
|
41
|
+
checkpointEventsProcessed: array<int>,
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
// Main thread -> Worker payloads
|
|
45
|
+
@tag("type")
|
|
46
|
+
type mainPayload =
|
|
47
|
+
| @as("response") Response({data: Js.Json.t})
|
|
48
|
+
| @as("error") Error({message: string})
|
|
49
|
+
|
|
50
|
+
// Message wrapper with id
|
|
51
|
+
type message<'payload> = {id: requestId, payload: 'payload}
|
|
52
|
+
type workerMessage = message<workerPayload>
|
|
53
|
+
type mainMessage = message<mainPayload>
|
|
54
|
+
|
|
55
|
+
// Pending request tracker
|
|
56
|
+
type pendingRequest = {
|
|
57
|
+
resolve: Js.Json.t => unit,
|
|
58
|
+
reject: exn => unit,
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
type t = {
|
|
62
|
+
parentPort: NodeJs.WorkerThreads.messagePort,
|
|
63
|
+
initialState: Persistence.initialState,
|
|
64
|
+
pendingRequests: dict<pendingRequest>,
|
|
65
|
+
mutable requestCounter: int,
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
let make = (~parentPort, ~initialState): t => {
|
|
69
|
+
let proxy = {
|
|
70
|
+
parentPort,
|
|
71
|
+
initialState,
|
|
72
|
+
pendingRequests: Js.Dict.empty(),
|
|
73
|
+
requestCounter: 0,
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Set up message listener for responses from main thread
|
|
77
|
+
parentPort->NodeJs.WorkerThreads.onPortMessage((msg: mainMessage) => {
|
|
78
|
+
let idStr = msg.id->Int.toString
|
|
79
|
+
let {resolve, reject} = switch proxy.pendingRequests->Utils.Dict.dangerouslyGetNonOption(
|
|
80
|
+
idStr,
|
|
81
|
+
) {
|
|
82
|
+
| Some(pending) => pending
|
|
83
|
+
| None => Js.Exn.raiseError(`TestIndexer: No pending request found for id ${idStr}`)
|
|
84
|
+
}
|
|
85
|
+
Js.Dict.unsafeDeleteKey(proxy.pendingRequests->Obj.magic, idStr)
|
|
86
|
+
|
|
87
|
+
switch msg.payload {
|
|
88
|
+
| Response({data}) => resolve(data)
|
|
89
|
+
| Error({message}) => reject(Utils.Error.make(message))
|
|
90
|
+
}
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
proxy
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
let nextRequestId = (proxy: t): requestId => {
|
|
97
|
+
proxy.requestCounter = proxy.requestCounter + 1
|
|
98
|
+
proxy.requestCounter
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
let sendRequest = (proxy: t, ~payload: workerPayload): promise<Js.Json.t> => {
|
|
102
|
+
Promise.make((resolve, reject) => {
|
|
103
|
+
let id = proxy->nextRequestId
|
|
104
|
+
proxy.pendingRequests->Js.Dict.set(id->Int.toString, {resolve, reject})
|
|
105
|
+
proxy.parentPort->NodeJs.WorkerThreads.postMessage({id, payload})
|
|
106
|
+
})
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
let makeStorage = (proxy: t): Persistence.storage => {
|
|
110
|
+
isInitialized: async () => true,
|
|
111
|
+
initialize: async (~chainConfigs as _=?, ~entities as _=?, ~enums as _=?) => {
|
|
112
|
+
Js.Exn.raiseError(
|
|
113
|
+
"TestIndexer: initialize should not be called. Use resumeInitialState instead.",
|
|
114
|
+
)
|
|
115
|
+
},
|
|
116
|
+
resumeInitialState: async () => proxy.initialState,
|
|
117
|
+
loadByIdsOrThrow: async (~ids, ~table: Table.table, ~rowsSchema) => {
|
|
118
|
+
let response = await proxy->sendRequest(~payload=LoadByIds({tableName: table.tableName, ids}))
|
|
119
|
+
response->S.parseOrThrow(rowsSchema)
|
|
120
|
+
},
|
|
121
|
+
loadByFieldOrThrow: async (
|
|
122
|
+
~fieldName,
|
|
123
|
+
~fieldSchema,
|
|
124
|
+
~fieldValue,
|
|
125
|
+
~operator,
|
|
126
|
+
~table: Table.table,
|
|
127
|
+
~rowsSchema,
|
|
128
|
+
) => {
|
|
129
|
+
let response = await proxy->sendRequest(
|
|
130
|
+
~payload=LoadByField({
|
|
131
|
+
tableName: table.tableName,
|
|
132
|
+
fieldName,
|
|
133
|
+
fieldValue: fieldValue->S.reverseConvertToJsonOrThrow(fieldSchema),
|
|
134
|
+
operator,
|
|
135
|
+
}),
|
|
136
|
+
)
|
|
137
|
+
response->S.parseOrThrow(rowsSchema)
|
|
138
|
+
},
|
|
139
|
+
setOrThrow: async (~items as _, ~table as _, ~itemSchema as _) => {
|
|
140
|
+
// Not used anywhere, no-op
|
|
141
|
+
()
|
|
142
|
+
},
|
|
143
|
+
writeBatch: async (
|
|
144
|
+
~batch,
|
|
145
|
+
~rawEvents as _,
|
|
146
|
+
~rollbackTargetCheckpointId as _,
|
|
147
|
+
~isInReorgThreshold as _,
|
|
148
|
+
~config as _,
|
|
149
|
+
~allEntities as _,
|
|
150
|
+
~updatedEffectsCache as _,
|
|
151
|
+
~updatedEntities,
|
|
152
|
+
) => {
|
|
153
|
+
// Encode entities to JSON for serialization across worker boundary
|
|
154
|
+
let serializableEntities = updatedEntities->Array.map((
|
|
155
|
+
{entityConfig, updates}: Persistence.updatedEntity,
|
|
156
|
+
) => {
|
|
157
|
+
let encodeChange = (change: Change.t<Internal.entity>): serializableChange => {
|
|
158
|
+
switch change {
|
|
159
|
+
| Set({entityId, entity, checkpointId}) =>
|
|
160
|
+
Set({
|
|
161
|
+
entityId,
|
|
162
|
+
entity: entity->S.reverseConvertToJsonOrThrow(entityConfig.schema),
|
|
163
|
+
checkpointId,
|
|
164
|
+
})
|
|
165
|
+
| Delete({entityId, checkpointId}) => Delete({entityId, checkpointId})
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
{
|
|
169
|
+
entityName: entityConfig.name,
|
|
170
|
+
updates: updates->Array.map(update => {
|
|
171
|
+
latestChange: encodeChange(update.latestChange),
|
|
172
|
+
history: update.history->Array.map(encodeChange),
|
|
173
|
+
containsRollbackDiffChange: update.containsRollbackDiffChange,
|
|
174
|
+
}),
|
|
175
|
+
}
|
|
176
|
+
})
|
|
177
|
+
let _ = await proxy->sendRequest(
|
|
178
|
+
~payload=WriteBatch({
|
|
179
|
+
updatedEntities: serializableEntities,
|
|
180
|
+
checkpointIds: batch.checkpointIds,
|
|
181
|
+
checkpointChainIds: batch.checkpointChainIds,
|
|
182
|
+
checkpointBlockNumbers: batch.checkpointBlockNumbers,
|
|
183
|
+
checkpointBlockHashes: batch.checkpointBlockHashes,
|
|
184
|
+
checkpointEventsProcessed: batch.checkpointEventsProcessed,
|
|
185
|
+
}),
|
|
186
|
+
)
|
|
187
|
+
},
|
|
188
|
+
setEffectCacheOrThrow: async (~effect as _, ~items as _, ~initialize as _) => (),
|
|
189
|
+
dumpEffectCache: async () => (),
|
|
190
|
+
executeUnsafe: async _ => Obj.magic(),
|
|
191
|
+
setChainMeta: async _ => Obj.magic(),
|
|
192
|
+
pruneStaleCheckpoints: async (~safeCheckpointId as _) => (),
|
|
193
|
+
pruneStaleEntityHistory: async (~entityName as _, ~entityIndex as _, ~safeCheckpointId as _) =>
|
|
194
|
+
(),
|
|
195
|
+
getRollbackTargetCheckpoint: async (~reorgChainId as _, ~lastKnownValidBlockNumber as _) => {
|
|
196
|
+
Js.Exn.raiseError(
|
|
197
|
+
"TestIndexer: Rollback is not supported. Set rollbackOnReorg to false in config.",
|
|
198
|
+
)
|
|
199
|
+
},
|
|
200
|
+
getRollbackProgressDiff: async (~rollbackTargetCheckpointId as _) => {
|
|
201
|
+
Js.Exn.raiseError(
|
|
202
|
+
"TestIndexer: Rollback is not supported. Set rollbackOnReorg to false in config.",
|
|
203
|
+
)
|
|
204
|
+
},
|
|
205
|
+
getRollbackData: async (~entityConfig as _, ~rollbackTargetCheckpointId as _) => {
|
|
206
|
+
Js.Exn.raiseError(
|
|
207
|
+
"TestIndexer: Rollback is not supported. Set rollbackOnReorg to false in config.",
|
|
208
|
+
)
|
|
209
|
+
},
|
|
210
|
+
}
|