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.
Files changed (134) hide show
  1. package/evm.schema.json +83 -11
  2. package/fuel.schema.json +83 -11
  3. package/index.d.ts +184 -3
  4. package/package.json +6 -6
  5. package/src/Batch.res +2 -2
  6. package/src/ChainFetcher.res +27 -3
  7. package/src/ChainFetcher.res.mjs +17 -3
  8. package/src/ChainManager.res +163 -0
  9. package/src/ChainManager.res.mjs +136 -0
  10. package/src/Config.res +213 -30
  11. package/src/Config.res.mjs +102 -41
  12. package/src/Core.res +16 -10
  13. package/src/Ecosystem.res +0 -3
  14. package/src/Env.res +2 -2
  15. package/src/Env.res.mjs +2 -2
  16. package/src/Envio.res +101 -2
  17. package/src/Envio.res.mjs +2 -3
  18. package/src/EventConfigBuilder.res +52 -0
  19. package/src/EventConfigBuilder.res.mjs +32 -0
  20. package/src/EventUtils.res +2 -2
  21. package/src/FetchState.res +126 -71
  22. package/src/FetchState.res.mjs +73 -51
  23. package/src/GlobalState.res +219 -363
  24. package/src/GlobalState.res.mjs +314 -491
  25. package/src/GlobalStateManager.res +49 -59
  26. package/src/GlobalStateManager.res.mjs +5 -4
  27. package/src/GlobalStateManager.resi +1 -1
  28. package/src/HandlerLoader.res +12 -1
  29. package/src/HandlerLoader.res.mjs +6 -1
  30. package/src/HandlerRegister.res +9 -9
  31. package/src/HandlerRegister.res.mjs +9 -9
  32. package/src/Hasura.res +102 -32
  33. package/src/Hasura.res.mjs +88 -34
  34. package/src/InMemoryStore.res +10 -1
  35. package/src/InMemoryStore.res.mjs +4 -1
  36. package/src/InMemoryTable.res +83 -136
  37. package/src/InMemoryTable.res.mjs +57 -86
  38. package/src/Internal.res +54 -5
  39. package/src/Internal.res.mjs +2 -8
  40. package/src/LazyLoader.res +2 -2
  41. package/src/LazyLoader.res.mjs +3 -3
  42. package/src/LoadLayer.res +47 -60
  43. package/src/LoadLayer.res.mjs +28 -50
  44. package/src/LoadLayer.resi +2 -5
  45. package/src/LogSelection.res +4 -4
  46. package/src/LogSelection.res.mjs +5 -7
  47. package/src/Logging.res +1 -1
  48. package/src/Main.res +61 -2
  49. package/src/Main.res.mjs +37 -1
  50. package/src/Persistence.res +3 -16
  51. package/src/PgStorage.res +125 -114
  52. package/src/PgStorage.res.mjs +112 -95
  53. package/src/Ports.res +5 -0
  54. package/src/Ports.res.mjs +9 -0
  55. package/src/Prometheus.res +3 -3
  56. package/src/Prometheus.res.mjs +4 -4
  57. package/src/ReorgDetection.res +4 -4
  58. package/src/ReorgDetection.res.mjs +4 -5
  59. package/src/SafeCheckpointTracking.res +16 -16
  60. package/src/SafeCheckpointTracking.res.mjs +2 -2
  61. package/src/SimulateItems.res +10 -14
  62. package/src/SimulateItems.res.mjs +5 -2
  63. package/src/Sink.res +1 -1
  64. package/src/Sink.res.mjs +1 -2
  65. package/src/SvmTypes.res +9 -0
  66. package/src/SvmTypes.res.mjs +14 -0
  67. package/src/TestIndexer.res +17 -57
  68. package/src/TestIndexer.res.mjs +14 -48
  69. package/src/TestIndexerProxyStorage.res +23 -23
  70. package/src/TestIndexerProxyStorage.res.mjs +12 -15
  71. package/src/Throttler.res +2 -2
  72. package/src/Time.res +2 -2
  73. package/src/Time.res.mjs +2 -2
  74. package/src/UserContext.res +19 -118
  75. package/src/UserContext.res.mjs +10 -66
  76. package/src/Utils.res +15 -15
  77. package/src/Utils.res.mjs +7 -8
  78. package/src/adapters/MarkBatchProcessedAdapter.res +5 -0
  79. package/src/adapters/MarkBatchProcessedAdapter.res.mjs +14 -0
  80. package/src/bindings/BigDecimal.res +1 -1
  81. package/src/bindings/BigDecimal.res.mjs +2 -2
  82. package/src/bindings/ClickHouse.res +8 -6
  83. package/src/bindings/ClickHouse.res.mjs +5 -5
  84. package/src/bindings/Hrtime.res +1 -1
  85. package/src/bindings/Pino.res +2 -2
  86. package/src/bindings/Pino.res.mjs +3 -4
  87. package/src/db/EntityFilter.res +410 -0
  88. package/src/db/EntityFilter.res.mjs +424 -0
  89. package/src/db/EntityHistory.res +1 -1
  90. package/src/db/EntityHistory.res.mjs +1 -1
  91. package/src/db/InternalTable.res +10 -10
  92. package/src/db/InternalTable.res.mjs +41 -45
  93. package/src/db/Schema.res +2 -2
  94. package/src/db/Schema.res.mjs +3 -3
  95. package/src/db/Table.res +106 -22
  96. package/src/db/Table.res.mjs +84 -35
  97. package/src/sources/EventRouter.res +67 -2
  98. package/src/sources/EventRouter.res.mjs +45 -3
  99. package/src/sources/Evm.res +0 -7
  100. package/src/sources/Evm.res.mjs +0 -15
  101. package/src/sources/EvmChain.res +1 -1
  102. package/src/sources/EvmChain.res.mjs +1 -2
  103. package/src/sources/EvmRpcClient.res +42 -0
  104. package/src/sources/EvmRpcClient.res.mjs +64 -0
  105. package/src/sources/Fuel.res +0 -7
  106. package/src/sources/Fuel.res.mjs +0 -15
  107. package/src/sources/HyperFuelSource.res +5 -4
  108. package/src/sources/HyperFuelSource.res.mjs +2 -2
  109. package/src/sources/HyperSyncClient.res +9 -5
  110. package/src/sources/HyperSyncClient.res.mjs +2 -2
  111. package/src/sources/HyperSyncHeightStream.res +2 -2
  112. package/src/sources/HyperSyncHeightStream.res.mjs +2 -2
  113. package/src/sources/HyperSyncSource.res +10 -9
  114. package/src/sources/HyperSyncSource.res.mjs +4 -4
  115. package/src/sources/Rpc.res +1 -5
  116. package/src/sources/Rpc.res.mjs +1 -9
  117. package/src/sources/RpcSource.res +57 -21
  118. package/src/sources/RpcSource.res.mjs +47 -20
  119. package/src/sources/RpcWebSocketHeightStream.res +1 -1
  120. package/src/sources/SourceManager.res +3 -2
  121. package/src/sources/SourceManager.res.mjs +1 -1
  122. package/src/sources/Svm.res +3 -10
  123. package/src/sources/Svm.res.mjs +4 -18
  124. package/src/sources/SvmHyperSyncClient.res +265 -0
  125. package/src/sources/SvmHyperSyncClient.res.mjs +28 -0
  126. package/src/sources/SvmHyperSyncSource.res +638 -0
  127. package/src/sources/SvmHyperSyncSource.res.mjs +557 -0
  128. package/src/tui/Tui.res +9 -2
  129. package/src/tui/Tui.res.mjs +18 -3
  130. package/src/tui/components/BufferedProgressBar.res +2 -2
  131. package/src/tui/components/TuiData.res +3 -0
  132. package/svm.schema.json +523 -14
  133. package/src/TableIndices.res +0 -115
  134. 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 = Belt.Array.makeUninitializedUnsafe(chainReorgCheckpoints->Array.length)
