envio 3.5.0-alpha.1 → 3.5.0-alpha.3

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 (99) hide show
  1. package/index.d.ts +10 -6
  2. package/package.json +6 -6
  3. package/src/BatchProcessing.res +27 -0
  4. package/src/BatchProcessing.res.mjs +12 -0
  5. package/src/ChainFetching.res +5 -5
  6. package/src/ChainFetching.res.mjs +3 -4
  7. package/src/ChainState.res +48 -25
  8. package/src/ChainState.res.mjs +23 -29
  9. package/src/ChainState.resi +2 -6
  10. package/src/Change.res +3 -3
  11. package/src/Config.res +2 -6
  12. package/src/Config.res.mjs +2 -12
  13. package/src/Core.res +3 -0
  14. package/src/CrossChainState.res +33 -10
  15. package/src/CrossChainState.res.mjs +21 -6
  16. package/src/CrossChainState.resi +3 -0
  17. package/src/EntityId.res +15 -0
  18. package/src/EntityId.res.mjs +9 -0
  19. package/src/EventConfigBuilder.res +1 -58
  20. package/src/EventConfigBuilder.res.mjs +1 -33
  21. package/src/FetchState.res +510 -460
  22. package/src/FetchState.res.mjs +413 -428
  23. package/src/FinalizeBackfill.res +34 -0
  24. package/src/FinalizeBackfill.res.mjs +26 -0
  25. package/src/HandlerRegister.res +45 -0
  26. package/src/HandlerRegister.res.mjs +43 -0
  27. package/src/InMemoryStore.res +11 -4
  28. package/src/InMemoryTable.res +26 -24
  29. package/src/InMemoryTable.res.mjs +19 -18
  30. package/src/IndexerState.res +71 -2
  31. package/src/IndexerState.res.mjs +67 -3
  32. package/src/IndexerState.resi +5 -0
  33. package/src/Internal.res +17 -8
  34. package/src/LoadLayer.res +9 -1
  35. package/src/LoadLayer.res.mjs +1 -0
  36. package/src/LogSelection.res +2 -1
  37. package/src/Metrics.res +37 -6
  38. package/src/Metrics.res.mjs +5 -1
  39. package/src/Persistence.res +12 -1
  40. package/src/PgStorage.res +411 -57
  41. package/src/PgStorage.res.mjs +262 -41
  42. package/src/SimulateItems.res +2 -2
  43. package/src/TestIndexer.res +9 -5
  44. package/src/TestIndexer.res.mjs +5 -2
  45. package/src/UserContext.res +3 -3
  46. package/src/Writing.res +12 -2
  47. package/src/Writing.res.mjs +13 -2
  48. package/src/bindings/ClickHouse.res +5 -3
  49. package/src/bindings/ClickHouse.res.mjs +2 -5
  50. package/src/bindings/Vitest.res +22 -1
  51. package/src/bindings/Vitest.res.mjs +15 -0
  52. package/src/db/EntityFilter.res +0 -4
  53. package/src/db/EntityFilter.res.mjs +1 -8
  54. package/src/db/EntityHistory.res +16 -7
  55. package/src/db/EntityHistory.res.mjs +7 -6
  56. package/src/db/IndexRegistry.res +219 -0
  57. package/src/db/IndexRegistry.res.mjs +195 -0
  58. package/src/db/InternalTable.res +7 -0
  59. package/src/db/InternalTable.res.mjs +7 -0
  60. package/src/db/Table.res +53 -22
  61. package/src/db/Table.res.mjs +56 -18
  62. package/src/sources/AddressSet.res +48 -0
  63. package/src/sources/AddressSet.res.mjs +11 -0
  64. package/src/sources/AddressStore.res +152 -0
  65. package/src/sources/AddressStore.res.mjs +97 -0
  66. package/src/sources/EvmChain.res +3 -0
  67. package/src/sources/EvmChain.res.mjs +4 -2
  68. package/src/sources/EvmHyperSyncSource.res +6 -3
  69. package/src/sources/EvmHyperSyncSource.res.mjs +3 -3
  70. package/src/sources/EvmRpcClient.res +6 -3
  71. package/src/sources/EvmRpcClient.res.mjs +3 -3
  72. package/src/sources/FuelHyperSync.res +4 -3
  73. package/src/sources/FuelHyperSync.res.mjs +3 -3
  74. package/src/sources/FuelHyperSync.resi +2 -1
  75. package/src/sources/FuelHyperSyncClient.res +12 -6
  76. package/src/sources/FuelHyperSyncClient.res.mjs +2 -2
  77. package/src/sources/FuelHyperSyncSource.res +7 -4
  78. package/src/sources/FuelHyperSyncSource.res.mjs +3 -3
  79. package/src/sources/HyperSync.res +6 -4
  80. package/src/sources/HyperSync.res.mjs +2 -3
  81. package/src/sources/HyperSync.resi +1 -1
  82. package/src/sources/HyperSyncClient.res +18 -9
  83. package/src/sources/HyperSyncClient.res.mjs +4 -4
  84. package/src/sources/RpcSource.res +15 -10
  85. package/src/sources/RpcSource.res.mjs +3 -4
  86. package/src/sources/SimulateSource.res +26 -11
  87. package/src/sources/SimulateSource.res.mjs +13 -5
  88. package/src/sources/Source.res +4 -2
  89. package/src/sources/SourceManager.res +2 -3
  90. package/src/sources/SourceManager.res.mjs +2 -3
  91. package/src/sources/Svm.res +1 -2
  92. package/src/sources/Svm.res.mjs +1 -1
  93. package/src/sources/SvmHyperSyncClient.res +12 -3
  94. package/src/sources/SvmHyperSyncClient.res.mjs +2 -2
  95. package/src/sources/SvmHyperSyncSource.res +10 -5
  96. package/src/sources/SvmHyperSyncSource.res.mjs +5 -4
  97. package/src/IndexingAddresses.res +0 -151
  98. package/src/IndexingAddresses.res.mjs +0 -130
  99. package/src/IndexingAddresses.resi +0 -39
