envio 3.13.0 → 3.14.0-subgraph

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 (111) hide show
  1. package/bin.mjs +10 -1
  2. package/index.d.ts +11 -0
  3. package/package.json +8 -20
  4. package/rescript.json +1 -4
  5. package/src/Config.res +78 -100
  6. package/src/Config.res.mjs +55 -64
  7. package/src/Core.res +67 -6
  8. package/src/Core.res.mjs +58 -4
  9. package/src/EventConfigBuilder.res +4 -10
  10. package/src/EventConfigBuilder.res.mjs +1 -3
  11. package/src/EventProcessing.res +6 -6
  12. package/src/EventProcessing.res.mjs +8 -6
  13. package/src/ExitOnCaughtUp.res +5 -1
  14. package/src/ExitOnCaughtUp.res.mjs +8 -3
  15. package/src/HandlerLoader.res +98 -36
  16. package/src/HandlerLoader.res.mjs +89 -24
  17. package/src/HandlerTypeCheck.mjs +14 -0
  18. package/src/HandlerTypeCheckWorker.mjs +68 -0
  19. package/src/Internal.res +1 -1
  20. package/src/LoadLayer.res +39 -7
  21. package/src/LoadLayer.res.mjs +65 -38
  22. package/src/LoadLayer.resi +28 -0
  23. package/src/Main.res +17 -12
  24. package/src/Main.res.mjs +9 -3
  25. package/src/NodeVersion.mjs +13 -0
  26. package/src/Persistence.res +79 -38
  27. package/src/Persistence.res.mjs +41 -10
  28. package/src/PgStorage.res +86 -39
  29. package/src/PgStorage.res.mjs +83 -32
  30. package/src/ResumePlan.res +172 -0
  31. package/src/ResumePlan.res.mjs +122 -0
  32. package/src/Supervisor.res +8 -8
  33. package/src/Supervisor.res.mjs +6 -1
  34. package/src/SyncWrites.res +86 -0
  35. package/src/SyncWrites.res.mjs +85 -0
  36. package/src/TestIndexer.res +14 -4
  37. package/src/TestIndexer.res.mjs +3 -2
  38. package/src/TsModuleHooks.mjs +121 -0
  39. package/src/UserContext.res +377 -62
  40. package/src/UserContext.res.mjs +297 -39
  41. package/src/Utils.res +0 -8
  42. package/src/Worker.res +8 -0
  43. package/src/Worker.res.mjs +11 -0
  44. package/src/bindings/NodeJs.res +8 -7
  45. package/src/bindings/NodeJs.res.mjs +0 -3
  46. package/src/bindings/Pino.res +2 -2
  47. package/src/db/InternalTable.res +52 -2
  48. package/src/db/InternalTable.res.mjs +32 -3
  49. package/src/db/Table.res +10 -0
  50. package/src/db/Table.res.mjs +22 -11
  51. package/src/sources/FuelHyperSync.res +2 -0
  52. package/src/sources/FuelHyperSync.res.mjs +1 -0
  53. package/src/sources/FuelHyperSync.resi +1 -0
  54. package/src/sources/FuelHyperSyncClient.res +25 -8
  55. package/src/sources/FuelHyperSyncClient.res.mjs +8 -2
  56. package/src/sources/FuelHyperSyncSource.res +24 -22
  57. package/src/sources/FuelHyperSyncSource.res.mjs +18 -13
  58. package/src/subgraph/abi-types.ts +63 -0
  59. package/src/subgraph/assemblyscript.ts +85 -0
  60. package/src/subgraph/blocks.ts +183 -0
  61. package/src/subgraph/calls.ts +149 -0
  62. package/src/subgraph/conformance.ts +83 -0
  63. package/src/subgraph/errors.ts +96 -0
  64. package/src/subgraph/graph-cli-worker.mjs +26 -0
  65. package/src/subgraph/graph-cli.ts +190 -0
  66. package/src/subgraph/graph-ts-enums.ts +28 -0
  67. package/src/subgraph/graph-ts-types/VERSION +2 -0
  68. package/src/subgraph/graph-ts-types/chain/arweave.d.ts +70 -0
  69. package/src/subgraph/graph-ts-types/chain/cosmos.d.ts +327 -0
  70. package/src/subgraph/graph-ts-types/chain/ethereum.d.ts +233 -0
  71. package/src/subgraph/graph-ts-types/chain/near.d.ts +253 -0
  72. package/src/subgraph/graph-ts-types/chain/starknet.d.ts +32 -0
  73. package/src/subgraph/graph-ts-types/common/collections.d.ts +136 -0
  74. package/src/subgraph/graph-ts-types/common/conversion.d.ts +11 -0
  75. package/src/subgraph/graph-ts-types/common/datasource.d.ts +30 -0
  76. package/src/subgraph/graph-ts-types/common/eager-offset.d.ts +0 -0
  77. package/src/subgraph/graph-ts-types/common/json.d.ts +17 -0
  78. package/src/subgraph/graph-ts-types/common/numbers.d.ts +120 -0
  79. package/src/subgraph/graph-ts-types/common/value.d.ts +120 -0
  80. package/src/subgraph/graph-ts-types/common/yaml.d.ts +90 -0
  81. package/src/subgraph/graph-ts-types/global/global.d.ts +194 -0
  82. package/src/subgraph/graph-ts-types/helper-functions.d.ts +22 -0
  83. package/src/subgraph/graph-ts-types/index.d.ts +102 -0
  84. package/src/subgraph/graph-ts.ts +2285 -0
  85. package/src/subgraph/hosts.ts +158 -0
  86. package/src/subgraph/rpc.ts +65 -0
  87. package/src/subgraph/runtime.ts +595 -0
  88. package/src/subgraph/scope.ts +68 -0
  89. package/src/tui/InitApi.res +85 -0
  90. package/src/tui/{components/CustomHooks.res.mjs → InitApi.res.mjs} +11 -64
  91. package/src/tui/Tui.res +142 -249
  92. package/src/tui/Tui.res.mjs +123 -332
  93. package/src/Js.shim.ts +0 -11
  94. package/src/bindings/DateFns.res +0 -74
  95. package/src/bindings/DateFns.res.mjs +0 -22
  96. package/src/sources/FuelSDK.res +0 -38
  97. package/src/sources/FuelSDK.res.mjs +0 -27
  98. package/src/sources/vendored-fuel-abi-coder.js +0 -1792
  99. package/src/tui/bindings/Ink.res +0 -369
  100. package/src/tui/bindings/Ink.res.mjs +0 -66
  101. package/src/tui/bindings/Style.res +0 -123
  102. package/src/tui/bindings/Style.res.mjs +0 -2
  103. package/src/tui/components/BufferedProgressBar.res +0 -40
  104. package/src/tui/components/BufferedProgressBar.res.mjs +0 -57
  105. package/src/tui/components/CustomHooks.res +0 -122
  106. package/src/tui/components/Messages.res +0 -40
  107. package/src/tui/components/Messages.res.mjs +0 -71
  108. package/src/tui/components/SyncETA.res +0 -179
  109. package/src/tui/components/SyncETA.res.mjs +0 -249
  110. package/src/tui/components/TuiData.res +0 -98
  111. package/src/tui/components/TuiData.res.mjs +0 -73
