envio 3.5.1 → 3.6.1-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.
- package/evm.schema.json +7 -0
- package/fuel.schema.json +7 -0
- package/index.d.ts +33 -10
- package/package.json +6 -6
- package/src/ChainState.res +22 -4
- package/src/ChainState.res.mjs +20 -3
- package/src/ChainState.resi +3 -0
- package/src/Config.res +72 -2
- package/src/Config.res.mjs +57 -15
- package/src/Core.res +15 -0
- package/src/Core.res.mjs +11 -0
- package/src/EffectState.res +2 -2
- package/src/EffectState.res.mjs +2 -2
- package/src/EntityTables.res +32 -0
- package/src/EntityTables.res.mjs +44 -0
- package/src/Envio.res +7 -7
- package/src/Envio.res.mjs +5 -6
- package/src/EventProcessing.res +6 -6
- package/src/EventProcessing.res.mjs +8 -6
- package/src/HandlerLoader.res +20 -8
- package/src/HandlerLoader.res.mjs +11 -2
- package/src/Hasura.res +37 -3
- package/src/Hasura.res.mjs +23 -5
- package/src/InMemoryStore.res +48 -8
- package/src/InMemoryStore.res.mjs +37 -7
- package/src/IndexerState.res +30 -24
- package/src/IndexerState.res.mjs +20 -35
- package/src/IndexerState.resi +8 -6
- package/src/Internal.res +29 -11
- package/src/Internal.res.mjs +27 -10
- package/src/LoadLayer.res +39 -8
- package/src/LoadLayer.res.mjs +36 -9
- package/src/LoadLayer.resi +2 -0
- package/src/Metrics.res +5 -5
- package/src/Metrics.res.mjs +5 -1
- package/src/Persistence.res +12 -1
- package/src/PgStorage.res +183 -37
- package/src/PgStorage.res.mjs +149 -34
- package/src/PruneStaleHistory.res +1 -0
- package/src/PruneStaleHistory.res.mjs +2 -1
- package/src/Sink.res +3 -3
- package/src/Sink.res.mjs +2 -2
- package/src/TestIndexer.res +128 -17
- package/src/TestIndexer.res.mjs +75 -9
- package/src/UserContext.res +375 -53
- package/src/UserContext.res.mjs +281 -39
- package/src/Writing.res +8 -16
- package/src/Writing.res.mjs +10 -10
- package/src/bindings/ClickHouse.res +40 -4
- package/src/bindings/ClickHouse.res.mjs +37 -5
- package/src/db/EntityHistory.res +64 -21
- package/src/db/EntityHistory.res.mjs +43 -22
- package/src/db/InternalTable.res.mjs +31 -31
- package/src/db/Table.res +24 -1
- package/src/db/Table.res.mjs +26 -4
- package/src/sources/SourceManager.res +6 -12
- package/src/sources/SourceManager.res.mjs +8 -5
- package/src/subgraph/blocks.ts +176 -0
- package/src/subgraph/calls.ts +213 -0
- package/src/subgraph/conformance.ts +99 -0
- package/src/subgraph/division.ts +100 -0
- package/src/subgraph/errors.ts +90 -0
- package/src/subgraph/graph-ts-types/VERSION +2 -0
- package/src/subgraph/graph-ts-types/chain/arweave.d.ts +70 -0
- package/src/subgraph/graph-ts-types/chain/cosmos.d.ts +327 -0
- package/src/subgraph/graph-ts-types/chain/ethereum.d.ts +233 -0
- package/src/subgraph/graph-ts-types/chain/near.d.ts +253 -0
- package/src/subgraph/graph-ts-types/chain/starknet.d.ts +32 -0
- package/src/subgraph/graph-ts-types/common/collections.d.ts +136 -0
- package/src/subgraph/graph-ts-types/common/conversion.d.ts +11 -0
- package/src/subgraph/graph-ts-types/common/datasource.d.ts +30 -0
- package/src/subgraph/graph-ts-types/common/eager-offset.d.ts +0 -0
- package/src/subgraph/graph-ts-types/common/json.d.ts +17 -0
- package/src/subgraph/graph-ts-types/common/numbers.d.ts +120 -0
- package/src/subgraph/graph-ts-types/common/value.d.ts +120 -0
- package/src/subgraph/graph-ts-types/common/yaml.d.ts +90 -0
- package/src/subgraph/graph-ts-types/global/global.d.ts +194 -0
- package/src/subgraph/graph-ts-types/helper-functions.d.ts +22 -0
- package/src/subgraph/graph-ts-types/index.d.ts +102 -0
- package/src/subgraph/graph-ts.ts +1695 -0
- package/src/subgraph/hosts.ts +148 -0
- package/src/subgraph/runtime.ts +827 -0
- package/src/subgraph/scope.ts +63 -0
- package/svm.schema.json +7 -0
package/src/TestIndexer.res
CHANGED
|
@@ -70,6 +70,19 @@ let getIndexingAddressesByChain = (state: testIndexerState): dict<
|
|
|
70
70
|
byChain
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
+
// Rows of a per-chain entity are keyed per (chain, id): the same id exists
|
|
74
|
+
// independently on every chain.
|
|
75
|
+
let rowKey = (~scope: Internal.chainScope, ~entityId: EntityId.t) =>
|
|
76
|
+
switch scope {
|
|
77
|
+
| CrossChain => entityId->EntityId.toKey
|
|
78
|
+
| Chain(chainId) => `${chainId->ChainId.toString}|${entityId->EntityId.toKey}`
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
let readChainId = (entity: Internal.entity, ~field: Table.field): option<ChainId.t> =>
|
|
82
|
+
entity
|
|
83
|
+
->(Utils.magic: Internal.entity => dict<ChainId.t>)
|
|
84
|
+
->Utils.Dict.dangerouslyGetNonOption(field.fieldName)
|
|
85
|
+
|
|
73
86
|
let handleLoad = (state: testIndexerState, ~tableName: string, ~filter: EntityFilter.t): array<
|
|
74
87
|
Internal.entity,
|
|
75
88
|
> => {
|
|
@@ -79,16 +92,28 @@ let handleLoad = (state: testIndexerState, ~tableName: string, ~filter: EntityFi
|
|
|
79
92
|
// entityConfig.
|
|
80
93
|
switch state.entityConfigs->Dict.get(tableName) {
|
|
81
94
|
| None => []
|
|
82
|
-
| Some(
|
|
95
|
+
| Some(entityConfig) =>
|
|
83
96
|
let entityDict = state.entities->Dict.get(tableName)->Option.getOr(Dict.make())
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
97
|
+
let matched =
|
|
98
|
+
entityDict
|
|
99
|
+
->Dict.valuesToArray
|
|
100
|
+
->Array.filter(entity => {
|
|
101
|
+
// The store holds decoded entities and the filter carries decoded values,
|
|
102
|
+
// so compare directly (same approach as InMemoryTable) — no JSON round-trip.
|
|
103
|
+
let entityAsDict = entity->(Utils.magic: Internal.entity => dict<EntityFilter.FieldValue.t>)
|
|
104
|
+
filter->EntityFilter.matches(~entity=entityAsDict)
|
|
105
|
+
})
|
|
106
|
+
// The chain is already fixed by the scope the load ran for, so the loaded
|
|
107
|
+
// entity is handed back in the shape the handlers see.
|
|
108
|
+
switch entityConfig.table->Table.getChainIdField {
|
|
109
|
+
| None => matched
|
|
110
|
+
| Some(field) =>
|
|
111
|
+
matched->Array.map(entity => {
|
|
112
|
+
let copy = entity->(Utils.magic: Internal.entity => dict<unknown>)->Utils.Dict.shallowCopy
|
|
113
|
+
copy->Utils.Dict.deleteInPlace(field.fieldName)
|
|
114
|
+
copy->(Utils.magic: dict<unknown> => Internal.entity)
|
|
115
|
+
})
|
|
116
|
+
}
|
|
92
117
|
}
|
|
93
118
|
}
|
|
94
119
|
|
|
@@ -104,8 +129,11 @@ let handleWriteBatch = (
|
|
|
104
129
|
// checkpointId -> entityName -> entityChange
|
|
105
130
|
let changesByCheckpoint: dict<dict<entityChange>> = Dict.make()
|
|
106
131
|
|
|
107
|
-
updatedEntities->Array.forEach(({entityConfig, changes}: Persistence.updatedEntity) => {
|
|
132
|
+
updatedEntities->Array.forEach(({entityConfig, scope, changes}: Persistence.updatedEntity) => {
|
|
108
133
|
let entityName = entityConfig.name
|
|
134
|
+
// The scope is what makes a per-chain row identifiable, so it's stamped
|
|
135
|
+
// onto the stored entity the same way the Postgres write path does.
|
|
136
|
+
let chainIdField = entityConfig.table->Table.getChainIdField
|
|
109
137
|
let entityDict = switch state.entities->Dict.get(entityName) {
|
|
110
138
|
| Some(dict) => dict
|
|
111
139
|
| None =>
|
|
@@ -138,10 +166,17 @@ let handleWriteBatch = (
|
|
|
138
166
|
// The store keeps decoded entities so load comparisons (bigint /
|
|
139
167
|
// BigDecimal) work on real values. Ids are keyed by their string form
|
|
140
168
|
// since they may be string/int/bigint.
|
|
141
|
-
|
|
169
|
+
let storedEntity = switch (chainIdField, scope->Internal.chainScopeChainId) {
|
|
170
|
+
| (Some(field), Some(chainId)) =>
|
|
171
|
+
entity->Internal.stampChainId(~fieldName=field.fieldName, ~chainId)
|
|
172
|
+
| _ => entity
|
|
173
|
+
}
|
|
174
|
+
entityDict->Dict.set(rowKey(~scope, ~entityId), storedEntity)
|
|
175
|
+
// The change already carries the checkpoint's chainId, so the entity
|
|
176
|
+
// inside it stays unstamped.
|
|
142
177
|
entityChangeFor(checkpointId).sets->Array.push(entity->Utils.magic)->ignore
|
|
143
178
|
| Delete({entityId, checkpointId}) =>
|
|
144
|
-
Dict.delete(entityDict->Obj.magic, entityId
|
|
179
|
+
Dict.delete(entityDict->Obj.magic, rowKey(~scope, ~entityId))
|
|
145
180
|
entityChangeFor(checkpointId).deleted->Array.push(entityId)->ignore
|
|
146
181
|
}
|
|
147
182
|
}
|
|
@@ -379,7 +414,10 @@ let copyEntity = (entity: Internal.entity): Internal.entity =>
|
|
|
379
414
|
->Utils.Dict.shallowCopy
|
|
380
415
|
->(Utils.magic: dict<unknown> => Internal.entity)
|
|
381
416
|
|
|
382
|
-
// Entity operations for direct manipulation outside of handlers
|
|
417
|
+
// Entity operations for direct manipulation outside of handlers. Unlike a
|
|
418
|
+
// handler, which always runs on a known chain, these are chain-agnostic — so a
|
|
419
|
+
// per-chain entity is looked up across every chain and an id present on more
|
|
420
|
+
// than one is an error rather than an arbitrary pick.
|
|
383
421
|
let getEntityFromState = (
|
|
384
422
|
~state: testIndexerState,
|
|
385
423
|
~entityConfig: Internal.entityConfig,
|
|
@@ -392,7 +430,25 @@ let getEntityFromState = (
|
|
|
392
430
|
)
|
|
393
431
|
}
|
|
394
432
|
let entityDict = state.entities->Dict.get(entityConfig.name)->Option.getOr(Dict.make())
|
|
395
|
-
|
|
433
|
+
switch entityConfig.table->Table.getChainIdField {
|
|
434
|
+
| None => entityDict->Dict.get(entityId)->Option.map(copyEntity)
|
|
435
|
+
| Some(field) =>
|
|
436
|
+
let matches = entityDict->Dict.valuesToArray->Array.filter(entity => entity.id === entityId)
|
|
437
|
+
switch matches {
|
|
438
|
+
| [] => None
|
|
439
|
+
| [entity] => Some(copyEntity(entity))
|
|
440
|
+
| _ =>
|
|
441
|
+
let chains =
|
|
442
|
+
matches
|
|
443
|
+
->Array.map(entity =>
|
|
444
|
+
entity->readChainId(~field)->Option.mapOr("unknown", ChainId.toString)
|
|
445
|
+
)
|
|
446
|
+
->Array.join(", ")
|
|
447
|
+
JsError.throwWithMessage(
|
|
448
|
+
`Entity \`${entityConfig.name}\` with id \`${entityId}\` exists on multiple chains (${chains}) — use getWhere({${field.fieldName}: {_eq: ...}}) to pick one.`,
|
|
449
|
+
)
|
|
450
|
+
}
|
|
451
|
+
}
|
|
396
452
|
}
|
|
397
453
|
|
|
398
454
|
let makeEntityGet = (~state: testIndexerState, ~entityConfig: Internal.entityConfig): (
|
|
@@ -435,7 +491,23 @@ let makeEntitySet = (~state: testIndexerState, ~entityConfig: Internal.entityCon
|
|
|
435
491
|
state.entities->Dict.set(entityConfig.name, dict)
|
|
436
492
|
dict
|
|
437
493
|
}
|
|
438
|
-
|
|
494
|
+
// Outside a handler there's no chain in context, so a per-chain entity has
|
|
495
|
+
// to say which chain the row belongs to.
|
|
496
|
+
let scope = switch entityConfig.table->Table.getChainIdField {
|
|
497
|
+
| None => Internal.CrossChain
|
|
498
|
+
| Some(field) =>
|
|
499
|
+
switch entity->readChainId(~field) {
|
|
500
|
+
| Some(chainId) => Internal.Chain(chainId)
|
|
501
|
+
| None =>
|
|
502
|
+
JsError.throwWithMessage(
|
|
503
|
+
`${entityConfig.name}.set() requires a \`${field.fieldName}\` because the entity is per-chain. Pass it alongside the entity fields.`,
|
|
504
|
+
)
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
entityDict->Dict.set(
|
|
508
|
+
rowKey(~scope, ~entityId=entity.id->EntityId.unsafeOfString),
|
|
509
|
+
copyEntity(entity),
|
|
510
|
+
)
|
|
439
511
|
}
|
|
440
512
|
}
|
|
441
513
|
|
|
@@ -453,9 +525,43 @@ let makeEntityGetAll = (~state: testIndexerState, ~entityConfig: Internal.entity
|
|
|
453
525
|
}
|
|
454
526
|
}
|
|
455
527
|
|
|
528
|
+
// The same filter syntax as `context.X.getWhere` in a handler, matched against
|
|
529
|
+
// the store instead of a database. A per-chain entity's rows carry their chain
|
|
530
|
+
// id, so `{chainId: {_eq: 1}}` is what narrows an id that exists on several
|
|
531
|
+
// chains.
|
|
532
|
+
let makeEntityGetWhere = (~state: testIndexerState, ~entityConfig: Internal.entityConfig): (
|
|
533
|
+
dict<dict<unknown>> => promise<array<Internal.entity>>
|
|
534
|
+
) => {
|
|
535
|
+
filter => {
|
|
536
|
+
if state.processInProgress {
|
|
537
|
+
JsError.throwWithMessage(
|
|
538
|
+
`Cannot call ${entityConfig.name}.getWhere() while indexer.process() is running. ` ++ "Wait for process() to complete before accessing entities directly.",
|
|
539
|
+
)
|
|
540
|
+
}
|
|
541
|
+
let filters =
|
|
542
|
+
filter->EntityFilter.parseGetWhereOrThrow(
|
|
543
|
+
~entityName=entityConfig.name,
|
|
544
|
+
~table=entityConfig.table,
|
|
545
|
+
)
|
|
546
|
+
let entityDict = state.entities->Dict.get(entityConfig.name)->Option.getOr(Dict.make())
|
|
547
|
+
// parseGetWhereOrThrow expands an operator group into alternatives whose
|
|
548
|
+
// matches are disjoint, so the union needs no dedup.
|
|
549
|
+
Promise.resolve(
|
|
550
|
+
entityDict
|
|
551
|
+
->Dict.valuesToArray
|
|
552
|
+
->Array.filter(entity => {
|
|
553
|
+
let entityAsDict = entity->(Utils.magic: Internal.entity => dict<EntityFilter.FieldValue.t>)
|
|
554
|
+
filters->Array.some(filter => filter->EntityFilter.matches(~entity=entityAsDict))
|
|
555
|
+
})
|
|
556
|
+
->Array.map(copyEntity),
|
|
557
|
+
)
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
|
|
456
561
|
type entityOperations = {
|
|
457
562
|
get: string => promise<option<Internal.entity>>,
|
|
458
563
|
getAll: unit => promise<array<Internal.entity>>,
|
|
564
|
+
getWhere: dict<dict<unknown>> => promise<array<Internal.entity>>,
|
|
459
565
|
getOrThrow: (string, ~message: string=?) => promise<Internal.entity>,
|
|
460
566
|
set: Internal.entity => unit,
|
|
461
567
|
}
|
|
@@ -507,8 +613,12 @@ let makeInMemoryStorage = (~state: testIndexerState): Persistence.storage => {
|
|
|
507
613
|
reset: async () => (),
|
|
508
614
|
setChainMeta: async _ => Obj.magic(),
|
|
509
615
|
pruneStaleCheckpoints: async (~safeCheckpointId as _) => (),
|
|
510
|
-
pruneStaleEntityHistory: async (
|
|
511
|
-
|
|
616
|
+
pruneStaleEntityHistory: async (
|
|
617
|
+
~entityName as _,
|
|
618
|
+
~entityIndex as _,
|
|
619
|
+
~chainIdColumn as _,
|
|
620
|
+
~safeCheckpointId as _,
|
|
621
|
+
) => (),
|
|
512
622
|
getRollbackTargetCheckpoint: async (~reorgChainId as _, ~lastKnownValidBlockNumber as _) =>
|
|
513
623
|
JsError.throwWithMessage(
|
|
514
624
|
"TestIndexer: Rollback is not supported. The runner forces rollbackOnReorg off, so this should be unreachable.",
|
|
@@ -625,6 +735,7 @@ let createTestIndexer = (): t<'processConfig> => {
|
|
|
625
735
|
{
|
|
626
736
|
get: makeEntityGet(~state, ~entityConfig),
|
|
627
737
|
getAll: makeEntityGetAll(~state, ~entityConfig),
|
|
738
|
+
getWhere: makeEntityGetWhere(~state, ~entityConfig),
|
|
628
739
|
getOrThrow: makeEntityGetOrThrow(~state, ~entityConfig),
|
|
629
740
|
set: makeEntitySet(~state, ~entityConfig),
|
|
630
741
|
},
|
package/src/TestIndexer.res.mjs
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
2
|
|
|
3
3
|
import * as Env from "./Env.res.mjs";
|
|
4
|
+
import * as Table from "./db/Table.res.mjs";
|
|
4
5
|
import * as Utils from "./Utils.res.mjs";
|
|
5
6
|
import * as Config from "./Config.res.mjs";
|
|
6
7
|
import * as ChainId from "./ChainId.res.mjs";
|
|
7
8
|
import * as Logging from "./Logging.res.mjs";
|
|
8
9
|
import * as ChainMap from "./ChainMap.res.mjs";
|
|
9
10
|
import * as EntityId from "./EntityId.res.mjs";
|
|
11
|
+
import * as Internal from "./Internal.res.mjs";
|
|
10
12
|
import * as Stdlib_Int from "@rescript/runtime/lib/es6/Stdlib_Int.js";
|
|
11
13
|
import * as IndexerLoop from "./IndexerLoop.res.mjs";
|
|
12
14
|
import * as Persistence from "./Persistence.res.mjs";
|
|
@@ -55,19 +57,44 @@ function getIndexingAddressesByChain(state) {
|
|
|
55
57
|
return byChain;
|
|
56
58
|
}
|
|
57
59
|
|
|
60
|
+
function rowKey(scope, entityId) {
|
|
61
|
+
if (scope === "crossChain") {
|
|
62
|
+
return EntityId.toKey(entityId);
|
|
63
|
+
} else {
|
|
64
|
+
return ChainId.toString(scope) + `|` + EntityId.toKey(entityId);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function readChainId(entity, field) {
|
|
69
|
+
return entity[field.fieldName];
|
|
70
|
+
}
|
|
71
|
+
|
|
58
72
|
function handleLoad(state, tableName, filter) {
|
|
59
|
-
let
|
|
60
|
-
if (
|
|
73
|
+
let entityConfig = state.entityConfigs[tableName];
|
|
74
|
+
if (entityConfig === undefined) {
|
|
61
75
|
return [];
|
|
62
76
|
}
|
|
63
77
|
let entityDict = Stdlib_Option.getOr(state.entities[tableName], {});
|
|
64
|
-
|
|
78
|
+
let matched = Object.values(entityDict).filter(entity => EntityFilter.matches(filter, entity));
|
|
79
|
+
let field = Table.getChainIdField(entityConfig.table);
|
|
80
|
+
if (field !== undefined) {
|
|
81
|
+
return matched.map(entity => {
|
|
82
|
+
let copy = Utils.Dict.shallowCopy(entity);
|
|
83
|
+
Utils.Dict.deleteInPlace(copy, field.fieldName);
|
|
84
|
+
return copy;
|
|
85
|
+
});
|
|
86
|
+
} else {
|
|
87
|
+
return matched;
|
|
88
|
+
}
|
|
65
89
|
}
|
|
66
90
|
|
|
67
91
|
function handleWriteBatch(state, updatedEntities, checkpointIds, checkpointChainIds, checkpointBlockNumbers, checkpointEventsProcessed) {
|
|
68
92
|
let changesByCheckpoint = {};
|
|
69
93
|
updatedEntities.forEach(param => {
|
|
70
|
-
let
|
|
94
|
+
let scope = param.scope;
|
|
95
|
+
let entityConfig = param.entityConfig;
|
|
96
|
+
let entityName = entityConfig.name;
|
|
97
|
+
let chainIdField = Table.getChainIdField(entityConfig.table);
|
|
71
98
|
let dict = state.entities[entityName];
|
|
72
99
|
let entityDict;
|
|
73
100
|
if (dict !== undefined) {
|
|
@@ -104,12 +131,14 @@ function handleWriteBatch(state, updatedEntities, checkpointIds, checkpointChain
|
|
|
104
131
|
let processChange = change => {
|
|
105
132
|
if (change.type === "SET") {
|
|
106
133
|
let entity = change.entity;
|
|
107
|
-
|
|
134
|
+
let match = Internal.chainScopeChainId(scope);
|
|
135
|
+
let storedEntity = chainIdField !== undefined && match !== undefined ? Internal.stampChainId(entity, chainIdField.fieldName, Primitive_option.valFromOption(match)) : entity;
|
|
136
|
+
entityDict[rowKey(scope, change.entityId)] = storedEntity;
|
|
108
137
|
entityChangeFor(change.checkpointId).sets.push(entity);
|
|
109
138
|
return;
|
|
110
139
|
}
|
|
111
140
|
let entityId = change.entityId;
|
|
112
|
-
Stdlib_Dict.$$delete(entityDict,
|
|
141
|
+
Stdlib_Dict.$$delete(entityDict, rowKey(scope, entityId));
|
|
113
142
|
entityChangeFor(change.checkpointId).deleted.push(entityId);
|
|
114
143
|
};
|
|
115
144
|
param.changes.forEach(processChange);
|
|
@@ -265,7 +294,21 @@ function getEntityFromState(state, entityConfig, entityId, methodName) {
|
|
|
265
294
|
Stdlib_JsError.throwWithMessage(`Cannot call ` + entityConfig.name + `.` + methodName + `() while indexer.process() is running. ` + "Wait for process() to complete before accessing entities directly.");
|
|
266
295
|
}
|
|
267
296
|
let entityDict = Stdlib_Option.getOr(state.entities[entityConfig.name], {});
|
|
268
|
-
|
|
297
|
+
let field = Table.getChainIdField(entityConfig.table);
|
|
298
|
+
if (field === undefined) {
|
|
299
|
+
return Stdlib_Option.map(entityDict[entityId], copyEntity);
|
|
300
|
+
}
|
|
301
|
+
let matches = Object.values(entityDict).filter(entity => entity.id === entityId);
|
|
302
|
+
let len = matches.length;
|
|
303
|
+
if (len !== 1) {
|
|
304
|
+
if (len === 0) {
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
let chains = matches.map(entity => Stdlib_Option.mapOr(entity[field.fieldName], "unknown", ChainId.toString)).join(", ");
|
|
308
|
+
return Stdlib_JsError.throwWithMessage(`Entity \`` + entityConfig.name + `\` with id \`` + entityId + `\` exists on multiple chains (` + chains + `) — use getWhere({` + field.fieldName + `: {_eq: ...}}) to pick one.`);
|
|
309
|
+
}
|
|
310
|
+
let entity = matches[0];
|
|
311
|
+
return Utils.Dict.shallowCopy(entity);
|
|
269
312
|
}
|
|
270
313
|
|
|
271
314
|
function makeEntityGet(state, entityConfig) {
|
|
@@ -297,7 +340,15 @@ function makeEntitySet(state, entityConfig) {
|
|
|
297
340
|
state.entities[entityConfig.name] = dict$1;
|
|
298
341
|
entityDict = dict$1;
|
|
299
342
|
}
|
|
300
|
-
|
|
343
|
+
let field = Table.getChainIdField(entityConfig.table);
|
|
344
|
+
let scope;
|
|
345
|
+
if (field !== undefined) {
|
|
346
|
+
let chainId = entity[field.fieldName];
|
|
347
|
+
scope = chainId !== undefined ? Primitive_option.valFromOption(chainId) : Stdlib_JsError.throwWithMessage(entityConfig.name + `.set() requires a \`` + field.fieldName + `\` because the entity is per-chain. Pass it alongside the entity fields.`);
|
|
348
|
+
} else {
|
|
349
|
+
scope = "crossChain";
|
|
350
|
+
}
|
|
351
|
+
entityDict[rowKey(scope, entity.id)] = Utils.Dict.shallowCopy(entity);
|
|
301
352
|
};
|
|
302
353
|
}
|
|
303
354
|
|
|
@@ -311,6 +362,17 @@ function makeEntityGetAll(state, entityConfig) {
|
|
|
311
362
|
};
|
|
312
363
|
}
|
|
313
364
|
|
|
365
|
+
function makeEntityGetWhere(state, entityConfig) {
|
|
366
|
+
return filter => {
|
|
367
|
+
if (state.processInProgress) {
|
|
368
|
+
Stdlib_JsError.throwWithMessage(`Cannot call ` + entityConfig.name + `.getWhere() while indexer.process() is running. ` + "Wait for process() to complete before accessing entities directly.");
|
|
369
|
+
}
|
|
370
|
+
let filters = EntityFilter.parseGetWhereOrThrow(filter, entityConfig.name, entityConfig.table);
|
|
371
|
+
let entityDict = Stdlib_Option.getOr(state.entities[entityConfig.name], {});
|
|
372
|
+
return Promise.resolve(Object.values(entityDict).filter(entity => filters.some(filter => EntityFilter.matches(filter, entity))).map(copyEntity));
|
|
373
|
+
};
|
|
374
|
+
}
|
|
375
|
+
|
|
314
376
|
function makeInMemoryStorage(state) {
|
|
315
377
|
return {
|
|
316
378
|
name: "test-inmemory",
|
|
@@ -325,7 +387,7 @@ function makeInMemoryStorage(state) {
|
|
|
325
387
|
reset: async () => {},
|
|
326
388
|
setChainMeta: async param => {},
|
|
327
389
|
pruneStaleCheckpoints: async param => {},
|
|
328
|
-
pruneStaleEntityHistory: async (param, param$1, param$2) => {},
|
|
390
|
+
pruneStaleEntityHistory: async (param, param$1, param$2, param$3) => {},
|
|
329
391
|
getRollbackTargetCheckpoint: async (param, param$1) => Stdlib_JsError.throwWithMessage("TestIndexer: Rollback is not supported. The runner forces rollbackOnReorg off, so this should be unreachable."),
|
|
330
392
|
getRollbackProgressDiff: async param => Stdlib_JsError.throwWithMessage("TestIndexer: Rollback is not supported. The runner forces rollbackOnReorg off, so this should be unreachable."),
|
|
331
393
|
getRollbackData: async (param, param$1) => Stdlib_JsError.throwWithMessage("TestIndexer: Rollback is not supported. The runner forces rollbackOnReorg off, so this should be unreachable."),
|
|
@@ -407,6 +469,7 @@ function createTestIndexer() {
|
|
|
407
469
|
entityOpsDict[entityConfig.name] = {
|
|
408
470
|
get: makeEntityGet(state, entityConfig),
|
|
409
471
|
getAll: makeEntityGetAll(state, entityConfig),
|
|
472
|
+
getWhere: makeEntityGetWhere(state, entityConfig),
|
|
410
473
|
getOrThrow: makeEntityGetOrThrow(state, entityConfig),
|
|
411
474
|
set: makeEntitySet(state, entityConfig)
|
|
412
475
|
};
|
|
@@ -613,6 +676,8 @@ function createTestIndexer() {
|
|
|
613
676
|
export {
|
|
614
677
|
toIndexingAddress,
|
|
615
678
|
getIndexingAddressesByChain,
|
|
679
|
+
rowKey,
|
|
680
|
+
readChainId,
|
|
616
681
|
handleLoad,
|
|
617
682
|
handleWriteBatch,
|
|
618
683
|
makeInitialState,
|
|
@@ -626,6 +691,7 @@ export {
|
|
|
626
691
|
makeEntityGetOrThrow,
|
|
627
692
|
makeEntitySet,
|
|
628
693
|
makeEntityGetAll,
|
|
694
|
+
makeEntityGetWhere,
|
|
629
695
|
makeInMemoryStorage,
|
|
630
696
|
cloneRegistrations,
|
|
631
697
|
registrationsRef,
|