envio 3.11.0 → 3.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (109) hide show
  1. package/index.d.ts +3 -0
  2. package/package.json +6 -6
  3. package/src/Batch.res +111 -78
  4. package/src/Batch.res.mjs +53 -46
  5. package/src/BatchProcessing.res +1 -11
  6. package/src/BatchProcessing.res.mjs +1 -4
  7. package/src/ChainFetching.res +7 -3
  8. package/src/ChainFetching.res.mjs +1 -1
  9. package/src/ChainState.res +82 -20
  10. package/src/ChainState.res.mjs +51 -13
  11. package/src/ChainState.resi +6 -1
  12. package/src/Config.res +58 -37
  13. package/src/Config.res.mjs +63 -53
  14. package/src/CrossChainState.res +50 -53
  15. package/src/CrossChainState.res.mjs +25 -23
  16. package/src/CrossChainState.resi +6 -13
  17. package/src/EffectState.res +13 -4
  18. package/src/EffectState.resi +7 -1
  19. package/src/EventProcessing.res +6 -6
  20. package/src/EventProcessing.res.mjs +6 -6
  21. package/src/FetchState.res +65 -46
  22. package/src/FetchState.res.mjs +68 -36
  23. package/src/FinalizeBackfill.res +16 -12
  24. package/src/FinalizeBackfill.res.mjs +3 -3
  25. package/src/HistoryPolicy.res +46 -0
  26. package/src/HistoryPolicy.res.mjs +50 -0
  27. package/src/InMemoryStore.res +30 -32
  28. package/src/InMemoryStore.res.mjs +33 -28
  29. package/src/InMemoryTable.res +155 -67
  30. package/src/InMemoryTable.res.mjs +151 -60
  31. package/src/IndexerState.res +66 -45
  32. package/src/IndexerState.res.mjs +57 -29
  33. package/src/IndexerState.resi +8 -13
  34. package/src/Internal.res +4 -2
  35. package/src/LoadLayer.res +68 -38
  36. package/src/LoadLayer.res.mjs +50 -64
  37. package/src/LoadLayer.resi +1 -1
  38. package/src/Main.res +25 -24
  39. package/src/Main.res.mjs +24 -25
  40. package/src/Metrics.res +49 -1
  41. package/src/Metrics.res.mjs +26 -3
  42. package/src/Persistence.res +55 -16
  43. package/src/Persistence.res.mjs +18 -3
  44. package/src/PgStorage.res +286 -180
  45. package/src/PgStorage.res.mjs +209 -122
  46. package/src/PruneStaleHistory.res +9 -15
  47. package/src/PruneStaleHistory.res.mjs +11 -20
  48. package/src/Rollback.res +9 -14
  49. package/src/Rollback.res.mjs +5 -3
  50. package/src/Sink.res +7 -5
  51. package/src/Sink.res.mjs +4 -4
  52. package/src/Staging.res +298 -0
  53. package/src/Staging.res.mjs +257 -0
  54. package/src/TestIndexer.res +14 -30
  55. package/src/TestIndexer.res.mjs +11 -9
  56. package/src/UserContext.res +20 -36
  57. package/src/UserContext.res.mjs +6 -9
  58. package/src/Utils.res +10 -4
  59. package/src/Utils.res.mjs +16 -16
  60. package/src/Writing.res +34 -20
  61. package/src/Writing.res.mjs +15 -20
  62. package/src/bindings/ClickHouse.res +41 -34
  63. package/src/bindings/ClickHouse.res.mjs +36 -30
  64. package/src/bindings/ClickHouseSink.res +45 -186
  65. package/src/bindings/ClickHouseSink.res.mjs +21 -174
  66. package/src/db/CheckpointSequence.res +109 -0
  67. package/src/db/CheckpointSequence.res.mjs +99 -0
  68. package/src/db/EntityFilter.res +487 -275
  69. package/src/db/EntityFilter.res.mjs +557 -309
  70. package/src/db/EntityHistory.res +6 -5
  71. package/src/db/EntityHistory.res.mjs +5 -5
  72. package/src/db/Frontier.res +86 -0
  73. package/src/db/Frontier.res.mjs +126 -0
  74. package/src/db/InternalTable.res +145 -27
  75. package/src/db/InternalTable.res.mjs +115 -29
  76. package/src/db/RollbackFloors.res +34 -41
  77. package/src/db/RollbackFloors.res.mjs +27 -71
  78. package/src/db/Table.res +21 -6
  79. package/src/db/Table.res.mjs +13 -4
  80. package/src/sources/BlockStore.res +8 -0
  81. package/src/sources/EvmHyperSyncSource.res +5 -1
  82. package/src/sources/EvmHyperSyncSource.res.mjs +4 -3
  83. package/src/sources/FuelHyperSyncSource.res +1 -0
  84. package/src/sources/FuelHyperSyncSource.res.mjs +1 -1
  85. package/src/sources/HyperSync.res +6 -0
  86. package/src/sources/HyperSync.res.mjs +5 -2
  87. package/src/sources/HyperSync.resi +2 -0
  88. package/src/sources/HyperSyncClient.res +6 -0
  89. package/src/sources/HyperSyncSSE.res +1 -1
  90. package/src/sources/HyperSyncSSE.res.mjs +4 -10
  91. package/src/sources/RequestStat.res +8 -1
  92. package/src/sources/RpcSource.res +1 -0
  93. package/src/sources/RpcSource.res.mjs +1 -1
  94. package/src/sources/SimulateSource.res +1 -0
  95. package/src/sources/SimulateSource.res.mjs +1 -1
  96. package/src/sources/Source.res +11 -3
  97. package/src/sources/SourceManager.res +34 -13
  98. package/src/sources/SourceManager.res.mjs +26 -9
  99. package/src/sources/SourceManager.resi +2 -0
  100. package/src/sources/SvmHyperSyncClient.res +5 -0
  101. package/src/sources/SvmHyperSyncSource.res +3 -0
  102. package/src/sources/SvmHyperSyncSource.res.mjs +4 -2
  103. package/src/tui/Tui.res +56 -143
  104. package/src/tui/Tui.res.mjs +70 -166
  105. package/src/tui/components/TuiData.res +61 -16
  106. package/src/tui/components/TuiData.res.mjs +53 -15
  107. package/svm.schema.json +0 -7
  108. package/src/db/CheckpointBounds.res +0 -53
  109. package/src/db/CheckpointBounds.res.mjs +0 -44
