envio 3.0.0-alpha.21 → 3.0.0-alpha.22

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 (219) hide show
  1. package/bin.mjs +2 -48
  2. package/evm.schema.json +67 -0
  3. package/fuel.schema.json +67 -0
  4. package/index.d.ts +822 -38
  5. package/index.js +5 -3
  6. package/package.json +10 -8
  7. package/rescript.json +5 -9
  8. package/src/Address.res +4 -5
  9. package/src/Address.res.mjs +9 -12
  10. package/src/Api.res +15 -0
  11. package/src/Api.res.mjs +20 -0
  12. package/src/Batch.res +32 -34
  13. package/src/Batch.res.mjs +172 -187
  14. package/src/Bin.res +89 -0
  15. package/src/Bin.res.mjs +97 -0
  16. package/src/ChainFetcher.res +33 -57
  17. package/src/ChainFetcher.res.mjs +197 -227
  18. package/src/ChainManager.res +6 -14
  19. package/src/ChainManager.res.mjs +74 -85
  20. package/src/ChainMap.res +14 -16
  21. package/src/ChainMap.res.mjs +38 -38
  22. package/src/Config.res +193 -135
  23. package/src/Config.res.mjs +566 -592
  24. package/src/Core.res +182 -0
  25. package/src/Core.res.mjs +207 -0
  26. package/src/Ecosystem.res +25 -4
  27. package/src/Ecosystem.res.mjs +12 -13
  28. package/src/Env.res +20 -13
  29. package/src/Env.res.mjs +124 -113
  30. package/src/EnvSafe.res +269 -0
  31. package/src/EnvSafe.res.mjs +296 -0
  32. package/src/EnvSafe.resi +18 -0
  33. package/src/Envio.res +37 -26
  34. package/src/Envio.res.mjs +59 -60
  35. package/src/ErrorHandling.res +2 -2
  36. package/src/ErrorHandling.res.mjs +15 -15
  37. package/src/EventConfigBuilder.res +219 -81
  38. package/src/EventConfigBuilder.res.mjs +259 -202
  39. package/src/EventProcessing.res +27 -38
  40. package/src/EventProcessing.res.mjs +165 -183
  41. package/src/EventUtils.res +11 -11
  42. package/src/EventUtils.res.mjs +21 -22
  43. package/src/EvmTypes.res +0 -1
  44. package/src/EvmTypes.res.mjs +5 -5
  45. package/src/FetchState.res +360 -256
  46. package/src/FetchState.res.mjs +958 -914
  47. package/src/GlobalState.res +365 -351
  48. package/src/GlobalState.res.mjs +958 -992
  49. package/src/GlobalStateManager.res +1 -2
  50. package/src/GlobalStateManager.res.mjs +36 -44
  51. package/src/HandlerLoader.res +107 -23
  52. package/src/HandlerLoader.res.mjs +128 -38
  53. package/src/HandlerRegister.res +127 -103
  54. package/src/HandlerRegister.res.mjs +164 -164
  55. package/src/HandlerRegister.resi +12 -4
  56. package/src/Hasura.res +35 -22
  57. package/src/Hasura.res.mjs +158 -167
  58. package/src/InMemoryStore.res +20 -27
  59. package/src/InMemoryStore.res.mjs +64 -80
  60. package/src/InMemoryTable.res +34 -39
  61. package/src/InMemoryTable.res.mjs +165 -170
  62. package/src/Internal.res +52 -33
  63. package/src/Internal.res.mjs +84 -81
  64. package/src/LazyLoader.res.mjs +55 -61
  65. package/src/LoadLayer.res +77 -78
  66. package/src/LoadLayer.res.mjs +160 -189
  67. package/src/LoadManager.res +16 -21
  68. package/src/LoadManager.res.mjs +79 -84
  69. package/src/LogSelection.res +236 -68
  70. package/src/LogSelection.res.mjs +211 -141
  71. package/src/Logging.res +13 -9
  72. package/src/Logging.res.mjs +130 -143
  73. package/src/Main.res +428 -51
  74. package/src/Main.res.mjs +528 -271
  75. package/src/Persistence.res +77 -84
  76. package/src/Persistence.res.mjs +131 -132
  77. package/src/PgStorage.res +291 -167
  78. package/src/PgStorage.res.mjs +797 -817
  79. package/src/Prometheus.res +50 -58
  80. package/src/Prometheus.res.mjs +345 -373
  81. package/src/ReorgDetection.res +22 -24
  82. package/src/ReorgDetection.res.mjs +100 -106
  83. package/src/SafeCheckpointTracking.res +7 -7
  84. package/src/SafeCheckpointTracking.res.mjs +40 -43
  85. package/src/SimulateItems.res +41 -49
  86. package/src/SimulateItems.res.mjs +257 -272
  87. package/src/Sink.res +2 -2
  88. package/src/Sink.res.mjs +22 -26
  89. package/src/TableIndices.res +1 -2
  90. package/src/TableIndices.res.mjs +42 -48
  91. package/src/TestIndexer.res +196 -189
  92. package/src/TestIndexer.res.mjs +536 -536
  93. package/src/TestIndexerProxyStorage.res +15 -16
  94. package/src/TestIndexerProxyStorage.res.mjs +98 -122
  95. package/src/TestIndexerWorker.res +4 -0
  96. package/src/TestIndexerWorker.res.mjs +7 -0
  97. package/src/Throttler.res +3 -3
  98. package/src/Throttler.res.mjs +23 -24
  99. package/src/Time.res +1 -1
  100. package/src/Time.res.mjs +18 -21
  101. package/src/TopicFilter.res +3 -3
  102. package/src/TopicFilter.res.mjs +29 -30
  103. package/src/UserContext.res +93 -54
  104. package/src/UserContext.res.mjs +197 -182
  105. package/src/Utils.res +141 -86
  106. package/src/Utils.res.mjs +334 -295
  107. package/src/bindings/BigDecimal.res +0 -2
  108. package/src/bindings/BigDecimal.res.mjs +19 -23
  109. package/src/bindings/ClickHouse.res +28 -27
  110. package/src/bindings/ClickHouse.res.mjs +243 -240
  111. package/src/bindings/DateFns.res +11 -11
  112. package/src/bindings/DateFns.res.mjs +7 -7
  113. package/src/bindings/EventSource.res.mjs +2 -2
  114. package/src/bindings/Express.res +2 -5
  115. package/src/bindings/Hrtime.res +2 -2
  116. package/src/bindings/Hrtime.res.mjs +30 -32
  117. package/src/bindings/Lodash.res.mjs +1 -1
  118. package/src/bindings/NodeJs.res +14 -9
  119. package/src/bindings/NodeJs.res.mjs +20 -20
  120. package/src/bindings/Pino.res +8 -10
  121. package/src/bindings/Pino.res.mjs +40 -43
  122. package/src/bindings/Postgres.res +2 -5
  123. package/src/bindings/Postgres.res.mjs +9 -9
  124. package/src/bindings/PromClient.res +17 -2
  125. package/src/bindings/PromClient.res.mjs +30 -7
  126. package/src/bindings/SDSL.res.mjs +2 -2
  127. package/src/bindings/Viem.res +4 -4
  128. package/src/bindings/Viem.res.mjs +20 -22
  129. package/src/bindings/Vitest.res +1 -1
  130. package/src/bindings/Vitest.res.mjs +2 -2
  131. package/src/bindings/WebSocket.res +1 -1
  132. package/src/db/EntityHistory.res +9 -3
  133. package/src/db/EntityHistory.res.mjs +84 -59
  134. package/src/db/InternalTable.res +62 -60
  135. package/src/db/InternalTable.res.mjs +271 -203
  136. package/src/db/Schema.res +1 -2
  137. package/src/db/Schema.res.mjs +28 -32
  138. package/src/db/Table.res +28 -27
  139. package/src/db/Table.res.mjs +276 -292
  140. package/src/sources/EventRouter.res +21 -16
  141. package/src/sources/EventRouter.res.mjs +55 -57
  142. package/src/sources/Evm.res +17 -1
  143. package/src/sources/Evm.res.mjs +16 -8
  144. package/src/sources/EvmChain.res +15 -17
  145. package/src/sources/EvmChain.res.mjs +40 -42
  146. package/src/sources/Fuel.res +14 -1
  147. package/src/sources/Fuel.res.mjs +16 -8
  148. package/src/sources/FuelSDK.res +1 -1
  149. package/src/sources/FuelSDK.res.mjs +6 -8
  150. package/src/sources/HyperFuel.res +8 -10
  151. package/src/sources/HyperFuel.res.mjs +113 -123
  152. package/src/sources/HyperFuelClient.res.mjs +6 -7
  153. package/src/sources/HyperFuelSource.res +19 -20
  154. package/src/sources/HyperFuelSource.res.mjs +339 -356
  155. package/src/sources/HyperSync.res +11 -13
  156. package/src/sources/HyperSync.res.mjs +206 -220
  157. package/src/sources/HyperSyncClient.res +5 -7
  158. package/src/sources/HyperSyncClient.res.mjs +70 -75
  159. package/src/sources/HyperSyncHeightStream.res +8 -9
  160. package/src/sources/HyperSyncHeightStream.res.mjs +78 -86
  161. package/src/sources/HyperSyncJsonApi.res +18 -15
  162. package/src/sources/HyperSyncJsonApi.res.mjs +201 -231
  163. package/src/sources/HyperSyncSource.res +17 -21
  164. package/src/sources/HyperSyncSource.res.mjs +268 -290
  165. package/src/sources/Rpc.res +5 -5
  166. package/src/sources/Rpc.res.mjs +168 -192
  167. package/src/sources/RpcSource.res +166 -167
  168. package/src/sources/RpcSource.res.mjs +972 -1046
  169. package/src/sources/RpcWebSocketHeightStream.res +10 -11
  170. package/src/sources/RpcWebSocketHeightStream.res.mjs +131 -145
  171. package/src/sources/SimulateSource.res +1 -1
  172. package/src/sources/SimulateSource.res.mjs +35 -38
  173. package/src/sources/Source.res +1 -1
  174. package/src/sources/Source.res.mjs +3 -3
  175. package/src/sources/SourceManager.res +39 -20
  176. package/src/sources/SourceManager.res.mjs +340 -371
  177. package/src/sources/SourceManager.resi +2 -1
  178. package/src/sources/Svm.res +12 -5
  179. package/src/sources/Svm.res.mjs +44 -41
  180. package/src/tui/Tui.res +23 -12
  181. package/src/tui/Tui.res.mjs +292 -290
  182. package/src/tui/bindings/Ink.res +2 -4
  183. package/src/tui/bindings/Ink.res.mjs +35 -41
  184. package/src/tui/components/BufferedProgressBar.res +7 -7
  185. package/src/tui/components/BufferedProgressBar.res.mjs +46 -46
  186. package/src/tui/components/CustomHooks.res +1 -2
  187. package/src/tui/components/CustomHooks.res.mjs +102 -122
  188. package/src/tui/components/Messages.res +1 -2
  189. package/src/tui/components/Messages.res.mjs +38 -42
  190. package/src/tui/components/SyncETA.res +10 -11
  191. package/src/tui/components/SyncETA.res.mjs +178 -196
  192. package/src/tui/components/TuiData.res +1 -1
  193. package/src/tui/components/TuiData.res.mjs +7 -6
  194. package/src/vendored/Rest.res +52 -66
  195. package/src/vendored/Rest.res.mjs +324 -364
  196. package/svm.schema.json +67 -0
  197. package/src/Address.gen.ts +0 -8
  198. package/src/Config.gen.ts +0 -19
  199. package/src/Envio.gen.ts +0 -55
  200. package/src/EvmTypes.gen.ts +0 -6
  201. package/src/InMemoryStore.gen.ts +0 -6
  202. package/src/Internal.gen.ts +0 -64
  203. package/src/PgStorage.gen.ts +0 -10
  204. package/src/PgStorage.res.d.mts +0 -5
  205. package/src/Types.ts +0 -56
  206. package/src/bindings/BigDecimal.gen.ts +0 -14
  207. package/src/bindings/BigDecimal.res.d.mts +0 -5
  208. package/src/bindings/BigInt.gen.ts +0 -10
  209. package/src/bindings/BigInt.res +0 -70
  210. package/src/bindings/BigInt.res.d.mts +0 -5
  211. package/src/bindings/BigInt.res.mjs +0 -154
  212. package/src/bindings/Ethers.res.d.mts +0 -5
  213. package/src/bindings/Pino.gen.ts +0 -17
  214. package/src/bindings/Postgres.gen.ts +0 -8
  215. package/src/bindings/Postgres.res.d.mts +0 -5
  216. package/src/bindings/Promise.res +0 -67
  217. package/src/bindings/Promise.res.mjs +0 -26
  218. package/src/db/InternalTable.gen.ts +0 -36
  219. package/src/sources/HyperSyncClient.gen.ts +0 -19