@@ -0,0 +1,34 @@
1
+ // The FinalizingIndexes phase. Runs once, from the processing loop, when every
2
+ // chain has caught up: processing is already paused (the loop awaits this),
3
+ // pending writes are flushed, then storage creates every missing schema-defined
4
+ // index and stamps `ready_at` in one transaction. A failure rolls back both, so
5
+ // the next batch simply retries — the indexer never reports ready with an index
6
+ // the schema promised still missing.
7
+
8
+ let run = async (state: IndexerState.t) => {
9
+ Logging.info(
10
+ "All chains are caught up. Finalizing the indexer before switching to realtime: flushing pending writes, then creating the indexes the schema promises.",
11
+ )
12
+
13
+ await Writing.flush(state)
14
+
15
+ // A failed write already surfaced through onError; committing ready_at on top
16
+ // of an incomplete write would claim progress that isn't durable.
17
+ if !(state->IndexerState.hasFailedWrite) {
18
+ let persistence = state->IndexerState.persistence
19
+ let storage = persistence->Persistence.getInitializedStorageOrThrow
20
+ let readyAt = Date.make()
21
+
22
+ await storage.finalizeBackfill(
23
+ ~entities=persistence.allEntities,
24
+ ~chainIds=state
25
+ ->IndexerState.chainStates
26
+ ->Dict.valuesToArray
27
+ ->Array.map(cs => (cs->ChainState.chainConfig).id),
28
+ ~readyAt,
29
+ )
30
+
31
+ state->IndexerState.markReady(~readyAt)
32
+ Logging.info("The indexer is ready. Switching to realtime indexing.")
33
+ }
34
+ }
@@ -0,0 +1,26 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as Logging from "./Logging.res.mjs";
4
+ import * as Writing from "./Writing.res.mjs";
5
+ import * as ChainState from "./ChainState.res.mjs";
6
+ import * as Persistence from "./Persistence.res.mjs";
7
+ import * as IndexerState from "./IndexerState.res.mjs";
8
+
9
+ async function run(state) {
10
+ Logging.info("All chains are caught up. Finalizing the indexer before switching to realtime: flushing pending writes, then creating the indexes the schema promises.");
11
+ await Writing.flush(state);
12
+ if (IndexerState.hasFailedWrite(state)) {
13
+ return;
14
+ }
15
+ let persistence = IndexerState.persistence(state);
16
+ let storage = Persistence.getInitializedStorageOrThrow(persistence);
17
+ let readyAt = new Date();
18
+ await storage.finalizeBackfill(persistence.allEntities, Object.values(IndexerState.chainStates(state)).map(cs => ChainState.chainConfig(cs).id), readyAt);
19
+ IndexerState.markReady(state, readyAt);
20
+ return Logging.info("The indexer is ready. Switching to realtime indexing.");
21
+ }
22
+
23
+ export {
24
+ run,
25
+ }
26
+ /* Logging Not a pure module */
@@ -215,6 +215,25 @@ let mergeRegistrations = (resolved: array<Internal.onEventRegistration>, ~config
215
215
  let isDroppedByWhere = (~config: Config.t, reg: Internal.onEventRegistration) =>
216
216
  config.ecosystem.name === Evm && (reg->getResolvedWhere).topicSelections->Utils.Array.isEmpty
217
217
 
218
+ // Names configured across every chain, used to explain a registration that
219
+ // matched nothing. Only built on the failure path.
220
+ let describeConfigured = (registration: activeRegistration, ~contractName) => {
221
+ let contractNames = Utils.Set.make()
222
+ let eventNames = Utils.Set.make()
223
+ registration.config.chainMap
224
+ ->ChainMap.values
225
+ ->Array.forEach(chainConfig =>
226
+ chainConfig.contracts->Array.forEach(contract => {
227
+ contractNames->Utils.Set.add(contract.name)->ignore
228
+ if contract.name === contractName {
229
+ contract.events->Array.forEach(e => eventNames->Utils.Set.add(e.name)->ignore)
230
+ }
231
+ })
232
+ )
233
+ let sorted = set => set->Utils.Set.toArray->Array.toSorted(String.compare)
234
+ (sorted(contractNames), sorted(eventNames))
235
+ }
236
+
218
237
  // Resolve one `onEvent`/`contractRegister` call into a registration for every
219
238
  // chain in the config (the full chain set) and store it in registration order.
220
239
  // Building runs the user's `where` callback here — once per chain — so a broken
@@ -230,6 +249,7 @@ let addOnEventRegistration = (
230
249
  ) => {
231
250
  let isWildcard = eventOptions->Option.flatMap(v => v.wildcard)->Option.getOr(false)
232
251
  let where = eventOptions->Option.flatMap(v => v.where)
252
+ let matched = ref(false)
233
253
  registration.config.chainMap
234
254
  ->ChainMap.values
235
255
  ->Array.forEach(chainConfig =>
@@ -239,6 +259,7 @@ let addOnEventRegistration = (
239
259
  switch contract.events->Array.find(e => e.name === eventName) {
240
260
  | None => ()
241
261
  | Some(eventConfig) =>
262
+ matched := true
242
263
  let reg = buildOnEventRegistrationWith(
243
264
  ~config=registration.config,
244
265
  ~chainId=chainConfig.id,
@@ -255,6 +276,30 @@ let addOnEventRegistration = (
255
276
  }
256
277
  }
257
278
  )
279
+
280
+ // Nothing matched on any chain, so the callback could never be dispatched.
281
+ // Reported at the registration call site, where the stack still points at the
282
+ // offending `onEvent`/`contractRegister`.
283
+ if !matched.contents {
284
+ let (contractNames, eventNames) = registration->describeConfigured(~contractName)
285
+ let listOr = (names, empty) =>
286
+ names->Utils.Array.isEmpty ? empty : names->Array.joinUnsafe(", ")
287
+ if eventNames->Utils.Array.isEmpty {
288
+ JsError.throwWithMessage(
289
+ `Contract "${contractName}" is not configured on any chain, so its handler for "${eventName}" would never run. Add it to your config, or remove the registration. Configured contracts: ${listOr(
290
+ contractNames,
291
+ "none",
292
+ )}.`,
293
+ )
294
+ } else {
295
+ JsError.throwWithMessage(
296
+ `Event "${eventName}" is not configured on contract "${contractName}", so its handler would never run. Add it to your config, or remove the registration. Configured events on "${contractName}": ${listOr(
297
+ eventNames,
298
+ "none",
299
+ )}.`,
300
+ )
301
+ }
302
+ }
258
303
  }
259
304
 
260
305
  let setHandler = (~contractName, ~eventName, handler, ~eventOptions) => {
@@ -8,6 +8,7 @@ import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
8
8
  import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
9
9
  import * as Primitive_object from "@rescript/runtime/lib/es6/Primitive_object.js";
10
10
  import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
11
+ import * as Primitive_string from "@rescript/runtime/lib/es6/Primitive_string.js";
11
12
  import * as S$RescriptSchema from "rescript-schema/src/S.res.mjs";
12
13
  import * as EventConfigBuilder from "./EventConfigBuilder.res.mjs";
13
14
  import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js";
@@ -145,9 +146,33 @@ function isDroppedByWhere(config, reg) {
145
146
  }
146
147
  }
147
148
 
149
+ function describeConfigured(registration, contractName) {
150
+ let contractNames = new Set();
151
+ let eventNames = new Set();
152
+ ChainMap.values(registration.config.chainMap).forEach(chainConfig => {
153
+ chainConfig.contracts.forEach(contract => {
154
+ contractNames.add(contract.name);
155
+ if (contract.name === contractName) {
156
+ contract.events.forEach(e => {
157
+ eventNames.add(e.name);
158
+ });
159
+ return;
160
+ }
161
+ });
162
+ });
163
+ let sorted = set => Array.from(set).toSorted(Primitive_string.compare);
164
+ return [
165
+ sorted(contractNames),
166
+ sorted(eventNames)
167
+ ];
168
+ }
169
+
148
170
  function addOnEventRegistration(registration, contractName, eventName, handler, contractRegister, eventOptions) {
149
171
  let isWildcard = Stdlib_Option.getOr(Stdlib_Option.flatMap(eventOptions, v => v.wildcard), false);
150
172
  let where = Stdlib_Option.flatMap(eventOptions, v => v.where);
173
+ let matched = {
174
+ contents: false
175
+ };
151
176
  ChainMap.values(registration.config.chainMap).forEach(chainConfig => {
152
177
  let contract = chainConfig.contracts.find(c => c.name === contractName);
153
178
  if (contract === undefined) {
@@ -157,9 +182,27 @@ function addOnEventRegistration(registration, contractName, eventName, handler,
157
182
  if (eventConfig === undefined) {
158
183
  return;
159
184
  }
185
+ matched.contents = true;
160
186
  let reg = buildOnEventRegistrationWith(registration.config, chainConfig.id, eventConfig, isWildcard, handler, contractRegister, where, contract.startBlock);
161
187
  getChainRegistrations(registration, chainConfig.id).onEventRegistrations.push(reg);
162
188
  });
189
+ if (matched.contents) {
190
+ return;
191
+ }
192
+ let match = describeConfigured(registration, contractName);
193
+ let eventNames = match[1];
194
+ let listOr = (names, empty) => {
195
+ if (Utils.$$Array.isEmpty(names)) {
196
+ return empty;
197
+ } else {
198
+ return names.join(", ");
199
+ }
200
+ };
201
+ if (Utils.$$Array.isEmpty(eventNames)) {
202
+ return Stdlib_JsError.throwWithMessage(`Contract "` + contractName + `" is not configured on any chain, so its handler for "` + eventName + `" would never run. Add it to your config, or remove the registration. Configured contracts: ` + listOr(match[0], "none") + `.`);
203
+ } else {
204
+ return Stdlib_JsError.throwWithMessage(`Event "` + eventName + `" is not configured on contract "` + contractName + `", so its handler would never run. Add it to your config, or remove the registration. Configured events on "` + contractName + `": ` + listOr(eventNames, "none") + `.`);
205
+ }
163
206
  }
164
207
 
165
208
  function setHandler(contractName, eventName, handler, eventOptions) {
@@ -45,7 +45,10 @@ let setEffectOutput = (
45
45
  | Some(_) => ()
46
46
  | None => inMemTable.changesCount = inMemTable.changesCount +. 1.
47
47
  }
48
- inMemTable.dict->Dict.set(cacheKey, Set({entityId: cacheKey, entity: output, checkpointId}))
48
+ inMemTable.dict->Dict.set(
49
+ cacheKey,
50
+ Set({entityId: cacheKey->EntityId.unsafeOfString, entity: output, checkpointId}),
51
+ )
49
52
  if shouldCache {
50
53
  inMemTable.idsToStore->Array.push(cacheKey)->ignore
51
54
  }
@@ -58,7 +61,11 @@ let initEffectOutputFromDb = (inMemTable: EffectState.effectCacheInMemTable, ~ca
58
61
  inMemTable.changesCount = inMemTable.changesCount +. 1.
59
62
  inMemTable.dict->Dict.set(
60
63
  cacheKey,
61
- Set({entityId: cacheKey, entity: output, checkpointId: Internal.loadedFromDbCheckpointId}),
64
+ Set({
65
+ entityId: cacheKey->EntityId.unsafeOfString,
66
+ entity: output,
67
+ checkpointId: Internal.loadedFromDbCheckpointId,
68
+ }),
62
69
  )
63
70
  }
64
71
 
@@ -131,7 +138,7 @@ let prepareRollbackDiff = async (
131
138
  entityTable->InMemoryTable.Entity.set(
132
139
  ~committedCheckpointId,
133
140
  Set({
134
- entityId: entity.id,
141
+ entityId: entity.id->EntityId.unsafeOfString,
135
142
  checkpointId: rollbackDiffCheckpointId,
136
143
  entity,
137
144
  }),
@@ -177,7 +184,7 @@ let setBatchDcs = (state: IndexerState.t, ~batch: Batch.t) => {
177
184
  inMemTable->InMemoryTable.Entity.set(
178
185
  ~committedCheckpointId,
179
186
  Set({
180
- entityId: entity.id,
187
+ entityId: entity.id->EntityId.unsafeOfString,
181
188
  checkpointId,
182
189
  entity: entity->InternalTable.EnvioAddresses.castToInternal,
183
190
  }),
@@ -2,7 +2,7 @@ module Entity = {
2
2
  type relatedEntityId = string
3
3
  type filterWithRelatedIds = (EntityFilter.t, Utils.Set.t<relatedEntityId>)
4
4
  // Keyed by EntityFilter.toString
5
- type filterIndices = dict<filterWithRelatedIds>
5
+ type filterIndexes = dict<filterWithRelatedIds>
6
6
 
7
7
  type entityFilters = Utils.Set.t<EntityFilter.t>
8
8
  type t = {
@@ -14,14 +14,16 @@ module Entity = {
14
14
  // previous changes persist in the background.
15
15
  mutable prevEntityChanges: array<Change.t<Internal.entity>>,
16
16
  mutable filtersByEntityId: dict<entityFilters>,
17
- mutable filterIndices: filterIndices,
17
+ mutable filterIndexes: filterIndexes,
18
18
  }
19
19
 
20
- // Helper to extract entity ID from any entity
20
+ // Helper to extract an entity's id as a dict key. The raw id may be a
21
+ // string/int/bigint, so it's stringified to a stable key for in-memory
22
+ // indexing.
21
23
  exception UnexpectedIdNotDefinedOnEntity
22
24
  let getEntityIdUnsafe = (entity: Internal.entity): string =>
23
- switch (entity->(Utils.magic: Internal.entity => {"id": option<string>}))["id"] {
24
- | Some(id) => id
25
+ switch (entity->(Utils.magic: Internal.entity => {"id": option<EntityId.t>}))["id"] {
26
+ | Some(id) => id->EntityId.toKey
25
27
  | None =>
26
28
  UnexpectedIdNotDefinedOnEntity->ErrorHandling.mkLogAndRaise(
27
29
  ~msg="Property 'id' does not exist on expected entity object",
@@ -42,7 +44,7 @@ module Entity = {
42
44
  changesCount: 0.,
43
45
  prevEntityChanges: [],
44
46
  filtersByEntityId: Dict.make(),
45
- filterIndices: Dict.make(),
47
+ filterIndexes: Dict.make(),
46
48
  }
47
49
 
48
50
  // Changes to persist for checkpoints in (committedCheckpointId, upToCheckpointId].
@@ -79,7 +81,7 @@ module Entity = {
79
81
  }
80
82
 
81
83
  // Frees committed changes: drops latest entries at or below committedCheckpointId
82
- // (re-readable from the db) and clears the per-batch indices (rebuilt on the next
84
+ // (re-readable from the db) and clears the per-batch indexes (rebuilt on the next
83
85
  // getWhere). Uncommitted changes are kept. With keepLoadedFromDb, entries seeded
84
86
  // from a db read are spared so the cheaper-to-re-derive writes are dropped first.
85
87
  let dropCommittedChanges = (self: t, ~committedCheckpointId, ~keepLoadedFromDb) => {
@@ -96,10 +98,10 @@ module Entity = {
96
98
  keysToDelete->Array.forEach(key => self.latestEntityChangeById->Utils.Dict.deleteInPlace(key))
97
99
  self.changesCount = self.changesCount -. keysToDelete->Array.length->Int.toFloat
98
100
  self.filtersByEntityId = Dict.make()
99
- self.filterIndices = Dict.make()
101
+ self.filterIndexes = Dict.make()
100
102
  }
101
103
 
102
- let updateIndices = (self: t, ~entity: Internal.entity) => {
104
+ let updateIndexes = (self: t, ~entity: Internal.entity) => {
103
105
  let entityId = entity->getEntityIdUnsafe
104
106
  let entityAsDict = entity->(Utils.magic: Internal.entity => dict<EntityFilter.FieldValue.t>)
105
107
 
@@ -114,7 +116,7 @@ module Entity = {
114
116
  })
115
117
  }
116
118
 
117
- self.filterIndices->Utils.Dict.forEach(((filter, relatedEntityIds)) => {
119
+ self.filterIndexes->Utils.Dict.forEach(((filter, relatedEntityIds)) => {
118
120
  if filter->EntityFilter.matches(~entity=entityAsDict) {
119
121
  //Add entity id to the filter index and the filter to entity filters
120
122
  relatedEntityIds->Utils.Set.add(entityId)->ignore
@@ -125,12 +127,12 @@ module Entity = {
125
127
  })
126
128
  }
127
129
 
128
- let deleteEntityFromIndices = (self: t, ~entityId: string) =>
130
+ let deleteEntityFromIndexes = (self: t, ~entityId: string) =>
129
131
  switch self.filtersByEntityId->Utils.Dict.dangerouslyGetNonOption(entityId) {
130
132
  | None => ()
131
133
  | Some(entityFilters) =>
132
134
  entityFilters->Utils.Set.forEach(filter => {
133
- switch self.filterIndices->Utils.Dict.dangerouslyGetNonOption(
135
+ switch self.filterIndexes->Utils.Dict.dangerouslyGetNonOption(
134
136
  filter->EntityFilter.toString,
135
137
  ) {
136
138
  | Some((_filter, relatedEntityIds)) =>
@@ -142,8 +144,8 @@ module Entity = {
142
144
  }
143
145
 
144
146
  let set = (inMemTable: t, ~committedCheckpointId, change: Change.t<Internal.entity>) => {
145
- let entityId = change->Change.getEntityId
146
- switch inMemTable.latestEntityChangeById->Utils.Dict.dangerouslyGetNonOption(entityId) {
147
+ let entityKey = change->Change.getEntityId->EntityId.toKey
148
+ switch inMemTable.latestEntityChangeById->Utils.Dict.dangerouslyGetNonOption(entityKey) {
147
149
  | Some(prev) =>
148
150
  let prevCheckpointId = prev->Change.getCheckpointId
149
151
  if (
@@ -157,10 +159,10 @@ module Entity = {
157
159
  }
158
160
 
159
161
  switch change {
160
- | Set({entity}) => inMemTable->updateIndices(~entity)
161
- | Delete({entityId}) => inMemTable->deleteEntityFromIndices(~entityId)
162
+ | Set({entity}) => inMemTable->updateIndexes(~entity)
163
+ | Delete({entityId}) => inMemTable->deleteEntityFromIndexes(~entityId=entityId->EntityId.toKey)
162
164
  }
163
- inMemTable.latestEntityChangeById->Dict.set(entityId, change)
165
+ inMemTable.latestEntityChangeById->Dict.set(entityKey, change)
164
166
  }
165
167
 
166
168
  // Only writes when the id isn't already present, so set always takes its
@@ -172,10 +174,10 @@ module Entity = {
172
174
  ~entity: option<Internal.entity>,
173
175
  ) =>
174
176
  if inMemTable.latestEntityChangeById->Utils.Dict.dangerouslyGetNonOption(key)->Option.isNone {
177
+ let entityId = key->EntityId.unsafeOfString
175
178
  let change: Change.t<Internal.entity> = switch entity {
176
- | Some(entity) =>
177
- Set({entityId: key, entity, checkpointId: Internal.loadedFromDbCheckpointId})
178
- | None => Delete({entityId: key, checkpointId: Internal.loadedFromDbCheckpointId})
179
+ | Some(entity) => Set({entityId, entity, checkpointId: Internal.loadedFromDbCheckpointId})
180
+ | None => Delete({entityId, checkpointId: Internal.loadedFromDbCheckpointId})
179
181
  }
180
182
  inMemTable->set(~committedCheckpointId, change)
181
183
  }
@@ -198,12 +200,12 @@ module Entity = {
198
200
 
199
201
  let hasIndex = (inMemTable: t) =>
200
202
  (filterKey: string) =>
201
- inMemTable.filterIndices->Utils.Dict.dangerouslyGetNonOption(filterKey) !== None
203
+ inMemTable.filterIndexes->Utils.Dict.dangerouslyGetNonOption(filterKey) !== None
202
204
 
203
205
  let getUnsafeOnIndex = (inMemTable: t) => {
204
206
  let getEntity = inMemTable->getUnsafe
205
207
  (filterKey: string) => {
206
- switch inMemTable.filterIndices->Utils.Dict.dangerouslyGetNonOption(filterKey) {
208
+ switch inMemTable.filterIndexes->Utils.Dict.dangerouslyGetNonOption(filterKey) {
207
209
  | None =>
208
210
  JsError.throwWithMessage(`Unexpected error. Must have an index for the filter ${filterKey}`)
209
211
  | Some((_filter, relatedEntityIds)) =>
@@ -221,7 +223,7 @@ module Entity = {
221
223
 
222
224
  let addEmptyIndex = (inMemTable: t, ~filter: EntityFilter.t) => {
223
225
  let filterKey = filter->EntityFilter.toString
224
- switch inMemTable.filterIndices->Utils.Dict.dangerouslyGetNonOption(filterKey) {
226
+ switch inMemTable.filterIndexes->Utils.Dict.dangerouslyGetNonOption(filterKey) {
225
227
  | Some(_) => () //Should not happen, this means the index already exists
226
228
  | None =>
227
229
  let relatedEntityIds = Utils.Set.make()
@@ -238,7 +240,7 @@ module Entity = {
238
240
  | None => ()
239
241
  }
240
242
  })
241
- inMemTable.filterIndices->Dict.set(filterKey, (filter, relatedEntityIds))
243
+ inMemTable.filterIndexes->Dict.set(filterKey, (filter, relatedEntityIds))
242
244
  }
243
245
  }
244
246
  }
@@ -1,6 +1,7 @@
1
1
  // Generated by ReScript, PLEASE EDIT WITH CARE
2
2
 
3
3
  import * as Utils from "./Utils.res.mjs";
4
+ import * as EntityId from "./EntityId.res.mjs";
4
5
  import * as Internal from "./Internal.res.mjs";
5
6
  import * as EntityFilter from "./db/EntityFilter.res.mjs";
6
7
  import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
@@ -15,7 +16,7 @@ let UnexpectedIdNotDefinedOnEntity = /* @__PURE__ */Primitive_exceptions.create(
15
16
  function getEntityIdUnsafe(entity) {
16
17
  let id = entity.id;
17
18
  if (id !== undefined) {
18
- return id;
19
+ return EntityId.toKey(Primitive_option.valFromOption(id));
19
20
  } else {
20
21
  return ErrorHandling.mkLogAndRaise(undefined, "Property 'id' does not exist on expected entity object", {
21
22
  RE_EXN_ID: UnexpectedIdNotDefinedOnEntity
@@ -39,7 +40,7 @@ function make() {
39
40
  changesCount: 0,
40
41
  prevEntityChanges: [],
41
42
  filtersByEntityId: {},
42
- filterIndices: {}
43
+ filterIndexes: {}
43
44
  };
44
45
  }
45
46
 
@@ -83,10 +84,10 @@ function dropCommittedChanges(self, committedCheckpointId, keepLoadedFromDb) {
83
84
  keysToDelete.forEach(key => Utils.Dict.deleteInPlace(self.latestEntityChangeById, key));
84
85
  self.changesCount = self.changesCount - keysToDelete.length;
85
86
  self.filtersByEntityId = {};
86
- self.filterIndices = {};
87
+ self.filterIndexes = {};
87
88
  }
88
89
 
89
- function updateIndices(self, entity) {
90
+ function updateIndexes(self, entity) {
90
91
  let entityId = getEntityIdUnsafe(entity);
91
92
  let entityFilters = self.filtersByEntityId[entityId];
92
93
  if (entityFilters !== undefined) {
@@ -98,7 +99,7 @@ function updateIndices(self, entity) {
98
99
  }
99
100
  });
100
101
  }
101
- Utils.Dict.forEach(self.filterIndices, param => {
102
+ Utils.Dict.forEach(self.filterIndexes, param => {
102
103
  let relatedEntityIds = param[1];
103
104
  let filter = param[0];
104
105
  if (EntityFilter.matches(filter, entity)) {
@@ -110,14 +111,14 @@ function updateIndices(self, entity) {
110
111
  });
111
112
  }
112
113
 
113
- function deleteEntityFromIndices(self, entityId) {
114
+ function deleteEntityFromIndexes(self, entityId) {
114
115
  let entityFilters = self.filtersByEntityId[entityId];
115
116
  if (entityFilters === undefined) {
116
117
  return;
117
118
  }
118
119
  let entityFilters$1 = Primitive_option.valFromOption(entityFilters);
119
120
  entityFilters$1.forEach(filter => {
120
- let match = self.filterIndices[EntityFilter.toString(filter)];
121
+ let match = self.filterIndexes[EntityFilter.toString(filter)];
121
122
  if (match !== undefined) {
122
123
  match[1].delete(entityId);
123
124
  }
@@ -126,8 +127,8 @@ function deleteEntityFromIndices(self, entityId) {
126
127
  }
127
128
 
128
129
  function set(inMemTable, committedCheckpointId, change) {
129
- let entityId = change.entityId;
130
- let prev = inMemTable.latestEntityChangeById[entityId];
130
+ let entityKey = EntityId.toKey(change.entityId);
131
+ let prev = inMemTable.latestEntityChangeById[entityKey];
131
132
  if (prev !== undefined) {
132
133
  let prevCheckpointId = prev.checkpointId;
133
134
  if (prevCheckpointId > committedCheckpointId && prevCheckpointId < change.checkpointId) {
@@ -138,11 +139,11 @@ function set(inMemTable, committedCheckpointId, change) {
138
139
  inMemTable.changesCount = inMemTable.changesCount + 1;
139
140
  }
140
141
  if (change.type === "SET") {
141
- updateIndices(inMemTable, change.entity);
142
+ updateIndexes(inMemTable, change.entity);
142
143
  } else {
143
- deleteEntityFromIndices(inMemTable, change.entityId);
144
+ deleteEntityFromIndexes(inMemTable, EntityId.toKey(change.entityId));
144
145
  }
145
- inMemTable.latestEntityChangeById[entityId] = change;
146
+ inMemTable.latestEntityChangeById[entityKey] = change;
146
147
  }
147
148
 
148
149
  function initValue(inMemTable, committedCheckpointId, key, entity) {
@@ -173,13 +174,13 @@ function getUnsafe(inMemTable) {
173
174
  }
174
175
 
175
176
  function hasIndex(inMemTable) {
176
- return filterKey => inMemTable.filterIndices[filterKey] !== undefined;
177
+ return filterKey => inMemTable.filterIndexes[filterKey] !== undefined;
177
178
  }
178
179
 
179
180
  function getUnsafeOnIndex(inMemTable) {
180
181
  let getEntity = getUnsafe(inMemTable);
181
182
  return filterKey => {
182
- let match = inMemTable.filterIndices[filterKey];
183
+ let match = inMemTable.filterIndexes[filterKey];
183
184
  if (match !== undefined) {
184
185
  return Stdlib_Array.filterMap(Array.from(match[1]), entityId => {
185
186
  if (entityId in inMemTable.latestEntityChangeById) {
@@ -194,7 +195,7 @@ function getUnsafeOnIndex(inMemTable) {
194
195
 
195
196
  function addEmptyIndex(inMemTable, filter) {
196
197
  let filterKey = EntityFilter.toString(filter);
197
- let match = inMemTable.filterIndices[filterKey];
198
+ let match = inMemTable.filterIndexes[filterKey];
198
199
  if (match !== undefined) {
199
200
  return;
200
201
  }
@@ -211,7 +212,7 @@ function addEmptyIndex(inMemTable, filter) {
211
212
  getOrCreateEntityFilters(inMemTable, entityId).add(filter);
212
213
  relatedEntityIds.add(entityId);
213
214
  });
214
- inMemTable.filterIndices[filterKey] = [
215
+ inMemTable.filterIndexes[filterKey] = [
215
216
  filter,
216
217
  relatedEntityIds
217
218
  ];
@@ -224,8 +225,8 @@ let Entity = {
224
225
  make: make,
225
226
  snapshotChanges: snapshotChanges,
226
227
  dropCommittedChanges: dropCommittedChanges,
227
- updateIndices: updateIndices,
228
- deleteEntityFromIndices: deleteEntityFromIndices,
228
+ updateIndexes: updateIndexes,
229
+ deleteEntityFromIndexes: deleteEntityFromIndexes,
229
230
  set: set,
230
231
  initValue: initValue,
231
232
  mapChangeToEntity: mapChangeToEntity,