@@ -0,0 +1,257 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
4
+
5
+ function kindOfOrdinal(ordinal) {
6
+ switch (ordinal) {
7
+ case 0 :
8
+ return 0;
9
+ case 1 :
10
+ return 1;
11
+ case 2 :
12
+ return 2;
13
+ case 3 :
14
+ return 3;
15
+ case 4 :
16
+ return 4;
17
+ default:
18
+ return Stdlib_JsError.throwWithMessage(`Unknown staged column kind ` + ordinal.toString());
19
+ }
20
+ }
21
+
22
+ let encoder = new TextEncoder();
23
+
24
+ function begin(arena, table, rows, columns) {
25
+ let match = arena.beginStage(table, rows);
26
+ let buffers = match.buffers;
27
+ let slot = {
28
+ contents: 0
29
+ };
30
+ let take = () => {
31
+ let buffer = buffers[slot.contents];
32
+ slot.contents = slot.contents + 1 | 0;
33
+ return buffer;
34
+ };
35
+ let takeVariable = () => {
36
+ let slot$1 = slot.contents;
37
+ let data = new Uint8Array(take());
38
+ let ends = new Uint32Array(take());
39
+ return {
40
+ data: data,
41
+ ends: ends,
42
+ slot: slot$1,
43
+ cursor: 0
44
+ };
45
+ };
46
+ let builders = columns.map((param, index) => {
47
+ let storage;
48
+ switch (param.kind) {
49
+ case 0 :
50
+ storage = {
51
+ TAG: "Floats",
52
+ _0: new Float64Array(take())
53
+ };
54
+ break;
55
+ case 1 :
56
+ storage = {
57
+ TAG: "Unsigned",
58
+ _0: new BigUint64Array(take())
59
+ };
60
+ break;
61
+ case 2 :
62
+ storage = {
63
+ TAG: "Signed",
64
+ _0: new BigInt64Array(take())
65
+ };
66
+ break;
67
+ case 3 :
68
+ storage = {
69
+ TAG: "Text",
70
+ _0: takeVariable()
71
+ };
72
+ break;
73
+ case 4 :
74
+ storage = {
75
+ TAG: "Bytes",
76
+ _0: takeVariable()
77
+ };
78
+ break;
79
+ }
80
+ return {
81
+ name: param.name,
82
+ isNullable: param.isNullable,
83
+ replacer: param.replacer,
84
+ index: index,
85
+ storage: storage,
86
+ nulls: new Uint8Array(take())
87
+ };
88
+ });
89
+ return {
90
+ arena: arena,
91
+ handle: match.handle,
92
+ buffers: buffers,
93
+ builders: builders
94
+ };
95
+ }
96
+
97
+ function ensure(stage, builder, variable, needed) {
98
+ if (needed <= variable.data.length) {
99
+ return;
100
+ }
101
+ let fresh = stage.arena.growStage(stage.handle, builder.index, needed, variable.data.buffer);
102
+ stage.buffers[variable.slot] = fresh;
103
+ variable.data = new Uint8Array(fresh);
104
+ }
105
+
106
+ function finiteOrThrow(number, column) {
107
+ if (isFinite(number)) {
108
+ return number;
109
+ } else {
110
+ return Stdlib_JsError.throwWithMessage(number.toString() + ` is not a finite number, so it cannot be stored in the \`` + column + `\` column. Store a finite number, or keep it out of the entity.`);
111
+ }
112
+ }
113
+
114
+ function toText(value, replacer) {
115
+ let match = typeof value;
116
+ if (match === "bigint") {
117
+ return String(value);
118
+ } else if (match === "string") {
119
+ return value;
120
+ } else {
121
+ return JSON.stringify(value, replacer);
122
+ }
123
+ }
124
+
125
+ let writeAscii = ((data, text, offset) => {
126
+ const length = text.length;
127
+ for (let index = 0; index < length; index++) {
128
+ const code = text.charCodeAt(index);
129
+ if (code > 0x7f) {
130
+ return -1;
131
+ }
132
+ data[offset + index] = code;
133
+ }
134
+ return length;
135
+ });
136
+
137
+ function markNull(builder, row) {
138
+ builder.nulls[row] = 1;
139
+ let match = builder.storage;
140
+ switch (match.TAG) {
141
+ case "Text" :
142
+ case "Bytes" :
143
+ break;
144
+ default:
145
+ return;
146
+ }
147
+ let variable = match._0;
148
+ variable.ends[row] = variable.cursor;
149
+ }
150
+
151
+ function checkedBigInt(value, builder, min, max) {
152
+ let value$1 = BigInt(value);
153
+ if (value$1 < min || value$1 > max) {
154
+ Stdlib_JsError.throwWithMessage(value$1.toString() + ` is out of range for the \`` + builder.name + `\` column`);
155
+ }
156
+ return value$1;
157
+ }
158
+
159
+ function writePresent(stage, builder, row, value) {
160
+ let floats = builder.storage;
161
+ switch (floats.TAG) {
162
+ case "Floats" :
163
+ floats._0[row] = finiteOrThrow(Number(value), builder.name);
164
+ return;
165
+ case "Unsigned" :
166
+ floats._0[row] = checkedBigInt(value, builder, 0n, 18446744073709551615n);
167
+ return;
168
+ case "Signed" :
169
+ floats._0[row] = checkedBigInt(value, builder, -9223372036854775808n, 9223372036854775807n);
170
+ return;
171
+ case "Text" :
172
+ let variable = floats._0;
173
+ let text = toText(value, builder.replacer);
174
+ let units = text.length;
175
+ ensure(stage, builder, variable, variable.cursor + units | 0);
176
+ let ascii = writeAscii(variable.data, text, variable.cursor);
177
+ let written;
178
+ if (ascii !== -1) {
179
+ written = ascii;
180
+ } else {
181
+ let match = encoder.encodeInto(text, variable.data.subarray(variable.cursor));
182
+ if (match.read < units) {
183
+ ensure(stage, builder, variable, variable.cursor + (units * 3 | 0) | 0);
184
+ written = encoder.encodeInto(text, variable.data.subarray(variable.cursor)).written;
185
+ } else {
186
+ written = match.written;
187
+ }
188
+ }
189
+ variable.cursor = variable.cursor + written | 0;
190
+ variable.ends[row] = variable.cursor;
191
+ return;
192
+ case "Bytes" :
193
+ let variable$1 = floats._0;
194
+ let length = value.length;
195
+ ensure(stage, builder, variable$1, variable$1.cursor + length | 0);
196
+ variable$1.data.set(value, variable$1.cursor);
197
+ variable$1.cursor = variable$1.cursor + length | 0;
198
+ variable$1.ends[row] = variable$1.cursor;
199
+ return;
200
+ }
201
+ }
202
+
203
+ function isAbsent(value) {
204
+ if (value === undefined) {
205
+ return true;
206
+ } else {
207
+ return value === null;
208
+ }
209
+ }
210
+
211
+ function writeValue(stage, column, row, value) {
212
+ let builder = stage.builders[column];
213
+ if (isAbsent(value)) {
214
+ if (builder.isNullable) {
215
+ return markNull(builder, row);
216
+ } else {
217
+ return Stdlib_JsError.throwWithMessage(`No value for the \`` + builder.name + `\` column, which is not nullable. Set the field before saving the entity, or make it optional in the schema.`);
218
+ }
219
+ } else {
220
+ return writePresent(stage, builder, row, value);
221
+ }
222
+ }
223
+
224
+ function writeDeletedValue(stage, column, row, value) {
225
+ let builder = stage.builders[column];
226
+ if (isAbsent(value)) {
227
+ return markNull(builder, row);
228
+ } else {
229
+ return writePresent(stage, builder, row, value);
230
+ }
231
+ }
232
+
233
+ function columnCount(stage) {
234
+ return stage.builders.length;
235
+ }
236
+
237
+ function commit(stage) {
238
+ stage.arena.commitStage(stage.handle, stage.buffers);
239
+ return stage.handle;
240
+ }
241
+
242
+ function abort(stage) {
243
+ stage.arena.abortStage(stage.handle, stage.buffers);
244
+ }
245
+
246
+ export {
247
+ kindOfOrdinal,
248
+ begin,
249
+ finiteOrThrow,
250
+ toText,
251
+ writeValue,
252
+ writeDeletedValue,
253
+ columnCount,
254
+ commit,
255
+ abort,
256
+ }
257
+ /* encoder Not a pure module */
@@ -75,15 +75,10 @@ let handleLoad = (state: testIndexerState, ~tableName: string, ~filter: EntityFi
75
75
  | None => []
76
76
  | Some(entityConfig) =>
77
77
  let entityDict = state.entities->Dict.get(tableName)->Option.getOr(Dict.make())
78
- let matched =
79
- entityDict
80
- ->Dict.valuesToArray
81
- ->Array.filter(entity => {
82
- // The store holds decoded entities and the filter carries decoded values,
83
- // so compare directly (same approach as InMemoryTable) — no JSON round-trip.
84
- let entityAsDict = entity->(Utils.magic: Internal.entity => dict<EntityFilter.FieldValue.t>)
85
- filter->EntityFilter.matches(~entity=entityAsDict)
86
- })
78
+ // The store holds decoded entities and the filter carries decoded values,
79
+ // so compare directly (same approach as InMemoryTable) — no JSON round-trip.
80
+ let matcher = filter->EntityFilter.makeMatcher(~table=entityConfig.table)
81
+ let matched = entityDict->Dict.valuesToArray->Array.filter(matcher)
87
82
  // The chain is already fixed by the scope the load ran for, so the loaded
88
83
  // entity is handed back in the shape the handlers see.
89
84
  switch entityConfig.table->Table.getChainIdField {
@@ -266,6 +261,7 @@ let makeInitialState = (
266
261
  sourceBlockNumber: processChainConfig.endBlock->Option.getOr(0),
267
262
  maxReorgDepth: 0, // No reorg support in test indexer
268
263
  progressBlockNumber: -1,
264
+ progressBlockTime: None,
269
265
  numEventsProcessed: 0.,
270
266
  firstEventBlockNumber: None,
271
267
  timestampCaughtUpToHeadOrEndblock: None,
@@ -279,7 +275,7 @@ let makeInitialState = (
279
275
  envioInfo: Some(JSON.Encode.object(Dict.make())),
280
276
  cache: Dict.make(),
281
277
  chains,
282
- checkpointId: InternalTable.Checkpoints.initialCheckpointId,
278
+ checkpointFrontier: Frontier.empty(),
283
279
  reorgCheckpoints: [],
284
280
  }
285
281
  }
@@ -543,23 +539,12 @@ let makeEntityGetWhere = (~state: testIndexerState, ~entityConfig: Internal.enti
543
539
  `Cannot call ${entityConfig.name}.getWhere() while indexer.process() is running. ` ++ "Wait for process() to complete before accessing entities directly.",
544
540
  )
545
541
  }
546
- let filters =
547
- filter->EntityFilter.parseGetWhereOrThrow(
548
- ~entityName=entityConfig.name,
549
- ~table=entityConfig.table,
550
- )
542
+ let matcher =
543
+ filter
544
+ ->EntityFilter.parseOrThrow(~entityName=entityConfig.name, ~table=entityConfig.table)
545
+ ->EntityFilter.makeMatcher(~table=entityConfig.table)
551
546
  let entityDict = state.entities->Dict.get(entityConfig.name)->Option.getOr(Dict.make())
552
- // parseGetWhereOrThrow expands an operator group into alternatives whose
553
- // matches are disjoint, so the union needs no dedup.
554
- Promise.resolve(
555
- entityDict
556
- ->Dict.valuesToArray
557
- ->Array.filter(entity => {
558
- let entityAsDict = entity->(Utils.magic: Internal.entity => dict<EntityFilter.FieldValue.t>)
559
- filters->Array.some(filter => filter->EntityFilter.matches(~entity=entityAsDict))
560
- })
561
- ->Array.map(copyEntity),
562
- )
547
+ Promise.resolve(entityDict->Dict.valuesToArray->Array.filter(matcher)->Array.map(copyEntity))
563
548
  }
