effective-indexer 0.2.2 → 0.2.4
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/LICENSE +5 -19
- package/README.md +192 -87
- package/dist/index.cjs +135 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +114 -2
- package/dist/index.d.ts +114 -2
- package/dist/index.js +134 -7
- package/dist/index.js.map +1 -1
- package/package.json +8 -2
package/LICENSE
CHANGED
|
@@ -1,21 +1,7 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
1
|
Copyright (c) 2026 Aleksandr Shenshin
|
|
2
|
+
All rights reserved.
|
|
4
3
|
|
|
5
|
-
|
|
6
|
-
of this software
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
|
4
|
+
This software and associated documentation files are proprietary and confidential.
|
|
5
|
+
No part of this software may be copied, modified, distributed, sublicensed,
|
|
6
|
+
published, sold, or used in any form without prior written permission from
|
|
7
|
+
the copyright holder.
|
package/README.md
CHANGED
|
@@ -1,7 +1,160 @@
|
|
|
1
1
|
# Effective Indexer
|
|
2
2
|
|
|
3
|
+
EVM event indexing without hosted lock-in.
|
|
4
|
+
|
|
5
|
+
`effective-indexer` runs as your own worker, writes directly to your SQLite database, and gives you a typed query API.
|
|
6
|
+
|
|
7
|
+
## Why this approach
|
|
8
|
+
|
|
9
|
+
- **Own your data**: events are stored in your DB, not in a third-party service.
|
|
10
|
+
- **Simple operations**: one worker process, one config file, no subgraph deployment pipeline.
|
|
11
|
+
- **Production-safe behavior**: checkpoint resume, reorg detection, retry/backoff, live polling.
|
|
12
|
+
- **Fast backfill**: parallel `eth_getLogs` with deterministic chunk ordering.
|
|
13
|
+
- **Typed DX**: TypeScript-first config and query surface.
|
|
14
|
+
|
|
15
|
+
## Install
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install effective-indexer effect
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
`effect` is a peer dependency.
|
|
22
|
+
|
|
23
|
+
## 5-minute setup
|
|
24
|
+
|
|
25
|
+
### 1) Create `indexer.config.ts`
|
|
26
|
+
|
|
27
|
+
```ts
|
|
28
|
+
import { defineIndexerConfig } from "effective-indexer"
|
|
29
|
+
import type { Abi } from "viem"
|
|
30
|
+
|
|
31
|
+
const transferAbi: Abi = [
|
|
32
|
+
{
|
|
33
|
+
type: "event",
|
|
34
|
+
name: "Transfer",
|
|
35
|
+
inputs: [
|
|
36
|
+
{ indexed: true, name: "from", type: "address" },
|
|
37
|
+
{ indexed: true, name: "to", type: "address" },
|
|
38
|
+
{ indexed: false, name: "value", type: "uint256" },
|
|
39
|
+
],
|
|
40
|
+
},
|
|
41
|
+
]
|
|
42
|
+
|
|
43
|
+
export default defineIndexerConfig({
|
|
44
|
+
rpcUrl: "https://rpc.mainnet.rootstock.io/{{EVM_RPC_API_KEY}}",
|
|
45
|
+
dbPath: "./data/events.db",
|
|
46
|
+
contracts: [
|
|
47
|
+
{
|
|
48
|
+
name: "Token",
|
|
49
|
+
address: "0xYourContractAddress",
|
|
50
|
+
abi: transferAbi,
|
|
51
|
+
events: ["Transfer"],
|
|
52
|
+
startBlock: 0n,
|
|
53
|
+
},
|
|
54
|
+
],
|
|
55
|
+
network: {
|
|
56
|
+
logs: {
|
|
57
|
+
chunkSize: 2000,
|
|
58
|
+
parallelRequests: 3,
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
})
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### 2) Create `scripts/indexer.ts`
|
|
65
|
+
|
|
66
|
+
```ts
|
|
67
|
+
import config from "../indexer.config"
|
|
68
|
+
import { resolveIndexerConfigFromEnv, runIndexerWorker } from "effective-indexer"
|
|
69
|
+
|
|
70
|
+
const resolvedConfig = resolveIndexerConfigFromEnv(config)
|
|
71
|
+
|
|
72
|
+
runIndexerWorker(resolvedConfig).catch(error => {
|
|
73
|
+
console.error("Indexer worker failed:", error)
|
|
74
|
+
process.exit(1)
|
|
75
|
+
})
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### 3) Add env and run
|
|
79
|
+
|
|
80
|
+
`.env`:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
EVM_RPC_API_KEY=your-rpc-api-key
|
|
84
|
+
# Optional full URL override:
|
|
85
|
+
# EVM_RPC_URL=https://rpc.mainnet.rootstock.io/<API_KEY>
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Run:
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
node --import tsx ./scripts/indexer.ts
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Query data
|
|
95
|
+
|
|
96
|
+
```ts
|
|
97
|
+
import config from "../indexer.config"
|
|
98
|
+
import { Indexer, resolveIndexerConfigFromEnv } from "effective-indexer"
|
|
99
|
+
|
|
100
|
+
const indexer = Indexer.create(resolveIndexerConfigFromEnv(config))
|
|
101
|
+
|
|
102
|
+
const events = await indexer.query({
|
|
103
|
+
contractName: "Token",
|
|
104
|
+
eventName: "Transfer",
|
|
105
|
+
order: "desc",
|
|
106
|
+
limit: 50,
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
console.log(events.length)
|
|
110
|
+
await indexer.stop()
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## Public API
|
|
114
|
+
|
|
115
|
+
- `defineIndexerConfig(config)`
|
|
116
|
+
Identity helper for typed config files (Hardhat-style).
|
|
117
|
+
- `resolveIndexerConfigFromEnv(config, options?)`
|
|
118
|
+
Resolves `{{ENV_VAR}}` placeholders and optional RPC URL override.
|
|
119
|
+
- `runIndexerWorker(config, options?)`
|
|
120
|
+
Runs long-lived worker with built-in DB directory creation and graceful shutdown.
|
|
121
|
+
- `Indexer.create(config)`
|
|
122
|
+
Returns handle: `start()`, `stop()`, `query()`, `count()`.
|
|
123
|
+
|
|
124
|
+
## Config essentials
|
|
125
|
+
|
|
126
|
+
- `rpcUrl`: RPC endpoint URL (supports placeholders like `{{EVM_RPC_API_KEY}}`)
|
|
127
|
+
- `dbPath`: SQLite path (default `./indexer.db`)
|
|
128
|
+
- `contracts`: non-empty list of contracts and events to index
|
|
129
|
+
- `network.polling`: block polling interval and confirmations
|
|
130
|
+
- `network.logs`: chunk size, retries, parallel requests
|
|
131
|
+
- `network.reorg.depth`: reorg buffer depth
|
|
132
|
+
- `telemetry.progress`: CLI progress rendering
|
|
133
|
+
- `logLevel`, `logFormat`, `enableTelemetry`
|
|
134
|
+
|
|
135
|
+
## Operational notes
|
|
136
|
+
|
|
137
|
+
- Run a single writer process per SQLite file.
|
|
138
|
+
- Keep DB on persistent storage.
|
|
139
|
+
- Worker resumes from checkpoint after restart.
|
|
140
|
+
- RPC must support `eth_getLogs`.
|
|
141
|
+
|
|
142
|
+
## Development
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
npm run build
|
|
146
|
+
npm run typecheck
|
|
147
|
+
npm run test
|
|
148
|
+
npm run check
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Repository: [github.com/cybervoid0/effective-indexer](https://github.com/cybervoid0/effective-indexer)
|
|
152
|
+
# Effective Indexer
|
|
153
|
+
|
|
3
154
|
Lightweight EVM smart contract event indexer built with [Effect](https://effect.website).
|
|
4
155
|
|
|
156
|
+
Index EVM events to your own database in minutes — no hosted lock-in, no PhD required.
|
|
157
|
+
|
|
5
158
|
Repository: [github.com/cybervoid0/effective-indexer](https://github.com/cybervoid0/effective-indexer)
|
|
6
159
|
|
|
7
160
|
Indexes smart contract events into SQLite with:
|
|
@@ -87,6 +240,21 @@ Returns `IndexerHandle`:
|
|
|
87
240
|
- `query(q?: EventQuery): Promise<ParsedEvent[]>`
|
|
88
241
|
- `count(q?: EventQuery): Promise<number>`
|
|
89
242
|
|
|
243
|
+
### `defineIndexerConfig(config)`
|
|
244
|
+
|
|
245
|
+
Identity helper for a typed config file (Hardhat-style DX).
|
|
246
|
+
|
|
247
|
+
### `resolveIndexerConfigFromEnv(config, options?)`
|
|
248
|
+
|
|
249
|
+
Resolves `{{ENV_VAR}}` placeholders in `rpcUrl` and supports optional RPC override from env (`EVM_RPC_URL` by default).
|
|
250
|
+
|
|
251
|
+
### `runIndexerWorker(config, options?)`
|
|
252
|
+
|
|
253
|
+
Runs a long-lived worker with built-in:
|
|
254
|
+
- SQLite directory creation
|
|
255
|
+
- graceful shutdown on `SIGINT` / `SIGTERM`
|
|
256
|
+
- keep-alive process loop
|
|
257
|
+
|
|
90
258
|
### `IndexerConfig`
|
|
91
259
|
|
|
92
260
|
| Field | Type | Default | Description |
|
|
@@ -159,7 +327,21 @@ Run the indexer as a dedicated long-lived worker process (not in request handler
|
|
|
159
327
|
Create `scripts/indexer.ts`:
|
|
160
328
|
|
|
161
329
|
```ts
|
|
162
|
-
import
|
|
330
|
+
import config from "../indexer.config"
|
|
331
|
+
import { resolveIndexerConfigFromEnv, runIndexerWorker } from "effective-indexer"
|
|
332
|
+
|
|
333
|
+
const resolvedConfig = resolveIndexerConfigFromEnv(config)
|
|
334
|
+
|
|
335
|
+
runIndexerWorker(resolvedConfig).catch(error => {
|
|
336
|
+
console.error("Indexer worker failed:", error)
|
|
337
|
+
process.exit(1)
|
|
338
|
+
})
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
Create `indexer.config.ts`:
|
|
342
|
+
|
|
343
|
+
```ts
|
|
344
|
+
import { defineIndexerConfig } from "effective-indexer"
|
|
163
345
|
import type { Abi } from "viem"
|
|
164
346
|
|
|
165
347
|
const transferAbi: Abi = [
|
|
@@ -174,104 +356,27 @@ const transferAbi: Abi = [
|
|
|
174
356
|
},
|
|
175
357
|
]
|
|
176
358
|
|
|
177
|
-
|
|
178
|
-
rpcUrl:
|
|
179
|
-
dbPath:
|
|
359
|
+
export default defineIndexerConfig({
|
|
360
|
+
rpcUrl: "https://rpc.mainnet.rootstock.io/{{EVM_RPC_API_KEY}}",
|
|
361
|
+
dbPath: "./data/events.db",
|
|
180
362
|
contracts: [
|
|
181
363
|
{
|
|
182
|
-
name:
|
|
183
|
-
address:
|
|
364
|
+
name: "Token",
|
|
365
|
+
address: "0xYourContractAddress",
|
|
184
366
|
abi: transferAbi,
|
|
185
367
|
events: ["Transfer"],
|
|
186
|
-
startBlock:
|
|
368
|
+
startBlock: 0n,
|
|
187
369
|
},
|
|
188
370
|
],
|
|
189
|
-
network: {
|
|
190
|
-
polling: {
|
|
191
|
-
intervalMs: Number(process.env.INDEXER_POLL_INTERVAL_MS ?? "12000"),
|
|
192
|
-
confirmations: Number(process.env.INDEXER_CONFIRMATIONS ?? "1"),
|
|
193
|
-
},
|
|
194
|
-
logs: {
|
|
195
|
-
chunkSize: Number(process.env.INDEXER_CHUNK_SIZE ?? "2000"),
|
|
196
|
-
parallelRequests: Number(process.env.INDEXER_PARALLEL_REQUESTS ?? "1"),
|
|
197
|
-
maxRetries: Number(process.env.INDEXER_MAX_RETRIES ?? "5"),
|
|
198
|
-
retry: {
|
|
199
|
-
baseDelayMs: Number(process.env.INDEXER_RETRY_BASE_MS ?? "1000"),
|
|
200
|
-
maxDelayMs: Number(process.env.INDEXER_RETRY_MAX_MS ?? "30000"),
|
|
201
|
-
},
|
|
202
|
-
},
|
|
203
|
-
reorg: {
|
|
204
|
-
depth: Number(process.env.INDEXER_REORG_DEPTH ?? "20"),
|
|
205
|
-
},
|
|
206
|
-
},
|
|
207
|
-
telemetry: {
|
|
208
|
-
progress: {
|
|
209
|
-
enabled: process.env.INDEXER_PROGRESS_ENABLED !== "false",
|
|
210
|
-
intervalMs: Number(process.env.INDEXER_PROGRESS_INTERVAL_MS ?? "3000"),
|
|
211
|
-
},
|
|
212
|
-
},
|
|
213
|
-
logLevel: (process.env.INDEXER_LOG_LEVEL ?? "info") as
|
|
214
|
-
| "trace"
|
|
215
|
-
| "debug"
|
|
216
|
-
| "info"
|
|
217
|
-
| "warning"
|
|
218
|
-
| "error"
|
|
219
|
-
| "none",
|
|
220
|
-
logFormat: (process.env.INDEXER_LOG_FORMAT ?? "pretty") as
|
|
221
|
-
| "pretty"
|
|
222
|
-
| "json"
|
|
223
|
-
| "structured",
|
|
224
|
-
enableTelemetry: process.env.INDEXER_TELEMETRY !== "false",
|
|
225
|
-
})
|
|
226
|
-
|
|
227
|
-
const start = async (): Promise<void> => {
|
|
228
|
-
await indexer.start()
|
|
229
|
-
console.log("Indexer worker started")
|
|
230
|
-
|
|
231
|
-
// Keep the process alive while indexing in background.
|
|
232
|
-
const keepAlive = setInterval(() => undefined, 60_000)
|
|
233
|
-
|
|
234
|
-
const stop = async (): Promise<void> => {
|
|
235
|
-
clearInterval(keepAlive)
|
|
236
|
-
await indexer.stop()
|
|
237
|
-
process.exit(0)
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
process.on("SIGINT", () => {
|
|
241
|
-
void stop()
|
|
242
|
-
})
|
|
243
|
-
process.on("SIGTERM", () => {
|
|
244
|
-
void stop()
|
|
245
|
-
})
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
start().catch(error => {
|
|
249
|
-
console.error("Indexer worker failed:", error)
|
|
250
|
-
process.exit(1)
|
|
251
371
|
})
|
|
252
372
|
```
|
|
253
373
|
|
|
254
374
|
Create `.env`:
|
|
255
375
|
|
|
256
376
|
```bash
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
INDEXER_CONTRACT_ADDRESS=0xYourContractAddress
|
|
261
|
-
INDEXER_START_BLOCK=0
|
|
262
|
-
INDEXER_POLL_INTERVAL_MS=12000
|
|
263
|
-
INDEXER_CONFIRMATIONS=1
|
|
264
|
-
INDEXER_CHUNK_SIZE=2000
|
|
265
|
-
INDEXER_PARALLEL_REQUESTS=1
|
|
266
|
-
INDEXER_MAX_RETRIES=5
|
|
267
|
-
INDEXER_RETRY_BASE_MS=1000
|
|
268
|
-
INDEXER_RETRY_MAX_MS=30000
|
|
269
|
-
INDEXER_REORG_DEPTH=20
|
|
270
|
-
INDEXER_PROGRESS_ENABLED=true
|
|
271
|
-
INDEXER_PROGRESS_INTERVAL_MS=3000
|
|
272
|
-
INDEXER_LOG_LEVEL=info
|
|
273
|
-
INDEXER_LOG_FORMAT=pretty
|
|
274
|
-
INDEXER_TELEMETRY=true
|
|
377
|
+
EVM_RPC_API_KEY=your-rpc-api-key
|
|
378
|
+
# Optional full RPC URL override:
|
|
379
|
+
# EVM_RPC_URL=https://rpc.mainnet.rootstock.io/<API_KEY>
|
|
275
380
|
```
|
|
276
381
|
|
|
277
382
|
Add scripts (with `tsx` installed):
|
package/dist/index.cjs
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
var promises = require('fs/promises');
|
|
4
|
+
var path = require('path');
|
|
3
5
|
var sqlSqliteNode = require('@effect/sql-sqlite-node');
|
|
4
6
|
var effect = require('effect');
|
|
5
7
|
var viem = require('viem');
|
|
@@ -20,6 +22,7 @@ var ConfigError = class extends effect.Data.TaggedError("ConfigError") {
|
|
|
20
22
|
};
|
|
21
23
|
|
|
22
24
|
// src/config.ts
|
|
25
|
+
var defineIndexerConfig = (config) => config;
|
|
23
26
|
var resolveNetwork = (config) => {
|
|
24
27
|
const n = config.network;
|
|
25
28
|
return {
|
|
@@ -135,6 +138,7 @@ var RpcProviderLive = effect.Layer.effect(
|
|
|
135
138
|
})
|
|
136
139
|
});
|
|
137
140
|
const getLogs = (params) => effect.Effect.tryPromise({
|
|
141
|
+
// Use raw request to pass topics exactly as eth_getLogs expects.
|
|
138
142
|
try: () => client.request({
|
|
139
143
|
method: "eth_getLogs",
|
|
140
144
|
params: [
|
|
@@ -712,6 +716,7 @@ var fetchLogs = (params) => effect.Stream.unwrap(
|
|
|
712
716
|
fromBlock: chunk.from,
|
|
713
717
|
toBlock: chunk.to
|
|
714
718
|
}).pipe(
|
|
719
|
+
// Retry policy is exponential and bounded by maxDelayMs.
|
|
715
720
|
effect.Effect.tapError(
|
|
716
721
|
(e) => effect.Effect.gen(function* () {
|
|
717
722
|
const n = yield* effect.Ref.getAndUpdate(attempt, (a) => a + 1);
|
|
@@ -910,11 +915,14 @@ var indexContract = (contract) => effect.Stream.unwrap(
|
|
|
910
915
|
);
|
|
911
916
|
}
|
|
912
917
|
const chunkSize = BigInt(config.network.logs.chunkSize);
|
|
913
|
-
const lastProcessed = yield* effect.Ref.modify(
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
+
const lastProcessed = yield* effect.Ref.modify(
|
|
919
|
+
processedBlocksRef,
|
|
920
|
+
(current) => {
|
|
921
|
+
const advanced = current + chunkSize;
|
|
922
|
+
const next = advanced > totalBackfillBlocks ? totalBackfillBlocks : advanced;
|
|
923
|
+
return [next, next];
|
|
924
|
+
}
|
|
925
|
+
);
|
|
918
926
|
yield* progress.update(
|
|
919
927
|
contract.name,
|
|
920
928
|
lastProcessed,
|
|
@@ -1097,6 +1105,64 @@ var QueryApiLive = effect.Layer.effect(
|
|
|
1097
1105
|
return { getEvents, getEventCount, getLatestBlock };
|
|
1098
1106
|
})
|
|
1099
1107
|
);
|
|
1108
|
+
var toConfigMap = (env) => {
|
|
1109
|
+
const map = /* @__PURE__ */ new Map();
|
|
1110
|
+
for (const [key, value] of Object.entries(env)) {
|
|
1111
|
+
if (typeof value === "string") {
|
|
1112
|
+
map.set(key, value);
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
return map;
|
|
1116
|
+
};
|
|
1117
|
+
var getProvider = (env) => effect.ConfigProvider.fromMap(toConfigMap(env ?? process.env));
|
|
1118
|
+
var readOptionalString = (name, provider) => effect.Effect.runSync(
|
|
1119
|
+
effect.Effect.withConfigProvider(provider)(
|
|
1120
|
+
effect.Config.option(effect.Config.nonEmptyString(name))
|
|
1121
|
+
)
|
|
1122
|
+
);
|
|
1123
|
+
var readRequiredString = (name, provider) => {
|
|
1124
|
+
try {
|
|
1125
|
+
return effect.Effect.runSync(
|
|
1126
|
+
effect.Effect.withConfigProvider(provider)(effect.Config.nonEmptyString(name))
|
|
1127
|
+
);
|
|
1128
|
+
} catch {
|
|
1129
|
+
throw new Error(`Missing required env var: ${name}`);
|
|
1130
|
+
}
|
|
1131
|
+
};
|
|
1132
|
+
var readRequiredRedactedString = (name, provider) => {
|
|
1133
|
+
try {
|
|
1134
|
+
const redacted = effect.Effect.runSync(
|
|
1135
|
+
effect.Effect.withConfigProvider(provider)(
|
|
1136
|
+
effect.Config.redacted(effect.Config.nonEmptyString(name))
|
|
1137
|
+
)
|
|
1138
|
+
);
|
|
1139
|
+
return effect.Redacted.value(redacted);
|
|
1140
|
+
} catch {
|
|
1141
|
+
throw new Error(`Missing required env var: ${name}`);
|
|
1142
|
+
}
|
|
1143
|
+
};
|
|
1144
|
+
var resolveRpcUrl = (template, provider, sensitiveEnvNames) => template.replace(
|
|
1145
|
+
/\{\{([A-Z0-9_]+)\}\}/g,
|
|
1146
|
+
(_input, envName) => sensitiveEnvNames.has(envName) ? readRequiredRedactedString(envName, provider) : readRequiredString(envName, provider)
|
|
1147
|
+
);
|
|
1148
|
+
var resolveIndexerConfigFromEnv = (config, options) => {
|
|
1149
|
+
const provider = getProvider(options?.env);
|
|
1150
|
+
const rpcUrlOverrideEnv = options?.rpcUrlOverrideEnv ?? "EVM_RPC_URL";
|
|
1151
|
+
const rpcUrlOverride = readOptionalString(rpcUrlOverrideEnv, provider);
|
|
1152
|
+
const sensitiveEnvNames = new Set(
|
|
1153
|
+
options?.sensitiveEnvNames ?? ["EVM_RPC_API_KEY"]
|
|
1154
|
+
);
|
|
1155
|
+
if (effect.Option.isSome(rpcUrlOverride)) {
|
|
1156
|
+
return {
|
|
1157
|
+
...config,
|
|
1158
|
+
rpcUrl: rpcUrlOverride.value
|
|
1159
|
+
};
|
|
1160
|
+
}
|
|
1161
|
+
return {
|
|
1162
|
+
...config,
|
|
1163
|
+
rpcUrl: resolveRpcUrl(config.rpcUrl, provider, sensitiveEnvNames)
|
|
1164
|
+
};
|
|
1165
|
+
};
|
|
1100
1166
|
|
|
1101
1167
|
// src/index.ts
|
|
1102
1168
|
var buildLayers = (config) => {
|
|
@@ -1194,6 +1260,67 @@ var createIndexer = (config) => {
|
|
|
1194
1260
|
}
|
|
1195
1261
|
};
|
|
1196
1262
|
};
|
|
1263
|
+
var defaultWorkerRuntime = {
|
|
1264
|
+
process,
|
|
1265
|
+
setInterval,
|
|
1266
|
+
clearInterval
|
|
1267
|
+
};
|
|
1268
|
+
var resolveDbPath = (config) => config.dbPath ?? "./indexer.db";
|
|
1269
|
+
var ensureDbDirectory = async (config) => {
|
|
1270
|
+
const dbPath = resolveDbPath(config);
|
|
1271
|
+
if (dbPath === ":memory:") {
|
|
1272
|
+
return;
|
|
1273
|
+
}
|
|
1274
|
+
await promises.mkdir(path.dirname(dbPath), { recursive: true });
|
|
1275
|
+
};
|
|
1276
|
+
var runIndexerWorker = async (config, options) => {
|
|
1277
|
+
if (options?.ensureDbDirectory ?? true) {
|
|
1278
|
+
await ensureDbDirectory(config);
|
|
1279
|
+
}
|
|
1280
|
+
const runtime = options?.runtime ?? defaultWorkerRuntime;
|
|
1281
|
+
const create = options?.createIndexer ?? createIndexer;
|
|
1282
|
+
const signals = options?.shutdownSignals ?? ["SIGINT", "SIGTERM"];
|
|
1283
|
+
const keepAliveIntervalMs = options?.keepAliveIntervalMs ?? 6e4;
|
|
1284
|
+
const indexer = create(config);
|
|
1285
|
+
await indexer.start();
|
|
1286
|
+
await new Promise((resolve, reject) => {
|
|
1287
|
+
const keepAliveTimer = runtime.setInterval(
|
|
1288
|
+
() => void 0,
|
|
1289
|
+
keepAliveIntervalMs
|
|
1290
|
+
);
|
|
1291
|
+
let stopping = false;
|
|
1292
|
+
const signalHandlers = /* @__PURE__ */ new Map();
|
|
1293
|
+
const cleanup = () => {
|
|
1294
|
+
runtime.clearInterval(keepAliveTimer);
|
|
1295
|
+
for (const signal of signals) {
|
|
1296
|
+
const handler = signalHandlers.get(signal);
|
|
1297
|
+
if (handler !== void 0) {
|
|
1298
|
+
runtime.process.off(signal, handler);
|
|
1299
|
+
}
|
|
1300
|
+
}
|
|
1301
|
+
};
|
|
1302
|
+
const handleStop = async () => {
|
|
1303
|
+
if (stopping) {
|
|
1304
|
+
return;
|
|
1305
|
+
}
|
|
1306
|
+
stopping = true;
|
|
1307
|
+
cleanup();
|
|
1308
|
+
try {
|
|
1309
|
+
await indexer.stop();
|
|
1310
|
+
resolve();
|
|
1311
|
+
} catch (error) {
|
|
1312
|
+
reject(error);
|
|
1313
|
+
}
|
|
1314
|
+
};
|
|
1315
|
+
for (const signal of signals) {
|
|
1316
|
+
const handler = () => {
|
|
1317
|
+
void handleStop();
|
|
1318
|
+
};
|
|
1319
|
+
signalHandlers.set(signal, handler);
|
|
1320
|
+
runtime.process.on(signal, handler);
|
|
1321
|
+
}
|
|
1322
|
+
});
|
|
1323
|
+
};
|
|
1197
1324
|
var Indexer = {
|
|
1198
1325
|
create: createIndexer
|
|
1199
1326
|
};
|
|
@@ -1222,6 +1349,9 @@ exports.Storage = Storage;
|
|
|
1222
1349
|
exports.StorageLive = StorageLive;
|
|
1223
1350
|
exports.computeSnapshot = computeSnapshot;
|
|
1224
1351
|
exports.createIndexer = createIndexer;
|
|
1352
|
+
exports.defineIndexerConfig = defineIndexerConfig;
|
|
1225
1353
|
exports.resolveConfig = resolveConfig;
|
|
1354
|
+
exports.resolveIndexerConfigFromEnv = resolveIndexerConfigFromEnv;
|
|
1355
|
+
exports.runIndexerWorker = runIndexerWorker;
|
|
1226
1356
|
//# sourceMappingURL=index.cjs.map
|
|
1227
1357
|
//# sourceMappingURL=index.cjs.map
|