19
- let checkpointBlockNumbers = Belt.Array.makeUninitializedUnsafe(
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->Belt.Array.setUnsafe(idx, checkpoint.checkpointId)
24
- checkpointBlockNumbers->Belt.Array.setUnsafe(idx, checkpoint.blockNumber)
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->Belt.Array.getUnsafe(0) >
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->Belt.Array.getUnsafe(idx.contents) >
52
+ safeCheckpointTracking.checkpointBlockNumbers->Array.getUnsafe(idx.contents) >
53
53
  safeBlockNumber
54
54
  ) {
55
55
  result :=
56
- Some(safeCheckpointTracking.checkpointIds->Belt.Array.getUnsafe(idx.contents - 1))
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->Belt.Array.getUnsafe(trackingCheckpointsCount - 1)
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->Belt.Array.getUnsafe(idx)
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->Belt.Array.getUnsafe(idx))
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->Belt.Array.getUnsafe(idx) === chainId {
97
- mutCheckpointIds->Array.push(batchCheckpointIds->Belt.Array.getUnsafe(idx))->ignore
96
+ if batchCheckpointChainIds->Array.getUnsafe(idx) === chainId {
97
+ mutCheckpointIds->Array.push(batchCheckpointIds->Array.getUnsafe(idx))->ignore
98
98
  mutCheckpointBlockNumbers
99
- ->Array.push(batchCheckpointBlockNumbers->Belt.Array.getUnsafe(idx))
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->Belt.Array.getUnsafe(idx)
116
+ let blockNumber = safeCheckpointTracking.checkpointBlockNumbers->Array.getUnsafe(idx)
117
117
  if blockNumber <= targetBlockNumber {
118
118
  mutCheckpointIds
119
- ->Array.push(safeCheckpointTracking.checkpointIds->Belt.Array.getUnsafe(idx))
119
+ ->Array.push(safeCheckpointTracking.checkpointIds->Array.getUnsafe(idx))
120
120
  ->ignore
121
121
  mutCheckpointBlockNumbers
122
- ->Array.push(safeCheckpointTracking.checkpointBlockNumbers->Belt.Array.getUnsafe(idx))
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 = new Array(chainReorgCheckpoints.length);
9
- let checkpointBlockNumbers = new Array(chainReorgCheckpoints.length);
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;
@@ -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 simulateRaw: option<array<JSON.t>> =
324
- (processChainJson->(Utils.magic: JSON.t => {..}))["simulate"]->Nullable.toOption
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
- // Use endBlock from processConfig (the user-specified range)
329
- let startBlock: int =
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->Belt.Array.map(({entityConfig, changes}) => {
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(Belt_Array.map(updatedEntities, param => ClickHouse.setUpdatesOrThrow(client, cache, param.changes, param.entityConfig, database)));
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
  };
@@ -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 */
@@ -43,67 +43,32 @@ let toIndexingAddress = (dc: InternalTable.EnvioAddresses.t): Internal.indexingA
43
43
  registrationBlock: dc.registrationBlock,
44
44
  }
45
45
 
46
- let handleLoadByIds = (
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
- // 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}`)
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<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
- }
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
- | LoadByIds({tableName, ids}) => state->handleLoadByIds(~tableName, ~ids)->respond
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,
@@ -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 TableIndices from "./TableIndices.res.mjs";
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 handleLoadByIds(state, tableName, ids) {
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
- ids.forEach(id => {
40
- let entity = entityDict[id];
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) {
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
- let entityFieldValue = entity[fieldName];
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
- switch (match.type) {
565
- case "loadByIds" :
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
- handleLoadByIds,
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("loadByIds") LoadByIds({tableName: string, ids: array<string>})
19
- | @as("loadByField")
20
- LoadByField({
18
+ | @as("load")
19
+ Load({
21
20
  tableName: string,
22
- fieldName: string,
23
- fieldValue: JSON.t,
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
- loadByIdsOrThrow: async (~ids, ~table: Table.table, ~rowsSchema) => {
111
- let response = await proxy->sendRequest(~payload=LoadByIds({tableName: table.tableName, ids}))
112
- response->S.parseOrThrow(rowsSchema)
113
- },
114
- loadByFieldOrThrow: async (
115
- ~fieldName,
116
- ~fieldSchema,
117
- ~fieldValue,
118
- ~operator,
119
- ~table: Table.table,
120
- ~rowsSchema,
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=LoadByField({
124
+ ~payload=Load({
124
125
  tableName: table.tableName,
125
- fieldName,
126
- fieldValue: fieldValue->S.reverseConvertToJsonOrThrow(fieldSchema),
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
- loadByIdsOrThrow: async (ids, table, rowsSchema) => {
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: "loadByIds",
63
+ type: "load",
57
64
  tableName: table.tableName,
58
- ids: ids
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
- /* S-RescriptSchema Not a pure module */
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->Belt.Int.toFloat,
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
- }, Belt.Int.fromFloat(throttler.intervalMillis -. timeSinceLastRun))
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->Belt.Int.toString}/${maxRetries->Belt.Int.toString} in ${backOffMillis->Belt.Int.toString}ms - waiting for correct result.`,
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->Belt.Int.toString}/${maxRetries->Belt.Int.toString} exceeded`,
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 ` + String(nextRetryCount) + `/` + String(maxRetries) + ` in ` + String(backOffMillis) + `ms - waiting for correct result.`,
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 ` + String(retryCount) + `/` + String(maxRetries) + ` exceeded`));
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
  }