package/index.d.ts CHANGED
@@ -1,19 +1,94 @@
1
- export type {
2
- logger as Logger,
3
- effect as Effect,
4
- effectContext as EffectContext,
5
- effectArgs as EffectArgs,
6
- effectOptions as EffectOptions,
7
- rateLimitDuration as RateLimitDuration,
8
- rateLimit as RateLimit,
9
- blockEvent as BlockEvent,
10
- fuelBlockEvent as FuelBlockEvent,
11
- svmOnBlockArgs as SvmOnBlockArgs,
12
- onBlockArgs as OnBlockArgs,
13
- onBlockOptions as OnBlockOptions,
14
- } from "./src/Envio.gen.ts";
15
- import type { Address } from "./src/Types.ts";
16
- export type { EffectCaller, Address } from "./src/Types.ts";
1
+ import * as Sury from "rescript-schema";
2
+ import type { default as BigDecimalT } from "bignumber.js";
3
+ export { default as BigDecimal } from "bignumber.js";
4
+
5
+ // Runtime value stubs used by the `S.*` namespace declarations further down
6
+ // so the exported `S.bigDecimal` / `S.bigint` consts pick up typed schemas.
7
+ // The implementations live in `index.js`, sourced from `.res.mjs` compiled
8
+ // output.
9
+ declare const bigDecimalSchema: Sury.Schema<BigDecimalT>;
10
+ declare const bigintSchema: Sury.Schema<bigint>;
11
+
12
+ /** Ethereum address — a 20-byte hex string prefixed with `0x`. */
13
+ export type Address = `0x${string}`;
14
+
15
+ /** Structured logger bound to an event or handler context. Messages are
16
+ * displayed in the console and the Envio Hosted Service. */
17
+ export type Logger = {
18
+ readonly debug: (
19
+ message: string,
20
+ params?: Record<string, unknown> | Error
21
+ ) => void;
22
+ readonly info: (
23
+ message: string,
24
+ params?: Record<string, unknown> | Error
25
+ ) => void;
26
+ readonly warn: (
27
+ message: string,
28
+ params?: Record<string, unknown> | Error
29
+ ) => void;
30
+ readonly error: (
31
+ message: string,
32
+ params?: Record<string, unknown> | Error
33
+ ) => void;
34
+ };
35
+
36
+ /** Handle for an external-call effect created via {@link createEffect}.
37
+ * Effects provide automatic deduplication, error handling, and caching. */
38
+ export declare abstract class Effect<I, O> {
39
+ protected opaque: I | O;
40
+ }
41
+
42
+ /** Calls an {@link Effect} with the given input and returns its output. */
43
+ export type EffectCaller = <I, O>(
44
+ effect: Effect<I, O>,
45
+ // This is a hack to make the call complain on undefined
46
+ // when it's not needed, instead of extending the input type.
47
+ // Might be not needed if I misunderstand something in TS.
48
+ input: I extends undefined ? undefined : I
49
+ ) => Promise<O>;
50
+
51
+ /** Context passed to an Effect's handler function. */
52
+ export type EffectContext = {
53
+ /** Access the logger instance with the event as context. */
54
+ readonly log: Logger;
55
+ /** Call another Effect from inside this one. */
56
+ readonly effect: EffectCaller;
57
+ /** Whether to cache this call's result. Defaults to the effect's `cache`
58
+ * option; set to `false` to skip caching for this specific invocation. */
59
+ cache: boolean;
60
+ };
61
+
62
+ /** Rate-limit window for an {@link Effect}. Strings resolve to common
63
+ * durations; a plain `number` is treated as milliseconds. */
64
+ export type RateLimitDuration = "second" | "minute" | number;
65
+
66
+ /** Rate-limit configuration for an {@link Effect}. `false` disables rate
67
+ * limiting; otherwise a `{calls, per}` pair caps invocations per duration. */
68
+ export type RateLimit =
69
+ | false
70
+ | { readonly calls: number; readonly per: RateLimitDuration };
71
+
72
+ /** Options accepted by {@link createEffect}. */
73
+ export type EffectOptions<Input, Output> = {
74
+ /** The name of the effect. Used for logging and debugging. */
75
+ readonly name: string;
76
+ /** The input schema of the effect. */
77
+ readonly input: Sury.Schema<Input>;
78
+ /** The output schema of the effect. */
79
+ readonly output: Sury.Schema<Output>;
80
+ /** Rate limit for the effect. Set to `false` to disable or provide
81
+ * `{calls, per: "second" | "minute"}` to enable. */
82
+ readonly rateLimit: RateLimit;
83
+ /** Whether the effect should be cached. */
84
+ readonly cache?: boolean;
85
+ };
86
+
87
+ /** Arguments passed to the handler function of an {@link Effect}. */
88
+ export type EffectArgs<Input> = {
89
+ readonly input: Input;
90
+ readonly context: EffectContext;
91
+ };
17
92
 
