envio 3.3.2 → 3.4.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 (60) hide show
  1. package/package.json +6 -6
  2. package/src/Api.res +1 -8
  3. package/src/Api.res.mjs +1 -5
  4. package/src/ChainState.res +6 -1
  5. package/src/ChainState.res.mjs +4 -3
  6. package/src/Config.res +33 -4
  7. package/src/Config.res.mjs +34 -5
  8. package/src/Core.res +30 -19
  9. package/src/Core.res.mjs +5 -5
  10. package/src/EnvioGlobal.res +0 -2
  11. package/src/EnvioGlobal.res.mjs +0 -1
  12. package/src/ExitOnCaughtUp.res +6 -2
  13. package/src/ExitOnCaughtUp.res.mjs +4 -0
  14. package/src/FetchState.res +3 -3
  15. package/src/HandlerRegister.res +357 -434
  16. package/src/HandlerRegister.res.mjs +202 -242
  17. package/src/HandlerRegister.resi +7 -15
  18. package/src/IndexerState.res +10 -0
  19. package/src/IndexerState.res.mjs +9 -3
  20. package/src/IndexerState.resi +3 -0
  21. package/src/Internal.res +10 -1
  22. package/src/Main.res.mjs +4 -4
  23. package/src/PgStorage.res +16 -1
  24. package/src/PgStorage.res.mjs +9 -1
  25. package/src/SimulateItems.res +61 -40
  26. package/src/SimulateItems.res.mjs +37 -21
  27. package/src/TestIndexer.res +446 -453
  28. package/src/TestIndexer.res.mjs +320 -343
  29. package/src/bindings/ClickHouse.res +68 -2
  30. package/src/bindings/ClickHouse.res.mjs +47 -2
  31. package/src/sources/EvmChain.res +1 -1
  32. package/src/sources/EvmChain.res.mjs +2 -2
  33. package/src/sources/FuelHyperSync.res +74 -0
  34. package/src/sources/FuelHyperSync.res.mjs +80 -0
  35. package/src/sources/FuelHyperSync.resi +22 -0
  36. package/src/sources/FuelHyperSyncClient.res +120 -0
  37. package/src/sources/FuelHyperSyncClient.res.mjs +71 -0
  38. package/src/sources/FuelHyperSyncSource.res +257 -0
  39. package/src/sources/FuelHyperSyncSource.res.mjs +252 -0
  40. package/src/sources/HyperSyncClient.res +2 -2
  41. package/src/sources/HyperSyncClient.res.mjs +1 -1
  42. package/src/sources/SvmHyperSyncClient.res +139 -102
  43. package/src/sources/SvmHyperSyncClient.res.mjs +45 -5
  44. package/src/sources/SvmHyperSyncSource.res +60 -440
  45. package/src/sources/SvmHyperSyncSource.res.mjs +42 -363
  46. package/src/TestIndexerProxyStorage.res +0 -196
  47. package/src/TestIndexerProxyStorage.res.mjs +0 -121
  48. package/src/TestIndexerWorker.res +0 -4
  49. package/src/TestIndexerWorker.res.mjs +0 -7
  50. package/src/sources/EventRouter.res +0 -165
  51. package/src/sources/EventRouter.res.mjs +0 -153
  52. package/src/sources/HyperFuel.res +0 -179
  53. package/src/sources/HyperFuel.res.mjs +0 -146
  54. package/src/sources/HyperFuel.resi +0 -36
  55. package/src/sources/HyperFuelClient.res +0 -127
  56. package/src/sources/HyperFuelClient.res.mjs +0 -20
  57. package/src/sources/HyperFuelSource.res +0 -502
  58. package/src/sources/HyperFuelSource.res.mjs +0 -481
  59. /package/src/sources/{HyperSyncSource.res → EvmHyperSyncSource.res} +0 -0
  60. /package/src/sources/{HyperSyncSource.res.mjs → EvmHyperSyncSource.res.mjs} +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "envio",
