@latticexyz/store-indexer 2.2.18-8d0ce55e964e646a1c804c401df01c4deb866f30 → 2.2.18-9fa07c8489f1fbf167d0db01cd9aaa645a29c8e2

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 (54) hide show
  1. package/dist/bin/postgres-decoded-indexer.cjs +322 -0
  2. package/dist/bin/postgres-decoded-indexer.cjs.map +1 -0
  3. package/dist/bin/postgres-decoded-indexer.d.cts +1 -0
  4. package/dist/bin/postgres-decoded-indexer.js +92 -1
  5. package/dist/bin/postgres-decoded-indexer.js.map +1 -1
  6. package/dist/bin/postgres-frontend.cjs +567 -0
  7. package/dist/bin/postgres-frontend.cjs.map +1 -0
  8. package/dist/bin/postgres-frontend.d.cts +1 -0
  9. package/dist/bin/postgres-frontend.js +258 -5
  10. package/dist/bin/postgres-frontend.js.map +1 -1
  11. package/dist/bin/postgres-indexer.cjs +368 -0
  12. package/dist/bin/postgres-indexer.cjs.map +1 -0
  13. package/dist/bin/postgres-indexer.d.cts +1 -0
  14. package/dist/bin/postgres-indexer.js +106 -1
  15. package/dist/bin/postgres-indexer.js.map +1 -1
  16. package/dist/bin/sqlite-indexer.cjs +567 -0
  17. package/dist/bin/sqlite-indexer.cjs.map +1 -0
  18. package/dist/bin/sqlite-indexer.d.cts +1 -0
  19. package/dist/bin/sqlite-indexer.js +243 -1
  20. package/dist/bin/sqlite-indexer.js.map +1 -1
  21. package/dist/chunk-66BWQNF7.js +38 -0
  22. package/dist/{chunk-R7HX5BT2.js.map → chunk-66BWQNF7.js.map} +1 -1
  23. package/dist/chunk-CGE4ONKA.js +44 -0
  24. package/dist/{chunk-YQ7E5W26.js.map → chunk-CGE4ONKA.js.map} +1 -1
  25. package/dist/chunk-GDNGJPVT.js +16 -0
  26. package/dist/{chunk-AYPBOJNL.js.map → chunk-GDNGJPVT.js.map} +1 -1
  27. package/dist/chunk-L5CWEDU6.js +53 -0
  28. package/dist/{chunk-O2SDU7EQ.js.map → chunk-L5CWEDU6.js.map} +1 -1
  29. package/dist/chunk-O4XAWAXU.js +72 -0
  30. package/dist/{chunk-ED45N3IT.js.map → chunk-O4XAWAXU.js.map} +1 -1
  31. package/dist/chunk-R7UQFYRA.js +99 -0
  32. package/dist/{chunk-JDWVOODJ.js.map → chunk-R7UQFYRA.js.map} +1 -1
  33. package/dist/chunk-SJLOWI5M.js +31 -0
  34. package/dist/{chunk-7O2ZWWUX.js.map → chunk-SJLOWI5M.js.map} +1 -1
  35. package/dist/healthcheck-2DQWYXPX.js +7 -0
  36. package/dist/helloWorld-6IXGINV6.js +7 -0
  37. package/dist/index.cjs +2 -0
  38. package/dist/index.d.cts +2 -0
  39. package/dist/metrics-HO5SO4EX.js +7 -0
  40. package/dist/metrics-HO5SO4EX.js.map +1 -0
  41. package/package.json +16 -7
  42. package/dist/chunk-7O2ZWWUX.js +0 -2
  43. package/dist/chunk-AYPBOJNL.js +0 -2
  44. package/dist/chunk-ED45N3IT.js +0 -2
  45. package/dist/chunk-JDWVOODJ.js +0 -2
  46. package/dist/chunk-O2SDU7EQ.js +0 -7
  47. package/dist/chunk-R7HX5BT2.js +0 -2
  48. package/dist/chunk-YQ7E5W26.js +0 -2
  49. package/dist/healthcheck-57YETUEX.js +0 -2
  50. package/dist/helloWorld-4VT4FZ7F.js +0 -2
  51. package/dist/metrics-4BMCDEZZ.js +0 -2
  52. /package/dist/{healthcheck-57YETUEX.js.map → healthcheck-2DQWYXPX.js.map} +0 -0
  53. /package/dist/{helloWorld-4VT4FZ7F.js.map → helloWorld-6IXGINV6.js.map} +0 -0
  54. /package/dist/{metrics-4BMCDEZZ.js.map → index.cjs.map} +0 -0