18
93
  export const TestHelpers: {
19
94
  Addresses: {
@@ -60,16 +135,6 @@ export type GetWhereFilter<E> = {
60
135
  [K in keyof E]?: WhereOperator<E[K]>;
61
136
  };
62
137
 
63
- import type {
64
- effect as Effect,
65
- effectArgs as EffectArgs,
66
- rateLimit as RateLimit,
67
- } from "./src/Envio.gen.ts";
68
-
69
- import { schema as bigDecimalSchema } from "./src/bindings/BigDecimal.gen.ts";
70
- import { schema as bigintSchema } from "./src/bindings/BigInt.gen.ts";
71
- import * as Sury from "rescript-schema";
72
-
73
138
  type UnknownToOutput<T> = T extends Sury.Schema<unknown>
74
139
  ? Sury.Output<T>
75
140
  : T extends (...args: any[]) => any
@@ -180,7 +245,9 @@ export declare namespace S {
180
245
  export const tuple: typeof Sury.tuple;
181
246
  export const merge: typeof Sury.merge;
182
247
  export const optional: typeof Sury.optional;
183
- export const nullable: typeof Sury.nullable;
248
+ export function nullable<Output, Input>(
249
+ schema: Sury.Schema<Output, Input>
250
+ ): Sury.Schema<Output | null, Input | null>;
184
251
  export const bigDecimal: typeof bigDecimalSchema;
185
252
  export const unknown: typeof Sury.unknown;
186
253
  // Nullish type will change in "sury@10"
@@ -206,9 +273,11 @@ export declare namespace S {
206
273
  export interface Global {}
207
274
 
208
275
  /**
209
- * Shape of the indexer configuration.
210
- * Will be used internally for defineConfig.
211
- * Currently should match the internal.config.json structure.
276
+ * Shape of the indexer configuration used internally for defineConfig.
277
+ * This models only the subset of fields defineConfig consumes; the JSON
278
+ * emitted by `envio config view` is a superset (enums, entities, per-chain
279
+ * sources, event metadata, EVM global field selections, and other
280
+ * serializer-only fields are intentionally omitted here).
212
281
  */
213
282
  type IndexerConfig = {
214
283
  /** The indexer name. */
@@ -391,15 +460,467 @@ type IndexerConfigTypes = {
391
460
  evm?: {
392
461
  chains: Record<string, { id: number }>;
393
462
  contracts?: Record<string, Record<string, { eventName: string }>>;
463
+ eventFilters?: Record<string, Record<string, { readonly params: object }>>;
394
464
  };
395
465
  fuel?: {
396
466
  chains: Record<string, { id: number }>;
397
467
  contracts?: Record<string, Record<string, { eventName: string }>>;
468
+ eventFilters?: Record<string, Record<string, { readonly params: object }>>;
398
469
  };
399
470
  svm?: { chains: Record<string, { id: number }> };
400
471
  entities?: Record<string, object>;
401
472
  };
402
473
 
474
+ // ============== onEvent / contractRegister Types ==============
475
+
476
+ // Extract contracts type from config
477
+ type EvmContracts<Config extends IndexerConfigTypes> =
478
+ Config["evm"] extends { contracts: infer C extends Record<string, Record<string, any>> }
479
+ ? C : {};
480
+
481
+ type FuelContracts<Config extends IndexerConfigTypes> =
482
+ Config["fuel"] extends { contracts: infer C extends Record<string, Record<string, any>> }
483
+ ? C : {};
484
+
485
+ // Extract eventFilters type from config — a sibling lookup table that maps
486
+ // contract+event to the `where` filter shape `{ params: { ... } }`. Split
487
+ // out from `EvmContracts` so per-event entries stay focused on the event
488
+ // payload and keep the two lookup tables independently composable.
489
+ type EvmEventFilters<Config extends IndexerConfigTypes> =
490
+ Config["evm"] extends { eventFilters: infer F extends Record<string, Record<string, any>> }
491
+ ? F : {};
492
+
493
+ type FuelEventFilters<Config extends IndexerConfigTypes> =
494
+ Config["fuel"] extends { eventFilters: infer F extends Record<string, Record<string, any>> }
495
+ ? F : {};
496
+
497
+ // Extract contract names for contract registration
498
+ type EvmContractNames<Config extends IndexerConfigTypes> =
499
+ Config["evm"] extends { contracts: Record<infer N, any> } ? N & string : never;
500
+
501
+ type FuelContractNames<Config extends IndexerConfigTypes> =
502
+ Config["fuel"] extends { contracts: Record<infer N, any> } ? N & string : never;
503
+
504
+ /** Event identity for onEvent/contractRegister calls. */
505
+ type EventIdentity<
506
+ Contracts extends Record<string, Record<string, any>>,
507
+ C extends keyof Contracts = keyof Contracts,
508
+ E extends keyof Contracts[C] & string = keyof Contracts[C] & string
509
+ > = {
510
+ /** The contract name as defined in config.yaml. */
511
+ readonly contract: C;
512
+ /** The event name as defined in the contract ABI. */
513
+ readonly event: E;
514
+ /** Whether to process all events (wildcard mode). */
515
+ readonly wildcard?: boolean;
516
+ };
517
+
518
+ /**
519
+ * Shared shape for handler contexts across ecosystems — logger, effect
520
+ * caller, preload flag, chain state, and the entity operations map derived
521
+ * from the project schema.
522
+ */
523
+ type BaseHandlerContext<Config extends IndexerConfigTypes, ChainId> = {
524
+ /** Access the logger instance. */
525
+ readonly log: Logger;
526
+ /** Call an Effect with the given input. */
527
+ readonly effect: EffectCaller;
528
+ /** True when running in preload mode (parallel pre-run for cache population). */
529
+ readonly isPreload: boolean;
530
+ /** Chain state for the current event's chain. */
531
+ readonly chain: {
532
+ readonly id: ChainId;
533
+ readonly isLive: boolean;
534
+ };
535
+ } & {
536
+ readonly [K in keyof ConfigEntities<Config>]: EntityOperations<ConfigEntities<Config>[K]>;
537
+ };
538
+
539
+ /** Context for onEvent handlers. Includes entity operations, logging, and chain info. */
540
+ export type EvmOnEventContext<Config extends IndexerConfigTypes> = Prettify<
541
+ BaseHandlerContext<Config, EvmChainIds<Config>>
542
+ >;
543
+
544
+ /** Context for onEvent handlers in Fuel ecosystem. */
545
+ export type FuelOnEventContext<Config extends IndexerConfigTypes> = Prettify<
546
+ BaseHandlerContext<Config, FuelChainIds<Config>>
547
+ >;
548
+
549
+ /** Context for `indexer.onSlot` handlers in SVM ecosystem. */
550
+ export type SvmOnSlotContext<Config extends IndexerConfigTypes> = Prettify<
551
+ BaseHandlerContext<Config, SvmChainIds<Config>>
552
+ >;
553
+
554
+ /** Entity operations available in handler contexts. */
555
+ type EntityOperations<Entity> = {
556
+ readonly get: (id: string) => Promise<Entity | undefined>;
557
+ readonly getOrThrow: (id: string, message?: string) => Promise<Entity>;
558
+ readonly getWhere: (filter: GetWhereFilter<Entity>) => Promise<Entity[]>;
559
+ readonly getOrCreate: (entity: Entity) => Promise<Entity>;
560
+ readonly set: (entity: Entity) => void;
561
+ readonly deleteUnsafe: (id: string) => void;
562
+ };
563
+
564
+ /** Contract registration handle. */
565
+ type ContractRegistration = {
566
+ /** Register a new contract address for dynamic indexing. */
567
+ readonly add: (address: Address) => void;
568
+ };
569
+
570
+ /** Context for contractRegister handlers. Chain object includes contract registration methods.
571
+ * `isLive` is intentionally absent: contract registration runs during historical sync,
572
+ * so the "live" distinction isn't meaningful and the runtime does not expose it. */
573
+ export type EvmContractRegisterContext<Config extends IndexerConfigTypes> = Prettify<{
574
+ readonly log: Logger;
575
+ readonly chain: {
576
+ readonly id: EvmChainIds<Config>;
577
+ } & {
578
+ readonly [K in EvmContractNames<Config>]: ContractRegistration;
579
+ };
580
+ }>;
581
+
582
+ /** Context for contractRegister handlers in Fuel ecosystem. `isLive` is intentionally
583
+ * absent — see EvmContractRegisterContext. */
584
+ export type FuelContractRegisterContext<Config extends IndexerConfigTypes> = Prettify<{
585
+ readonly log: Logger;
586
+ readonly chain: {
587
+ readonly id: FuelChainIds<Config>;
588
+ } & {
589
+ readonly [K in FuelContractNames<Config>]: ContractRegistration;
590
+ };
591
+ }>;
592
+
593
+ // ============== onEvent / contractRegister Named Types ==============
594
+
595
+ /** Constraint: any event must have literal contractName and eventName fields. */
596
+ type EventLike = { readonly contractName: string; readonly eventName: string };
597
+
598
+ /** Scalar value or array of values — used by event filter fields to accept either a
599
+ * single topic or multiple alternatives (OR semantics). */
600
+ export type SingleOrMultiple<T> = T | readonly T[];
601
+
602
+ /** EVM event type resolved by contract and event name. Union of all events when no generics provided.
603
+ * The mapped form distributes `K in C` so disjoint event sets across contracts survive — using
604
+ * `EvmContracts<Config>[C][E]` directly would collapse to keys common to *all* contracts (often `never`). */
605
+ export type EvmOnEvent<
606
+ Config extends IndexerConfigTypes,
607
+ C extends keyof EvmContracts<Config> = keyof EvmContracts<Config>,
608
+ E extends string = string
609
+ > = {
610
+ [K in C]: EvmContracts<Config>[K][E & keyof EvmContracts<Config>[K]];
611
+ }[C];
612
+
613
+ /** The chain object passed into the EVM dynamic `where` callback form. Exposes
614
+ * the chain `id` and the event's own contract under its capitalized name,
615
+ * with `addresses` listing the indexed contract addresses on this chain.
616
+ *
617
+ * Only the event's own contract is exposed — multi-contract address
618
+ * filtering is not supported in this iteration. */
619
+ export type EvmOnEventWhereChain<ContractName extends string> = {
620
+ readonly id: number;
621
+ } & {
622
+ readonly [K in ContractName]: { readonly addresses: readonly Address[] };
623
+ };
624
+
625
+ /** Arguments passed to the EVM dynamic `where` callback form. Return an
626
+ * `EvmOnEventWhereFilter` to apply a filter, or `true` / `false` to keep / skip
627
+ * all events for that invocation. */
628
+ export type EvmOnEventWhereArgs<ContractName extends string> = {
629
+ readonly chain: EvmOnEventWhereChain<ContractName>;
630
+ };
631
+
632
+ /** A single EVM `where` filter condition. `params` accepts either a single
633
+ * AND-conjunction of indexed-parameter narrowings, or an array of them (OR
634
+ * semantics). `block.number._gte` promotes to the event's startBlock and
635
+ * overrides the contract-level `start_block` — use it to restrict per-event
636
+ * processing without touching `config.yaml`. Only `_gte` is supported on
637
+ * event filters; use `indexer.onBlock` for `_lte` / `_every`. */
638
+ export type EvmOnEventWhereFilter<Params> = {
639
+ readonly params?: Params | readonly Params[];
640
+ readonly block?: {
641
+ readonly number?: {
642
+ readonly _gte?: number;
643
+ };
644
+ };
645
+ };
646
+
647
+ /** The `where` option value of `indexer.onEvent` / `indexer.contractRegister`
648
+ * in the EVM ecosystem.
649
+ *
650
+ * TypeScript accepts either a static filter object or a dynamic callback.
651
+ * The dynamic callback may return a boolean to keep (`true`) or skip (`false`)
652
+ * all events on that invocation, or an `EvmOnEventWhereFilter` for narrowing.
653
+ *
654
+ * The ReScript surface only exposes the callback form — multi-condition OR
655
+ * semantics are always expressed via an array on `params`, not at the top
656
+ * level of `where`. */
657
+ export type EvmOnEventWhere<Params, ContractName extends string> =
658
+ | EvmOnEventWhereFilter<Params>
659
+ | ((args: EvmOnEventWhereArgs<ContractName>) => EvmOnEventWhereFilter<Params> | boolean);
660
+
661
+ /** The chain object passed into the Fuel dynamic `where` callback form. */
662
+ export type FuelOnEventWhereChain<ContractName extends string> = EvmOnEventWhereChain<ContractName>;
663
+ /** Arguments passed to the Fuel dynamic `where` callback form. */
664
+ export type FuelOnEventWhereArgs<ContractName extends string> = EvmOnEventWhereArgs<ContractName>;
665
+ /** A single Fuel `where` filter condition. Keyed on `block.height` instead
666
+ * of `block.number`. */
667
+ export type FuelOnEventWhereFilter<Params> = {
668
+ readonly params?: Params | readonly Params[];
669
+ readonly block?: {
670
+ readonly height?: {
671
+ readonly _gte?: number;
672
+ };
673
+ };
674
+ };
675
+ /** The `where` option value of `indexer.onEvent` / `indexer.contractRegister` in the Fuel ecosystem. */
676
+ export type FuelOnEventWhere<Params, ContractName extends string> =
677
+ | FuelOnEventWhereFilter<Params>
678
+ | ((args: FuelOnEventWhereArgs<ContractName>) => FuelOnEventWhereFilter<Params> | boolean);
679
+
680
+ /** Options for registering an EVM onEvent handler. Contract and event literal names are derived from the Event type.
681
+ * The conditional `Event extends EventLike` distributes over union members so that each member's
682
+ * contractName/eventName pair is constrained together — preventing invalid cross-member pairings.
683
+ * The `Params` generic carries the indexed-parameter shape (looked up via `EvmEventFilters[C][E]["params"]`
684
+ * by callers) so the `where` option enforces the same per-event narrowing as the inline handler signature. */
685
+ export type EvmOnEventOptions<Event extends EventLike, Params = {}> = Event extends EventLike
686
+ ? {
687
+ readonly contract: Event["contractName"];
688
+ readonly event: Event["eventName"];
689
+ readonly wildcard?: boolean;
690
+ readonly where?: EvmOnEventWhere<Params, Event["contractName"] & string>;
691
+ }
692
+ : never;
693
+
694
+ /** Handler function for an EVM onEvent registration. Context is provided as a separate generic so the project alias can bind it. */
695
+ export type EvmOnEventHandler<Event extends EventLike, Context> = (args: {
696
+ event: Event;
697
+ context: Context;
698
+ }) => Promise<void>;
699
+
700
+ /** Options for registering an EVM contractRegister handler. Same shape as EvmOnEventOptions. */
701
+ export type EvmContractRegisterOptions<Event extends EventLike, Params = {}> = EvmOnEventOptions<
702
+ Event,
703
+ Params
704
+ >;
705
+
706
+ /** Handler function for an EVM contractRegister registration. */
707
+ export type EvmContractRegisterHandler<Event extends EventLike, Context> = EvmOnEventHandler<Event, Context>;
708
+
709
+ /** Fuel event type resolved by contract and event name. Same distributive-mapped pattern as `EvmOnEvent`. */
710
+ export type FuelOnEvent<
711
+ Config extends IndexerConfigTypes,
712
+ C extends keyof FuelContracts<Config> = keyof FuelContracts<Config>,
713
+ E extends string = string
714
+ > = {
715
+ [K in C]: FuelContracts<Config>[K][E & keyof FuelContracts<Config>[K]];
716
+ }[C];
717
+
718
+ /** Options for registering a Fuel onEvent handler. */
719
+ export type FuelOnEventOptions<Event extends EventLike, Params = {}> = EvmOnEventOptions<
720
+ Event,
721
+ Params
722
+ >;
723
+
724
+ /** Handler function for a Fuel onEvent registration. */
725
+ export type FuelOnEventHandler<Event extends EventLike, Context> = EvmOnEventHandler<Event, Context>;
726
+
727
+ /** Options for registering a Fuel contractRegister handler. */
728
+ export type FuelContractRegisterOptions<Event extends EventLike, Params = {}> = EvmOnEventOptions<
729
+ Event,
730
+ Params
731
+ >;
732
+
733
+ /** Handler function for a Fuel contractRegister registration. */
734
+ export type FuelContractRegisterHandler<Event extends EventLike, Context> = EvmOnEventHandler<Event, Context>;
735
+
736
+ // ============== EVM onBlock types ==============
737
+
738
+ /**
739
+ * Structured filter object returned by an EVM `indexer.onBlock` `where`
740
+ * predicate. `_every` alignment is relative to `_gte` (or the chain's
741
+ * configured `startBlock` when `_gte` is omitted), preserving
742
+ * `(blockNumber - startBlock) % _every === 0`.
743
+ */
744
+ export type EvmOnBlockFilter = {
745
+ readonly block?: {
746
+ readonly number?: {
747
+ /** Matches blocks whose number is greater than or equal to the given value. */
748
+ readonly _gte?: number;
749
+ /** Matches blocks whose number is less than or equal to the given value. */
750
+ readonly _lte?: number;
751
+ /** Match every Nth block. Alignment is relative to `_gte`. */
752
+ readonly _every?: number;
753
+ };
754
+ };
755
+ };
756
+
757
+ /**
758
+ * Return type of an EVM `indexer.onBlock` `where` predicate. The predicate
759
+ * must explicitly return — an implicit `undefined` is not accepted.
760
+ * - `false` → skip this chain entirely.
761
+ * - `true` → register on the chain with no extra filter.
762
+ * - {@link EvmOnBlockFilter} → register with the given range/stride.
763
+ */
764
+ export type EvmOnBlockWhereResult = boolean | EvmOnBlockFilter;
765
+
766
+ /** Argument passed to an EVM `indexer.onBlock` `where` predicate. */
767
+ export type EvmOnBlockWhereArgs<Config extends IndexerConfigTypes> = {
768
+ /** Configured chain being evaluated. Use `chain.id` to branch per chain. */
769
+ readonly chain: EvmChain<EvmChainIds<Config>, EvmContractNames<Config>>;
770
+ };
771
+
772
+ /** Context for EVM `indexer.onBlock` handlers. Alias of {@link EvmOnEventContext}. */
773
+ export type EvmOnBlockContext<Config extends IndexerConfigTypes> = EvmOnEventContext<Config>;
774
+
775
+ /** Arguments passed to an EVM block handler. */
776
+ export type EvmOnBlockHandlerArgs<Config extends IndexerConfigTypes> = {
777
+ /** Block being processed. Contains the block number; extended fields are
778
+ opt-in via `field_selection` in config.yaml. */
779
+ readonly block: { readonly number: number };
780
+ /** Handler context: entity operations, logger, effect caller, chain state. */
781
+ readonly context: EvmOnBlockContext<Config>;
782
+ };
783
+
784
+ /** Handler function for an EVM `indexer.onBlock` registration. */
785
+ export type EvmOnBlockHandler<Config extends IndexerConfigTypes> = (
786
+ args: EvmOnBlockHandlerArgs<Config>,
787
+ ) => Promise<void>;
788
+
789
+ /** Options for an EVM `indexer.onBlock` registration. */
790
+ export type EvmOnBlockOptions<Config extends IndexerConfigTypes> = {
791
+ /** Unique name for this block handler. Used as the key in error messages
792
+ and in persisted progress tracking. */
793
+ readonly name: string;
794
+ /** Optional predicate evaluated once per configured chain at registration
795
+ time. Return `false` to skip a chain, `true` to match every block, or
796
+ a filter object to restrict by block number range and stride. */
797
+ readonly where?: (args: EvmOnBlockWhereArgs<Config>) => EvmOnBlockWhereResult;
798
+ };
799
+
800
+ // ============== Fuel onBlock types ==============
801
+
802
+ /**
803
+ * Structured filter object returned by a Fuel `indexer.onBlock` `where`
804
+ * predicate. `_every` alignment is relative to `_gte` (or the chain's
805
+ * configured `startBlock` when `_gte` is omitted), preserving
806
+ * `(blockNumber - startBlock) % _every === 0`.
807
+ */
808
+ export type FuelOnBlockFilter = {
809
+ readonly block?: {
810
+ readonly height?: {
811
+ /** Matches blocks whose height is greater than or equal to the given value. */
812
+ readonly _gte?: number;
813
+ /** Matches blocks whose height is less than or equal to the given value. */
814
+ readonly _lte?: number;
815
+ /** Match every Nth block. Alignment is relative to `_gte`. */
816
+ readonly _every?: number;
817
+ };
818
+ };
819
+ };
820
+
821
+ /**
822
+ * Return type of a Fuel `indexer.onBlock` `where` predicate. The predicate
823
+ * must explicitly return — an implicit `undefined` is not accepted.
824
+ * - `false` → skip this chain.
825
+ * - `true` → register on the chain with no extra filter.
826
+ * - {@link FuelOnBlockFilter} → register with the given range/stride.
827
+ */
828
+ export type FuelOnBlockWhereResult = boolean | FuelOnBlockFilter;
829
+
830
+ /** Argument passed to a Fuel `indexer.onBlock` `where` predicate. */
831
+ export type FuelOnBlockWhereArgs<Config extends IndexerConfigTypes> = {
832
+ /** Configured chain being evaluated. Use `chain.id` to branch per chain. */
833
+ readonly chain: FuelChain<FuelChainIds<Config>, FuelContractNames<Config>>;
834
+ };
835
+
836
+ /** Context for Fuel `indexer.onBlock` handlers. Alias of {@link FuelOnEventContext}. */
837
+ export type FuelOnBlockContext<Config extends IndexerConfigTypes> = FuelOnEventContext<Config>;
838
+
839
+ /** Arguments passed to a Fuel block handler. */
840
+ export type FuelOnBlockHandlerArgs<Config extends IndexerConfigTypes> = {
841
+ /** Block being processed. Contains the block height; extended fields are
842
+ opt-in via `field_selection` in config.yaml. */
843
+ readonly block: { readonly height: number };
844
+ /** Handler context: entity operations, logger, effect caller, chain state. */
845
+ readonly context: FuelOnBlockContext<Config>;
846
+ };
847
+
848
+ /** Handler function for a Fuel `indexer.onBlock` registration. */
849
+ export type FuelOnBlockHandler<Config extends IndexerConfigTypes> = (
850
+ args: FuelOnBlockHandlerArgs<Config>,
851
+ ) => Promise<void>;
852
+
853
+ /** Options for a Fuel `indexer.onBlock` registration. */
854
+ export type FuelOnBlockOptions<Config extends IndexerConfigTypes> = {
855
+ /** Unique name for this block handler. Used as the key in error messages
856
+ and in persisted progress tracking. */
857
+ readonly name: string;
858
+ /** Optional predicate evaluated once per configured chain at registration
859
+ time. Return `false` to skip a chain, `true` to match every block, or
860
+ a filter object to restrict by block height range and stride. */
861
+ readonly where?: (args: FuelOnBlockWhereArgs<Config>) => FuelOnBlockWhereResult;
862
+ };
863
+
864
+ // ============== SVM onSlot types ==============
865
+
866
+ /**
867
+ * Structured filter object returned by an SVM `indexer.onSlot` `where`
868
+ * predicate. `_every` alignment is relative to `_gte` (or the chain's
869
+ * configured `startBlock` when `_gte` is omitted), preserving
870
+ * `(slot - startBlock) % _every === 0`.
871
+ */
872
+ export type SvmOnSlotFilter = {
873
+ readonly slot?: {
874
+ /** Matches slots whose number is greater than or equal to the given value. */
875
+ readonly _gte?: number;
876
+ /** Matches slots whose number is less than or equal to the given value. */
877
+ readonly _lte?: number;
878
+ /** Match every Nth slot. Alignment is relative to `_gte`. */
879
+ readonly _every?: number;
880
+ };
881
+ };
882
+
883
+ /**
884
+ * Return type of an SVM `indexer.onSlot` `where` predicate. The predicate
885
+ * must explicitly return — an implicit `undefined` is not accepted.
886
+ * - `false` → skip this chain.
887
+ * - `true` → register on the chain with no extra filter.
888
+ * - {@link SvmOnSlotFilter} → register with the given range/stride.
889
+ */
890
+ export type SvmOnSlotWhereResult = boolean | SvmOnSlotFilter;
891
+
892
+ /** Argument passed to an SVM `indexer.onSlot` `where` predicate. */
893
+ export type SvmOnSlotWhereArgs<Config extends IndexerConfigTypes> = {
894
+ /** Configured chain being evaluated. Use `chain.id` to branch per chain. */
895
+ readonly chain: SvmChain<SvmChainIds<Config>>;
896
+ };
897
+
898
+ /** Arguments passed to an SVM slot handler. */
899
+ export type SvmOnSlotHandlerArgs<Config extends IndexerConfigTypes> = {
900
+ /** Slot number being processed. */
901
+ readonly slot: number;
902
+ /** Handler context: entity operations, logger, effect caller, chain state. */
903
+ readonly context: SvmOnSlotContext<Config>;
904
+ };
905
+
906
+ /** Handler function for an SVM `indexer.onSlot` registration. */
907
+ export type SvmOnSlotHandler<Config extends IndexerConfigTypes> = (
908
+ args: SvmOnSlotHandlerArgs<Config>,
909
+ ) => Promise<void>;
910
+
911
+ /** Options for an SVM `indexer.onSlot` registration. */
912
+ export type SvmOnSlotOptions<Config extends IndexerConfigTypes> = {
913
+ /** Unique name for this slot handler. Used as the key in error messages
914
+ and in persisted progress tracking. */
915
+ readonly name: string;
916
+ /** Optional predicate evaluated once per configured chain at registration
917
+ time. Return `false` to skip a chain, `true` to match every slot, or
918
+ a filter object to restrict by slot range and stride. */
919
+ readonly where?: (args: SvmOnSlotWhereArgs<Config>) => SvmOnSlotWhereResult;
920
+ };
921
+
922
+ // ============== Indexer Types ==============
923
+
403
924
  // Helper: Check if ecosystem is configured in a given config
404
925
  type HasEvm<Config> = "evm" extends keyof Config ? true : false;
405
926
  type HasFuel<Config> = "fuel" extends keyof Config ? true : false;
@@ -414,7 +935,11 @@ type EcosystemTuple<Config> = [
414
935
  ];
415
936
  type EcosystemCount<Config> = EcosystemTuple<Config>["length"];
416
937
 
417
- // EVM ecosystem type
938
+ // EVM ecosystem type — includes chains plus handler registration methods.
939
+ // NOTE: options use inline { contract: C; event: E } shape for TypeScript inference.
940
+ // Using EvmOnEventOptions<Contracts[C][E]> would break inference since C/E can't be
941
+ // derived from indexed access types. The named EvmOnEventOptions type is for end-user
942
+ // reference; the inline shape here is structurally identical.
418
943
  type EvmEcosystem<Config extends IndexerConfigTypes> =
419
944
  "evm" extends keyof Config
420
945
  ? Config["evm"] extends {
@@ -437,12 +962,69 @@ type EvmEcosystem<Config extends IndexerConfigTypes> =
437
962
  ContractName extends string ? ContractName : never
438
963
  >;
439
964
  };
965
+ /**
966
+ * Register a block handler. `where` is evaluated once per configured
967
+ * chain at registration time; return `false` to skip a chain, `true`
968
+ * to match every block, or an {@link EvmOnBlockFilter} describing a
969
+ * block-number range and stride. Always available regardless of
970
+ * whether `contracts` are configured — block handlers don't need
971
+ * any contract context.
972
+ */
973
+ readonly onBlock: (
974
+ options: EvmOnBlockOptions<Config>,
975
+ handler: EvmOnBlockHandler<Config>,
976
+ ) => void;
977
+ } & (Config["evm"] extends {
978
+ contracts: infer Contracts extends Record<string, Record<string, any>>;
440
979
  }
980
+ ? {
981
+ /** Register an event handler. */
982
+ readonly onEvent: <
983
+ C extends keyof Contracts & string,
984
+ E extends keyof Contracts[C] & string
985
+ >(
986
+ options: {
987
+ readonly contract: C;
988
+ readonly event: E;
989
+ readonly wildcard?: boolean;
990
+ readonly where?: EvmOnEventWhere<
991
+ EvmEventFilters<Config>[C] extends Record<string, any>
992
+ ? EvmEventFilters<Config>[C][E & keyof EvmEventFilters<Config>[C]] extends { readonly params: infer P }
993
+ ? P
994
+ : {}
995
+ : {},
996
+ C
997
+ >;
998
+ },
999
+ handler: EvmOnEventHandler<Contracts[C][E], EvmOnEventContext<Config>>
1000
+ ) => void;
1001
+ /** Register a contract register handler for dynamic contract indexing. */
1002
+ readonly contractRegister: <
1003
+ C extends keyof Contracts & string,
1004
+ E extends keyof Contracts[C] & string
1005
+ >(
1006
+ options: {
1007
+ readonly contract: C;
1008
+ readonly event: E;
1009
+ readonly wildcard?: boolean;
1010
+ readonly where?: EvmOnEventWhere<
1011
+ EvmEventFilters<Config>[C] extends Record<string, any>
1012
+ ? EvmEventFilters<Config>[C][E & keyof EvmEventFilters<Config>[C]] extends { readonly params: infer P }
1013
+ ? P
1014
+ : {}
1015
+ : {},
1016
+ C
1017
+ >;
1018
+ },
1019
+ handler: EvmContractRegisterHandler<Contracts[C][E], EvmContractRegisterContext<Config>>
1020
+ ) => void;
1021
+ }
1022
+ : {})
441
1023
  : never
442
1024
  : never
443
1025
  : never;
444
1026
 
445
- // Fuel ecosystem type
1027
+ // Fuel ecosystem type — chains plus handler registration methods.
446
1028
  type FuelEcosystem<Config extends IndexerConfigTypes> =
447
1029
  "fuel" extends keyof Config
448
1030
  ? Config["fuel"] extends {
@@ -465,12 +1047,64 @@ type FuelEcosystem<Config extends IndexerConfigTypes> =
465
1047
  ContractName extends string ? ContractName : never
466
1048
  >;
467
1049
  };
1050
+ /** Register a Fuel block handler. See `EvmEcosystem.onBlock` for
1051
+ * `where` semantics; Fuel filters on `block.height`. Always
1052
+ * available regardless of whether `contracts` are configured. */
1053
+ readonly onBlock: (
1054
+ options: FuelOnBlockOptions<Config>,
1055
+ handler: FuelOnBlockHandler<Config>,
1056
+ ) => void;
1057
+ } & (Config["fuel"] extends {
1058
+ contracts: infer Contracts extends Record<string, Record<string, any>>;
468
1059
  }
1060
+ ? {
1061
+ /** Register an event handler. */
1062
+ readonly onEvent: <
1063
+ C extends keyof Contracts & string,
1064
+ E extends keyof Contracts[C] & string
1065
+ >(
1066
+ options: {
1067
+ readonly contract: C;
1068
+ readonly event: E;
1069
+ readonly wildcard?: boolean;
1070
+ readonly where?: FuelOnEventWhere<
1071
+ FuelEventFilters<Config>[C] extends Record<string, any>
1072
+ ? FuelEventFilters<Config>[C][E & keyof FuelEventFilters<Config>[C]] extends { readonly params: infer P }
1073
+ ? P
1074
+ : {}
1075
+ : {},
1076
+ C
1077
+ >;
1078
+ },
1079
+ handler: FuelOnEventHandler<Contracts[C][E], FuelOnEventContext<Config>>
1080
+ ) => void;
1081
+ /** Register a contract register handler for dynamic contract indexing. */
1082
+ readonly contractRegister: <
1083
+ C extends keyof Contracts & string,
1084
+ E extends keyof Contracts[C] & string
1085
+ >(
1086
+ options: {
1087
+ readonly contract: C;
1088
+ readonly event: E;
1089
+ readonly wildcard?: boolean;
1090
+ readonly where?: FuelOnEventWhere<
1091
+ FuelEventFilters<Config>[C] extends Record<string, any>
1092
+ ? FuelEventFilters<Config>[C][E & keyof FuelEventFilters<Config>[C]] extends { readonly params: infer P }
1093
+ ? P
1094
+ : {}
1095
+ : {},
1096
+ C
1097
+ >;
1098
+ },
1099
+ handler: FuelContractRegisterHandler<Contracts[C][E], FuelContractRegisterContext<Config>>
1100
+ ) => void;
1101
+ }
1102
+ : {})
469
1103
  : never
470
1104
  : never
471
1105
  : never;
472
1106
 
473
- // SVM ecosystem type
1107
+ // SVM ecosystem type — chains plus onSlot handler method. SVM has no onEvent yet.
474
1108
  type SvmEcosystem<Config extends IndexerConfigTypes> =
475
1109
  "svm" extends keyof Config
476
1110
  ? Config["svm"] extends { chains: infer Chains }
@@ -484,12 +1118,23 @@ type SvmEcosystem<Config extends IndexerConfigTypes> =
484
1118
  } & {
485
1119
  readonly [K in keyof Chains]: SvmChain<Chains[K]["id"]>;
486
1120
  };
1121
+ /**
1122
+ * Register a slot handler. `where` is evaluated once per configured
1123
+ * chain at registration time; return `false` to skip a chain, `true`
1124
+ * to match every slot, or an {@link SvmOnSlotFilter} describing a
1125
+ * slot range and stride.
1126
+ */
1127
+ readonly onSlot: (
1128
+ options: SvmOnSlotOptions<Config>,
1129
+ handler: SvmOnSlotHandler<Config>,
1130
+ ) => void;
487
1131
  }
488
1132
  : never
489
1133
  : never
490
1134
  : never;
491
1135
 
492
- // Single ecosystem chains (flattened at root level)
1136
+ // Single ecosystem chains (flattened at root level). Includes handler methods
1137
+ // since, for single-ecosystem indexers, they live at root alongside `chains`.
493
1138
  type SingleEcosystemChains<Config extends IndexerConfigTypes> =
494
1139
  HasEvm<Config> extends true
495
1140
  ? EvmEcosystem<Config>
@@ -499,7 +1144,9 @@ type SingleEcosystemChains<Config extends IndexerConfigTypes> =
499
1144
  ? SvmEcosystem<Config>
500
1145
  : {};
501
1146
 
502
- // Multi-ecosystem chains (namespaced by ecosystem)
1147
+ // Multi-ecosystem chains (namespaced by ecosystem). Each ecosystem branch
1148
+ // includes its handler registration methods — mirrors the runtime object
1149
+ // built in `Main.res`.
503
1150
  type MultiEcosystemChains<Config extends IndexerConfigTypes> =
504
1151
  (HasEvm<Config> extends true
505
1152
  ? {
@@ -608,6 +1255,16 @@ type FuelTestIndexerChainConfig<Config extends IndexerConfigTypes> = {
608
1255
  simulate?: FuelSimulateItem<Config>[];
609
1256
  };
610
1257
 
1258
+ /** Configuration for a single SVM chain in the test indexer. SVM has no
1259
+ * `onEvent` handlers yet, so simulate items aren't supported — only slot
1260
+ * range overrides for driving `indexer.onSlot` block handlers under test. */
1261
+ type SvmTestIndexerChainConfig = {
1262
+ /** The slot number to start processing from. Defaults to config startBlock or progressBlock+1. */
1263
+ startBlock?: number;
1264
+ /** The slot number to stop processing at. */
1265
+ endBlock?: number;
1266
+ };
1267
+
611
1268
  /** Entity change value containing sets and/or deleted IDs. */
612
1269
  type EntityChangeValue<Entity> = {
613
1270
  /** Entities that were created or updated. */
@@ -674,6 +1331,13 @@ type FuelChainIds<Config extends IndexerConfigTypes> =
674
1331
  : never
675
1332
  : never;
676
1333
 
1334
+ type SvmChainIds<Config extends IndexerConfigTypes> =
1335
+ Config["svm"] extends { chains: infer Chains }
1336
+ ? Chains extends Record<string, { id: number }>
1337
+ ? Chains[keyof Chains]["id"]
1338
+ : never
1339
+ : never;
1340
+
677
1341
  // Per-ecosystem chain config mappings
678
1342
  type EvmTestChains<Config extends IndexerConfigTypes> =
679
1343
  HasEvm<Config> extends true
@@ -685,15 +1349,129 @@ type FuelTestChains<Config extends IndexerConfigTypes> =
685
1349
  ? { [K in FuelChainIds<Config>]?: FuelTestIndexerChainConfig<Config> }
686
1350
  : {};
687
1351
 
1352
+ type SvmTestChains<Config extends IndexerConfigTypes> =
1353
+ HasSvm<Config> extends true
1354
+ ? { [K in SvmChainIds<Config>]?: SvmTestIndexerChainConfig }
1355
+ : {};
1356
+
688
1357
  /** Process configuration for the test indexer, with chains keyed by chain ID. */
689
1358
  export type TestIndexerProcessConfig<Config extends IndexerConfigTypes> = {
690
1359
  /** Chain configurations keyed by chain ID. Each chain specifies start and end blocks. */
691
1360
  chains: Prettify<
692
1361
  EvmTestChains<Config> &
693
- FuelTestChains<Config>
1362
+ FuelTestChains<Config> &
1363
+ SvmTestChains<Config>
694
1364
  >;
695
1365
  };
696
1366
 
1367
+ // Per-ecosystem test-indexer ecosystem types — structurally the `chainIds +
1368
+ // chains` slice of the real ecosystem types, but without the handler
1369
+ // registration methods (the test indexer doesn't let you register new
1370
+ // handlers, only run the existing ones over simulated or persisted data).
1371
+ // Kept as a separate type family so the real and test surfaces can evolve
1372
+ // independently without one silently lying about the other.
1373
+ type EvmTestEcosystem<Config extends IndexerConfigTypes> =
1374
+ "evm" extends keyof Config
1375
+ ? Config["evm"] extends {
1376
+ chains: infer Chains;
1377
+ contracts?: Record<infer ContractName, any>;
1378
+ }
1379
+ ? Chains extends Record<string, { id: number }>
1380
+ ? {
1381
+ /** Array of all EVM chain IDs. */
1382
+ readonly chainIds: readonly Chains[keyof Chains]["id"][];
1383
+ /** Per-chain configuration keyed by chain name or ID. */
1384
+ readonly chains: {
1385
+ readonly [K in Chains[keyof Chains]["id"]]: EvmChain<
1386
+ K,
1387
+ ContractName extends string ? ContractName : never
1388
+ >;
1389
+ } & {
1390
+ readonly [K in keyof Chains]: EvmChain<
1391
+ Chains[K]["id"],
1392
+ ContractName extends string ? ContractName : never
1393
+ >;
1394
+ };
1395
+ }
1396
+ : never
1397
+ : never
1398
+ : never;
1399
+
1400
+ type FuelTestEcosystem<Config extends IndexerConfigTypes> =
1401
+ "fuel" extends keyof Config
1402
+ ? Config["fuel"] extends {
1403
+ chains: infer Chains;
1404
+ contracts?: Record<infer ContractName, any>;
1405
+ }
1406
+ ? Chains extends Record<string, { id: number }>
1407
+ ? {
1408
+ /** Array of all Fuel chain IDs. */
1409
+ readonly chainIds: readonly Chains[keyof Chains]["id"][];
1410
+ /** Per-chain configuration keyed by chain name or ID. */
1411
+ readonly chains: {
1412
+ readonly [K in Chains[keyof Chains]["id"]]: FuelChain<
1413
+ K,
1414
+ ContractName extends string ? ContractName : never
1415
+ >;
1416
+ } & {
1417
+ readonly [K in keyof Chains]: FuelChain<
1418
+ Chains[K]["id"],
1419
+ ContractName extends string ? ContractName : never
1420
+ >;
1421
+ };
1422
+ }
1423
+ : never
1424
+ : never
1425
+ : never;
1426
+
1427
+ type SvmTestEcosystem<Config extends IndexerConfigTypes> =
1428
+ "svm" extends keyof Config
1429
+ ? Config["svm"] extends { chains: infer Chains }
1430
+ ? Chains extends Record<string, { id: number }>
1431
+ ? {
1432
+ /** Array of all SVM chain IDs. */
1433
+ readonly chainIds: readonly Chains[keyof Chains]["id"][];
1434
+ /** Per-chain configuration keyed by chain name or ID. */
1435
+ readonly chains: {
1436
+ readonly [K in Chains[keyof Chains]["id"]]: SvmChain<K>;
1437
+ } & {
1438
+ readonly [K in keyof Chains]: SvmChain<Chains[K]["id"]>;
1439
+ };
1440
+ }
1441
+ : never
1442
+ : never
1443
+ : never;
1444
+
1445
+ // Test-side single/multi chain selectors, parallel to the real ones.
1446
+ type SingleEcosystemTestChains<Config extends IndexerConfigTypes> =
1447
+ HasEvm<Config> extends true
1448
+ ? EvmTestEcosystem<Config>
1449
+ : HasFuel<Config> extends true
1450
+ ? FuelTestEcosystem<Config>
1451
+ : HasSvm<Config> extends true
1452
+ ? SvmTestEcosystem<Config>
1453
+ : {};
1454
+
1455
+ type MultiEcosystemTestChains<Config extends IndexerConfigTypes> =
1456
+ (HasEvm<Config> extends true
1457
+ ? {
1458
+ /** EVM ecosystem configuration. */
1459
+ readonly evm: EvmTestEcosystem<Config>;
1460
+ }
1461
+ : {}) &
1462
+ (HasFuel<Config> extends true
1463
+ ? {
1464
+ /** Fuel ecosystem configuration. */
1465
+ readonly fuel: FuelTestEcosystem<Config>;
1466
+ }
1467
+ : {}) &
1468
+ (HasSvm<Config> extends true
1469
+ ? {
1470
+ /** SVM ecosystem configuration. */
1471
+ readonly svm: SvmTestEcosystem<Config>;
1472
+ }
1473
+ : {});
1474
+
697
1475
  /**
698
1476
  * Test indexer type resolved from config.
699
1477
  * Allows running the indexer for specific block ranges and inspecting results.
@@ -707,10 +1485,16 @@ export type TestIndexerFromConfig<Config extends IndexerConfigTypes> = {
707
1485
  readonly changes: readonly EntityChange<Config>[];
708
1486
  }>;
709
1487
  } & (EcosystemCount<Config> extends 1
710
- ? SingleEcosystemChains<Config>
711
- : MultiEcosystemChains<Config>) & {
1488
+ ? SingleEcosystemTestChains<Config>
1489
+ : MultiEcosystemTestChains<Config>) & {
712
1490
  /** Entity operations for direct manipulation outside of handlers. */
713
1491
  readonly [K in keyof ConfigEntities<Config>]: TestIndexerEntityOperations<
714
1492
  ConfigEntities<Config>[K]
715
1493
  >;
716
1494
  };
1495
+
1496
+ // ============== Runtime values ==============
1497
+
1498
+ // `never` steers callers to the project-typed `generated` re-export.
1499
+ export declare const indexer: never;
1500
+ export declare const createTestIndexer: () => never;