564
549
  }
565
550
 
@@ -590,7 +575,7 @@ let makeInMemoryStorage = (~state: testIndexerState): Persistence.storage => {
590
575
  JsError.throwWithMessage(
591
576
  "TestIndexer: initialize should not be called; the initial state is derived from config.",
592
577
  ),
593
- resumeInitialState: async (~entities as _, ~throwIfIncompatible as _) =>
578
+ resumeInitialState: async (~entities as _, ~chainIds as _, ~throwIfIncompatible as _) =>
594
579
  JsError.throwWithMessage(
595
580
  "TestIndexer: resumeInitialState should not be called; the initial state is derived from config.",
596
581
  ),
@@ -599,13 +584,12 @@ let makeInMemoryStorage = (~state: testIndexerState): Persistence.storage => {
599
584
  ->handleLoad(~tableName=table.tableName, ~filter)
600
585
  ->(Utils.magic: array<Internal.entity> => array<unknown>),
601
586
  // The in-memory storage has no indexes to build, and it's always ready.
602
- ensureQueryIndexes: async (~table as _, ~filters as _) => (),
603
- ensureSchemaIndexes: async (~entities as _) => (),
587
+ ensureQueryIndexes: async (~entityConfig as _, ~scope as _, ~filters as _) => (),
588
+ ensureSchemaIndexes: async (~entities as _, ~chainIds as _) => (),
604
589
  finalizeBackfill: async (~entities as _, ~chainIds as _, ~readyAt as _) => (),
605
590
  writeBatch: async (
606
591
  ~batch,
607
592
  ~rollback as _,
608
- ~isInReorgThreshold as _,
609
593
  ~config,
610
594
  ~allEntities as _,
611
595
  ~updatedEffectsCache as _,
@@ -8,6 +8,7 @@ import * as ChainId from "./ChainId.res.mjs";
8
8
  import * as Logging from "./Logging.res.mjs";
9
9
  import * as ChainMap from "./ChainMap.res.mjs";
10
10
  import * as EntityId from "./EntityId.res.mjs";
11
+ import * as Frontier from "./db/Frontier.res.mjs";
11
12
  import * as Internal from "./Internal.res.mjs";
12
13
  import * as ChainState from "./ChainState.res.mjs";
13
14
  import * as Stdlib_Int from "@rescript/runtime/lib/es6/Stdlib_Int.js";
@@ -19,7 +20,6 @@ import * as EntityFilter from "./db/EntityFilter.res.mjs";
19
20
  import * as IndexerState from "./IndexerState.res.mjs";
20
21
  import * as ErrorHandling from "./ErrorHandling.res.mjs";
21
22
  import * as HandlerLoader from "./HandlerLoader.res.mjs";
22
- import * as InternalTable from "./db/InternalTable.res.mjs";
23
23
  import * as Primitive_int from "@rescript/runtime/lib/es6/Primitive_int.js";
24
24
  import * as SimulateItems from "./SimulateItems.res.mjs";
25
25
  import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
@@ -56,7 +56,8 @@ function handleLoad(state, tableName, filter) {
56
56
  return [];
57
57
  }
58
58
  let entityDict = Stdlib_Option.getOr(state.entities[tableName], {});
59
- let matched = Object.values(entityDict).filter(entity => EntityFilter.matches(filter, entity));
59
+ let matcher = EntityFilter.makeMatcher(filter, entityConfig.table);
60
+ let matched = Object.values(entityDict).filter(matcher);
60
61
  let field = Table.getChainIdField(entityConfig.table);
61
62
  if (field !== undefined) {
62
63
  return matched.map(entity => {
@@ -183,6 +184,7 @@ function makeInitialState(config, processConfigChains, addressRowsByChain, contr
183
184
  endBlock: processChainConfig.endBlock,
184
185
  maxReorgDepth: 0,
185
186
  progressBlockNumber: -1,
187
+ progressBlockTime: undefined,
186
188
  numEventsProcessed: 0,
187
189
  firstEventBlockNumber: undefined,
188
190
  timestampCaughtUpToHeadOrEndblock: undefined,
@@ -196,7 +198,7 @@ function makeInitialState(config, processConfigChains, addressRowsByChain, contr
196
198
  envioInfo: {},
197
199
  cache: {},
198
200
  chains: chains,
199
- checkpointId: InternalTable.Checkpoints.initialCheckpointId,
201
+ checkpointFrontier: Frontier.empty(),
200
202
  reorgCheckpoints: []
201
203
  };
202
204
  }
@@ -375,9 +377,9 @@ function makeEntityGetWhere(state, entityConfig) {
375
377
  if (state.processInProgress) {
376
378
  Stdlib_JsError.throwWithMessage(`Cannot call ` + entityConfig.name + `.getWhere() while indexer.process() is running. ` + "Wait for process() to complete before accessing entities directly.");
377
379
  }
378
- let filters = EntityFilter.parseGetWhereOrThrow(filter, entityConfig.name, entityConfig.table);
380
+ let matcher = EntityFilter.makeMatcher(EntityFilter.parseOrThrow(filter, entityConfig.name, entityConfig.table), entityConfig.table);
379
381
  let entityDict = Stdlib_Option.getOr(state.entities[entityConfig.name], {});
380
- return Promise.resolve(Object.values(entityDict).filter(entity => filters.some(filter => EntityFilter.matches(filter, entity))).map(copyEntity));
382
+ return Promise.resolve(Object.values(entityDict).filter(matcher).map(copyEntity));
381
383
  };
382
384
  }
383
385
 
@@ -386,10 +388,10 @@ function makeInMemoryStorage(state) {
386
388
  name: "test-inmemory",
387
389
  isInitialized: async () => true,
388
390
  initialize: async (param, param$1, param$2, param$3, param$4) => Stdlib_JsError.throwWithMessage("TestIndexer: initialize should not be called; the initial state is derived from config."),
389
- resumeInitialState: async (param, param$1) => Stdlib_JsError.throwWithMessage("TestIndexer: resumeInitialState should not be called; the initial state is derived from config."),
391
+ resumeInitialState: async (param, param$1, param$2) => Stdlib_JsError.throwWithMessage("TestIndexer: resumeInitialState should not be called; the initial state is derived from config."),
390
392
  loadOrThrow: async (filter, table) => handleLoad(state, table.tableName, filter),
391
- ensureQueryIndexes: async (param, param$1) => {},
392
- ensureSchemaIndexes: async param => {},
393
+ ensureQueryIndexes: async (param, param$1, param$2) => {},
394
+ ensureSchemaIndexes: async (param, param$1) => {},
393
395
  finalizeBackfill: async (param, param$1, param$2) => {},
394
396
  dumpEffectCache: async () => {},
395
397
  reset: async () => {},
@@ -399,7 +401,7 @@ function makeInMemoryStorage(state) {
399
401
  getRollbackTargetCheckpoint: async (param, param$1) => Stdlib_JsError.throwWithMessage("TestIndexer: Rollback is not supported. The runner forces rollbackOnReorg off, so this should be unreachable."),
400
402
  getRollbackProgressDiff: async param => Stdlib_JsError.throwWithMessage("TestIndexer: Rollback is not supported. The runner forces rollbackOnReorg off, so this should be unreachable."),
401
403
  getRollbackData: async (param, param$1) => Stdlib_JsError.throwWithMessage("TestIndexer: Rollback is not supported. The runner forces rollbackOnReorg off, so this should be unreachable."),
402
- writeBatch: async (batch, param, param$1, config, param$2, param$3, updatedEntities, registeredAddresses, param$4, param$5) => handleWriteBatch(state, config, updatedEntities, registeredAddresses, batch.checkpointIds, batch.checkpointChainIds, batch.checkpointBlockNumbers, batch.checkpointEventsProcessed),
404
+ writeBatch: async (batch, param, config, param$1, param$2, updatedEntities, registeredAddresses, param$3, param$4) => handleWriteBatch(state, config, updatedEntities, registeredAddresses, batch.checkpointIds, batch.checkpointChainIds, batch.checkpointBlockNumbers, batch.checkpointEventsProcessed),
403
405
  close: async () => {}
404
406
  };
405
407
  }
@@ -95,6 +95,7 @@ let initEffect = (params: contextParams) => {
95
95
  input,
96
96
  context: effectContext,
97
97
  cacheKey: input->S.reverseConvertOrThrow(effect.input)->Utils.Hash.makeOrThrow,
98
+ chainId: handlerChainId,
98
99
  checkpointId: params.checkpointId,
99
100
  }
100
101
  LoadLayer.loadEffect(
@@ -121,39 +122,20 @@ type entityContextParams = {
121
122
  // The handler context is always chain-scoped, so a per-chain entity resolves to
122
123
  // the chain the handler runs on and a cross-chain one to the shared partition.
123
124
  let entityScope = (params: entityContextParams) =>
124
- params.entityConfig->InMemoryStore.entityScope(
125
- ~chainId=params.item->Internal.getItemChainId,
126
- )
127
-
128
- let getWhereHandler = (params: entityContextParams, filter: dict<dict<unknown>>) => {
129
- let entityConfig = params.entityConfig
130
-
131
- @inline
132
- let loadWithFilter = filter =>
133
- LoadLayer.loadByFilter(
134
- ~loadManager=params.loadManager,
135
- ~persistence=params.persistence,
136
- ~entityConfig,
137
- ~scope=params->entityScope,
138
- ~indexerState=params.indexerState,
139
- ~shouldGroup=params.isPreload,
140
- ~item=params.item,
141
- ~ecosystem=params.config.ecosystem,
142
- ~filter,
143
- )
125
+ params.entityConfig->InMemoryStore.entityScope(~chainId=params.item->Internal.getItemChainId)
144
126
 
145
- switch filter->EntityFilter.parseGetWhereOrThrow(
146
- ~entityName=entityConfig.name,
147
- ~table=entityConfig.table,
148
- ) {
149
- | [single] => loadWithFilter(single)
150
- | filters =>
151
- filters
152
- ->Array.map(filter => loadWithFilter(filter))
153
- ->Promise.all
154
- ->Promise.thenResolve(results => results->Array.flat)
155
- }
156
- }
127
+ let getWhereHandler = (params: entityContextParams, filter: dict<dict<unknown>>) =>
128
+ LoadLayer.loadByFilter(
129
+ ~loadManager=params.loadManager,
130
+ ~persistence=params.persistence,
131
+ ~entityConfig=params.entityConfig,
132
+ ~scope=params->entityScope,
133
+ ~indexerState=params.indexerState,
134
+ ~shouldGroup=params.isPreload,
135
+ ~item=params.item,
136
+ ~ecosystem=params.config.ecosystem,
137
+ ~filter,
138
+ )
157
139
 
158
140
  let noopSet = (_entity: Internal.entity) => ()
159
141
  let noopDeleteUnsafe = (_entityId: EntityId.t) => ()
@@ -171,14 +153,16 @@ let entityTraps: Utils.Proxy.traps<entityContextParams> = {
171
153
  let prop = prop->(Utils.magic: unknown => string)
172
154
 
173
155
  let isClickHouseOnly = !params.entityConfig.storage.postgres
156
+ let scope = params->entityScope
157
+ let committedCheckpointId = params.indexerState->IndexerState.committedCheckpointIdFor(~scope)
174
158
 
175
159
  let set = params.isPreload
176
160
  ? noopSet
177
161
  : (entity: Internal.entity) => {
178
162
  params.indexerState
179
- ->InMemoryStore.getInMemTable(~entityConfig=params.entityConfig, ~scope=params->entityScope)
163
+ ->InMemoryStore.getInMemTable(~entityConfig=params.entityConfig, ~scope)
180
164
  ->InMemoryTable.Entity.set(
181
- ~committedCheckpointId=params.indexerState->IndexerState.committedCheckpointId,
165
+ ~committedCheckpointId,
182
166
  Set({
183
167
  entityId: entity.id->EntityId.unsafeOfString,
184
168
  checkpointId: params.checkpointId,
@@ -288,9 +272,9 @@ let entityTraps: Utils.Proxy.traps<entityContextParams> = {
288
272
  } else {
289
273
  entityId => {
290
274
  params.indexerState
291
- ->InMemoryStore.getInMemTable(~entityConfig=params.entityConfig, ~scope=params->entityScope)
275
+ ->InMemoryStore.getInMemTable(~entityConfig=params.entityConfig, ~scope)
292
276
  ->InMemoryTable.Entity.set(
293
- ~committedCheckpointId=params.indexerState->IndexerState.committedCheckpointId,
277
+ ~committedCheckpointId,
294
278
  Delete({
295
279
  entityId,
296
280
  checkpointId: params.checkpointId,
@@ -64,6 +64,7 @@ function initEffect(params) {
64
64
  input: input,
65
65
  context: effectContext,
66
66
  cacheKey: effectArgs_cacheKey,
67
+ chainId: handlerChainId,
67
68
  checkpointId: effectArgs_checkpointId
68
69
  };
69
70
  return LoadLayer.loadEffect(params.loadManager, params.persistence, effect, effectArgs, scope, params.indexerState, params.isPreload, params.item, params.config.ecosystem);
@@ -76,13 +77,7 @@ function entityScope(params) {
76
77
  }
77
78
 
78
79
  function getWhereHandler(params, filter) {
79
- let entityConfig = params.entityConfig;
80
- let filters = EntityFilter.parseGetWhereOrThrow(filter, entityConfig.name, entityConfig.table);
81
- if (filters.length !== 1) {
82
- return Promise.all(filters.map(filter => LoadLayer.loadByFilter(params.loadManager, params.persistence, entityConfig, entityScope(params), params.indexerState, params.isPreload, params.item, params.config.ecosystem, filter))).then(results => results.flat());
83
- } else {
84
- return LoadLayer.loadByFilter(params.loadManager, params.persistence, entityConfig, entityScope(params), params.indexerState, params.isPreload, params.item, params.config.ecosystem, filters[0]);
85
- }
80
+ return LoadLayer.loadByFilter(params.loadManager, params.persistence, params.entityConfig, entityScope(params), params.indexerState, params.isPreload, params.item, params.config.ecosystem, filter);
86
81
  }
87
82
 
88
83
  function noopSet(_entity) {
@@ -99,7 +94,9 @@ function throwClickHouseReadOnly(entityConfig, op) {
99
94
 
100
95
  let entityTraps_get = (params, prop) => {
101
96
  let isClickHouseOnly = !params.entityConfig.storage.postgres;
102
- let set = params.isPreload ? noopSet : entity => InMemoryTable.Entity.set(InMemoryStore.getInMemTable(params.indexerState, params.entityConfig, entityScope(params)), IndexerState.committedCheckpointId(params.indexerState), {
97
+ let scope = entityScope(params);
98
+ let committedCheckpointId = IndexerState.committedCheckpointIdFor(params.indexerState, scope);
99
+ let set = params.isPreload ? noopSet : entity => InMemoryTable.Entity.set(InMemoryStore.getInMemTable(params.indexerState, params.entityConfig, scope), committedCheckpointId, {
103
100
  type: "SET",
104
101
  entityId: entity.id,
105
102
  entity: entity,
@@ -110,7 +107,7 @@ let entityTraps_get = (params, prop) => {
110
107
  if (params.isPreload) {
111
108
  return noopDeleteUnsafe;
112
109
  } else {
113
- return entityId => InMemoryTable.Entity.set(InMemoryStore.getInMemTable(params.indexerState, params.entityConfig, entityScope(params)), IndexerState.committedCheckpointId(params.indexerState), {
110
+ return entityId => InMemoryTable.Entity.set(InMemoryStore.getInMemTable(params.indexerState, params.entityConfig, scope), committedCheckpointId, {
114
111
  type: "DELETE",
115
112
  entityId: entityId,
116
113
  checkpointId: params.checkpointId
package/src/Utils.res CHANGED
@@ -166,6 +166,15 @@ module Dict = {
166
166
 
167
167
  let merge: (dict<'a>, dict<'a>) => dict<'a> = %raw(`(dictA, dictB) => ({...dictA, ...dictB})`)
168
168
 
169
+ // Keeps every key either dict has; `pick` decides a key both have.
170
+ let mergeWith: (dict<'a>, dict<'a>, ('a, 'a) => 'a) => dict<'a> = %raw(`(a, b, pick) => {
171
+ var merged = {...a}, i;
172
+ for (i in b) {
173
+ merged[i] = i in merged ? pick(merged[i], b[i]) : b[i];
174
+ }
175
+ return merged;
176
+ }`)
177
+
169
178
  @val
170
179
  external mergeInPlace: (dict<'a>, dict<'a>) => dict<'a> = "Object.assign"
171
180
 
@@ -264,7 +273,7 @@ module Dict = {
264
273
  }
265
274
 
266
275
  module Math = {
267
- let minOptInt = (a, b) =>
276
+ let minOptInt = (a: option<int>, b: option<int>) =>
268
277
  switch (a, b) {
269
278
  | (Some(a), Some(b)) => Some(a < b ? a : b)
270
279
  | (Some(a), None) => Some(a)
@@ -365,9 +374,6 @@ module Array = {
365
374
  /**
366
375
  Helper to check if a value exists in an array
367
376
  */
368
- let includes = (arr: array<'a>, val: 'a) =>
369
- arr->Array.find(item => item == val)->Stdlib.Option.isSome
370
-
371
377
  let isEmpty = (arr: array<_>) =>
372
378
  switch arr {
373
379
  | [] => true
package/src/Utils.res.mjs CHANGED
@@ -6,11 +6,9 @@ import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
6
6
  import * as Stdlib_JsExn from "@rescript/runtime/lib/es6/Stdlib_JsExn.js";
7
7
  import * as Primitive_int from "@rescript/runtime/lib/es6/Primitive_int.js";
8
8
  import * as Stdlib_BigInt from "@rescript/runtime/lib/es6/Stdlib_BigInt.js";
9
- import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
10
9
  import * as Stdlib_String from "@rescript/runtime/lib/es6/Stdlib_String.js";
11
10
  import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
12
11
  import * as Stdlib_Promise from "@rescript/runtime/lib/es6/Stdlib_Promise.js";
13
- import * as Primitive_object from "@rescript/runtime/lib/es6/Primitive_object.js";
14
12
  import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
15
13
  import * as Primitive_string from "@rescript/runtime/lib/es6/Primitive_string.js";
16
14
  import * as S$RescriptSchema from "rescript-schema/src/S.res.mjs";
@@ -114,6 +112,14 @@ function pushMany(dict, key, values) {
114
112
 
115
113
  let merge = ((dictA, dictB) => ({...dictA, ...dictB}));
116
114
 
115
+ let mergeWith = ((a, b, pick) => {
116
+ var merged = {...a}, i;
117
+ for (i in b) {
118
+ merged[i] = i in merged ? pick(merged[i], b[i]) : b[i];
119
+ }
120
+ return merged;
121
+ });
122
+
117
123
  let mapValues = ((dict, f) => {
118
124
  var target = {}, i;
119
125
  for (i in dict) {
@@ -197,6 +203,7 @@ let Dict = {
197
203
  push: push,
198
204
  pushMany: pushMany,
199
205
  merge: merge,
206
+ mergeWith: mergeWith,
200
207
  mapValues: mapValues,
201
208
  filterMapValues: filterMapValues,
202
209
  mapValuesToArray: mapValuesToArray,
@@ -212,19 +219,17 @@ let Dict = {
212
219
  };
213
220
 
214
221
  function minOptInt(a, b) {
215
- if (a === undefined) {
222
+ if (a !== undefined) {
216
223
  if (b !== undefined) {
217
- return Primitive_option.some(Primitive_option.valFromOption(b));
224
+ return a < b ? a : b;
218
225
  } else {
219
- return;
226
+ return a;
220
227
  }
228
+ } else if (b !== undefined) {
229
+ return b;
230
+ } else {
231
+ return;
221
232
  }
222
- let a$1 = Primitive_option.valFromOption(a);
223
- if (b === undefined) {
224
- return Primitive_option.some(a$1);
225
- }
226
- let b$1 = Primitive_option.valFromOption(b);
227
- return Primitive_option.some(Primitive_object.lessthan(a$1, b$1) ? a$1 : b$1);
228
233
  }
229
234
 
230
235
  let $$Math = {
@@ -315,10 +320,6 @@ function transposeResults(results) {
315
320
  };
316
321
  }
317
322
 
318
- function includes(arr, val) {
319
- return Stdlib_Option.isSome(arr.find(item => Primitive_object.equal(item, val)));
320
- }
321
-
322
323
  function isEmpty$1(arr) {
323
324
  return arr.length === 0;
324
325
  }
@@ -426,7 +427,6 @@ let $$Array$1 = {
426
427
  clearInPlace: clearInPlace$1,
427
428
  setIndexImmutable: setIndexImmutable,
428
429
  transposeResults: transposeResults,
429
- includes: includes,
430
430
  isEmpty: isEmpty$1,
431
431
  notEmpty: notEmpty,
432
432
  awaitEach: awaitEach,