envio 3.10.0-subgraph → 3.11.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 (76) hide show
  1. package/evm.schema.json +21 -4
  2. package/fuel.schema.json +21 -4
  3. package/index.d.ts +1 -1
  4. package/package.json +6 -6
  5. package/src/ChainState.res +9 -73
  6. package/src/ChainState.res.mjs +6 -58
  7. package/src/Config.res +73 -27
  8. package/src/Config.res.mjs +71 -21
  9. package/src/Core.res +0 -15
  10. package/src/Core.res.mjs +0 -11
  11. package/src/EventConfigBuilder.res +14 -10
  12. package/src/EventConfigBuilder.res.mjs +6 -4
  13. package/src/EventProcessing.res +6 -6
  14. package/src/EventProcessing.res.mjs +6 -8
  15. package/src/HandlerLoader.res +8 -20
  16. package/src/HandlerLoader.res.mjs +2 -11
  17. package/src/HandlerRegister.res +12 -11
  18. package/src/HandlerRegister.res.mjs +6 -5
  19. package/src/Internal.res +20 -4
  20. package/src/Internal.res.mjs +9 -0
  21. package/src/Main.res +8 -1
  22. package/src/Main.res.mjs +3 -3
  23. package/src/Persistence.res +11 -1
  24. package/src/Persistence.res.mjs +6 -2
  25. package/src/PgStorage.res +1 -1
  26. package/src/PgStorage.res.mjs +1 -1
  27. package/src/SimulateItems.res +13 -6
  28. package/src/SimulateItems.res.mjs +7 -6
  29. package/src/TestIndexer.res +6 -5
  30. package/src/TestIndexer.res.mjs +5 -4
  31. package/src/UserContext.res +51 -358
  32. package/src/UserContext.res.mjs +35 -271
  33. package/src/db/InternalTable.res +1 -1
  34. package/src/db/InternalTable.res.mjs +2 -1
  35. package/src/sources/ChainSources.res +72 -0
  36. package/src/sources/ChainSources.res.mjs +55 -0
  37. package/src/sources/EvmHyperSyncSource.res +10 -22
  38. package/src/sources/EvmHyperSyncSource.res.mjs +9 -41
  39. package/src/sources/FuelHyperSyncSource.res +10 -18
  40. package/src/sources/FuelHyperSyncSource.res.mjs +8 -38
  41. package/src/sources/HyperSync.res +31 -0
  42. package/src/sources/HyperSync.res.mjs +31 -0
  43. package/src/sources/HyperSync.resi +16 -0
  44. package/src/sources/StartBlockResolver.res +173 -0
  45. package/src/sources/StartBlockResolver.res.mjs +134 -0
  46. package/src/sources/Svm.res +0 -51
  47. package/src/sources/Svm.res.mjs +0 -46
  48. package/src/sources/SvmHyperSyncSource.res +20 -9
  49. package/src/sources/SvmHyperSyncSource.res.mjs +34 -12
  50. package/svm.schema.json +78 -63
  51. package/src/subgraph/blocks.ts +0 -176
  52. package/src/subgraph/calls.ts +0 -217
  53. package/src/subgraph/conformance.ts +0 -102
  54. package/src/subgraph/division.ts +0 -133
  55. package/src/subgraph/errors.ts +0 -90
  56. package/src/subgraph/graph-ts-types/VERSION +0 -2
  57. package/src/subgraph/graph-ts-types/chain/arweave.d.ts +0 -70
  58. package/src/subgraph/graph-ts-types/chain/cosmos.d.ts +0 -327
  59. package/src/subgraph/graph-ts-types/chain/ethereum.d.ts +0 -233
  60. package/src/subgraph/graph-ts-types/chain/near.d.ts +0 -253
  61. package/src/subgraph/graph-ts-types/chain/starknet.d.ts +0 -32
  62. package/src/subgraph/graph-ts-types/common/collections.d.ts +0 -136
  63. package/src/subgraph/graph-ts-types/common/conversion.d.ts +0 -11
  64. package/src/subgraph/graph-ts-types/common/datasource.d.ts +0 -30
  65. package/src/subgraph/graph-ts-types/common/eager-offset.d.ts +0 -0
  66. package/src/subgraph/graph-ts-types/common/json.d.ts +0 -17
  67. package/src/subgraph/graph-ts-types/common/numbers.d.ts +0 -120
  68. package/src/subgraph/graph-ts-types/common/value.d.ts +0 -120
  69. package/src/subgraph/graph-ts-types/common/yaml.d.ts +0 -90
  70. package/src/subgraph/graph-ts-types/global/global.d.ts +0 -194
  71. package/src/subgraph/graph-ts-types/helper-functions.d.ts +0 -22
  72. package/src/subgraph/graph-ts-types/index.d.ts +0 -102
  73. package/src/subgraph/graph-ts.ts +0 -1948
  74. package/src/subgraph/hosts.ts +0 -148
  75. package/src/subgraph/runtime.ts +0 -863
  76. package/src/subgraph/scope.ts +0 -66