3
- "version": "3.3.2",
3
+ "version": "3.4.0",
4
4
  "type": "module",
5
5
  "description": "A latency and sync speed optimized, developer friendly blockchain data indexer.",
6
6
  "bin": "./bin.mjs",
@@ -69,10 +69,10 @@
69
69
  "tsx": "4.21.0"
70
70
  },
71
71
  "optionalDependencies": {
72
- "envio-linux-x64": "3.3.2",
73
- "envio-linux-x64-musl": "3.3.2",
74
- "envio-linux-arm64": "3.3.2",
75
- "envio-darwin-x64": "3.3.2",
76
- "envio-darwin-arm64": "3.3.2"
72
+ "envio-linux-x64": "3.4.0",
73
+ "envio-linux-x64-musl": "3.4.0",
74
+ "envio-linux-arm64": "3.4.0",
75
+ "envio-darwin-x64": "3.4.0",
76
+ "envio-darwin-arm64": "3.4.0"
77
77
  }
78
78
  }
package/src/Api.res CHANGED
@@ -4,12 +4,5 @@
4
4
  let indexer: unknown = Main.getGlobalIndexer()
5
5
 
6
6
  let createTestIndexer: unit => unknown = () => {
7
- let workerPath =
8
- NodeJs.Path.join(
9
- NodeJs.Path.getDirname(NodeJs.ImportMeta.importMeta),
10
- "TestIndexerWorker.res.mjs",
11
- )->NodeJs.Path.toString
12
- TestIndexer.makeCreateTestIndexer(~config=Config.load(), ~workerPath)()->(
13
- Utils.magic: TestIndexer.t<'a> => unknown
14
- )
7
+ TestIndexer.createTestIndexer()->(Utils.magic: TestIndexer.t<'a> => unknown)
15
8
  }
package/src/Api.res.mjs CHANGED
@@ -1,16 +1,12 @@
1
1
  // Generated by ReScript, PLEASE EDIT WITH CARE
2
2
 
3
3
  import * as Main from "./Main.res.mjs";
4
- import * as Path from "path";
5
- import * as Config from "./Config.res.mjs";
6
- import * as NodeJs from "./bindings/NodeJs.res.mjs";
7
4
  import * as TestIndexer from "./TestIndexer.res.mjs";
8
5
 
9
6
  let indexer = Main.getGlobalIndexer();
10
7
 
11
8
  function createTestIndexer() {
12
- let workerPath = Path.join(NodeJs.Path.getDirname(import.meta), "TestIndexerWorker.res.mjs");
13
- return TestIndexer.makeCreateTestIndexer(Config.load(), workerPath)();
9
+ return TestIndexer.createTestIndexer();
14
10
  }
15
11
 
16
12
  export {
@@ -224,7 +224,12 @@ let makeInternal = (
224
224
  ~lowercaseAddresses,
225
225
  )
226
226
  | Config.FuelSourceConfig({hypersync}) => [
227
- HyperFuelSource.make({chain, endpointUrl: hypersync, apiToken: Env.envioApiToken}),
227
+ FuelHyperSyncSource.make({
228
+ chain,
229
+ endpointUrl: hypersync,
230
+ apiToken: Env.envioApiToken,
231
+ onEventRegistrations,
232
+ }),
228
233
  ]
229
234
  | Config.SvmSourceConfig({hypersync, rpc}) =>
230
235
  switch (hypersync, rpc) {
@@ -17,12 +17,12 @@ import * as SourceManager from "./sources/SourceManager.res.mjs";
17
17
  import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
18
18
  import * as ReorgDetection from "./ReorgDetection.res.mjs";
19
19
  import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
20
- import * as HyperFuelSource from "./sources/HyperFuelSource.res.mjs";
21
20
  import * as Primitive_float from "@rescript/runtime/lib/es6/Primitive_float.js";
22
21
  import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
23
22
  import * as TransactionStore from "./sources/TransactionStore.res.mjs";
24
23
  import * as IndexingAddresses from "./IndexingAddresses.res.mjs";
25
24
  import * as SvmHyperSyncSource from "./sources/SvmHyperSyncSource.res.mjs";
25
+ import * as FuelHyperSyncSource from "./sources/FuelHyperSyncSource.res.mjs";
26
26
  import * as SafeCheckpointTracking from "./SafeCheckpointTracking.res.mjs";
27
27
 
28
28
  function configAddresses(chainConfig) {
@@ -132,10 +132,11 @@ function makeInternal(chainConfig, indexingAddresses, startBlock, endBlock, firs
132
132
  sources$1 = EvmChain.makeSources(chain, onEventRegistrations, sources.hypersync, evmRpcs, lowercaseAddresses);
133
133
  break;
134
134
  case "FuelSourceConfig" :
135
- sources$1 = [HyperFuelSource.make({
135
+ sources$1 = [FuelHyperSyncSource.make({
136
136
  chain: chain,
137
137
  endpointUrl: sources.hypersync,
138
- apiToken: Env.envioApiToken
138
+ apiToken: Env.envioApiToken,
139
+ onEventRegistrations: onEventRegistrations
139
140
  })];
140
141
  break;
141
142
  case "SvmSourceConfig" :
package/src/Config.res CHANGED
@@ -314,10 +314,27 @@ let propertySchema = S.schema(s =>
314
314
  }
315
315
  )
316
316
 
317
+ let clickhouseTableOptionsSchema: S.t<Internal.clickhouseTableOptions> = S.object(s => {
318
+ Internal.partitionBy: ?s.field("partitionBy", S.option(S.string)),
319
+ orderBy: ?s.field("orderBy", S.option(S.array(S.string))),
320
+ ttl: ?s.field("ttl", S.option(S.string)),
321
+ })
322
+
323
+ // The entity's `clickhouse` storage arg mirrors the @storage directive:
324
+ // either a boolean or a table options object (which implies enabled).
325
+ type entityClickhouseStorageJson =
326
+ | Enabled(bool)
327
+ | TableOptions(Internal.clickhouseTableOptions)
328
+
329
+ let entityClickhouseStorageJsonSchema = S.union([
330
+ S.bool->S.shape(enabled => Enabled(enabled)),
331
+ clickhouseTableOptionsSchema->S.shape(options => TableOptions(options)),
332
+ ])
333
+
317
334
  let entityStorageSchema = S.schema(s =>
318
335
  {
319
336
  "postgres": s.matches(S.option(S.bool)),
320
- "clickhouse": s.matches(S.option(S.bool)),
337
+ "clickhouse": s.matches(S.option(entityClickhouseStorageJsonSchema)),
321
338
  }
322
339
  )
323
340
 
@@ -483,9 +500,21 @@ let parseEntitiesFromJson = (
483
500
  // config didn't enable, and that at least one backend stays true
484
501
  // for an annotated entity — so `getOr(false)` is safe here.
485
502
  let storage: Internal.entityStorage = switch entityJson["storage"] {
486
- | Some(s) => {
487
- postgres: s["postgres"]->Option.getOr(false),
488
- clickhouse: s["clickhouse"]->Option.getOr(false),
503
+ | Some(s) =>
504
+ switch s["clickhouse"] {
505
+ | Some(TableOptions(options)) => {
506
+ postgres: s["postgres"]->Option.getOr(false),
507
+ clickhouse: true,
508
+ clickhouseOptions: options,
509
+ }
510
+ | Some(Enabled(clickhouse)) => {
511
+ postgres: s["postgres"]->Option.getOr(false),
512
+ clickhouse,
513
+ }
514
+ | None => {
515
+ postgres: s["postgres"]->Option.getOr(false),
516
+ clickhouse: false,
517
+ }
489
518
  }
490
519
  | None => {
491
520
  postgres: globalStorage.postgres,
@@ -192,9 +192,26 @@ let propertySchema = S$RescriptSchema.schema(s => ({
192
192
  description: s.m(S$RescriptSchema.option(S$RescriptSchema.string))
193
193
  }));
194
194
 
195
+ let clickhouseTableOptionsSchema = S$RescriptSchema.object(s => ({
196
+ partitionBy: s.f("partitionBy", S$RescriptSchema.option(S$RescriptSchema.string)),
197
+ orderBy: s.f("orderBy", S$RescriptSchema.option(S$RescriptSchema.array(S$RescriptSchema.string))),
198
+ ttl: s.f("ttl", S$RescriptSchema.option(S$RescriptSchema.string))
199
+ }));
200
+
201
+ let entityClickhouseStorageJsonSchema = S$RescriptSchema.union([
202
+ S$RescriptSchema.shape(S$RescriptSchema.bool, enabled => ({
203
+ TAG: "Enabled",
204
+ _0: enabled
205
+ })),
206
+ S$RescriptSchema.shape(clickhouseTableOptionsSchema, options => ({
207
+ TAG: "TableOptions",
208
+ _0: options
209
+ }))
210
+ ]);
211
+
195
212
  let entityStorageSchema = S$RescriptSchema.schema(s => ({
196
213
  postgres: s.m(S$RescriptSchema.option(S$RescriptSchema.bool)),
197
- clickhouse: s.m(S$RescriptSchema.option(S$RescriptSchema.bool))
214
+ clickhouse: s.m(S$RescriptSchema.option(entityClickhouseStorageJsonSchema))
198
215
  }));
199
216
 
200
217
  let entityJsonSchema = S$RescriptSchema.schema(s => ({
@@ -349,10 +366,20 @@ function parseEntitiesFromJson(entitiesJson, enumConfigsByName, globalStorage) {
349
366
  let storage;
350
367
  if (s !== undefined) {
351
368
  let s$1 = Primitive_option.valFromOption(s);
352
- storage = {
353
- postgres: Stdlib_Option.getOr(s$1.postgres, false),
354
- clickhouse: Stdlib_Option.getOr(s$1.clickhouse, false)
355
- };
369
+ let match = s$1.clickhouse;
370
+ storage = match !== undefined ? (
371
+ match.TAG === "Enabled" ? ({
372
+ postgres: Stdlib_Option.getOr(s$1.postgres, false),
373
+ clickhouse: match._0
374
+ }) : ({
375
+ postgres: Stdlib_Option.getOr(s$1.postgres, false),
376
+ clickhouse: true,
377
+ clickhouseOptions: match._0
378
+ })
379
+ ) : ({
380
+ postgres: Stdlib_Option.getOr(s$1.postgres, false),
381
+ clickhouse: false
382
+ });
356
383
  } else {
357
384
  storage = {
358
385
  postgres: globalStorage.postgres,
@@ -1042,6 +1069,8 @@ export {
1042
1069
  compositeIndexFieldSchema,
1043
1070
  derivedFieldSchema,
1044
1071
  propertySchema,
1072
+ clickhouseTableOptionsSchema,
1073
+ entityClickhouseStorageJsonSchema,
1045
1074
  entityStorageSchema,
1046
1075
  entityJsonSchema,
1047
1076
  getFieldTypeAndSchema,
package/src/Core.res CHANGED
@@ -4,32 +4,37 @@
4
4
 
5
5
  // NAPI encodes Rust `Option<T>` as `null | T` (never `undefined`), so the
6
6
  // tighter `Null.t` captures the exact boundary shape.
7
- type evmHypersyncClientCtor
7
+ type evmHyperSyncClientCtor
8
8
  type evmRpcClientCtor
9
- type svmHypersyncClientCtor
10
- type hyperfuelClientCtor
9
+ type svmHyperSyncClientCtor
10
+ type fuelHyperSyncClientCtor
11
11
  type transactionStoreCtor
12
12
  type blockStoreCtor
13
- type parseConfigYamlOptions = {
13
+ type fromUserApiOptions = {
14
14
  schema?: string,
15
15
  env?: dict<string>,
16
16
  files?: dict<string>,
17
- isRescript?: bool,
17
+ withIndexerTypes?: bool,
18
+ }
19
+
20
+ type fromUserApiResult = {
21
+ config: string,
22
+ indexerTypes: Null.t<string>,
18
23
  }
19
24
 
20
25
  type addon = {
21
26
  getConfigJson: (~configPath: Null.t<string>, ~directory: Null.t<string>) => string,
22
27
  encodeIndexedTopic: (~abiType: string, ~value: unknown) => EvmTypes.Hex.t,
23
- parseConfigYaml: (string, parseConfigYamlOptions) => string,
28
+ fromUserApi: (string, fromUserApiOptions) => fromUserApiResult,
24
29
  runCli: (~args: array<string>, ~envioPackageDir: Null.t<string>) => promise<Null.t<string>>,
25
- @as("EvmHypersyncClient")
26
- evmHypersyncClient: evmHypersyncClientCtor,
30
+ @as("EvmHyperSyncClient")
31
+ evmHyperSyncClient: evmHyperSyncClientCtor,
27
32
  @as("EvmRpcClient")
28
33
  evmRpcClient: evmRpcClientCtor,
29
- @as("SvmHypersyncClient")
30
- svmHypersyncClient: svmHypersyncClientCtor,
31
- @as("HyperfuelClient")
32
- hyperfuelClient: hyperfuelClientCtor,
34
+ @as("SvmHyperSyncClient")
35
+ svmHyperSyncClient: svmHyperSyncClientCtor,
36
+ @as("FuelHyperSyncClient")
37
+ fuelHyperSyncClient: fuelHyperSyncClientCtor,
33
38
  @as("TransactionStore")
34
39
  transactionStore: transactionStoreCtor,
35
40
  @as("BlockStore")
@@ -207,14 +212,20 @@ let getConfigJson = (~configPath=?, ~directory=?) => {
207
212
 
208
213
  // Pure config entry point: no cwd, config file, schema file, .env, or process env lookup.
209
214
  // A supplied schema is authoritative; omitted or blank schema text means an empty schema.
210
- let parseConfigYaml = (~schema=?, ~env=?, ~files=?, ~isRescript=false, yaml) => {
215
+ // With `withIndexerTypes`, the single parse also returns the generated
216
+ // `.envio/types.d.ts`, so callers can type-check handlers against the config's
217
+ // `indexer` surface without re-parsing.
218
+ let fromUserApi = (~schema=?, ~env=?, ~files=?, ~withIndexerTypes=false, yaml) => {
211
219
  let addon = getAddon()
212
- addon.parseConfigYaml(yaml, {
213
- ?schema,
214
- ?env,
215
- ?files,
216
- isRescript,
217
- })
220
+ addon.fromUserApi(
221
+ yaml,
222
+ {
223
+ ?schema,
224
+ ?env,
225
+ ?files,
226
+ withIndexerTypes,
227
+ },
228
+ )
218
229
  }
219
230
 
220
231
  let runCli = args => {
package/src/Core.res.mjs CHANGED
@@ -180,14 +180,14 @@ function getConfigJson(configPath, directory) {
180
180
  return addon.getConfigJson(Stdlib_Null.fromOption(configPath), Stdlib_Null.fromOption(directory));
181
181
  }
182
182
 
183
- function parseConfigYaml(schema, env, files, isRescriptOpt, yaml) {
184
- let isRescript = isRescriptOpt !== undefined ? isRescriptOpt : false;
183
+ function fromUserApi(schema, env, files, withIndexerTypesOpt, yaml) {
184
+ let withIndexerTypes = withIndexerTypesOpt !== undefined ? withIndexerTypesOpt : false;
185
185
  let addon = getAddon();
186
- return addon.parseConfigYaml(yaml, {
186
+ return addon.fromUserApi(yaml, {
187
187
  schema: schema,
188
188
  env: env,
189
189
  files: files,
190
- isRescript: isRescript
190
+ withIndexerTypes: withIndexerTypes
191
191
  });
192
192
  }
193
193
 
@@ -207,7 +207,7 @@ export {
207
207
  addonRef,
208
208
  getAddon,
209
209
  getConfigJson,
210
- parseConfigYaml,
210
+ fromUserApi,
211
211
  runCli,
212
212
  }
213
213
  /* envioPackageDir Not a pure module */
@@ -15,7 +15,6 @@
15
15
  // deduplication hint instead of silently mixing shapes across builds.
16
16
  type t = {
17
17
  version: string,
18
- eventRegistrations: dict<unknown>,
19
18
  mutable activeRegistration: option<unknown>,
20
19
  preRegistered: array<unknown>,
21
20
  rollbackCommitCallbacks: array<unknown>,
@@ -40,7 +39,6 @@ let value: t = {
40
39
  | None =>
41
40
  let fresh = {
42
41
  version,
43
- eventRegistrations: Dict.make(),
44
42
  activeRegistration: None,
45
43
  preRegistered: [],
46
44
  rollbackCommitCallbacks: [],
@@ -12,7 +12,6 @@ let value;
12
12
  if (existing == null) {
13
13
  let fresh = {
14
14
  version: version,
15
- eventRegistrations: {},
16
15
  activeRegistration: undefined,
17
16
  preRegistered: [],
18
17
  rollbackCommitCallbacks: [],
@@ -10,8 +10,12 @@ let run = async (state: IndexerState.t) => {
10
10
  ->IndexerState.simulateDeadInputTracker
11
11
  ->Option.flatMap(SimulateDeadInputTracker.failureMessage) {
12
12
  | None =>
13
- Logging.info("Exiting with success")
14
- NodeJs.process->NodeJs.exitWithCode(Success)
13
+ switch state->IndexerState.onExit {
14
+ | Some(onExit) => onExit()
15
+ | None =>
16
+ Logging.info("Exiting with success")
17
+ NodeJs.process->NodeJs.exitWithCode(Success)
18
+ }
15
19
  | Some(message) => state->IndexerState.errorExit(ErrorHandling.make(Utils.Error.make(message)))
16
20
  }
17
21
  }
@@ -18,6 +18,10 @@ async function run(state) {
18
18
  let message = Stdlib_Option.flatMap(IndexerState.simulateDeadInputTracker(state), SimulateDeadInputTracker.failureMessage);
19
19
  if (message !== undefined) {
20
20
  return IndexerState.errorExit(state, ErrorHandling.make(new Error(message), undefined, undefined));
21
+ }
22
+ let onExit = IndexerState.onExit(state);
23
+ if (onExit !== undefined) {
24
+ return onExit();
21
25
  } else {
22
26
  Logging.info("Exiting with success");
23
27
  Process.exit(0);
@@ -1006,8 +1006,8 @@ OptimizedPartitions.t => {
1006
1006
 
1007
1007
  // Addresses with different start blocks within range share a partition;
1008
1008
  // events before each address's effectiveStartBlock are dropped on the
1009
- // client side (EventRouter for the srcAddress, the event's
1010
- // clientAddressFilter for address-valued params).
1009
+ // client side (the event's clientAddressFilter for address-valued
1010
+ // params).
1011
1011
  if shouldJoinCurrentStartBlock {
1012
1012
  addressesRef :=
1013
1013
  addressesRef.contents->Array.concat(byStartBlock->Dict.getUnsafe(nextStartBlockKey))
@@ -1411,7 +1411,7 @@ let registerDynamicContracts = (
1411
1411
  // Drop events an address-param filter rejects. A merged partition may
1412
1412
  // over-fetch a wildcard event whose indexed address param references an
1413
1413
  // address registered after the log's block; `clientAddressFilter` is the
1414
- // param-level analogue of EventRouter's srcAddress effectiveStartBlock check.
1414
+ // param-level analogue of the srcAddress effectiveStartBlock check.
1415
1415
  let filterByClientAddress = (
1416
1416
  items: array<Internal.item>,
1417
1417
  ~indexingAddresses: IndexingAddresses.t,