@@ -0,0 +1,567 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (let key of __getOwnPropNames(from))
12
+ if (!__hasOwnProp.call(to, key) && key !== except)
13
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
+ }
15
+ return to;
16
+ };
17
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
18
+ // If the importer is in node compatibility mode or this is not an ESM
19
+ // file that has been converted to a CommonJS file using a Babel-
20
+ // compatible transform (i.e. "__esModule" has not been set), then set
21
+ // "default" to the CommonJS "module.exports" for node compatibility.
22
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
23
+ mod
24
+ ));
25
+
26
+ // src/bin/sqlite-indexer.ts
27
+ var import_config = require("dotenv/config");
28
+ var import_node_fs = __toESM(require("fs"), 1);
29
+ var import_zod2 = require("zod");
30
+ var import_drizzle_orm2 = require("drizzle-orm");
31
+ var import_better_sqlite3 = require("drizzle-orm/better-sqlite3");
32
+ var import_better_sqlite32 = __toESM(require("better-sqlite3"), 1);
33
+ var import_koa = __toESM(require("koa"), 1);
34
+ var import_cors = __toESM(require("@koa/cors"), 1);
35
+ var import_trpc_koa_adapter = require("trpc-koa-adapter");
36
+ var import_trpc_indexer = require("@latticexyz/store-sync/trpc-indexer");
37
+ var import_sqlite2 = require("@latticexyz/store-sync/sqlite");
38
+
39
+ // src/sqlite/getTablesWithRecords.ts
40
+ var import_drizzle_orm = require("drizzle-orm");
41
+ var import_sqlite = require("@latticexyz/store-sync/sqlite");
42
+ var import_viem = require("viem");
43
+ var import_internal = require("@latticexyz/protocol-parser/internal");
44
+ var import_common = require("@latticexyz/common");
45
+ var import_utils = require("@latticexyz/common/utils");
46
+ function getTablesWithRecords(database, {
47
+ chainId,
48
+ address,
49
+ filters = []
50
+ }) {
51
+ const metadata = database.select().from(import_sqlite.chainState).where((0, import_drizzle_orm.eq)(import_sqlite.chainState.chainId, chainId)).limit(1).all().find(() => true);
52
+ const tableIds = Array.from(new Set(filters.map((filter2) => filter2.tableId)));
53
+ const tables = (0, import_sqlite.getTables)(database).filter((table) => address == null || (0, import_viem.getAddress)(address) === (0, import_viem.getAddress)(table.address)).filter((table) => !tableIds.length || tableIds.includes(table.tableId));
54
+ const tablesWithRecords = tables.map((table) => {
55
+ const sqliteTable = (0, import_sqlite.buildTable)(table);
56
+ const records = database.select().from(sqliteTable).where((0, import_drizzle_orm.eq)(sqliteTable.__isDeleted, false)).orderBy(
57
+ (0, import_drizzle_orm.asc)(sqliteTable.__lastUpdatedBlockNumber)
58
+ // TODO: add logIndex (https://github.com/latticexyz/mud/issues/1979)
59
+ ).all();
60
+ const filteredRecords = !filters.length ? records : records.filter((record) => {
61
+ const keyTuple = (0, import_internal.decodeDynamicField)("bytes32[]", record.__key);
62
+ return filters.some(
63
+ (filter2) => filter2.tableId === table.tableId && (filter2.key0 == null || filter2.key0 === keyTuple[0]) && (filter2.key1 == null || filter2.key1 === keyTuple[1])
64
+ );
65
+ });
66
+ const resource = (0, import_common.hexToResource)(table.tableId);
67
+ return {
68
+ ...table,
69
+ type: resource.type,
70
+ schema: (0, import_utils.mapObject)({ ...table.keySchema, ...table.valueSchema }, (type) => ({ type, internalType: type })),
71
+ key: Object.keys(table.keySchema),
72
+ records: filteredRecords.map((record) => {
73
+ const key = Object.fromEntries(Object.entries(table.keySchema).map(([name]) => [name, record[name]]));
74
+ const value = Object.fromEntries(Object.entries(table.valueSchema).map(([name]) => [name, record[name]]));
75
+ return { key, value, fields: { ...key, ...value } };
76
+ })
77
+ };
78
+ });
79
+ return {
80
+ blockNumber: metadata?.lastUpdatedBlockNumber ?? null,
81
+ tables: tablesWithRecords
82
+ };
83
+ }
84
+
85
+ // src/sqlite/createQueryAdapter.ts
86
+ var import_store_sync = require("@latticexyz/store-sync");
87
+ async function createQueryAdapter(database) {
88
+ const adapter = {
89
+ async getLogs(opts) {
90
+ const { blockNumber, tables } = getTablesWithRecords(database, opts);
91
+ const logs = (0, import_store_sync.tablesWithRecordsToLogs)(tables);
92
+ return { blockNumber: blockNumber ?? 0n, logs };
93
+ },
94
+ async findAll(opts) {
95
+ return getTablesWithRecords(database, opts);
96
+ }
97
+ };
98
+ return adapter;
99
+ }
100
+
101
+ // src/bin/sqlite-indexer.ts
102
+ var import_rxjs = require("rxjs");
103
+
104
+ // src/bin/parseEnv.ts
105
+ var import_viem2 = require("viem");
106
+ var import_zod = require("zod");
107
+ var frontendEnvSchema = import_zod.z.object({
108
+ HOST: import_zod.z.string().default("0.0.0.0"),
109
+ PORT: import_zod.z.coerce.number().positive().default(3001)
110
+ });
111
+ var indexerEnvSchema = import_zod.z.intersection(
112
+ import_zod.z.object({
113
+ FOLLOW_BLOCK_TAG: import_zod.z.enum(["latest", "safe", "finalized"]).default("safe"),
114
+ START_BLOCK: import_zod.z.coerce.bigint().nonnegative().default(0n),
115
+ MAX_BLOCK_RANGE: import_zod.z.coerce.bigint().positive().default(1000n),
116
+ POLLING_INTERVAL: import_zod.z.coerce.number().positive().default(1e3),
117
+ STORE_ADDRESS: import_zod.z.string().optional().transform((input2) => input2 === "" ? void 0 : input2).refine(isHexOrUndefined),
118
+ INTERNAL__VALIDATE_BLOCK_RANGE: import_zod.z.string().optional().transform((input2) => input2 === "true" || input2 === "1")
119
+ }),
120
+ import_zod.z.union([
121
+ import_zod.z.object({
122
+ RPC_HTTP_URL: import_zod.z.string(),
123
+ RPC_WS_URL: import_zod.z.string().optional()
124
+ }),
125
+ import_zod.z.object({
126
+ RPC_HTTP_URL: import_zod.z.string().optional(),
127
+ RPC_WS_URL: import_zod.z.string()
128
+ })
129
+ ])
130
+ );
131
+ function parseEnv(envSchema) {
132
+ try {
133
+ return envSchema.parse(process.env);
134
+ } catch (error2) {
135
+ if (error2 instanceof import_zod.ZodError) {
136
+ const { ...invalidEnvVars } = error2.format();
137
+ console.error(`
138
+ Missing or invalid environment variables:
139
+
140
+ ${Object.keys(invalidEnvVars).join("\n ")}
141
+ `);
142
+ process.exit(1);
143
+ }
144
+ throw error2;
145
+ }
146
+ }
147
+ function isHexOrUndefined(input2) {
148
+ return input2 === void 0 || (0, import_viem2.isHex)(input2);
149
+ }
150
+
151
+ // src/koa-middleware/healthcheck.ts
152
+ function healthcheck({ isHealthy, isReady } = {}) {
153
+ return async function healthcheckMiddleware(ctx, next) {
154
+ if (ctx.path === "/healthz") {
155
+ if (isHealthy == null || isHealthy()) {
156
+ ctx.status = 200;
157
+ ctx.body = "healthy";
158
+ } else {
159
+ ctx.status = 503;
160
+ ctx.body = "not healthy";
161
+ }
162
+ return;
163
+ }
164
+ if (ctx.path === "/readyz") {
165
+ if (isReady == null || isReady()) {
166
+ ctx.status = 200;
167
+ ctx.body = "ready";
168
+ } else {
169
+ ctx.status = 503;
170
+ ctx.body = "not ready";
171
+ }
172
+ return;
173
+ }
174
+ await next();
175
+ };
176
+ }
177
+
178
+ // src/koa-middleware/helloWorld.ts
179
+ function helloWorld() {
180
+ return async function helloWorldMiddleware(ctx, next) {
181
+ if (ctx.path === "/") {
182
+ ctx.status = 200;
183
+ ctx.body = "emit HelloWorld();";
184
+ return;
185
+ }
186
+ await next();
187
+ };
188
+ }
189
+
190
+ // src/sqlite/apiRoutes.ts
191
+ var import_router = __toESM(require("@koa/router"), 1);
192
+ var import_koa_compose = __toESM(require("koa-compose"), 1);
193
+ var import_indexer_client = require("@latticexyz/store-sync/indexer-client");
194
+ var import_store_sync2 = require("@latticexyz/store-sync");
195
+
196
+ // src/debug.ts
197
+ var import_debug = __toESM(require("debug"), 1);
198
+ var debug = (0, import_debug.default)("mud:store-indexer");
199
+ var error = (0, import_debug.default)("mud:store-indexer");
200
+ debug.log = console.debug.bind(console);
201
+ error.log = console.error.bind(console);
202
+
203
+ // src/sqlite/apiRoutes.ts
204
+ var import_common2 = require("@latticexyz/common");
205
+
206
+ // src/koa-middleware/compress.ts
207
+ var import_node_stream = require("stream");
208
+ var import_accepts = __toESM(require("accepts"), 1);
209
+ var import_node_zlib = require("zlib");
210
+ var import_utils2 = require("@latticexyz/common/utils");
211
+ var encodings = {
212
+ br: import_node_zlib.createBrotliCompress,
213
+ gzip: import_node_zlib.createGzip,
214
+ deflate: import_node_zlib.createDeflate
215
+ };
216
+ var encodingNames = Object.keys(encodings);
217
+ function flushEvery(stream, bytesThreshold) {
218
+ let bytesSinceFlush = 0;
219
+ stream.on("data", (data) => {
220
+ bytesSinceFlush += data.length;
221
+ if (bytesSinceFlush > bytesThreshold) {
222
+ bytesSinceFlush = 0;
223
+ stream.flush();
224
+ }
225
+ });
226
+ return stream;
227
+ }
228
+ function compress({ flushThreshold = 1024 * 4 } = {}) {
229
+ return async function compressMiddleware(ctx, next) {
230
+ ctx.vary("Accept-Encoding");
231
+ await next();
232
+ const encoding = (0, import_accepts.default)(ctx.req).encoding(encodingNames);
233
+ if (!(0, import_utils2.includes)(encodingNames, encoding)) return;
234
+ const compressed = flushEvery(encodings[encoding](), flushThreshold);
235
+ ctx.set("Content-Encoding", encoding);
236
+ ctx.body = ctx.body instanceof import_node_stream.Stream ? ctx.body.pipe(compressed) : compressed.end(ctx.body);
237
+ };
238
+ }
239
+
240
+ // src/sqlite/apiRoutes.ts
241
+ function apiRoutes(database) {
242
+ const router = new import_router.default();
243
+ router.get("/api/logs", compress(), async (ctx) => {
244
+ const benchmark = (0, import_common2.createBenchmark)("sqlite:logs");
245
+ let options;
246
+ try {
247
+ options = import_indexer_client.input.parse(typeof ctx.query.input === "string" ? JSON.parse(ctx.query.input) : {});
248
+ } catch (error2) {
249
+ ctx.status = 400;
250
+ ctx.body = JSON.stringify(error2);
251
+ debug(error2);
252
+ return;
253
+ }
254
+ try {
255
+ options.filters = options.filters.length > 0 ? [...options.filters, { tableId: import_store_sync2.schemasTable.tableId }] : [];
256
+ benchmark("parse config");
257
+ const { blockNumber, tables } = getTablesWithRecords(database, options);
258
+ benchmark("query tables with records");
259
+ const logs = (0, import_store_sync2.tablesWithRecordsToLogs)(tables);
260
+ benchmark("convert records to logs");
261
+ ctx.body = JSON.stringify({ blockNumber: blockNumber?.toString() ?? "-1", logs });
262
+ ctx.status = 200;
263
+ } catch (error2) {
264
+ ctx.status = 500;
265
+ ctx.body = JSON.stringify(error2);
266
+ debug(error2);
267
+ }
268
+ });
269
+ return (0, import_koa_compose.default)([router.routes(), router.allowedMethods()]);
270
+ }
271
+
272
+ // src/koa-middleware/sentry.ts
273
+ var Sentry = __toESM(require("@sentry/node"), 1);
274
+ var import_profiling_node = require("@sentry/profiling-node");
275
+ var import_utils3 = require("@sentry/utils");
276
+ var import_koa_compose2 = __toESM(require("koa-compose"), 1);
277
+ function errorHandler() {
278
+ return async function errorHandlerMiddleware(ctx, next) {
279
+ try {
280
+ await next();
281
+ } catch (err) {
282
+ Sentry.withScope((scope) => {
283
+ scope.addEventProcessor((event) => {
284
+ return Sentry.addRequestDataToEvent(event, ctx.request);
285
+ });
286
+ Sentry.captureException(err);
287
+ });
288
+ throw err;
289
+ }
290
+ };
291
+ }
292
+ function requestHandler() {
293
+ return async function requestHandlerMiddleware(ctx, next) {
294
+ await Sentry.runWithAsyncContext(async () => {
295
+ const hub = Sentry.getCurrentHub();
296
+ hub.configureScope(
297
+ (scope) => scope.addEventProcessor(
298
+ (event) => Sentry.addRequestDataToEvent(event, ctx.request, {
299
+ include: {
300
+ user: false
301
+ }
302
+ })
303
+ )
304
+ );
305
+ await next();
306
+ });
307
+ };
308
+ }
309
+ function tracing() {
310
+ return async function tracingMiddleware(ctx, next) {
311
+ const reqMethod = (ctx.method || "").toUpperCase();
312
+ const reqUrl = ctx.url && (0, import_utils3.stripUrlQueryAndFragment)(ctx.url);
313
+ let traceparentData;
314
+ if (ctx.request.get("sentry-trace")) {
315
+ traceparentData = Sentry.extractTraceparentData(ctx.request.get("sentry-trace"));
316
+ }
317
+ const transaction = Sentry.startTransaction({
318
+ name: `${reqMethod} ${reqUrl}`,
319
+ op: "http.server",
320
+ ...traceparentData
321
+ });
322
+ ctx.__sentry_transaction = transaction;
323
+ Sentry.getCurrentHub().configureScope((scope) => {
324
+ scope.setSpan(transaction);
325
+ });
326
+ ctx.res.on("finish", () => {
327
+ setImmediate(() => {
328
+ if (ctx._matchedRoute) {
329
+ const mountPath = ctx.mountPath || "";
330
+ transaction.setName(`${reqMethod} ${mountPath}${ctx._matchedRoute}`);
331
+ }
332
+ transaction.setHttpStatus(ctx.status);
333
+ transaction.finish();
334
+ });
335
+ });
336
+ await next();
337
+ };
338
+ }
339
+ function sentry(dsn) {
340
+ debug("Initializing Sentry");
341
+ Sentry.init({
342
+ dsn,
343
+ integrations: [
344
+ // Automatically instrument Node.js libraries and frameworks
345
+ ...Sentry.autoDiscoverNodePerformanceMonitoringIntegrations(),
346
+ new import_profiling_node.ProfilingIntegration()
347
+ ],
348
+ // Performance Monitoring
349
+ tracesSampleRate: 1,
350
+ // Set sampling rate for profiling - this is relative to tracesSampleRate
351
+ profilesSampleRate: 1
352
+ });
353
+ return (0, import_koa_compose2.default)([errorHandler(), requestHandler(), tracing()]);
354
+ }
355
+
356
+ // src/koa-middleware/metrics.ts
357
+ var import_prom_client = __toESM(require("prom-client"), 1);
358
+ function metrics({
359
+ isHealthy,
360
+ isReady,
361
+ getLatestStoredBlockNumber,
362
+ getDistanceFromFollowBlock,
363
+ followBlockTag
364
+ } = {}) {
365
+ import_prom_client.default.collectDefaultMetrics();
366
+ if (isHealthy != null) {
367
+ new import_prom_client.default.Gauge({
368
+ name: "health_status",
369
+ help: "Health status (0 = unhealthy, 1 = healthy)",
370
+ collect() {
371
+ this.set(Number(isHealthy()));
372
+ }
373
+ });
374
+ }
375
+ if (isReady != null) {
376
+ new import_prom_client.default.Gauge({
377
+ name: "readiness_status",
378
+ help: "Readiness status (whether the service is ready to receive requests, 0 = not ready, 1 = ready)",
379
+ collect() {
380
+ this.set(Number(isReady()));
381
+ }
382
+ });
383
+ }
384
+ if (getLatestStoredBlockNumber != null) {
385
+ new import_prom_client.default.Gauge({
386
+ name: "latest_stored_block_number",
387
+ help: "Latest block number stored in the database",
388
+ async collect() {
389
+ this.set(Number(await getLatestStoredBlockNumber()));
390
+ }
391
+ });
392
+ }
393
+ if (followBlockTag != null) {
394
+ const blockTagGauge = new import_prom_client.default.Gauge({
395
+ name: "follow_block_tag",
396
+ help: "Block tag the indexer is following (0 = finalized, 1 = safe, 2 = latest)"
397
+ });
398
+ const blockTagToValue = {
399
+ finalized: 0,
400
+ safe: 1,
401
+ latest: 2
402
+ };
403
+ blockTagGauge.set(blockTagToValue[followBlockTag]);
404
+ }
405
+ if (getDistanceFromFollowBlock != null) {
406
+ new import_prom_client.default.Gauge({
407
+ name: "distance_from_follow_block",
408
+ help: "Block distance from the block tag this the indexer is following",
409
+ async collect() {
410
+ this.set(Number(await getDistanceFromFollowBlock()));
411
+ }
412
+ });
413
+ }
414
+ return async function metricsMiddleware(ctx, next) {
415
+ if (ctx.path === "/metrics") {
416
+ ctx.status = 200;
417
+ ctx.body = await import_prom_client.default.register.metrics();
418
+ return;
419
+ }
420
+ await next();
421
+ };
422
+ }
423
+
424
+ // src/bin/getClientOptions.ts
425
+ var import_viem3 = require("viem");
426
+ var import_utils4 = require("@latticexyz/common/utils");
427
+ var import_actions = require("viem/actions");
428
+ async function getClientOptions(env) {
429
+ if (env.INTERNAL__VALIDATE_BLOCK_RANGE) {
430
+ const rpcHttpUrl = env.RPC_HTTP_URL;
431
+ if (!rpcHttpUrl) {
432
+ throw new Error("Must provide RPC_HTTP_URL when using INTERNAL__VALIDATE_BLOCK_RANGE.");
433
+ }
434
+ const chainId = await (0, import_actions.getChainId)((0, import_viem3.createClient)({ transport: (0, import_viem3.http)(rpcHttpUrl) }));
435
+ const chain = {
436
+ id: chainId,
437
+ name: "Unknown",
438
+ nativeCurrency: { decimals: 18, name: "Ether", symbol: "ETH" },
439
+ rpcUrls: { default: { http: [rpcHttpUrl] } }
440
+ };
441
+ return {
442
+ internal_clientOptions: {
443
+ chain,
444
+ pollingInterval: env.POLLING_INTERVAL,
445
+ validateBlockRange: env.INTERNAL__VALIDATE_BLOCK_RANGE
446
+ }
447
+ };
448
+ }
449
+ const transport = (0, import_viem3.fallback)(
450
+ [
451
+ // prefer WS when specified
452
+ env.RPC_WS_URL ? (0, import_viem3.webSocket)(env.RPC_WS_URL) : void 0,
453
+ // otherwise use or fallback to HTTP
454
+ env.RPC_HTTP_URL ? (0, import_viem3.http)(env.RPC_HTTP_URL) : void 0
455
+ ].filter(import_utils4.isDefined)
456
+ );
457
+ const publicClient = (0, import_viem3.createClient)({
458
+ transport,
459
+ pollingInterval: env.POLLING_INTERVAL
460
+ });
461
+ return { publicClient };
462
+ }
463
+
464
+ // src/bin/sqlite-indexer.ts
465
+ var import_block_logs_stream = require("@latticexyz/block-logs-stream");
466
+ var import_actions2 = require("viem/actions");
467
+ (async () => {
468
+ const env = parseEnv(
469
+ import_zod2.z.intersection(
470
+ import_zod2.z.intersection(indexerEnvSchema, frontendEnvSchema),
471
+ import_zod2.z.object({
472
+ SQLITE_FILENAME: import_zod2.z.string().default("indexer.db"),
473
+ SENTRY_DSN: import_zod2.z.string().optional()
474
+ })
475
+ )
476
+ );
477
+ const clientOptions = await getClientOptions(env);
478
+ const chainId = await (0, import_actions2.getChainId)((0, import_block_logs_stream.getRpcClient)(clientOptions));
479
+ const database = (0, import_better_sqlite3.drizzle)(new import_better_sqlite32.default(env.SQLITE_FILENAME));
480
+ let startBlock = env.START_BLOCK;
481
+ async function getCurrentChainState() {
482
+ try {
483
+ const currentChainStates = database.select().from(import_sqlite2.chainState).where((0, import_drizzle_orm2.eq)(import_sqlite2.chainState.chainId, chainId)).all();
484
+ const currentChainState2 = currentChainStates[0];
485
+ return currentChainState2;
486
+ } catch (error2) {
487
+ }
488
+ }
489
+ async function getLatestStoredBlockNumber() {
490
+ const currentChainState2 = await getCurrentChainState();
491
+ return currentChainState2?.lastUpdatedBlockNumber ?? void 0;
492
+ }
493
+ async function getDistanceFromFollowBlock() {
494
+ const [latestStoredBlockNumber, latestFollowBlock] = await Promise.all([
495
+ getLatestStoredBlockNumber(),
496
+ (0, import_actions2.getBlock)((0, import_block_logs_stream.getRpcClient)(clientOptions), { blockTag: env.FOLLOW_BLOCK_TAG })
497
+ ]);
498
+ return latestFollowBlock.number - (latestStoredBlockNumber ?? -1n);
499
+ }
500
+ const currentChainState = await getCurrentChainState();
501
+ if (currentChainState) {
502
+ if (currentChainState.schemaVersion != import_sqlite2.schemaVersion) {
503
+ console.log(
504
+ "schema version changed from",
505
+ currentChainState.schemaVersion,
506
+ "to",
507
+ import_sqlite2.schemaVersion,
508
+ "recreating database"
509
+ );
510
+ import_node_fs.default.truncateSync(env.SQLITE_FILENAME);
511
+ } else if (currentChainState.lastUpdatedBlockNumber != null) {
512
+ console.log("resuming from block number", currentChainState.lastUpdatedBlockNumber + 1n);
513
+ startBlock = currentChainState.lastUpdatedBlockNumber + 1n;
514
+ }
515
+ }
516
+ const { latestBlockNumber$, storedBlockLogs$ } = await (0, import_sqlite2.syncToSqlite)({
517
+ ...clientOptions,
518
+ database,
519
+ followBlockTag: env.FOLLOW_BLOCK_TAG,
520
+ startBlock,
521
+ maxBlockRange: env.MAX_BLOCK_RANGE,
522
+ address: env.STORE_ADDRESS
523
+ });
524
+ let isCaughtUp = false;
525
+ (0, import_rxjs.combineLatest)([latestBlockNumber$, storedBlockLogs$]).pipe(
526
+ (0, import_rxjs.filter)(
527
+ ([latestBlockNumber, { blockNumber: lastBlockNumberProcessed }]) => latestBlockNumber === lastBlockNumberProcessed
528
+ ),
529
+ (0, import_rxjs.first)()
530
+ ).subscribe(() => {
531
+ isCaughtUp = true;
532
+ console.log("all caught up");
533
+ });
534
+ const server = new import_koa.default();
535
+ if (env.SENTRY_DSN) {
536
+ server.use(sentry(env.SENTRY_DSN));
537
+ }
538
+ server.use((0, import_cors.default)());
539
+ server.use(
540
+ healthcheck({
541
+ isReady: () => isCaughtUp
542
+ })
543
+ );
544
+ server.use(
545
+ metrics({
546
+ isHealthy: () => true,
547
+ isReady: () => isCaughtUp,
548
+ getLatestStoredBlockNumber,
549
+ getDistanceFromFollowBlock,
550
+ followBlockTag: env.FOLLOW_BLOCK_TAG
551
+ })
552
+ );
553
+ server.use(helloWorld());
554
+ server.use(apiRoutes(database));
555
+ server.use(
556
+ (0, import_trpc_koa_adapter.createKoaMiddleware)({
557
+ prefix: "/trpc",
558
+ router: (0, import_trpc_indexer.createAppRouter)(),
559
+ createContext: async () => ({
560
+ queryAdapter: await createQueryAdapter(database)
561
+ })
562
+ })
563
+ );
564
+ server.listen({ host: env.HOST, port: env.PORT });
565
+ console.log(`sqlite indexer frontend listening on http://${env.HOST}:${env.PORT}`);
566
+ })();
567
+ //# sourceMappingURL=sqlite-indexer.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/bin/sqlite-indexer.ts","../../src/sqlite/getTablesWithRecords.ts","../../src/sqlite/createQueryAdapter.ts","../../src/bin/parseEnv.ts","../../src/koa-middleware/healthcheck.ts","../../src/koa-middleware/helloWorld.ts","../../src/sqlite/apiRoutes.ts","../../src/debug.ts","../../src/koa-middleware/compress.ts","../../src/koa-middleware/sentry.ts","../../src/koa-middleware/metrics.ts","../../src/bin/getClientOptions.ts"],"sourcesContent":["#!/usr/bin/env node\nimport \"dotenv/config\";\nimport fs from \"node:fs\";\nimport { z } from \"zod\";\nimport { eq } from \"drizzle-orm\";\nimport { drizzle } from \"drizzle-orm/better-sqlite3\";\nimport Database from \"better-sqlite3\";\nimport Koa from \"koa\";\nimport cors from \"@koa/cors\";\nimport { createKoaMiddleware } from \"trpc-koa-adapter\";\nimport { createAppRouter } from \"@latticexyz/store-sync/trpc-indexer\";\nimport { chainState, schemaVersion, syncToSqlite } from \"@latticexyz/store-sync/sqlite\";\nimport { createQueryAdapter } from \"../sqlite/createQueryAdapter\";\nimport { combineLatest, filter, first } from \"rxjs\";\nimport { frontendEnvSchema, indexerEnvSchema, parseEnv } from \"./parseEnv\";\nimport { healthcheck } from \"../koa-middleware/healthcheck\";\nimport { helloWorld } from \"../koa-middleware/helloWorld\";\nimport { apiRoutes } from \"../sqlite/apiRoutes\";\nimport { sentry } from \"../koa-middleware/sentry\";\nimport { metrics } from \"../koa-middleware/metrics\";\nimport { getClientOptions } from \"./getClientOptions\";\nimport { getRpcClient } from \"@latticexyz/block-logs-stream\";\nimport { getBlock, getChainId } from \"viem/actions\";\n\n// Workaround for:\n// Top-level await is currently not supported with the \"cjs\" output format\n(async (): Promise<void> => {\n const env = parseEnv(\n z.intersection(\n z.intersection(indexerEnvSchema, frontendEnvSchema),\n z.object({\n SQLITE_FILENAME: z.string().default(\"indexer.db\"),\n SENTRY_DSN: z.string().optional(),\n }),\n ),\n );\n\n const clientOptions = await getClientOptions(env);\n\n const chainId = await getChainId(getRpcClient(clientOptions));\n const database = drizzle(new Database(env.SQLITE_FILENAME));\n\n let startBlock = env.START_BLOCK;\n\n async function getCurrentChainState(): Promise<\n | {\n schemaVersion: number;\n chainId: number;\n lastUpdatedBlockNumber: bigint | null;\n lastError: string | null;\n }\n | undefined\n > {\n // This will throw if the DB doesn't exist yet, so we wrap in a try/catch and ignore the error.\n try {\n const currentChainStates = database.select().from(chainState).where(eq(chainState.chainId, chainId)).all();\n // TODO: replace this type workaround with `noUncheckedIndexedAccess: true` when we can fix all the issues related (https://github.com/latticexyz/mud/issues/1212)\n const currentChainState: (typeof currentChainStates)[number] | undefined = currentChainStates[0];\n return currentChainState;\n } catch (error) {\n // ignore errors, this is optional\n }\n }\n\n async function getLatestStoredBlockNumber(): Promise<bigint | undefined> {\n const currentChainState = await getCurrentChainState();\n return currentChainState?.lastUpdatedBlockNumber ?? undefined;\n }\n\n async function getDistanceFromFollowBlock(): Promise<bigint> {\n const [latestStoredBlockNumber, latestFollowBlock] = await Promise.all([\n getLatestStoredBlockNumber(),\n getBlock(getRpcClient(clientOptions), { blockTag: env.FOLLOW_BLOCK_TAG }),\n ]);\n return latestFollowBlock.number - (latestStoredBlockNumber ?? -1n);\n }\n\n const currentChainState = await getCurrentChainState();\n if (currentChainState) {\n // Reset the db if the version changed\n if (currentChainState.schemaVersion != schemaVersion) {\n console.log(\n \"schema version changed from\",\n currentChainState.schemaVersion,\n \"to\",\n schemaVersion,\n \"recreating database\",\n );\n fs.truncateSync(env.SQLITE_FILENAME);\n } else if (currentChainState.lastUpdatedBlockNumber != null) {\n // Resume from latest block stored in DB. This will throw if the DB doesn't exist yet, so we wrap in a try/catch and ignore the error.\n console.log(\"resuming from block number\", currentChainState.lastUpdatedBlockNumber + 1n);\n startBlock = currentChainState.lastUpdatedBlockNumber + 1n;\n }\n }\n\n const { latestBlockNumber$, storedBlockLogs$ } = await syncToSqlite({\n ...clientOptions,\n database,\n followBlockTag: env.FOLLOW_BLOCK_TAG,\n startBlock,\n maxBlockRange: env.MAX_BLOCK_RANGE,\n address: env.STORE_ADDRESS,\n });\n\n let isCaughtUp = false;\n combineLatest([latestBlockNumber$, storedBlockLogs$])\n .pipe(\n filter(\n ([latestBlockNumber, { blockNumber: lastBlockNumberProcessed }]) =>\n latestBlockNumber === lastBlockNumberProcessed,\n ),\n first(),\n )\n .subscribe(() => {\n isCaughtUp = true;\n console.log(\"all caught up\");\n });\n\n const server = new Koa();\n\n if (env.SENTRY_DSN) {\n server.use(sentry(env.SENTRY_DSN));\n }\n\n server.use(cors());\n server.use(\n healthcheck({\n isReady: () => isCaughtUp,\n }),\n );\n server.use(\n metrics({\n isHealthy: () => true,\n isReady: () => isCaughtUp,\n getLatestStoredBlockNumber,\n getDistanceFromFollowBlock,\n followBlockTag: env.FOLLOW_BLOCK_TAG,\n }),\n );\n server.use(helloWorld());\n server.use(apiRoutes(database));\n\n server.use(\n createKoaMiddleware({\n prefix: \"/trpc\",\n router: createAppRouter(),\n createContext: async () => ({\n queryAdapter: await createQueryAdapter(database),\n }),\n }),\n );\n\n server.listen({ host: env.HOST, port: env.PORT });\n console.log(`sqlite indexer frontend listening on http://${env.HOST}:${env.PORT}`);\n})();\n","import { asc, eq } from \"drizzle-orm\";\nimport { BaseSQLiteDatabase } from \"drizzle-orm/sqlite-core\";\nimport { buildTable, chainState, getTables } from \"@latticexyz/store-sync/sqlite\";\nimport { Hex, getAddress } from \"viem\";\nimport { decodeDynamicField } from \"@latticexyz/protocol-parser/internal\";\nimport { SyncFilter, TableRecord, TableWithRecords } from \"@latticexyz/store-sync\";\nimport { hexToResource } from \"@latticexyz/common\";\nimport { mapObject } from \"@latticexyz/common/utils\";\n\n// TODO: refactor sqlite and replace this with getLogs to match postgres (https://github.com/latticexyz/mud/issues/1970)\n\n/**\n * @deprecated\n * */\nexport function getTablesWithRecords(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n database: BaseSQLiteDatabase<\"sync\", any>,\n {\n chainId,\n address,\n filters = [],\n }: {\n readonly chainId: number;\n readonly address?: Hex;\n readonly filters?: readonly SyncFilter[];\n },\n): { blockNumber: bigint | null; tables: readonly TableWithRecords[] } {\n const metadata = database\n .select()\n .from(chainState)\n .where(eq(chainState.chainId, chainId))\n .limit(1)\n .all()\n .find(() => true);\n\n // If _any_ filter has a table ID, this will filter down all data to just those tables. Which mean we can't yet mix table filters with key-only filters.\n // TODO: improve this so we can express this in the query (need to be able to query data across tables more easily)\n const tableIds = Array.from(new Set(filters.map((filter) => filter.tableId)));\n const tables = getTables(database)\n .filter((table) => address == null || getAddress(address) === getAddress(table.address))\n .filter((table) => !tableIds.length || tableIds.includes(table.tableId));\n\n const tablesWithRecords = tables.map((table) => {\n const sqliteTable = buildTable(table);\n const records = database\n .select()\n .from(sqliteTable)\n .where(eq(sqliteTable.__isDeleted, false))\n .orderBy(\n asc(sqliteTable.__lastUpdatedBlockNumber),\n // TODO: add logIndex (https://github.com/latticexyz/mud/issues/1979)\n )\n .all();\n const filteredRecords = !filters.length\n ? records\n : records.filter((record) => {\n const keyTuple = decodeDynamicField(\"bytes32[]\", record.__key);\n return filters.some(\n (filter) =>\n filter.tableId === table.tableId &&\n (filter.key0 == null || filter.key0 === keyTuple[0]) &&\n (filter.key1 == null || filter.key1 === keyTuple[1]),\n );\n });\n const resource = hexToResource(table.tableId);\n return {\n ...table,\n type: resource.type as never,\n schema: mapObject({ ...table.keySchema, ...table.valueSchema }, (type) => ({ type, internalType: type })),\n key: Object.keys(table.keySchema),\n records: filteredRecords.map((record): TableRecord => {\n const key = Object.fromEntries(Object.entries(table.keySchema).map(([name]) => [name, record[name]]));\n const value = Object.fromEntries(Object.entries(table.valueSchema).map(([name]) => [name, record[name]]));\n return { key, value, fields: { ...key, ...value } };\n }),\n } satisfies TableWithRecords;\n });\n\n return {\n blockNumber: metadata?.lastUpdatedBlockNumber ?? null,\n tables: tablesWithRecords,\n };\n}\n","import { BaseSQLiteDatabase } from \"drizzle-orm/sqlite-core\";\nimport { QueryAdapter } from \"@latticexyz/store-sync/trpc-indexer\";\nimport { getTablesWithRecords } from \"./getTablesWithRecords\";\nimport { tablesWithRecordsToLogs } from \"@latticexyz/store-sync\";\n\n/**\n * Creates a storage adapter for the tRPC server/client to query data from SQLite.\n *\n * @param {BaseSQLiteDatabase<\"sync\", any>} database SQLite database object from Drizzle\n * @returns {Promise<QueryAdapter>} A set of methods used by tRPC endpoints.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport async function createQueryAdapter(database: BaseSQLiteDatabase<\"sync\", any>): Promise<QueryAdapter> {\n const adapter: QueryAdapter = {\n async getLogs(opts) {\n const { blockNumber, tables } = getTablesWithRecords(database, opts);\n const logs = tablesWithRecordsToLogs(tables);\n return { blockNumber: blockNumber ?? 0n, logs };\n },\n async findAll(opts) {\n return getTablesWithRecords(database, opts);\n },\n };\n return adapter;\n}\n","import { Hex, isHex } from \"viem\";\nimport { z, ZodError, ZodTypeAny } from \"zod\";\n\nexport const frontendEnvSchema = z.object({\n HOST: z.string().default(\"0.0.0.0\"),\n PORT: z.coerce.number().positive().default(3001),\n});\n\nexport const indexerEnvSchema = z.intersection(\n z.object({\n FOLLOW_BLOCK_TAG: z.enum([\"latest\", \"safe\", \"finalized\"]).default(\"safe\"),\n START_BLOCK: z.coerce.bigint().nonnegative().default(0n),\n MAX_BLOCK_RANGE: z.coerce.bigint().positive().default(1000n),\n POLLING_INTERVAL: z.coerce.number().positive().default(1000),\n STORE_ADDRESS: z\n .string()\n .optional()\n .transform((input) => (input === \"\" ? undefined : input))\n .refine(isHexOrUndefined),\n INTERNAL__VALIDATE_BLOCK_RANGE: z\n .string()\n .optional()\n .transform((input) => input === \"true\" || input === \"1\"),\n }),\n z.union([\n z.object({\n RPC_HTTP_URL: z.string(),\n RPC_WS_URL: z.string().optional(),\n }),\n z.object({\n RPC_HTTP_URL: z.string().optional(),\n RPC_WS_URL: z.string(),\n }),\n ]),\n);\n\nexport function parseEnv<TSchema extends ZodTypeAny>(envSchema: TSchema): z.infer<TSchema> {\n try {\n return envSchema.parse(process.env);\n } catch (error) {\n if (error instanceof ZodError) {\n const { ...invalidEnvVars } = error.format();\n console.error(`\\nMissing or invalid environment variables:\\n\\n ${Object.keys(invalidEnvVars).join(\"\\n \")}\\n`);\n process.exit(1);\n }\n throw error;\n }\n}\n\nfunction isHexOrUndefined(input: unknown): input is Hex | undefined {\n return input === undefined || isHex(input);\n}\n","import { Middleware } from \"koa\";\n\ntype HealthcheckOptions = {\n isHealthy?: () => boolean;\n isReady?: () => boolean;\n};\n\n/**\n * Middleware to add Kubernetes healthcheck endpoints\n */\nexport function healthcheck({ isHealthy, isReady }: HealthcheckOptions = {}): Middleware {\n return async function healthcheckMiddleware(ctx, next): Promise<void> {\n if (ctx.path === \"/healthz\") {\n if (isHealthy == null || isHealthy()) {\n ctx.status = 200;\n ctx.body = \"healthy\";\n } else {\n ctx.status = 503;\n ctx.body = \"not healthy\";\n }\n return;\n }\n\n if (ctx.path === \"/readyz\") {\n if (isReady == null || isReady()) {\n ctx.status = 200;\n ctx.body = \"ready\";\n } else {\n ctx.status = 503;\n ctx.body = \"not ready\";\n }\n return;\n }\n\n await next();\n };\n}\n","import { Middleware } from \"koa\";\n\nexport function helloWorld(): Middleware {\n return async function helloWorldMiddleware(ctx, next): Promise<void> {\n if (ctx.path === \"/\") {\n ctx.status = 200;\n ctx.body = \"emit HelloWorld();\";\n return;\n }\n await next();\n };\n}\n","import { Middleware } from \"koa\";\nimport Router from \"@koa/router\";\nimport compose from \"koa-compose\";\nimport { input } from \"@latticexyz/store-sync/indexer-client\";\nimport { schemasTable, tablesWithRecordsToLogs } from \"@latticexyz/store-sync\";\nimport { debug } from \"../debug\";\nimport { createBenchmark } from \"@latticexyz/common\";\nimport { compress } from \"../koa-middleware/compress\";\nimport { getTablesWithRecords } from \"./getTablesWithRecords\";\nimport { BaseSQLiteDatabase } from \"drizzle-orm/sqlite-core\";\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function apiRoutes(database: BaseSQLiteDatabase<\"sync\", any>): Middleware {\n const router = new Router();\n\n router.get(\"/api/logs\", compress(), async (ctx) => {\n const benchmark = createBenchmark(\"sqlite:logs\");\n\n let options: ReturnType<typeof input.parse>;\n\n try {\n options = input.parse(typeof ctx.query.input === \"string\" ? JSON.parse(ctx.query.input) : {});\n } catch (error) {\n ctx.status = 400;\n ctx.body = JSON.stringify(error);\n debug(error);\n return;\n }\n\n try {\n options.filters = options.filters.length > 0 ? [...options.filters, { tableId: schemasTable.tableId }] : [];\n benchmark(\"parse config\");\n const { blockNumber, tables } = getTablesWithRecords(database, options);\n benchmark(\"query tables with records\");\n const logs = tablesWithRecordsToLogs(tables);\n benchmark(\"convert records to logs\");\n\n ctx.body = JSON.stringify({ blockNumber: blockNumber?.toString() ?? \"-1\", logs });\n ctx.status = 200;\n } catch (error) {\n ctx.status = 500;\n ctx.body = JSON.stringify(error);\n debug(error);\n }\n });\n\n return compose([router.routes(), router.allowedMethods()]) as Middleware;\n}\n","import createDebug from \"debug\";\n\nexport const debug = createDebug(\"mud:store-indexer\");\nexport const error = createDebug(\"mud:store-indexer\");\n\n// Pipe debug output to stdout instead of stderr\ndebug.log = console.debug.bind(console);\n\n// Pipe error output to stderr\nerror.log = console.error.bind(console);\n","import { Middleware } from \"koa\";\nimport { Readable, Stream } from \"node:stream\";\nimport accepts from \"accepts\";\nimport { Zlib, createBrotliCompress, createDeflate, createGzip } from \"node:zlib\";\nimport { includes } from \"@latticexyz/common/utils\";\n\n// Loosely based on https://github.com/holic/koa-compress/blob/master/lib/index.js\n// with better handling of streams better with occasional flushing\n\nconst encodings = {\n br: createBrotliCompress,\n gzip: createGzip,\n deflate: createDeflate,\n} as const;\n\nconst encodingNames = Object.keys(encodings) as (keyof typeof encodings)[];\n\nfunction flushEvery<stream extends Zlib & Readable>(stream: stream, bytesThreshold: number): stream {\n let bytesSinceFlush = 0;\n stream.on(\"data\", (data) => {\n bytesSinceFlush += data.length;\n if (bytesSinceFlush > bytesThreshold) {\n bytesSinceFlush = 0;\n stream.flush();\n }\n });\n return stream;\n}\n\ntype CompressOptions = {\n flushThreshold?: number;\n};\n\nexport function compress({ flushThreshold = 1024 * 4 }: CompressOptions = {}): Middleware {\n return async function compressMiddleware(ctx, next) {\n ctx.vary(\"Accept-Encoding\");\n\n await next();\n\n const encoding = accepts(ctx.req).encoding(encodingNames);\n if (!includes(encodingNames, encoding)) return;\n\n const compressed = flushEvery(encodings[encoding](), flushThreshold);\n\n ctx.set(\"Content-Encoding\", encoding);\n ctx.body = ctx.body instanceof Stream ? ctx.body.pipe(compressed) : compressed.end(ctx.body);\n };\n}\n","import * as Sentry from \"@sentry/node\";\nimport { ProfilingIntegration } from \"@sentry/profiling-node\";\nimport { stripUrlQueryAndFragment } from \"@sentry/utils\";\nimport { debug } from \"../debug\";\nimport Koa from \"koa\";\nimport compose from \"koa-compose\";\n\nexport function errorHandler(): Koa.Middleware {\n return async function errorHandlerMiddleware(ctx, next) {\n try {\n await next();\n } catch (err) {\n Sentry.withScope((scope) => {\n scope.addEventProcessor((event) => {\n return Sentry.addRequestDataToEvent(event, ctx.request);\n });\n Sentry.captureException(err);\n });\n throw err;\n }\n };\n}\n\nexport function requestHandler(): Koa.Middleware {\n return async function requestHandlerMiddleware(ctx, next) {\n await Sentry.runWithAsyncContext(async () => {\n const hub = Sentry.getCurrentHub();\n hub.configureScope((scope) =>\n scope.addEventProcessor((event) =>\n Sentry.addRequestDataToEvent(event, ctx.request, {\n include: {\n user: false,\n },\n }),\n ),\n );\n await next();\n });\n };\n}\n\nexport function tracing(): Koa.Middleware {\n // creates a Sentry transaction per request\n return async function tracingMiddleware(ctx, next) {\n const reqMethod = (ctx.method || \"\").toUpperCase();\n const reqUrl = ctx.url && stripUrlQueryAndFragment(ctx.url);\n\n // Connect to trace of upstream app\n let traceparentData;\n if (ctx.request.get(\"sentry-trace\")) {\n traceparentData = Sentry.extractTraceparentData(ctx.request.get(\"sentry-trace\"));\n }\n\n const transaction = Sentry.startTransaction({\n name: `${reqMethod} ${reqUrl}`,\n op: \"http.server\",\n ...traceparentData,\n });\n\n ctx.__sentry_transaction = transaction;\n\n // We put the transaction on the scope so users can attach children to it\n Sentry.getCurrentHub().configureScope((scope) => {\n scope.setSpan(transaction);\n });\n\n ctx.res.on(\"finish\", () => {\n // Push `transaction.finish` to the next event loop so open spans have a chance to finish before the transaction closes\n setImmediate(() => {\n // If you're using koa router, set the matched route as transaction name\n if (ctx._matchedRoute) {\n const mountPath = ctx.mountPath || \"\";\n transaction.setName(`${reqMethod} ${mountPath}${ctx._matchedRoute}`);\n }\n\n transaction.setHttpStatus(ctx.status);\n transaction.finish();\n });\n });\n\n await next();\n };\n}\n\nexport function sentry(dsn: string): Koa.Middleware {\n debug(\"Initializing Sentry\");\n Sentry.init({\n dsn,\n integrations: [\n // Automatically instrument Node.js libraries and frameworks\n ...Sentry.autoDiscoverNodePerformanceMonitoringIntegrations(),\n new ProfilingIntegration(),\n ],\n // Performance Monitoring\n tracesSampleRate: 1.0,\n // Set sampling rate for profiling - this is relative to tracesSampleRate\n profilesSampleRate: 1.0,\n });\n\n return compose([errorHandler(), requestHandler(), tracing()]);\n}\n","import { Middleware } from \"koa\";\nimport promClient from \"prom-client\";\n\ntype MetricsOptions = {\n isHealthy?: () => boolean;\n isReady?: () => boolean;\n getLatestStoredBlockNumber?: () => Promise<bigint | undefined>;\n getDistanceFromFollowBlock?: () => Promise<bigint>;\n followBlockTag?: \"latest\" | \"safe\" | \"finalized\";\n};\n\n/**\n * Middleware to add Prometheus metrics endpoints\n */\nexport function metrics({\n isHealthy,\n isReady,\n getLatestStoredBlockNumber,\n getDistanceFromFollowBlock,\n followBlockTag,\n}: MetricsOptions = {}): Middleware {\n promClient.collectDefaultMetrics();\n if (isHealthy != null) {\n new promClient.Gauge({\n name: \"health_status\",\n help: \"Health status (0 = unhealthy, 1 = healthy)\",\n collect(): void {\n this.set(Number(isHealthy()));\n },\n });\n }\n\n if (isReady != null) {\n new promClient.Gauge({\n name: \"readiness_status\",\n help: \"Readiness status (whether the service is ready to receive requests, 0 = not ready, 1 = ready)\",\n collect(): void {\n this.set(Number(isReady()));\n },\n });\n }\n\n if (getLatestStoredBlockNumber != null) {\n new promClient.Gauge({\n name: \"latest_stored_block_number\",\n help: \"Latest block number stored in the database\",\n async collect(): Promise<void> {\n this.set(Number(await getLatestStoredBlockNumber()));\n },\n });\n }\n\n if (followBlockTag != null) {\n const blockTagGauge = new promClient.Gauge({\n name: \"follow_block_tag\",\n help: \"Block tag the indexer is following (0 = finalized, 1 = safe, 2 = latest)\",\n });\n const blockTagToValue = {\n finalized: 0,\n safe: 1,\n latest: 2,\n };\n blockTagGauge.set(blockTagToValue[followBlockTag]);\n }\n\n if (getDistanceFromFollowBlock != null) {\n new promClient.Gauge({\n name: \"distance_from_follow_block\",\n help: \"Block distance from the block tag this the indexer is following\",\n async collect(): Promise<void> {\n this.set(Number(await getDistanceFromFollowBlock()));\n },\n });\n }\n\n return async function metricsMiddleware(ctx, next): Promise<void> {\n if (ctx.path === \"/metrics\") {\n ctx.status = 200;\n ctx.body = await promClient.register.metrics();\n return;\n }\n\n await next();\n };\n}\n","import { z } from \"zod\";\nimport { indexerEnvSchema } from \"./parseEnv\";\nimport { GetRpcClientOptions } from \"@latticexyz/block-logs-stream\";\nimport { Chain, createClient, fallback, http, webSocket } from \"viem\";\nimport { isDefined } from \"@latticexyz/common/utils\";\nimport { getChainId } from \"viem/actions\";\n\nexport async function getClientOptions(env: z.infer<typeof indexerEnvSchema>): Promise<GetRpcClientOptions> {\n if (env.INTERNAL__VALIDATE_BLOCK_RANGE) {\n const rpcHttpUrl = env.RPC_HTTP_URL;\n if (!rpcHttpUrl) {\n throw new Error(\"Must provide RPC_HTTP_URL when using INTERNAL__VALIDATE_BLOCK_RANGE.\");\n }\n\n const chainId = await getChainId(createClient({ transport: http(rpcHttpUrl) }));\n\n // Mock a chain config so we can use in client options\n const chain = {\n id: chainId,\n name: \"Unknown\",\n nativeCurrency: { decimals: 18, name: \"Ether\", symbol: \"ETH\" },\n rpcUrls: { default: { http: [rpcHttpUrl] } },\n } satisfies Chain;\n\n return {\n internal_clientOptions: {\n chain,\n pollingInterval: env.POLLING_INTERVAL,\n validateBlockRange: env.INTERNAL__VALIDATE_BLOCK_RANGE,\n },\n };\n }\n\n const transport = fallback(\n [\n // prefer WS when specified\n env.RPC_WS_URL ? webSocket(env.RPC_WS_URL) : undefined,\n // otherwise use or fallback to HTTP\n env.RPC_HTTP_URL ? http(env.RPC_HTTP_URL) : undefined,\n ].filter(isDefined),\n );\n\n const publicClient = createClient({\n transport,\n pollingInterval: env.POLLING_INTERVAL,\n });\n\n return { publicClient };\n}\n"],"mappings":"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","names":["import_zod","import_drizzle_orm","import_better_sqlite3","import_sqlite","filter","import_viem","input","error","import_store_sync","createDebug","import_common","import_utils","accepts","Router","error","compose","import_utils","import_koa_compose","compose","promClient","import_viem","import_utils","import_actions","Database","currentChainState","error","fs","Koa","cors"]}
@@ -0,0 +1 @@
1
+ #!/usr/bin/env node