envio 3.13.0 → 3.14.0-subgraph
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin.mjs +10 -1
- package/index.d.ts +11 -0
- package/package.json +8 -20
- package/rescript.json +1 -4
- package/src/Config.res +78 -100
- package/src/Config.res.mjs +55 -64
- package/src/Core.res +67 -6
- package/src/Core.res.mjs +58 -4
- package/src/EventConfigBuilder.res +4 -10
- package/src/EventConfigBuilder.res.mjs +1 -3
- package/src/EventProcessing.res +6 -6
- package/src/EventProcessing.res.mjs +8 -6
- package/src/ExitOnCaughtUp.res +5 -1
- package/src/ExitOnCaughtUp.res.mjs +8 -3
- package/src/HandlerLoader.res +98 -36
- package/src/HandlerLoader.res.mjs +89 -24
- package/src/HandlerTypeCheck.mjs +14 -0
- package/src/HandlerTypeCheckWorker.mjs +68 -0
- package/src/Internal.res +1 -1
- package/src/LoadLayer.res +39 -7
- package/src/LoadLayer.res.mjs +65 -38
- package/src/LoadLayer.resi +28 -0
- package/src/Main.res +17 -12
- package/src/Main.res.mjs +9 -3
- package/src/NodeVersion.mjs +13 -0
- package/src/Persistence.res +79 -38
- package/src/Persistence.res.mjs +41 -10
- package/src/PgStorage.res +86 -39
- package/src/PgStorage.res.mjs +83 -32
- package/src/ResumePlan.res +172 -0
- package/src/ResumePlan.res.mjs +122 -0
- package/src/Supervisor.res +8 -8
- package/src/Supervisor.res.mjs +6 -1
- package/src/SyncWrites.res +86 -0
- package/src/SyncWrites.res.mjs +85 -0
- package/src/TestIndexer.res +14 -4
- package/src/TestIndexer.res.mjs +3 -2
- package/src/TsModuleHooks.mjs +121 -0
- package/src/UserContext.res +377 -62
- package/src/UserContext.res.mjs +297 -39
- package/src/Utils.res +0 -8
- package/src/Worker.res +8 -0
- package/src/Worker.res.mjs +11 -0
- package/src/bindings/NodeJs.res +8 -7
- package/src/bindings/NodeJs.res.mjs +0 -3
- package/src/bindings/Pino.res +2 -2
- package/src/db/InternalTable.res +52 -2
- package/src/db/InternalTable.res.mjs +32 -3
- package/src/db/Table.res +10 -0
- package/src/db/Table.res.mjs +22 -11
- package/src/sources/FuelHyperSync.res +2 -0
- package/src/sources/FuelHyperSync.res.mjs +1 -0
- package/src/sources/FuelHyperSync.resi +1 -0
- package/src/sources/FuelHyperSyncClient.res +25 -8
- package/src/sources/FuelHyperSyncClient.res.mjs +8 -2
- package/src/sources/FuelHyperSyncSource.res +24 -22
- package/src/sources/FuelHyperSyncSource.res.mjs +18 -13
- package/src/subgraph/abi-types.ts +63 -0
- package/src/subgraph/assemblyscript.ts +85 -0
- package/src/subgraph/blocks.ts +183 -0
- package/src/subgraph/calls.ts +149 -0
- package/src/subgraph/conformance.ts +83 -0
- package/src/subgraph/errors.ts +96 -0
- package/src/subgraph/graph-cli-worker.mjs +26 -0
- package/src/subgraph/graph-cli.ts +190 -0
- package/src/subgraph/graph-ts-enums.ts +28 -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 +2285 -0
- package/src/subgraph/hosts.ts +158 -0
- package/src/subgraph/rpc.ts +65 -0
- package/src/subgraph/runtime.ts +595 -0
- package/src/subgraph/scope.ts +68 -0
- package/src/tui/InitApi.res +85 -0
- package/src/tui/{components/CustomHooks.res.mjs → InitApi.res.mjs} +11 -64
- package/src/tui/Tui.res +142 -249
- package/src/tui/Tui.res.mjs +123 -332
- package/src/Js.shim.ts +0 -11
- package/src/bindings/DateFns.res +0 -74
- package/src/bindings/DateFns.res.mjs +0 -22
- package/src/sources/FuelSDK.res +0 -38
- package/src/sources/FuelSDK.res.mjs +0 -27
- package/src/sources/vendored-fuel-abi-coder.js +0 -1792
- package/src/tui/bindings/Ink.res +0 -369
- package/src/tui/bindings/Ink.res.mjs +0 -66
- package/src/tui/bindings/Style.res +0 -123
- package/src/tui/bindings/Style.res.mjs +0 -2
- package/src/tui/components/BufferedProgressBar.res +0 -40
- package/src/tui/components/BufferedProgressBar.res.mjs +0 -57
- package/src/tui/components/CustomHooks.res +0 -122
- package/src/tui/components/Messages.res +0 -40
- package/src/tui/components/Messages.res.mjs +0 -71
- package/src/tui/components/SyncETA.res +0 -179
- package/src/tui/components/SyncETA.res.mjs +0 -249
- package/src/tui/components/TuiData.res +0 -98
- package/src/tui/components/TuiData.res.mjs +0 -73
|
@@ -1,26 +1,46 @@
|
|
|
1
1
|
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
2
|
|
|
3
3
|
import * as Url from "url";
|
|
4
|
+
import * as Core from "./Core.res.mjs";
|
|
4
5
|
import * as Path from "path";
|
|
5
6
|
import * as Utils from "./Utils.res.mjs";
|
|
6
7
|
import * as Logging from "./Logging.res.mjs";
|
|
7
8
|
import * as Process from "process";
|
|
8
|
-
import * as
|
|
9
|
+
import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
|
|
9
10
|
import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
|
|
10
11
|
import * as Stdlib_Promise from "@rescript/runtime/lib/es6/Stdlib_Promise.js";
|
|
11
12
|
import * as HandlerRegister from "./HandlerRegister.res.mjs";
|
|
12
13
|
import * as Promises from "node:fs/promises";
|
|
14
|
+
import * as TsModuleHooksMjs from "./TsModuleHooks.mjs";
|
|
13
15
|
import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js";
|
|
16
|
+
import * as HandlerTypeCheckMjs from "./HandlerTypeCheck.mjs";
|
|
14
17
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
} catch (exn) {
|
|
18
|
-
|
|
18
|
+
function registerTsHooksWith(prim) {
|
|
19
|
+
TsModuleHooksMjs.register(prim);
|
|
19
20
|
}
|
|
20
21
|
|
|
21
|
-
function
|
|
22
|
-
let
|
|
23
|
-
|
|
22
|
+
function registerTsHooks() {
|
|
23
|
+
let addon = Core.getAddon();
|
|
24
|
+
TsModuleHooksMjs.register({
|
|
25
|
+
loadTs: addon.loadTs,
|
|
26
|
+
tsResolveCandidates: addon.tsResolveCandidates,
|
|
27
|
+
tsNotFoundCandidates: addon.tsNotFoundCandidates
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function isTypeScript(file) {
|
|
32
|
+
return /\.m?tsx?$/.test(file);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function toAbsolutePath(file) {
|
|
36
|
+
return Path.resolve(Process.cwd(), file);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async function importHandler(file) {
|
|
40
|
+
if (isTypeScript(file)) {
|
|
41
|
+
Core.getAddon().tsCheckHandlerFormat(toAbsolutePath(file));
|
|
42
|
+
}
|
|
43
|
+
return await import(Url.pathToFileURL(toAbsolutePath(file)).toString());
|
|
24
44
|
}
|
|
25
45
|
|
|
26
46
|
async function registerContractHandlers(contractName, handler) {
|
|
@@ -28,7 +48,7 @@ async function registerContractHandlers(contractName, handler) {
|
|
|
28
48
|
return;
|
|
29
49
|
}
|
|
30
50
|
try {
|
|
31
|
-
await
|
|
51
|
+
await importHandler(handler);
|
|
32
52
|
return;
|
|
33
53
|
} catch (raw_exn) {
|
|
34
54
|
let exn = Primitive_exceptions.internalToException(raw_exn);
|
|
@@ -38,35 +58,80 @@ async function registerContractHandlers(contractName, handler) {
|
|
|
38
58
|
}
|
|
39
59
|
}
|
|
40
60
|
|
|
41
|
-
async function
|
|
61
|
+
async function globAutoLoadFiles(handlers) {
|
|
42
62
|
let srcPattern = `./` + handlers + `/**/*.{js,mjs,ts}`;
|
|
43
|
-
let handlerFiles;
|
|
44
63
|
try {
|
|
45
64
|
let iterator = Promises.glob(srcPattern);
|
|
46
65
|
let files = await Utils.$$Array.fromAsyncIterator(iterator);
|
|
47
|
-
|
|
66
|
+
return files.filter(file => !(file.includes(".test.") || file.includes(".spec.") || file.includes("_test.")));
|
|
48
67
|
} catch (raw_exn) {
|
|
49
68
|
let exn = Primitive_exceptions.internalToException(raw_exn);
|
|
50
|
-
|
|
69
|
+
return Stdlib_JsError.throwWithMessage(`Failed to glob src/handlers directory for auto-loading handlers. Pattern: ` + srcPattern + `. Error: ` + Utils.prettifyExn(exn));
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async function getAutoLoadFiles(config) {
|
|
74
|
+
let match = config.subgraph;
|
|
75
|
+
if (match !== undefined) {
|
|
76
|
+
return [];
|
|
77
|
+
} else {
|
|
78
|
+
return await globAutoLoadFiles(config.handlers);
|
|
51
79
|
}
|
|
52
|
-
await Promise.all(handlerFiles.map(file => Stdlib_Promise.$$catch(import(toImportUrl(file)), exn => {
|
|
53
|
-
let cause = Utils.prettifyExn(exn);
|
|
54
|
-
Logging.errorWithExn(exn, `Failed to auto-load handler file: ` + file);
|
|
55
|
-
return Stdlib_JsError.throwWithMessage(`Failed to auto-load handler file: ` + file + `. Cause: ` + cause);
|
|
56
|
-
})));
|
|
57
80
|
}
|
|
58
81
|
|
|
59
|
-
|
|
82
|
+
function checkTypesInWorker(prim0, prim1) {
|
|
83
|
+
return HandlerTypeCheckMjs.check(prim0, prim1);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async function typeCheck(config, autoLoadFiles) {
|
|
87
|
+
let files = Array.from(new Set(autoLoadFiles.concat(Stdlib_Array.filterMap(config.contractHandlers, param => param.handler)).filter(isTypeScript).map(toAbsolutePath)));
|
|
88
|
+
if (files.length === 0) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
let match = await HandlerTypeCheckMjs.check(Process.cwd(), files);
|
|
92
|
+
let skipped = match.skipped;
|
|
93
|
+
let errors = match.errors;
|
|
94
|
+
if (errors !== undefined) {
|
|
95
|
+
return Stdlib_JsError.throwWithMessage(`Handler files have type errors:\n\n` + errors);
|
|
96
|
+
} else if (skipped !== undefined) {
|
|
97
|
+
return Logging.warn(skipped);
|
|
98
|
+
} else {
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
let registerSubgraph = ((subgraph, isDev) =>
|
|
104
|
+
import("./subgraph/runtime.ts").then((m) => m.registerSubgraph({ ...subgraph, isDev })));
|
|
105
|
+
|
|
106
|
+
async function registerAllHandlers(config, autoLoadFiles) {
|
|
60
107
|
HandlerRegister.startRegistration(config);
|
|
61
|
-
|
|
62
|
-
|
|
108
|
+
registerTsHooks();
|
|
109
|
+
let subgraph = config.subgraph;
|
|
110
|
+
if (subgraph !== undefined) {
|
|
111
|
+
await registerSubgraph(subgraph, config.isDev);
|
|
112
|
+
} else {
|
|
113
|
+
await Promise.all(autoLoadFiles.map(file => Stdlib_Promise.$$catch(importHandler(file), exn => {
|
|
114
|
+
let cause = Utils.prettifyExn(exn);
|
|
115
|
+
Logging.errorWithExn(exn, `Failed to auto-load handler file: ` + file);
|
|
116
|
+
return Stdlib_JsError.throwWithMessage(`Failed to auto-load handler file: ` + file + `. Cause: ` + cause);
|
|
117
|
+
})));
|
|
118
|
+
await Promise.all(config.contractHandlers.map(param => registerContractHandlers(param.name, param.handler)));
|
|
119
|
+
}
|
|
63
120
|
return HandlerRegister.finishRegistration(config);
|
|
64
121
|
}
|
|
65
122
|
|
|
66
123
|
export {
|
|
67
|
-
|
|
124
|
+
registerTsHooksWith,
|
|
125
|
+
registerTsHooks,
|
|
126
|
+
isTypeScript,
|
|
127
|
+
toAbsolutePath,
|
|
128
|
+
importHandler,
|
|
68
129
|
registerContractHandlers,
|
|
69
|
-
|
|
130
|
+
globAutoLoadFiles,
|
|
131
|
+
getAutoLoadFiles,
|
|
132
|
+
checkTypesInWorker,
|
|
133
|
+
typeCheck,
|
|
134
|
+
registerSubgraph,
|
|
70
135
|
registerAllHandlers,
|
|
71
136
|
}
|
|
72
|
-
/*
|
|
137
|
+
/* url Not a pure module */
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Worker } from "node:worker_threads";
|
|
2
|
+
|
|
3
|
+
// The compiler blocks the thread it runs on, so it gets its own and the
|
|
4
|
+
// database setup carries on meanwhile.
|
|
5
|
+
export const check = (cwd, files) =>
|
|
6
|
+
new Promise((resolve, reject) => {
|
|
7
|
+
const worker = new Worker(new URL("./HandlerTypeCheckWorker.mjs", import.meta.url), {
|
|
8
|
+
workerData: { cwd, files },
|
|
9
|
+
});
|
|
10
|
+
worker.once("message", resolve);
|
|
11
|
+
worker.once("error", reject);
|
|
12
|
+
// Settles nothing once the result has arrived.
|
|
13
|
+
worker.once("exit", () => reject(new Error("The handler type check exited without a result.")));
|
|
14
|
+
});
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { createRequire } from "node:module";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { stripVTControlCharacters } from "node:util";
|
|
5
|
+
import { parentPort, workerData } from "node:worker_threads";
|
|
6
|
+
|
|
7
|
+
const check = ({ cwd, files }) => {
|
|
8
|
+
// The ReScript compiler checks a ReScript project's handlers, so it has no
|
|
9
|
+
// reason to set up TypeScript, and the warning would only be noise there.
|
|
10
|
+
// `rescript.json` at the root is what makes codegen treat it as ReScript.
|
|
11
|
+
const skip = (warning) => (existsSync(path.join(cwd, "rescript.json")) ? {} : { skipped: warning });
|
|
12
|
+
|
|
13
|
+
let ts;
|
|
14
|
+
try {
|
|
15
|
+
// The project's own compiler, so handlers are checked exactly as the
|
|
16
|
+
// user's editor and `tsc` check them.
|
|
17
|
+
ts = createRequire(path.join(cwd, "package.json"))("typescript");
|
|
18
|
+
} catch {
|
|
19
|
+
return skip("Skipped the handler type check: the project doesn't depend on typescript.");
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const configPath = ts.findConfigFile(cwd, ts.sys.fileExists);
|
|
23
|
+
if (configPath === undefined) {
|
|
24
|
+
return skip(
|
|
25
|
+
"Skipped the handler type check: no tsconfig.json found. Add one to type-check handlers on start, like the one envio init creates."
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const host = {
|
|
30
|
+
getCanonicalFileName: (file) => file,
|
|
31
|
+
getCurrentDirectory: () => cwd,
|
|
32
|
+
getNewLine: () => "\n",
|
|
33
|
+
};
|
|
34
|
+
const format = (diagnostics) =>
|
|
35
|
+
stripVTControlCharacters(ts.formatDiagnosticsWithColorAndContext(diagnostics, host)).trimEnd();
|
|
36
|
+
|
|
37
|
+
const parsed = ts.getParsedCommandLineOfConfigFile(
|
|
38
|
+
configPath,
|
|
39
|
+
{ noEmit: true },
|
|
40
|
+
{
|
|
41
|
+
...ts.sys,
|
|
42
|
+
onUnRecoverableConfigFileDiagnostic: (diagnostic) => {
|
|
43
|
+
throw new Error(format([diagnostic]));
|
|
44
|
+
},
|
|
45
|
+
}
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
// The tsconfig's files are roots, as tsc makes them, since any of them can
|
|
49
|
+
// declare globals a handler relies on. Only the handlers' errors count.
|
|
50
|
+
const handlerFiles = files.map((file) => path.resolve(cwd, file));
|
|
51
|
+
const program = ts.createProgram({
|
|
52
|
+
rootNames: [...parsed.fileNames, ...handlerFiles],
|
|
53
|
+
options: parsed.options,
|
|
54
|
+
projectReferences: parsed.projectReferences,
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
// Every file is a root, so each has a source file. Without one,
|
|
58
|
+
// `getSemanticDiagnostics` would check the whole program.
|
|
59
|
+
const diagnostics = handlerFiles
|
|
60
|
+
.map((file) => program.getSourceFile(file))
|
|
61
|
+
.flatMap((sourceFile) => [
|
|
62
|
+
...program.getSyntacticDiagnostics(sourceFile),
|
|
63
|
+
...program.getSemanticDiagnostics(sourceFile),
|
|
64
|
+
]);
|
|
65
|
+
return diagnostics.length === 0 ? {} : { errors: format(diagnostics) };
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
parentPort.postMessage(check(workerData));
|
package/src/Internal.res
CHANGED
package/src/LoadLayer.res
CHANGED
|
@@ -332,6 +332,44 @@ let loadEffect = (
|
|
|
332
332
|
)
|
|
333
333
|
}
|
|
334
334
|
|
|
335
|
+
// Keying an _in walks every value, so it's computed once and handed on rather
|
|
336
|
+
// than recomputed by each reader. Before answering "not in memory", the rows
|
|
337
|
+
// already loaded for other filters are checked: they may cover this one.
|
|
338
|
+
let indexFilter = (inMemTable, ~filter, ~table) => {
|
|
339
|
+
let filterKey = filter->EntityFilter.toString(~table)
|
|
340
|
+
if !(inMemTable->InMemoryTable.Entity.hasIndex)(filterKey) {
|
|
341
|
+
inMemTable->InMemoryTable.Entity.tryIndexFromLoadedValues(~filter, ~table)
|
|
342
|
+
}
|
|
343
|
+
filterKey
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// A recorded absence is an answer too: `Some(None)` means the entity is known
|
|
347
|
+
// not to exist, so no load is needed to say so.
|
|
348
|
+
let getByIdInMemory = (
|
|
349
|
+
~entityConfig: Internal.entityConfig,
|
|
350
|
+
~scope,
|
|
351
|
+
~indexerState,
|
|
352
|
+
~entityId: string,
|
|
353
|
+
) => {
|
|
354
|
+
let inMemTable = indexerState->InMemoryStore.getInMemTable(~entityConfig, ~scope)
|
|
355
|
+
inMemTable.latestEntityChangeById->Dict.has(entityId)
|
|
356
|
+
? Some((inMemTable->InMemoryTable.Entity.getUnsafe)(entityId))
|
|
357
|
+
: None
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
let getByFilterInMemory = (
|
|
361
|
+
~entityConfig: Internal.entityConfig,
|
|
362
|
+
~scope,
|
|
363
|
+
~indexerState,
|
|
364
|
+
~filter: EntityFilter.t,
|
|
365
|
+
) => {
|
|
366
|
+
let inMemTable = indexerState->InMemoryStore.getInMemTable(~entityConfig, ~scope)
|
|
367
|
+
let filterKey = inMemTable->indexFilter(~filter, ~table=entityConfig.table)
|
|
368
|
+
(inMemTable->InMemoryTable.Entity.hasIndex)(filterKey)
|
|
369
|
+
? Some((inMemTable->InMemoryTable.Entity.getUnsafeOnIndex)(filterKey))
|
|
370
|
+
: None
|
|
371
|
+
}
|
|
372
|
+
|
|
335
373
|
let loadByParsedFilter = (
|
|
336
374
|
~loadManager,
|
|
337
375
|
~persistence: Persistence.t,
|
|
@@ -425,13 +463,7 @@ let loadByParsedFilter = (
|
|
|
425
463
|
)
|
|
426
464
|
}
|
|
427
465
|
|
|
428
|
-
|
|
429
|
-
// the load manager rather than recomputed by the hasher.
|
|
430
|
-
let filterKey = filter->EntityFilter.toString(~table=entityConfig.table)
|
|
431
|
-
|
|
432
|
-
if !(inMemTable->InMemoryTable.Entity.hasIndex)(filterKey) {
|
|
433
|
-
inMemTable->InMemoryTable.Entity.tryIndexFromLoadedValues(~filter, ~table=entityConfig.table)
|
|
434
|
-
}
|
|
466
|
+
let filterKey = inMemTable->indexFilter(~filter, ~table=entityConfig.table)
|
|
435
467
|
|
|
436
468
|
loadManager->LoadManager.call(
|
|
437
469
|
~key,
|
package/src/LoadLayer.res.mjs
CHANGED
|
@@ -15,6 +15,7 @@ import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
|
|
|
15
15
|
import * as ErrorHandling from "./ErrorHandling.res.mjs";
|
|
16
16
|
import * as InMemoryStore from "./InMemoryStore.res.mjs";
|
|
17
17
|
import * as InMemoryTable from "./InMemoryTable.res.mjs";
|
|
18
|
+
import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
|
|
18
19
|
import * as S$RescriptSchema from "rescript-schema/src/S.res.mjs";
|
|
19
20
|
import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js";
|
|
20
21
|
|
|
@@ -182,46 +183,69 @@ function loadEffect(loadManager, persistence, effect, effectArgs, scope, indexer
|
|
|
182
183
|
return LoadManager.call(loadManager, effectArgs, key, load, args => args.cacheKey, shouldGroup, hash => InMemoryStore.hasEffectOutput(inMemTable, hash), hash => InMemoryStore.getEffectOutputUnsafe(inMemTable, hash));
|
|
183
184
|
}
|
|
184
185
|
|
|
186
|
+
function indexFilter(inMemTable, filter, table) {
|
|
187
|
+
let filterKey = EntityFilter.toString(filter, table);
|
|
188
|
+
if (!InMemoryTable.Entity.hasIndex(inMemTable)(filterKey)) {
|
|
189
|
+
InMemoryTable.Entity.tryIndexFromLoadedValues(inMemTable, filter, table);
|
|
190
|
+
}
|
|
191
|
+
return filterKey;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function getByIdInMemory(entityConfig, scope, indexerState, entityId) {
|
|
195
|
+
let inMemTable = InMemoryStore.getInMemTable(indexerState, entityConfig, scope);
|
|
196
|
+
if (entityId in inMemTable.latestEntityChangeById) {
|
|
197
|
+
return Primitive_option.some(InMemoryTable.Entity.getUnsafe(inMemTable)(entityId));
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function getByFilterInMemory(entityConfig, scope, indexerState, filter) {
|
|
202
|
+
let inMemTable = InMemoryStore.getInMemTable(indexerState, entityConfig, scope);
|
|
203
|
+
let filterKey = indexFilter(inMemTable, filter, entityConfig.table);
|
|
204
|
+
if (InMemoryTable.Entity.hasIndex(inMemTable)(filterKey)) {
|
|
205
|
+
return InMemoryTable.Entity.getUnsafeOnIndex(inMemTable)(filterKey);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function loadByParsedFilter(loadManager, persistence, entityConfig, scope, indexerState, shouldGroup, item, ecosystem, filter) {
|
|
210
|
+
let key = EntityFilter.toOperationKey(filter, entityConfig.name) + scopeKeySuffix(scope);
|
|
211
|
+
let inMemTable = InMemoryStore.getInMemTable(indexerState, entityConfig, scope);
|
|
212
|
+
let load = async (filters, param) => {
|
|
213
|
+
let storage = Persistence.getInitializedStorageOrThrow(persistence);
|
|
214
|
+
let timerRef = IndexerState.startStorageLoad(indexerState, storage.name, key);
|
|
215
|
+
let size = {
|
|
216
|
+
contents: 0
|
|
217
|
+
};
|
|
218
|
+
filters.forEach(filter => InMemoryTable.Entity.addEmptyIndex(inMemTable, filter, entityConfig.table));
|
|
219
|
+
await storage.ensureQueryIndexes(entityConfig, scope, filters);
|
|
220
|
+
let queries = EntityFilter.merge(filters);
|
|
221
|
+
await Promise.all(queries.map(async filter => {
|
|
222
|
+
try {
|
|
223
|
+
let entities = await storage.loadOrThrow(EntityFilter.scoped(filter, entityConfig.table, scope), entityConfig.table);
|
|
224
|
+
let committedCheckpointId = IndexerState.committedCheckpointIdFor(indexerState, scope);
|
|
225
|
+
entities.forEach(entity => InMemoryTable.Entity.initValue(inMemTable, committedCheckpointId, entity.id, entity));
|
|
226
|
+
InMemoryTable.Entity.recordLoadedValues(inMemTable, filter, entityConfig.table);
|
|
227
|
+
size.contents = size.contents + entities.length | 0;
|
|
228
|
+
return;
|
|
229
|
+
} catch (raw_exn) {
|
|
230
|
+
let exn = Primitive_exceptions.internalToException(raw_exn);
|
|
231
|
+
if (exn.RE_EXN_ID === Persistence.StorageError) {
|
|
232
|
+
return ErrorHandling.mkLogAndRaise(Logging.createChildFrom(Ecosystem.getItemLogger(item, ecosystem), {
|
|
233
|
+
operation: key,
|
|
234
|
+
params: EntityFilter.getParams(filter)
|
|
235
|
+
}), exn.message, exn.reason);
|
|
236
|
+
}
|
|
237
|
+
throw exn;
|
|
238
|
+
}
|
|
239
|
+
}));
|
|
240
|
+
return IndexerState.endStorageLoad(indexerState, timerRef, storage.name, key, Stdlib_Array.reduce(queries, 0, (acc, query) => acc + EntityFilter.valuesCount(query) | 0), size.contents);
|
|
241
|
+
};
|
|
242
|
+
let filterKey = indexFilter(inMemTable, filter, entityConfig.table);
|
|
243
|
+
return LoadManager.call(loadManager, filter, key, load, param => filterKey, shouldGroup, InMemoryTable.Entity.hasIndex(inMemTable), InMemoryTable.Entity.getUnsafeOnIndex(inMemTable));
|
|
244
|
+
}
|
|
245
|
+
|
|
185
246
|
function loadByFilter(loadManager, persistence, entityConfig, scope, indexerState, shouldGroup, item, ecosystem, filter) {
|
|
186
247
|
try {
|
|
187
|
-
|
|
188
|
-
let key = EntityFilter.toOperationKey(filter$1, entityConfig.name) + scopeKeySuffix(scope);
|
|
189
|
-
let inMemTable = InMemoryStore.getInMemTable(indexerState, entityConfig, scope);
|
|
190
|
-
let load = async (filters, param) => {
|
|
191
|
-
let storage = Persistence.getInitializedStorageOrThrow(persistence);
|
|
192
|
-
let timerRef = IndexerState.startStorageLoad(indexerState, storage.name, key);
|
|
193
|
-
let size = {
|
|
194
|
-
contents: 0
|
|
195
|
-
};
|
|
196
|
-
filters.forEach(filter => InMemoryTable.Entity.addEmptyIndex(inMemTable, filter, entityConfig.table));
|
|
197
|
-
await storage.ensureQueryIndexes(entityConfig, scope, filters);
|
|
198
|
-
let queries = EntityFilter.merge(filters);
|
|
199
|
-
await Promise.all(queries.map(async filter => {
|
|
200
|
-
try {
|
|
201
|
-
let entities = await storage.loadOrThrow(EntityFilter.scoped(filter, entityConfig.table, scope), entityConfig.table);
|
|
202
|
-
let committedCheckpointId = IndexerState.committedCheckpointIdFor(indexerState, scope);
|
|
203
|
-
entities.forEach(entity => InMemoryTable.Entity.initValue(inMemTable, committedCheckpointId, entity.id, entity));
|
|
204
|
-
InMemoryTable.Entity.recordLoadedValues(inMemTable, filter, entityConfig.table);
|
|
205
|
-
size.contents = size.contents + entities.length | 0;
|
|
206
|
-
return;
|
|
207
|
-
} catch (raw_exn) {
|
|
208
|
-
let exn = Primitive_exceptions.internalToException(raw_exn);
|
|
209
|
-
if (exn.RE_EXN_ID === Persistence.StorageError) {
|
|
210
|
-
return ErrorHandling.mkLogAndRaise(Logging.createChildFrom(Ecosystem.getItemLogger(item, ecosystem), {
|
|
211
|
-
operation: key,
|
|
212
|
-
params: EntityFilter.getParams(filter)
|
|
213
|
-
}), exn.message, exn.reason);
|
|
214
|
-
}
|
|
215
|
-
throw exn;
|
|
216
|
-
}
|
|
217
|
-
}));
|
|
218
|
-
return IndexerState.endStorageLoad(indexerState, timerRef, storage.name, key, Stdlib_Array.reduce(queries, 0, (acc, query) => acc + EntityFilter.valuesCount(query) | 0), size.contents);
|
|
219
|
-
};
|
|
220
|
-
let filterKey = EntityFilter.toString(filter$1, entityConfig.table);
|
|
221
|
-
if (!InMemoryTable.Entity.hasIndex(inMemTable)(filterKey)) {
|
|
222
|
-
InMemoryTable.Entity.tryIndexFromLoadedValues(inMemTable, filter$1, entityConfig.table);
|
|
223
|
-
}
|
|
224
|
-
return LoadManager.call(loadManager, filter$1, key, load, param => filterKey, shouldGroup, InMemoryTable.Entity.hasIndex(inMemTable), InMemoryTable.Entity.getUnsafeOnIndex(inMemTable));
|
|
248
|
+
return loadByParsedFilter(loadManager, persistence, entityConfig, scope, indexerState, shouldGroup, item, ecosystem, EntityFilter.parseOrThrow(filter, entityConfig.name, entityConfig.table));
|
|
225
249
|
} catch (raw_exn) {
|
|
226
250
|
let exn = Primitive_exceptions.internalToException(raw_exn);
|
|
227
251
|
return Promise.reject(Utils.prettifyExn(exn));
|
|
@@ -231,6 +255,9 @@ function loadByFilter(loadManager, persistence, entityConfig, scope, indexerStat
|
|
|
231
255
|
export {
|
|
232
256
|
loadById,
|
|
233
257
|
loadByFilter,
|
|
258
|
+
loadByParsedFilter,
|
|
259
|
+
getByIdInMemory,
|
|
260
|
+
getByFilterInMemory,
|
|
234
261
|
loadEffect,
|
|
235
262
|
}
|
|
236
263
|
/* Utils Not a pure module */
|
package/src/LoadLayer.resi
CHANGED
|
@@ -22,6 +22,34 @@ let loadByFilter: (
|
|
|
22
22
|
~filter: dict<dict<unknown>>,
|
|
23
23
|
) => promise<array<Internal.entity>>
|
|
24
24
|
|
|
25
|
+
let loadByParsedFilter: (
|
|
26
|
+
~loadManager: LoadManager.t,
|
|
27
|
+
~persistence: Persistence.t,
|
|
28
|
+
~entityConfig: Internal.entityConfig,
|
|
29
|
+
~scope: Internal.chainScope,
|
|
30
|
+
~indexerState: IndexerState.t,
|
|
31
|
+
~shouldGroup: bool,
|
|
32
|
+
~item: Internal.item,
|
|
33
|
+
~ecosystem: Ecosystem.t,
|
|
34
|
+
~filter: EntityFilter.t,
|
|
35
|
+
) => promise<array<Internal.entity>>
|
|
36
|
+
|
|
37
|
+
// What a get or getWhere answers without a load, or None when it would need
|
|
38
|
+
// one. The sync context reads through these and suspends on None.
|
|
39
|
+
let getByIdInMemory: (
|
|
40
|
+
~entityConfig: Internal.entityConfig,
|
|
41
|
+
~scope: Internal.chainScope,
|
|
42
|
+
~indexerState: IndexerState.t,
|
|
43
|
+
~entityId: string,
|
|
44
|
+
) => option<option<Internal.entity>>
|
|
45
|
+
|
|
46
|
+
let getByFilterInMemory: (
|
|
47
|
+
~entityConfig: Internal.entityConfig,
|
|
48
|
+
~scope: Internal.chainScope,
|
|
49
|
+
~indexerState: IndexerState.t,
|
|
50
|
+
~filter: EntityFilter.t,
|
|
51
|
+
) => option<array<Internal.entity>>
|
|
52
|
+
|
|
25
53
|
let loadEffect: (
|
|
26
54
|
~loadManager: LoadManager.t,
|
|
27
55
|
~persistence: Persistence.t,
|
package/src/Main.res
CHANGED
|
@@ -411,8 +411,6 @@ let getGlobalIndexer = (): 'indexer => {
|
|
|
411
411
|
Utils.Proxy.make(Utils.Object.createNullObject(), traps)->(Utils.magic: {..} => 'indexer)
|
|
412
412
|
}
|
|
413
413
|
|
|
414
|
-
// The RPC-stripped public config that the storage layer persists in
|
|
415
|
-
// `envio_info` (on initialize) and validates against (on resume).
|
|
416
414
|
let migrate = async (~reset) => {
|
|
417
415
|
let config = Config.load()
|
|
418
416
|
let persistence = PgStorage.makePersistenceFromConfig(~config)
|
|
@@ -420,8 +418,8 @@ let migrate = async (~reset) => {
|
|
|
420
418
|
~reset,
|
|
421
419
|
~chainConfigs=config.chainMap->ChainMap.values,
|
|
422
420
|
~contractMapping=config.contractMapping,
|
|
423
|
-
~envioInfo=
|
|
424
|
-
~resetCommand="envio local db-migrate setup",
|
|
421
|
+
~envioInfo=config.envioInfo,
|
|
422
|
+
~resetCommand="envio local db-migrate setup"->Config.withProjectFlags(~config),
|
|
425
423
|
~runCommand=None,
|
|
426
424
|
~lowercaseAddresses=config.lowercaseAddresses,
|
|
427
425
|
// A migration command runs once and exits, with nobody watching it recover:
|
|
@@ -466,18 +464,23 @@ exception FatalError(exn)
|
|
|
466
464
|
| None => PgStorage.makePersistenceFromConfig(~config)
|
|
467
465
|
}
|
|
468
466
|
setGlobalPersistence(persistence)
|
|
469
|
-
await
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
467
|
+
let autoLoadFiles = await HandlerLoader.getAutoLoadFiles(~config)
|
|
468
|
+
// A worker's supervisor has checked the handlers already, and the test
|
|
469
|
+
// indexer leaves type checking to the user's test setup.
|
|
470
|
+
let typeCheck =
|
|
471
|
+
isTest || Worker.isEnabled
|
|
472
|
+
? Promise.resolve()
|
|
473
|
+
: HandlerLoader.typeCheck(~config, ~autoLoadFiles)
|
|
474
|
+
let _ = await Promise.all2((
|
|
475
|
+
persistence->Persistence.initForRun(~config, ~reset, ~isDevelopmentMode),
|
|
476
|
+
typeCheck,
|
|
477
|
+
))
|
|
475
478
|
|
|
476
479
|
// Loads user handler files, which register handler/contractRegister/where
|
|
477
480
|
// state into the global `HandlerRegister` registry as a side effect; this
|
|
478
481
|
// returns that state resolved into per-chain registrations. `config` itself
|
|
479
482
|
// is never mutated by registration — it holds only event definitions.
|
|
480
|
-
let registrationsByChainId = await HandlerLoader.registerAllHandlers(~config)
|
|
483
|
+
let registrationsByChainId = await HandlerLoader.registerAllHandlers(~config, ~autoLoadFiles)
|
|
481
484
|
let config = if isTest {
|
|
482
485
|
{...config, shouldRollbackOnReorg: false}
|
|
483
486
|
} else {
|
|
@@ -535,7 +538,7 @@ exception FatalError(exn)
|
|
|
535
538
|
~onError,
|
|
536
539
|
)
|
|
537
540
|
if shouldUseTui {
|
|
538
|
-
|
|
541
|
+
Tui.start(~config, ~getMetrics=() => state->IndexerState.toMetrics)
|
|
539
542
|
}
|
|
540
543
|
Worker.bindRun(
|
|
541
544
|
~getMetrics=() => state->IndexerState.toMetrics,
|
|
@@ -565,6 +568,8 @@ let start = async (
|
|
|
565
568
|
Worker.config->Option.forEach(({chainIds, isDev}) =>
|
|
566
569
|
Config.prime(Config.getPublicConfigJson()->Config.withCommandFields(~chainIds, ~isDev))
|
|
567
570
|
)
|
|
571
|
+
// An unsupported Node fails here, before the database is touched.
|
|
572
|
+
HandlerLoader.registerTsHooks()
|
|
568
573
|
let config = Config.load()
|
|
569
574
|
switch isTest ? None : Supervisor.planForRun(~config) {
|
|
570
575
|
| Some(workers) => await Supervisor.run(~config, ~workers, ~reset)
|
package/src/Main.res.mjs
CHANGED
|
@@ -351,7 +351,7 @@ function getGlobalIndexer() {
|
|
|
351
351
|
async function migrate(reset) {
|
|
352
352
|
let config = Config.load();
|
|
353
353
|
let persistence = PgStorage.makePersistenceFromConfig(config, undefined);
|
|
354
|
-
await Persistence.init(persistence, ChainMap.values(config.chainMap), config.contractMapping,
|
|
354
|
+
await Persistence.init(persistence, ChainMap.values(config.chainMap), config.contractMapping, config.envioInfo, Config.withProjectFlags("envio local db-migrate setup", config), undefined, reset, config.lowercaseAddresses, undefined, undefined, "Once");
|
|
355
355
|
return await persistence.storage.close();
|
|
356
356
|
}
|
|
357
357
|
|
|
@@ -372,8 +372,13 @@ async function startIndexer(config, persistence, resetOpt, isTestOpt, exitAfterF
|
|
|
372
372
|
let isDevelopmentMode = !isTest && config.isDev;
|
|
373
373
|
let persistence$1 = persistence !== undefined ? persistence : PgStorage.makePersistenceFromConfig(config, undefined);
|
|
374
374
|
EnvioGlobal.value.persistence = Primitive_option.some(persistence$1);
|
|
375
|
-
await
|
|
376
|
-
let
|
|
375
|
+
let autoLoadFiles = await HandlerLoader.getAutoLoadFiles(config);
|
|
376
|
+
let typeCheck = isTest || Worker.isEnabled ? Promise.resolve() : HandlerLoader.typeCheck(config, autoLoadFiles);
|
|
377
|
+
await Promise.all([
|
|
378
|
+
Persistence.initForRun(persistence$1, config, reset, isDevelopmentMode),
|
|
379
|
+
typeCheck
|
|
380
|
+
]);
|
|
381
|
+
let registrationsByChainId = await HandlerLoader.registerAllHandlers(config, autoLoadFiles);
|
|
377
382
|
let config$1;
|
|
378
383
|
if (isTest) {
|
|
379
384
|
let newrecord = {...config};
|
|
@@ -417,6 +422,7 @@ async function start(persistence, resetOpt, isTestOpt, exitAfterFirstEventBlockO
|
|
|
417
422
|
let isTest = isTestOpt !== undefined ? isTestOpt : false;
|
|
418
423
|
let exitAfterFirstEventBlock = exitAfterFirstEventBlockOpt !== undefined ? exitAfterFirstEventBlockOpt : false;
|
|
419
424
|
Stdlib_Option.forEach(Worker.config, param => Config.prime(Config.withCommandFields(Config.getPublicConfigJson(), param.chainIds, param.isDev)));
|
|
425
|
+
HandlerLoader.registerTsHooks();
|
|
420
426
|
let config = Config.load();
|
|
421
427
|
let workers = isTest ? undefined : Supervisor.planForRun(config, undefined);
|
|
422
428
|
if (workers !== undefined) {
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
const MINIMUM = [22, 15, 0];
|
|
2
|
+
|
|
3
|
+
// The message for a Node.js older than envio supports, or undefined.
|
|
4
|
+
export const unsupportedNodeMessage = () => {
|
|
5
|
+
const current = process.versions.node;
|
|
6
|
+
const parts = current.split(".").map(Number);
|
|
7
|
+
const index = MINIMUM.findIndex((minimum, i) => parts[i] !== minimum);
|
|
8
|
+
if (index === -1 || parts[index] > MINIMUM[index]) return undefined;
|
|
9
|
+
return [
|
|
10
|
+
`envio needs Node.js ${MINIMUM.join(".")} or newer, and this is Node.js ${current}.`,
|
|
11
|
+
"Install a newer one from https://nodejs.org/en/download, or with your version manager, for example: nvm install --lts",
|
|
12
|
+
].join("\n");
|
|
13
|
+
};
|