@@ -1,37 +1,3 @@
1
- // Thrown by a sync op that can't be served from memory. Owned by envio so the
2
- // subgraph runtime's replay loop can tell it apart from a real handler error.
3
- // User code never swallows it: AssemblyScript has no try/catch, and the traps
4
- // keep re-throwing it while the context stays aborted.
5
- exception Suspend
6
-
7
- let isSuspend = exn =>
8
- switch exn {
9
- | Suspend => true
10
- | _ => false
11
- }
12
-
13
- type contextStatus =
14
- | Active
15
- // Set by a suspended sync op. Every trap access and every op closure
16
- // re-throws the stored error, so even a caught suspend stops the handler
17
- // at its next context interaction.
18
- | Aborted(exn)
19
- | Resolved
20
-
21
- // Held by reference so the entity sub-proxies, which copy the rest of the
22
- // params by value, observe the same lifecycle as the handler context.
23
- type syncState = {
24
- mutable status: contextStatus,
25
- // Ops scheduled by this round's suspended reads, awaited before the replay.
26
- mutable pending: option<array<promise<unit>>>,
27
- // Effect outputs already resolved for this handler invocation. Replay rounds
28
- // and the preload -> execute transition reuse them even when the in-memory
29
- // effect table drops the entry (`cache: false`).
30
- mutable memo: option<dict<Internal.effectOutput>>,
31
- }
32
-
33
- let makeSyncState = () => {status: Active, pending: None, memo: None}
34
-
35
1
  type contextParams = {
36
2
  item: Internal.item,
37
3
  checkpointId: Internal.checkpointId,
@@ -41,45 +7,7 @@ type contextParams = {
41
7
  isPreload: bool,
42
8
  chains: Internal.chains,
43
9
  config: Config.t,
44
- sync: syncState,
45
- }
46
-
47
- let getPending = (sync: syncState) =>
48
- switch sync.pending {
49
- | Some(pending) => pending
50
- | None =>
51
- let pending = []
52
- sync.pending = Some(pending)
53
- pending
54
- }
55
-
56
- let getMemo = (sync: syncState) =>
57
- switch sync.memo {
58
- | Some(memo) => memo
59
- | None =>
60
- let memo = Dict.make()
61
- sync.memo = Some(memo)
62
- memo
63
- }
64
-
65
- let checkStatusOrThrow = (params: contextParams, ~access: string) =>
66
- switch params.sync.status {
67
- | Active => ()
68
- | Aborted(exn) => throw(exn)
69
- | Resolved =>
70
- Utils.Error.make(
71
- `Impossible to access ${access} after the handler is resolved. Make sure you didn't miss an await in the handler.`,
72
- )->ErrorHandling.mkLogAndRaise(
73
- ~logger=Ecosystem.getItemLogger(params.item, ~ecosystem=params.config.ecosystem),
74
- )
75
- }
76
-
77
- // Fires the async op behind a sync miss, records it for the replay loop and
78
- // aborts the context.
79
- let scheduleAndSuspend = (params: contextParams, promise: promise<'a>): 'b => {
80
- params.sync->getPending->Array.push(promise->Utils.Promise.ignoreValue)
81
- params.sync.status = Aborted(Suspend)
82
- throw(Suspend)
10
+ mutable isResolved: bool,
83
11
  }
84
12
 
85
13
  // We don't want to expose the params to the user
@@ -130,15 +58,8 @@ external makeEffectContext: (
130
58
  ~callEffect: (Internal.effect, Internal.effectInput) => promise<Internal.effectOutput>,
131
59
  ) => Internal.effectContext = "EffectContext"
132
60
 
133
- // Builds the scope and args a call to `effect` resolves against. Split out of
134
- // `initEffect` so the sync caller derives the same cache key and scope without
135
- // duplicating the nested-caller rules.
136
- let rec prepareEffectCall = (
137
- params: contextParams,
138
- ~effect: Internal.effect,
139
- ~input: Internal.effectInput,
140
- ~caller: option<Internal.effect>,
141
- ) => {
61
+ let initEffect = (params: contextParams) => {
62
+ let handlerChainId = params.item->Internal.getItemChainId
142
63
  // An effect that didn't state a scope follows the config: cross-chain by
143
64
  // default, per-chain under `disable_default_cross_chain`.
144
65
  let isCrossChain = (effect: Internal.effect) =>
@@ -146,101 +67,50 @@ let rec prepareEffectCall = (
146
67
  // A chain-scoped effect always resolves against the chain of the handler that
147
68
  // triggered the call, even several effects deep, so the chain id is captured
148
69
  // once from the item and reused for the whole nested-call tree.
149
- let scope = effect->isCrossChain
150
- ? Internal.CrossChain
151
- : Internal.Chain(params.item->Internal.getItemChainId)
152
-
153
- switch caller {
154
- | Some(callerEffect) if callerEffect->isCrossChain && !(effect->isCrossChain) =>
155
- // A cross-chain effect isn't tied to a single chain, so it has no chain
156
- // to resolve a chain-scoped child against. Reject before any cache work.
157
- JsError.throwWithMessage(
158
- `The cross-chain effect "${callerEffect.name}" cannot call the chain-scoped effect "${effect.name}", because a cross-chain effect isn't tied to a single chain. Make "${effect.name}" cross-chain (\`crossChain: true\`), or make "${callerEffect.name}" chain-scoped (\`crossChain: false\`).`,
159
- )
160
- | _ => ()
161
- }
162
-
163
- let effectContext = makeEffectContext(
164
- params,
165
- ~chainId=switch scope {
166
- | Internal.Chain(chainId) => Some(chainId)
167
- | Internal.CrossChain => None
168
- },
169
- ~effectName=effect.name,
170
- ~defaultShouldCache=effect.defaultShouldCache,
171
- // Nested calls made by the effect handler itself stay async: only the
172
- // handler that started the sync run needs a sync answer.
173
- ~callEffect=(nested, nestedInput) =>
174
- params->callEffectAsync(~effect=nested, ~input=nestedInput, ~caller=Some(effect)),
175
- )
176
- let effectArgs: Internal.effectArgs = {
177
- input,
178
- context: effectContext,
179
- cacheKey: input->S.reverseConvertOrThrow(effect.input)->Utils.Hash.makeOrThrow,
180
- checkpointId: params.checkpointId,
181
- }
182
- (scope, effectArgs)
183
- }
184
-
185
- and callEffectAsync = (params: contextParams, ~effect, ~input, ~caller) => {
186
- let (scope, effectArgs) = params->prepareEffectCall(~effect, ~input, ~caller)
187
- LoadLayer.loadEffect(
188
- ~loadManager=params.loadManager,
189
- ~persistence=params.persistence,
190
- ~effect,
191
- ~effectArgs,
192
- ~scope,
193
- ~indexerState=params.indexerState,
194
- ~shouldGroup=params.isPreload,
195
- ~item=params.item,
196
- ~ecosystem=params.config.ecosystem,
197
- )
198
- }
199
-
200
- let initEffect = (params: contextParams) => {
201
- (effect: Internal.effect, input: Internal.effectInput) => {
202
- params->checkStatusOrThrow(~access="context.effect")
203
- params->callEffectAsync(~effect, ~input, ~caller=None)
204
- }
205
- }
206
-
207
- let effectMemoKey = (~effect: Internal.effect, ~scope: Internal.chainScope, ~cacheKey) =>
208
- switch scope {
209
- | CrossChain => `${effect.name}.${cacheKey}`
210
- | Chain(chainId) => `${effect.name}.${chainId->ChainId.toString}.${cacheKey}`
211
- }
212
-
213
- let initEffectSync = (params: contextParams) => {
214
- (effect: Internal.effect, input: Internal.effectInput) => {
215
- params->checkStatusOrThrow(~access="context.effectSync")
216
- let (scope, effectArgs) = params->prepareEffectCall(~effect, ~input, ~caller=None)
217
- let memo = params.sync->getMemo
218
- let memoKey = effectMemoKey(~effect, ~scope, ~cacheKey=effectArgs.cacheKey)
219
- switch memo->Utils.Dict.dangerouslyGetNonOption(memoKey) {
220
- | Some(output) => output
221
- | None =>
222
- let inMemTable = params.indexerState->InMemoryStore.getEffectInMemTable(~effect, ~scope)
223
- if inMemTable->InMemoryStore.hasEffectOutput(effectArgs.cacheKey) {
224
- let output = inMemTable->InMemoryStore.getEffectOutputUnsafe(effectArgs.cacheKey)
225
- memo->Dict.set(memoKey, output)
226
- output
227
- } else {
228
- params->scheduleAndSuspend(
229
- LoadLayer.loadEffect(
230
- ~loadManager=params.loadManager,
231
- ~persistence=params.persistence,
232
- ~effect,
233
- ~effectArgs,
234
- ~scope,
235
- ~indexerState=params.indexerState,
236
- ~shouldGroup=params.isPreload,
237
- ~item=params.item,
238
- ~ecosystem=params.config.ecosystem,
239
- ),
70
+ let rec makeCaller = (~caller: option<Internal.effect>) => {
71
+ (effect: Internal.effect, input: Internal.effectInput) => {
72
+ let scope = effect->isCrossChain ? Internal.CrossChain : Internal.Chain(handlerChainId)
73
+
74
+ switch caller {
75
+ | Some(callerEffect) if callerEffect->isCrossChain && !(effect->isCrossChain) =>
76
+ // A cross-chain effect isn't tied to a single chain, so it has no chain
77
+ // to resolve a chain-scoped child against. Reject before any cache work.
78
+ JsError.throwWithMessage(
79
+ `The cross-chain effect "${callerEffect.name}" cannot call the chain-scoped effect "${effect.name}", because a cross-chain effect isn't tied to a single chain. Make "${effect.name}" cross-chain (\`crossChain: true\`), or make "${callerEffect.name}" chain-scoped (\`crossChain: false\`).`,
240
80
  )
81
+ | _ => ()
241
82
  }
83
+
84
+ let effectContext = makeEffectContext(
85
+ params,
86
+ ~chainId=switch scope {
87
+ | Internal.Chain(chainId) => Some(chainId)
88
+ | Internal.CrossChain => None
89
+ },
90
+ ~effectName=effect.name,
91
+ ~defaultShouldCache=effect.defaultShouldCache,
92
+ ~callEffect=makeCaller(~caller=Some(effect)),
93
+ )
94
+ let effectArgs: Internal.effectArgs = {
95
+ input,
96
+ context: effectContext,
97
+ cacheKey: input->S.reverseConvertOrThrow(effect.input)->Utils.Hash.makeOrThrow,
98
+ checkpointId: params.checkpointId,
99
+ }
100
+ LoadLayer.loadEffect(
101
+ ~loadManager=params.loadManager,
102
+ ~persistence=params.persistence,
103
+ ~effect,
104
+ ~effectArgs,
105
+ ~scope,
106
+ ~indexerState=params.indexerState,
107
+ ~shouldGroup=params.isPreload,
108
+ ~item=params.item,
109
+ ~ecosystem=params.config.ecosystem,
110
+ )
242
111
  }
243
112
  }
113
+ makeCaller(~caller=None)
244
114
  }
245
115
 
246
116
  type entityContextParams = {
@@ -296,111 +166,15 @@ let throwClickHouseReadOnly = (entityConfig: Internal.entityConfig, op: string)
296
166
  `context.${entityConfig.name}.${op}() is unavailable: ClickHouse storage is currently write-only. Follow Envio releases to be notified when ClickHouse supports both reads and writes from handlers.`,
297
167
  )
298
168
 
299
- // A sync read against the in-memory entity table: a hit is returned as-is
300
- // (including a recorded absence), a miss schedules the async load and suspends.
301
- let getSyncHandler = (params: entityContextParams, entityId: string) => {
302
- let inMemTable =
303
- params.indexerState->InMemoryStore.getInMemTable(
304
- ~entityConfig=params.entityConfig,
305
- ~scope=params->entityScope,
306
- )
307
- if inMemTable.latestEntityChangeById->Dict.has(entityId) {
308
- (inMemTable->InMemoryTable.Entity.getUnsafe)(entityId)
309
- } else {
310
- (params :> contextParams)->scheduleAndSuspend(
311
- LoadLayer.loadById(
312
- ~loadManager=params.loadManager,
313
- ~persistence=params.persistence,
314
- ~entityConfig=params.entityConfig,
315
- ~scope=params->entityScope,
316
- ~indexerState=params.indexerState,
317
- ~shouldGroup=params.isPreload,
318
- ~item=params.item,
319
- ~ecosystem=params.config.ecosystem,
320
- ~entityId,
321
- ),
322
- )
323
- }
324
- }
325
-
326
- let getWhereSyncHandler = (params: entityContextParams, filter: dict<dict<unknown>>) => {
327
- let entityConfig = params.entityConfig
328
- let inMemTable =
329
- params.indexerState->InMemoryStore.getInMemTable(~entityConfig, ~scope=params->entityScope)
330
- let hasIndex = inMemTable->InMemoryTable.Entity.hasIndex
331
- let getOnIndex = inMemTable->InMemoryTable.Entity.getUnsafeOnIndex
332
-
333
- let filters =
334
- filter->EntityFilter.parseGetWhereOrThrow(
335
- ~entityName=entityConfig.name,
336
- ~table=entityConfig.table,
337
- )
338
-
339
- let missing = []
340
- let entities = []
341
- filters->Array.forEach(filter => {
342
- let filterKey = filter->EntityFilter.toString
343
- if hasIndex(filterKey) {
344
- entities->Array.pushMany(getOnIndex(filterKey))
345
- } else {
346
- missing->Array.push(
347
- LoadLayer.loadByFilter(
348
- ~loadManager=params.loadManager,
349
- ~persistence=params.persistence,
350
- ~entityConfig,
351
- ~scope=params->entityScope,
352
- ~indexerState=params.indexerState,
353
- ~shouldGroup=params.isPreload,
354
- ~item=params.item,
355
- ~ecosystem=params.config.ecosystem,
356
- ~filter,
357
- )->Utils.Promise.ignoreValue,
358
- )
359
- }
360
- })
361
-
362
- if missing->Utils.Array.notEmpty {
363
- let pending = params.sync->getPending
364
- missing->Array.forEach(promise => pending->Array.push(promise))
365
- params.sync.status = Aborted(Suspend)
366
- throw(Suspend)
367
- }
368
- entities
369
- }
370
-
371
- // Never suspends: the in-memory table spans the whole batch, so a change only
372
- // counts as "in this block" when it was written at this handler's checkpoint.
373
- let getInBlockSyncHandler = (params: entityContextParams, entityId: string) => {
374
- let inMemTable =
375
- params.indexerState->InMemoryStore.getInMemTable(
376
- ~entityConfig=params.entityConfig,
377
- ~scope=params->entityScope,
378
- )
379
- switch inMemTable.latestEntityChangeById->Utils.Dict.dangerouslyGetNonOption(entityId) {
380
- | Some(change) if change->Change.getCheckpointId == params.checkpointId =>
381
- change->InMemoryTable.Entity.mapChangeToEntity
382
- | _ => None
383
- }
384
- }
385
-
386
169
  let entityTraps: Utils.Proxy.traps<entityContextParams> = {
387
170
  get: (~target as params, ~prop: unknown) => {
388
171
  let prop = prop->(Utils.magic: unknown => string)
389
172
 
390
- (params :> contextParams)->checkStatusOrThrow(
391
- ~access=`context.${params.entityConfig.name}.${prop}`,
392
- )
393
-
394
173
  let isClickHouseOnly = !params.entityConfig.storage.postgres
395
174
 
396
175
  let set = params.isPreload
397
176
  ? noopSet
398
177
  : (entity: Internal.entity) => {
399
- // The check lives inside the closure too: a handler that grabbed
400
- // `context.X.set` before a suspend must not keep writing.
401
- (params :> contextParams)->checkStatusOrThrow(
402
- ~access=`context.${params.entityConfig.name}.set`,
403
- )
404
178
  params.indexerState
405
179
  ->InMemoryStore.getInMemTable(~entityConfig=params.entityConfig, ~scope=params->entityScope)
406
180
  ->InMemoryTable.Entity.set(
@@ -414,18 +188,6 @@ let entityTraps: Utils.Proxy.traps<entityContextParams> = {
414
188
  }
415
189
 
416
190
  switch prop {
417
- | "getSync" =>
418
- (entityId => params->getSyncHandler(entityId))->(
419
- Utils.magic: (string => option<Internal.entity>) => unknown
420
- )
421
- | "getWhereSync" =>
422
- (
423
- filter => params->getWhereSyncHandler(filter->(Utils.magic: unknown => dict<dict<unknown>>))
424
- )->(Utils.magic: (unknown => array<Internal.entity>) => unknown)
425
- | "getInBlockSync" =>
426
- (entityId => params->getInBlockSyncHandler(entityId))->(
427
- Utils.magic: (string => option<Internal.entity>) => unknown
428
- )
429
191
  | "get" =>
430
192
  if isClickHouseOnly {
431
193
  ((_entityId: string) => throwClickHouseReadOnly(params.entityConfig, "get"))->(
@@ -525,9 +287,6 @@ let entityTraps: Utils.Proxy.traps<entityContextParams> = {
525
287
  noopDeleteUnsafe
526
288
  } else {
527
289
  entityId => {
528
- (params :> contextParams)->checkStatusOrThrow(
529
- ~access=`context.${params.entityConfig.name}.deleteUnsafe`,
530
- )
531
290
  params.indexerState
532
291
  ->InMemoryStore.getInMemTable(~entityConfig=params.entityConfig, ~scope=params->entityScope)
533
292
  ->InMemoryTable.Entity.set(
@@ -545,83 +304,17 @@ let entityTraps: Utils.Proxy.traps<entityContextParams> = {
545
304
  },
546
305
  }
547
306
 
548
- // Deterministic mappings always make progress, so the cap only exists to turn
549
- // a non-deterministic one into a clear error instead of a hang.
550
- let maxSyncRounds = 10000
551
-
552
- // Runs a synchronous body, replaying it from the top each time it suspends on
553
- // a read that wasn't in memory yet.
554
- let rec runSyncRound = async (params: contextParams, fn: unit => unit, ~round) => {
555
- if round > maxSyncRounds {
556
- JsError.throwWithMessage(
557
- `The handler suspended on a synchronous read too many times: gave up after ${maxSyncRounds->Int.toString} rounds. This usually means the code isn't deterministic across reruns.`,
558
- )
559
- }
560
- params.sync.status = Active
561
- params.sync.pending = None
562
-
563
- let suspended = switch fn() {
564
- | () => false
565
- | exception exn =>
566
- if exn->isSuspend {
567
- true
568
- } else {
569
- // The body threw its own error after scheduling a read. Those ops are
570
- // nobody's result now, and an unhandled rejection would replace the
571
- // error the handler actually raised.
572
- switch params.sync.pending {
573
- | None => ()
574
- | Some(pending) => {
575
- params.sync.pending = None
576
- pending->Array.forEach(promise => promise->Utils.Promise.silentCatch->ignore)
577
- }
578
- }
579
- throw(exn)
580
- }
581
- }
582
-
583
- switch params.sync.pending {
584
- | None => ()
585
- | Some(pending) =>
586
- params.sync.pending = None
587
- if suspended {
588
- let errors = []
589
- let _ =
590
- await pending
591
- ->Array.map(promise =>
592
- promise->Promise.catch(exn => {
593
- errors->Array.push(exn)
594
- Promise.resolve()
595
- })
596
- )
597
- ->Promise.all
598
- switch errors->Array.get(0) {
599
- | Some(exn) => throw(exn)
600
- | None => ()
601
- }
602
- await params->runSyncRound(fn, ~round=round + 1)
603
- } else {
604
- // The body swallowed the suspend and returned anyway. The scheduled ops
605
- // are nobody's result now, but they must not surface as unhandled
606
- // rejections.
607
- pending->Array.forEach(promise => promise->Utils.Promise.silentCatch->ignore)
608
- }
609
- }
610
- }
611
-
612
307
  let handlerTraps: Utils.Proxy.traps<contextParams> = {
613
308
  get: (~target as params, ~prop: unknown) => {
614
309
  let prop = prop->(Utils.magic: unknown => string)
615
- params->checkStatusOrThrow(~access=`context.${prop}`)
616
- switch prop {
617
- | "effectSync" =>
618
- initEffectSync((params :> contextParams))->(
619
- Utils.magic: ((Internal.effect, Internal.effectInput) => Internal.effectOutput) => unknown
620
- )
621
- | "runSync" =>
622
- (fn => params->runSyncRound(fn, ~round=1))->(
623
- Utils.magic: ((unit => unit) => promise<unit>) => unknown
310
+ if params.isResolved {
311
+ Utils.Error.make(
312
+ `Impossible to access context.${prop} after the handler is resolved. Make sure you didn't miss an await in the handler.`,
313
+ )->ErrorHandling.mkLogAndRaise(
314
+ ~logger=Ecosystem.getItemLogger(params.item, ~ecosystem=params.config.ecosystem),
624
315
  )
316
+ }
317
+ switch prop {
625
318
  | "log" =>
626
319
  (
627
320
  params.isPreload
@@ -653,7 +346,7 @@ let handlerTraps: Utils.Proxy.traps<contextParams> = {
653
346
  persistence: params.persistence,
654
347
  checkpointId: params.checkpointId,
655
348
  chains: params.chains,
656
- sync: params.sync,
349
+ isResolved: params.isResolved,
657
350
  config: params.config,
658
351
  entityConfig,
659
352
  }