package/src/Core.res CHANGED
@@ -12,6 +12,7 @@ type transactionStoreCtor
12
12
  type blockStoreCtor
13
13
  type clickHouseSinkCtor
14
14
  type addressStoreCtor
15
+ type tuiCtor
15
16
  // Test-only: a local HyperSync server, bound by MockHyperSyncServer in envio-tests.
16
17
  type mockHyperSyncServerCtor
17
18
  type fromUserApiOptions = {
@@ -27,12 +28,25 @@ type fromUserApiResult = {
27
28
  indexerCode: Null.t<string>,
28
29
  }
29
30
 
31
+ type fromSubgraphOptions = {
32
+ name?: string,
33
+ env?: dict<string>,
34
+ files?: dict<string>,
35
+ root?: string,
36
+ }
37
+
30
38
  type addon = {
31
39
  getConfigJson: (~configPath: Null.t<string>, ~directory: Null.t<string>) => string,
40
+ fromSubgraph: (string, string, fromSubgraphOptions) => fromUserApiResult,
41
+ subgraphFileToJavascript: (string, string) => string,
32
42
  encodeIndexedTopic: (~abiType: string, ~value: unknown) => EvmTypes.Hex.t,
33
43
  isSvmPubkey: (~value: string) => bool,
34
44
  fromUserApi: (string, fromUserApiOptions) => fromUserApiResult,
35
45
  runCli: (~args: array<string>, ~envioPackageDir: Null.t<string>) => promise<Null.t<string>>,
46
+ loadTs: string => string,
47
+ tsCheckHandlerFormat: string => unit,
48
+ tsResolveCandidates: (string, Null.t<string>) => array<string>,
49
+ tsNotFoundCandidates: (string, Null.t<string>, string) => array<string>,
36
50
  @as("EvmHyperSyncClient")
37
51
  evmHyperSyncClient: evmHyperSyncClientCtor,
38
52
  @as("EvmRpcClient")
@@ -49,6 +63,8 @@ type addon = {
49
63
  addressStore: addressStoreCtor,
50
64
  @as("ClickHouseSink")
51
65
  clickHouseSink: clickHouseSinkCtor,
66
+ @as("Tui")
67
+ tui: tuiCtor,
52
68
  @as("MockHyperSyncServer")
53
69
  mockHyperSyncServer: mockHyperSyncServerCtor,
54
70
  encodeAddresses: (~ecosystem: string, ~addresses: array<Address.t>) => array<NodeJs.Buffer.t>,
@@ -99,7 +115,7 @@ let callRequire: ({..}, string) => addon = %raw(`(req, id) => req(id)`)
99
115
  let envioPackageDir = pathDirname(pathDirname(fileURLToPath(importMetaUrl)))
100
116
 
101
117
  // Runs `cargo build` on every invocation (like `cargo run`).
102
- let loadDevAddon: ({..}, string) => addon = %raw(`function(req, envioDir) {
118
+ let loadDevAddon: ({..}, string) => Null.t<addon> = %raw(`function(req, envioDir) {
103
119
  var cp = Nodechild_process;
104
120
  var path = Nodepath;
105
121
  var fs = Nodefs;
@@ -177,6 +193,22 @@ let loadDevAddon: ({..}, string) => addon = %raw(`function(req, envioDir) {
177
193
  // `code`, and any other fields a diagnostic might rely on.
178
194
  let rethrow: JsExn.t => 'a = %raw(`function(e) { throw e }`)
179
195
 
196
+ type packageJson = {version: string}
197
+ let requirePackageJson: ({..}, string) => packageJson = %raw(`(req, p) => req(p)`)
198
+ let devVersion = "0.0.1-dev"
199
+
200
+ let isGlibc: unit => bool = %raw(`() => Boolean(process.report?.getReport().header.glibcVersionRuntime)`)
201
+
202
+ @val external npmUserAgent: option<string> = "process.env.npm_config_user_agent"
203
+
204
+ let addCommand = () =>
205
+ switch npmUserAgent {
206
+ | Some(ua) if ua->String.startsWith("pnpm/") => "pnpm add"
207
+ | Some(ua) if ua->String.startsWith("yarn/") => "yarn add"
208
+ | Some(ua) if ua->String.startsWith("bun/") => "bun add"
209
+ | _ => "npm install"
210
+ }
211
+
180
212
  let loadAddon = () => {
181
213
  let req = createRequire(importMetaUrl)
182
214
 
@@ -212,15 +244,32 @@ let loadAddon = () => {
212
244
  switch tryRequire(0) {
213
245
  | Some(addon) => addon
214
246
  | None =>
215
- // Dev build fallback (cargo build on every run)
216
- switch loadDevAddon(req, envioPackageDir)->(Utils.magic: addon => option<addon>) {
247
+ let version = requirePackageJson(req, "../package.json").version
248
+ // Publishing stamps the real version, so only a monorepo checkout (or a
249
+ // `file:` link to one) can have a dev build to fall back to. Skipping it
250
+ // spares published installs a `pnpm list` spawn before the error below.
251
+ let devAddon = if version === devVersion {
252
+ loadDevAddon(req, envioPackageDir)->Null.toOption
253
+ } else {
254
+ None
255
+ }
256
+ switch devAddon {
217
257
  | Some(addon) => addon
218
258
  | None =>
219
259
  let host = `${processPlatform}-${processArch}`
220
- let msg = if candidates->Array.length === 0 {
260
+ let msg = switch candidates {
261
+ | []
262
+ if processPlatform === "win32" => `envio doesn't run natively on Windows. Use WSL 2 instead: https://learn.microsoft.com/windows/wsl/install`
263
+ | [] =>
221
264
  `envio doesn't support ${host}. Supported: linux-x64 (glibc/musl), linux-arm64, darwin-x64, darwin-arm64.`
222
- } else {
223
- `Couldn't load the envio native addon for ${host}. Reinstall envio (ensure optional dependencies aren't skipped).`
265
+ | _ =>
266
+ let pkg = if processPlatform === "linux" && processArch === "x64" && !isGlibc() {
267
+ `envio-linux-x64-musl`
268
+ } else {
269
+ candidates->Array.getUnsafe(0)
270
+ }
271
+ `envio's native binary for ${host} isn't installed (package "${pkg}"). This happens when optional dependencies are skipped (--omit=optional, --no-optional) or the lockfile was generated on another platform.
272
+ Reinstall dependencies, or add the package explicitly: ${addCommand()} ${pkg}@${version}`
224
273
  }
225
274
  JsError.throwWithMessage(msg)
226
275
  }
@@ -265,6 +314,18 @@ let fromUserApi = (~schema=?, ~env=?, ~files=?, ~withIndexerTypes=false, yaml) =
265
314
  )
266
315
  }
267
316
 
317
+ /// Parses a subgraph project without touching the filesystem: subgraph.yaml and
318
+ /// its schema inline, ABI bodies through `files`.
319
+ let fromSubgraph = (~name=?, ~env=?, ~files=?, ~root=?, ~manifest, ~schema) => {
320
+ let addon = getAddon()
321
+ addon.fromSubgraph(manifest, schema, {?name, ?env, ?files, ?root})
322
+ }
323
+
324
+ let subgraphFileToJavascript = (~source, ~path) => {
325
+ let addon = getAddon()
326
+ addon.subgraphFileToJavascript(source, path)
327
+ }
328
+
268
329
  let runCli = args => {
269
330
  let addon = getAddon()
270
331
  addon.runCli(~args, ~envioPackageDir=Null.make(envioPackageDir))
package/src/Core.res.mjs CHANGED
@@ -6,6 +6,7 @@ import * as Nodepath from "node:path";
6
6
  import * as Stdlib_Null from "@rescript/runtime/lib/es6/Stdlib_Null.js";
7
7
  import * as Nodemodule from "node:module";
8
8
  import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
9
+ import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
9
10
  import * as Nodechild_process from "node:child_process";
10
11
  import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js";
11
12
 
@@ -97,6 +98,29 @@ let loadDevAddon = (function(req, envioDir) {
97
98
 
98
99
  let rethrow = (function(e) { throw e });
99
100
 
101
+ let requirePackageJson = ((req, p) => req(p));
102
+
103
+ let devVersion = "0.0.1-dev";
104
+
105
+ let isGlibc = (() => Boolean(process.report?.getReport().header.glibcVersionRuntime));
106
+
107
+ function addCommand() {
108
+ let ua = process.env.npm_config_user_agent;
109
+ if (ua !== undefined) {
110
+ if (ua.startsWith("pnpm/")) {
111
+ return "pnpm add";
112
+ } else if (ua.startsWith("yarn/")) {
113
+ return "yarn add";
114
+ } else if (ua.startsWith("bun/")) {
115
+ return "bun add";
116
+ } else {
117
+ return "npm install";
118
+ }
119
+ } else {
120
+ return "npm install";
121
+ }
122
+ }
123
+
100
124
  function loadAddon() {
101
125
  let req = Nodemodule.createRequire(import.meta.url);
102
126
  let match = process.platform;
@@ -161,12 +185,21 @@ function loadAddon() {
161
185
  if (addon !== undefined) {
162
186
  return addon;
163
187
  }
164
- let addon$1 = loadDevAddon(req, envioPackageDir);
165
- if (addon$1 !== undefined) {
166
- return addon$1;
188
+ let version = requirePackageJson(req, "../package.json").version;
189
+ let devAddon = version === devVersion ? Primitive_option.fromNull(loadDevAddon(req, envioPackageDir)) : undefined;
190
+ if (devAddon !== undefined) {
191
+ return devAddon;
167
192
  }
168
193
  let host = process.platform + `-` + process.arch;
169
- return Stdlib_JsError.throwWithMessage(candidates.length === 0 ? `envio doesn't support ` + host + `. Supported: linux-x64 (glibc/musl), linux-arm64, darwin-x64, darwin-arm64.` : `Couldn't load the envio native addon for ` + host + `. Reinstall envio (ensure optional dependencies aren't skipped).`);
194
+ let tmp;
195
+ if (candidates.length !== 0) {
196
+ let pkg = process.platform === "linux" && process.arch === "x64" && !isGlibc() ? `envio-linux-x64-musl` : candidates[0];
197
+ tmp = `envio's native binary for ` + host + ` isn't installed (package "` + pkg + `"). This happens when optional dependencies are skipped (--omit=optional, --no-optional) or the lockfile was generated on another platform.
198
+ Reinstall dependencies, or add the package explicitly: ` + addCommand() + ` ` + pkg + `@` + version;
199
+ } else {
200
+ tmp = process.platform === "win32" ? `envio doesn't run natively on Windows. Use WSL 2 instead: https://learn.microsoft.com/windows/wsl/install` : `envio doesn't support ` + host + `. Supported: linux-x64 (glibc/musl), linux-arm64, darwin-x64, darwin-arm64.`;
201
+ }
202
+ return Stdlib_JsError.throwWithMessage(tmp);
170
203
  }
171
204
 
172
205
  let addonRef = {
@@ -199,6 +232,21 @@ function fromUserApi(schema, env, files, withIndexerTypesOpt, yaml) {
199
232
  });
200
233
  }
201
234
 
235
+ function fromSubgraph(name, env, files, root, manifest, schema) {
236
+ let addon = getAddon();
237
+ return addon.fromSubgraph(manifest, schema, {
238
+ name: name,
239
+ env: env,
240
+ files: files,
241
+ root: root
242
+ });
243
+ }
244
+
245
+ function subgraphFileToJavascript(source, path) {
246
+ let addon = getAddon();
247
+ return addon.subgraphFileToJavascript(source, path);
248
+ }
249
+
202
250
  function runCli(args) {
203
251
  let addon = getAddon();
204
252
  return addon.runCli(args, envioPackageDir);
@@ -211,11 +259,17 @@ export {
211
259
  envioPackageDir,
212
260
  loadDevAddon,
213
261
  rethrow,
262
+ requirePackageJson,
263
+ devVersion,
264
+ isGlibc,
265
+ addCommand,
214
266
  loadAddon,
215
267
  addonRef,
216
268
  getAddon,
217
269
  getConfigJson,
218
270
  fromUserApi,
271
+ fromSubgraph,
272
+ subgraphFileToJavascript,
219
273
  runCli,
220
274
  }
221
275
  /* envioPackageDir Not a pure module */
@@ -343,7 +343,7 @@ let validateFieldsShapeOrThrow = (
343
343
  }
344
344
 
345
345
  let selectionList = (fields: unknown, key: string): option<array<string>> =>
346
- switch (fields->(Utils.magic: unknown => dict<unknown>))->Dict.get(key) {
346
+ switch fields->(Utils.magic: unknown => dict<unknown>)->Dict.get(key) {
347
347
  | None => None
348
348
  | Some(value) => Some(value->(Utils.magic: unknown => array<string>))
349
349
  }
@@ -371,8 +371,8 @@ let parseFieldsOrThrow = (
371
371
  if !(valid->Utils.Set.has(name)) {
372
372
  JsError.throwWithMessage(
373
373
  `Invalid "${name}" field in the fields.${kind} option of ${registration}. Valid ${kind} fields: ${Utils.Array.quotedJoin(
374
- valid->Utils.Set.toArray,
375
- )}.`,
374
+ valid->Utils.Set.toArray,
375
+ )}.`,
376
376
  )
377
377
  }
378
378
  if seen->Utils.Set.has(name) {
@@ -858,13 +858,7 @@ let buildFuelEventConfig = (
858
858
  ~rawAbi: JSON.t,
859
859
  ): Internal.fuelEventConfig => {
860
860
  let fuelKind = switch kind {
861
- | "logData" =>
862
- // Transpile raw Fuel ABI to the format expected by the vendored ABI coder
863
- let abi = FuelSDK.transpileAbi(rawAbi)
864
- Internal.LogData({
865
- logId: sighash,
866
- decode: FuelSDK.Receipt.getLogDataDecoder(~abi, ~logId=sighash),
867
- })
861
+ | "logData" => Internal.LogData({logId: sighash, abi: rawAbi})
868
862
  | "mint" => Mint
869
863
  | "burn" => Burn
870
864
  | "transfer" => Transfer
@@ -6,7 +6,6 @@ import * as Core from "./Core.res.mjs";
6
6
  import * as Fuel from "./sources/Fuel.res.mjs";
7
7
  import * as Utils from "./Utils.res.mjs";
8
8
  import * as Address from "./Address.res.mjs";
9
- import * as FuelSDK from "./sources/FuelSDK.res.mjs";
10
9
  import * as Internal from "./Internal.res.mjs";
11
10
  import * as LogSelection from "./LogSelection.res.mjs";
12
11
  import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
@@ -590,11 +589,10 @@ function buildFuelEventConfig(contractName, eventName, kind, sighash, rawAbi) {
590
589
  fuelKind = "Call";
591
590
  break;
592
591
  case "logData" :
593
- let abi = FuelSDK.transpileAbi(rawAbi);
594
592
  fuelKind = {
595
593
  TAG: "LogData",
596
594
  logId: sighash,
597
- decode: FuelSDK.Receipt.getLogDataDecoder(abi, sighash)
595
+ abi: rawAbi
598
596
  };
599
597
  break;
600
598
  case "mint" :
@@ -51,7 +51,7 @@ let runEventHandlerOrThrow = async (
51
51
  isPreload: false,
52
52
  chains,
53
53
  config,
54
- isResolved: false,
54
+ sync: UserContext.makeSyncState(),
55
55
  }
56
56
  await handler(
57
57
  (
@@ -61,7 +61,7 @@ let runEventHandlerOrThrow = async (
61
61
  }: Internal.handlerArgs
62
62
  ),
63
63
  )
64
- contextParams.isResolved = true
64
+ contextParams.sync.status = Resolved
65
65
  } catch {
66
66
  | exn =>
67
67
  throw(
@@ -102,7 +102,7 @@ let runHandlerOrThrow = async (
102
102
  isPreload: false,
103
103
  chains,
104
104
  config,
105
- isResolved: false,
105
+ sync: UserContext.makeSyncState(),
106
106
  }
107
107
  await handler(
108
108
  Ecosystem.makeOnBlockArgs(
@@ -111,7 +111,7 @@ let runHandlerOrThrow = async (
111
111
  ~context=UserContext.getHandlerContext(contextParams),
112
112
  ),
113
113
  )
114
- contextParams.isResolved = true
114
+ contextParams.sync.status = Resolved
115
115
  } catch {
116
116
  | exn =>
117
117
  throw(
@@ -186,7 +186,7 @@ let preloadBatchOrThrow = async (
186
186
  checkpointId,
187
187
  isPreload: true,
188
188
  chains,
189
- isResolved: false,
189
+ sync: UserContext.makeSyncState(),
190
190
  config,
191
191
  }),
192
192
  })
@@ -221,7 +221,7 @@ let preloadBatchOrThrow = async (
221
221
  checkpointId,
222
222
  isPreload: true,
223
223
  chains,
224
- isResolved: false,
224
+ sync: UserContext.makeSyncState(),
225
225
  config,
226
226
  }),
227
227
  )
@@ -38,6 +38,7 @@ let ProcessingError = /* @__PURE__ */Primitive_exceptions.create("EventProcessin
38
38
  async function runEventHandlerOrThrow(item, checkpointId, handler, indexerState, loadManager, persistence, chains, config) {
39
39
  let timeBeforeHandler = Performance.now();
40
40
  try {
41
+ let contextParams_sync = UserContext.makeSyncState();
41
42
  let contextParams = {
42
43
  item: item,
43
44
  checkpointId: checkpointId,
@@ -47,13 +48,13 @@ async function runEventHandlerOrThrow(item, checkpointId, handler, indexerState,
47
48
  isPreload: false,
48
49
  chains: chains,
49
50
  config: config,
50
- isResolved: false
51
+ sync: contextParams_sync
51
52
  };
52
53
  await handler({
53
54
  event: Ecosystem.getItemEvent(item, config.ecosystem),
54
55
  context: UserContext.getHandlerContext(contextParams)
55
56
  });
56
- contextParams.isResolved = true;
57
+ contextParams_sync.status = "Resolved";
57
58
  } catch (raw_exn) {
58
59
  let exn = Primitive_exceptions.internalToException(raw_exn);
59
60
  throw {
@@ -79,6 +80,7 @@ async function runHandlerOrThrow(item, checkpointId, indexerState, loadManager,
79
80
  }
80
81
  }
81
82
  try {
83
+ let contextParams_sync = UserContext.makeSyncState();
82
84
  let contextParams = {
83
85
  item: item,
84
86
  checkpointId: checkpointId,
@@ -88,10 +90,10 @@ async function runHandlerOrThrow(item, checkpointId, indexerState, loadManager,
88
90
  isPreload: false,
89
91
  chains: chains,
90
92
  config: config,
91
- isResolved: false
93
+ sync: contextParams_sync
92
94
  };
93
95
  await item.onBlockRegistration.handler(Ecosystem.makeOnBlockArgs(item.blockNumber, config.ecosystem, UserContext.getHandlerContext(contextParams)));
94
- contextParams.isResolved = true;
96
+ contextParams_sync.status = "Resolved";
95
97
  return;
96
98
  } catch (raw_exn) {
97
99
  let exn = Primitive_exceptions.internalToException(raw_exn);
@@ -133,7 +135,7 @@ async function preloadBatchOrThrow(batch, loadManager, persistence, config, inde
133
135
  isPreload: true,
134
136
  chains: chains,
135
137
  config: config,
136
- isResolved: false
138
+ sync: UserContext.makeSyncState()
137
139
  })
138
140
  }).then(() => IndexerState.endPreloadHandler(indexerState, timerRef, contractName, eventName))));
139
141
  } catch (exn) {
@@ -151,7 +153,7 @@ async function preloadBatchOrThrow(batch, loadManager, persistence, config, inde
151
153
  isPreload: true,
152
154
  chains: chains,
153
155
  config: config,
154
- isResolved: false
156
+ sync: UserContext.makeSyncState()
155
157
  })))));
156
158
  } catch (exn$1) {
157
159
 
@@ -14,7 +14,11 @@ let run = async (state: IndexerState.t) => {
14
14
  | Some(onExit) => onExit()
15
15
  | None =>
16
16
  Logging.info("Exiting with success")
17
- NodeJs.process->NodeJs.exitWithCode(Success)
17
+ if Worker.isEnabled {
18
+ Worker.exitAfterReporting(~metrics=state->IndexerState.toMetrics)
19
+ } else {
20
+ NodeJs.process->NodeJs.exitWithCode(Success)
21
+ }
18
22
  }
19
23
  | Some(message) => state->IndexerState.errorExit(ErrorHandling.make(Utils.Error.make(message)))
20
24
  }
@@ -1,5 +1,6 @@
1
1
  // Generated by ReScript, PLEASE EDIT WITH CARE
2
2
 
3
+ import * as Worker from "./Worker.res.mjs";
3
4
  import * as Logging from "./Logging.res.mjs";
4
5
  import * as Writing from "./Writing.res.mjs";
5
6
  import * as Process from "process";
@@ -24,12 +25,16 @@ async function run(state) {
24
25
  return onExit();
25
26
  } else {
26
27
  Logging.info("Exiting with success");
27
- Process.exit(0);
28
- return;
28
+ if (Worker.isEnabled) {
29
+ return Worker.exitAfterReporting(IndexerState.toMetrics(state));
30
+ } else {
31
+ Process.exit(0);
32
+ return;
33
+ }
29
34
  }
30
35
  }
31
36
 
32
37
  export {
33
38
  run,
34
39
  }
35
- /* Logging Not a pure module */
40
+ /* Worker Not a pure module */
@@ -1,20 +1,37 @@
1
1
  @module("node:fs/promises")
2
2
  external globIterator: string => Utils.asyncIterator<string> = "glob"
3
3
 
4
- // Register tsx for TypeScript handler support
5
- // Wrapped in try-catch because if tsx is already loaded via --import (e.g., in tests),
6
- // calling module.register again will throw an error
7
- try {
8
- NodeJs.Module.register("tsx/esm", NodeJs.ImportMeta.url)
9
- } catch {
10
- | _ => () // tsx already loaded, ignore
4
+ type tsHooksAddon = {
5
+ loadTs: string => string,
6
+ tsResolveCandidates: (string, Null.t<string>) => array<string>,
7
+ tsNotFoundCandidates: (string, Null.t<string>, string) => array<string>,
11
8
  }
12
9
 
13
- // Convert a relative path to a file:// URL for dynamic import
14
- // Paths are resolved relative to process.cwd() (project root)
15
- let toImportUrl = (relativePath: string) => {
16
- let absolutePath = NodeJs.Path.resolve([NodeJs.Process.cwd(), relativePath])->NodeJs.Path.toString
17
- NodeJs.Url.pathToFileURL(absolutePath)->NodeJs.Url.toString
10
+ @module("./TsModuleHooks.mjs")
11
+ external registerTsHooksWith: tsHooksAddon => unit = "register"
12
+
13
+ // The resolve hook calls into the addon, and loading the addon goes through
14
+ // the resolve hook, so the addon has to be loaded before the hooks exist.
15
+ let registerTsHooks = () => {
16
+ let addon = Core.getAddon()
17
+ registerTsHooksWith({
18
+ loadTs: addon.loadTs,
19
+ tsResolveCandidates: addon.tsResolveCandidates,
20
+ tsNotFoundCandidates: addon.tsNotFoundCandidates,
21
+ })
22
+ }
23
+
24
+ let isTypeScript = file => /\.m?tsx?$/->RegExp.test(file)
25
+
26
+ // Paths are relative to the project root, which is the working directory.
27
+ let toAbsolutePath = (file: string) =>
28
+ NodeJs.Path.resolve([NodeJs.Process.cwd(), file])->NodeJs.Path.toString
29
+
30
+ let importHandler = async file => {
31
+ if isTypeScript(file) {
32
+ Core.getAddon().tsCheckHandlerFormat(toAbsolutePath(file))
33
+ }
34
+ await Utils.importPath(toAbsolutePath(file)->NodeJs.Url.pathToFileURL->NodeJs.Url.toString)
18
35
  }
19
36
 
20
37
  let registerContractHandlers = async (~contractName, ~handler: option<string>) => {
@@ -22,7 +39,7 @@ let registerContractHandlers = async (~contractName, ~handler: option<string>) =
22
39
  | None => ()
23
40
  | Some(handlerPath) =>
24
41
  try {
25
- let _ = await Utils.importPath(toImportUrl(handlerPath))
42
+ let _ = await importHandler(handlerPath)
26
43
  } catch {
27
44
  | exn =>
28
45
  let cause = exn->Utils.prettifyExn->Obj.magic
@@ -37,10 +54,9 @@ let registerContractHandlers = async (~contractName, ~handler: option<string>) =
37
54
  }
38
55
  }
39
56
 
40
- let autoLoadFromSrcHandlers = async (~handlers: string) => {
41
- // Relative to cwd (project root)
57
+ let globAutoLoadFiles = async (~handlers) => {
42
58
  let srcPattern = `./${handlers}/**/*.{js,mjs,ts}`
43
- let handlerFiles = try {
59
+ try {
44
60
  let iterator = globIterator(srcPattern)
45
61
  let files = await iterator->Utils.Array.fromAsyncIterator
46
62
  // Filter out test and spec files
@@ -54,22 +70,46 @@ let autoLoadFromSrcHandlers = async (~handlers: string) => {
54
70
  } catch {
55
71
  | exn =>
56
72
  JsError.throwWithMessage(
57
- `Failed to glob src/handlers directory for auto-loading handlers. Pattern: ${srcPattern}. Before continuing, check that you're using Node.js >=22 version. Error: ${exn
73
+ `Failed to glob src/handlers directory for auto-loading handlers. Pattern: ${srcPattern}. Error: ${exn
58
74
  ->Utils.prettifyExn
59
75
  ->Obj.magic}`,
60
76
  )
61
77
  }
78
+ }
62
79
 
63
- // Import handler files using absolute file:// URLs resolved from cwd
64
- let _ = await handlerFiles
65
- ->Array.map(file => {
66
- Utils.importPath(toImportUrl(file))->Promise.catch(exn => {
67
- let cause = exn->Utils.prettifyExn->Obj.magic
68
- Logging.errorWithExn(exn, `Failed to auto-load handler file: ${file}`)
69
- JsError.throwWithMessage(`Failed to auto-load handler file: ${file}. Cause: ${cause}`)
70
- })
71
- })
72
- ->Promise.all
80
+ let getAutoLoadFiles = async (~config: Config.t) =>
81
+ switch config.subgraph {
82
+ // A subgraph project's `src/` holds AssemblyScript mappings, not envio
83
+ // handlers: auto-loading or type-checking them as TypeScript would fail.
84
+ | Some(_) => []
85
+ | None => await globAutoLoadFiles(~handlers=config.handlers)
86
+ }
87
+
88
+ type typeCheckResult = {
89
+ skipped?: string,
90
+ errors?: string,
91
+ }
92
+
93
+ @module("./HandlerTypeCheck.mjs")
94
+ external checkTypesInWorker: (~cwd: string, ~files: array<string>) => promise<typeCheckResult> =
95
+ "check"
96
+
97
+ let typeCheck = async (~config: Config.t, ~autoLoadFiles) => {
98
+ // A contract's `handler:` may also be an auto-loaded file.
99
+ let files =
100
+ autoLoadFiles
101
+ ->Array.concat(config.contractHandlers->Array.filterMap(({handler}) => handler))
102
+ ->Array.filter(isTypeScript)
103
+ ->Array.map(toAbsolutePath)
104
+ ->Utils.Set.fromArray
105
+ ->Utils.Set.toArray
106
+ if files->Array.length > 0 {
107
+ switch await checkTypesInWorker(~cwd=NodeJs.Process.cwd(), ~files) {
108
+ | {errors} => JsError.throwWithMessage(`Handler files have type errors:\n\n${errors}`)
109
+ | {skipped} => Logging.warn(skipped)
110
+ | _ => ()
111
+ }
112
+ }
73
113
  }
74
114
 
75
115
  // `Config` holds only event definitions; handler + per-chain `where`
@@ -77,18 +117,40 @@ let autoLoadFromSrcHandlers = async (~handlers: string) => {
77
117
  // `HandlerRegister.finishRegistration`. This loads the user handler files
78
118
  // (populating the global `HandlerRegister` registry as a side effect) and
79
119
  // returns the resulting per-chain registrations.
80
- let registerAllHandlers = async (~config: Config.t): HandlerRegister.registrationsByChainId => {
120
+ // The subgraph runtime lives in this package but is never exported: it's
121
+ // reached only from here, when the resolved config carries a translated
122
+ // manifest.
123
+ let registerSubgraph: (JSON.t, ~isDev: bool) => promise<unit> = %raw(`(subgraph, isDev) =>
124
+ import("./subgraph/runtime.ts").then((m) => m.registerSubgraph({ ...subgraph, isDev }))`)
125
+
126
+ let registerAllHandlers = async (
127
+ ~config: Config.t,
128
+ ~autoLoadFiles,
129
+ ): HandlerRegister.registrationsByChainId => {
81
130
  HandlerRegister.startRegistration(~config)
131
+ registerTsHooks()
82
132
 
83
- // Auto-load all .js files from src/handlers directory
84
- await autoLoadFromSrcHandlers(~handlers=config.handlers)
133
+ switch config.subgraph {
134
+ // Registered after the TypeScript hooks, so the subgraph runtime's own hooks
135
+ // see a mapping first.
136
+ | Some(subgraph) => await registerSubgraph(subgraph, ~isDev=config.isDev)
137
+ | None =>
138
+ let _ = await autoLoadFiles
139
+ ->Array.map(file => {
140
+ importHandler(file)->Promise.catch(exn => {
141
+ let cause = exn->Utils.prettifyExn->Obj.magic
142
+ Logging.errorWithExn(exn, `Failed to auto-load handler file: ${file}`)
143
+ JsError.throwWithMessage(`Failed to auto-load handler file: ${file}. Cause: ${cause}`)
144
+ })
145
+ })
146
+ ->Promise.all
85
147
 
86
- // Load contract-specific handlers
87
- let _ = await config.contractHandlers
88
- ->Array.map(({name, handler}) => {
89
- registerContractHandlers(~contractName=name, ~handler)
90
- })
91
- ->Promise.all
148
+ let _ = await config.contractHandlers
149
+ ->Array.map(({name, handler}) => {
150
+ registerContractHandlers(~contractName=name, ~handler)
151
+ })
152
+ ->Promise.all
153
+ }
92
154
 
93
155
  HandlerRegister.finishRegistration(~config)
94
156
  }