@ensnode/ponder-metadata 0.1.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +105 -0
- package/dist/{middleware.js → index.js} +113 -65
- package/dist/index.js.map +1 -0
- package/package.json +16 -15
- package/dist/middleware.d.ts +0 -18
- package/dist/middleware.js.map +0 -1
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { EnsRainbow } from '@ensnode/ensrainbow-sdk';
|
|
2
|
+
import { MiddlewareHandler } from 'hono';
|
|
3
|
+
import { ReadonlyDrizzle } from 'ponder';
|
|
4
|
+
import { PublicClient } from 'viem';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Basic information about a block.
|
|
8
|
+
*/
|
|
9
|
+
interface BlockInfo {
|
|
10
|
+
/** block number */
|
|
11
|
+
number: number;
|
|
12
|
+
/** block unix timestamp */
|
|
13
|
+
timestamp: number;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Network indexing status for a chain.
|
|
17
|
+
*/
|
|
18
|
+
interface NetworkIndexingStatus {
|
|
19
|
+
/**
|
|
20
|
+
* First block required to be indexed during the historical sync.
|
|
21
|
+
*/
|
|
22
|
+
firstBlockToIndex: BlockInfo;
|
|
23
|
+
/**
|
|
24
|
+
* Latest block synced into indexer's RPC cache.
|
|
25
|
+
*/
|
|
26
|
+
lastSyncedBlock: BlockInfo | null;
|
|
27
|
+
/**
|
|
28
|
+
* Last block processed & indexed by the indexer.
|
|
29
|
+
*/
|
|
30
|
+
lastIndexedBlock: BlockInfo | null;
|
|
31
|
+
/**
|
|
32
|
+
* Latest safe block available on the chain.
|
|
33
|
+
*/
|
|
34
|
+
latestSafeBlock: BlockInfo;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
type PonderEnvVarsInfo = Record<string, unknown>;
|
|
38
|
+
interface PonderMetadataMiddlewareOptions<AppInfo, EnvVars extends PonderEnvVarsInfo> {
|
|
39
|
+
/** Database access object (readonly Drizzle) */
|
|
40
|
+
db: ReadonlyDrizzle<Record<string, unknown>>;
|
|
41
|
+
/** Application info */
|
|
42
|
+
app: AppInfo;
|
|
43
|
+
/** Environment settings info */
|
|
44
|
+
env: EnvVars;
|
|
45
|
+
/** Query methods */
|
|
46
|
+
query: {
|
|
47
|
+
/** Fetches prometheus metrics for Ponder application */
|
|
48
|
+
prometheusMetrics(): Promise<string>;
|
|
49
|
+
/** Fetches the first block do be indexed for a requested chain ID */
|
|
50
|
+
firstBlockToIndexByChainId(chainId: number, publicClient: PublicClient): Promise<BlockInfo>;
|
|
51
|
+
/** Fetches ENSRainbow version information */
|
|
52
|
+
ensRainbowVersion?(): Promise<EnsRainbow.VersionInfo>;
|
|
53
|
+
};
|
|
54
|
+
/** Public clients for each blockchain network fetching data */
|
|
55
|
+
publicClients: Record<number, PublicClient>;
|
|
56
|
+
}
|
|
57
|
+
interface PonderMetadataMiddlewareResponse<AppInfo, EnvVarsInfo extends PonderEnvVarsInfo, RuntimeInfo> {
|
|
58
|
+
/** Application info */
|
|
59
|
+
app: AppInfo;
|
|
60
|
+
/** Dependencies info */
|
|
61
|
+
deps: {
|
|
62
|
+
/** Ponder application version */
|
|
63
|
+
ponder: string;
|
|
64
|
+
/** Node.js runtime version */
|
|
65
|
+
nodejs: string;
|
|
66
|
+
};
|
|
67
|
+
/** Environment settings info */
|
|
68
|
+
env: EnvVarsInfo;
|
|
69
|
+
/** Runtime status info */
|
|
70
|
+
runtime: RuntimeInfo;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Ponder Metadata types definition.
|
|
75
|
+
*/
|
|
76
|
+
interface PonderMetadataModule {
|
|
77
|
+
/** Application info */
|
|
78
|
+
AppInfo: {
|
|
79
|
+
/** Application name */
|
|
80
|
+
name: string;
|
|
81
|
+
/** Application version */
|
|
82
|
+
version: string;
|
|
83
|
+
};
|
|
84
|
+
/** Environment Variables info */
|
|
85
|
+
EnvVars: {
|
|
86
|
+
/** Database schema */
|
|
87
|
+
DATABASE_SCHEMA: string;
|
|
88
|
+
} & PonderEnvVarsInfo;
|
|
89
|
+
/** Runtime info */
|
|
90
|
+
RuntimeInfo: {
|
|
91
|
+
/**
|
|
92
|
+
* Application build id
|
|
93
|
+
* https://github.com/ponder-sh/ponder/blob/626e524/packages/core/src/build/index.ts#L425-L431
|
|
94
|
+
**/
|
|
95
|
+
codebaseBuildId: string;
|
|
96
|
+
/** Network indexing status by chain ID */
|
|
97
|
+
networkIndexingStatusByChainId: Record<number, NetworkIndexingStatus>;
|
|
98
|
+
/** ENSRainbow version info */
|
|
99
|
+
ensRainbow?: EnsRainbow.VersionInfo;
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
type MetadataMiddlewareResponse = PonderMetadataMiddlewareResponse<PonderMetadataModule["AppInfo"], PonderMetadataModule["EnvVars"], PonderMetadataModule["RuntimeInfo"]>;
|
|
103
|
+
declare function ponderMetadata<AppInfo extends PonderMetadataModule["AppInfo"], EnvVars extends PonderMetadataModule["EnvVars"]>({ app, db, env, query, publicClients, }: PonderMetadataMiddlewareOptions<AppInfo, EnvVars>): MiddlewareHandler;
|
|
104
|
+
|
|
105
|
+
export { type BlockInfo, type MetadataMiddlewareResponse, type NetworkIndexingStatus, type PonderMetadataMiddlewareResponse, ponderMetadata };
|
|
@@ -1,20 +1,23 @@
|
|
|
1
|
+
// src/middleware.ts
|
|
2
|
+
import { HTTPException } from "hono/http-exception";
|
|
3
|
+
|
|
1
4
|
// src/db-helpers.ts
|
|
2
5
|
import { pgSchema, pgTable } from "drizzle-orm/pg-core";
|
|
3
6
|
import { eq } from "ponder";
|
|
4
|
-
var
|
|
5
|
-
if (
|
|
7
|
+
var getPonderMetaTableSchema = (databaseNamespace) => {
|
|
8
|
+
if (databaseNamespace === "public") {
|
|
6
9
|
return pgTable("_ponder_meta", (t) => ({
|
|
7
10
|
key: t.text().primaryKey().$type(),
|
|
8
11
|
value: t.jsonb().$type().notNull()
|
|
9
12
|
}));
|
|
10
13
|
}
|
|
11
|
-
return pgSchema(
|
|
14
|
+
return pgSchema(databaseNamespace).table("_ponder_meta", (t) => ({
|
|
12
15
|
key: t.text().primaryKey().$type(),
|
|
13
16
|
value: t.jsonb().$type().notNull()
|
|
14
17
|
}));
|
|
15
18
|
};
|
|
16
|
-
var
|
|
17
|
-
if (
|
|
19
|
+
var getPonderStatusTableSchema = (databaseNamespace) => {
|
|
20
|
+
if (databaseNamespace === "public") {
|
|
18
21
|
return pgTable("_ponder_status", (t) => ({
|
|
19
22
|
network_name: t.text().primaryKey(),
|
|
20
23
|
block_number: t.bigint({ mode: "number" }),
|
|
@@ -22,7 +25,7 @@ var getPonderStatus = (namespace) => {
|
|
|
22
25
|
ready: t.boolean().notNull()
|
|
23
26
|
}));
|
|
24
27
|
}
|
|
25
|
-
return pgSchema(
|
|
28
|
+
return pgSchema(databaseNamespace).table("_ponder_status", (t) => ({
|
|
26
29
|
network_name: t.text().primaryKey(),
|
|
27
30
|
block_number: t.bigint({ mode: "number" }),
|
|
28
31
|
block_timestamp: t.bigint({ mode: "number" }),
|
|
@@ -30,15 +33,16 @@ var getPonderStatus = (namespace) => {
|
|
|
30
33
|
}));
|
|
31
34
|
};
|
|
32
35
|
async function queryPonderStatus(namespace, db) {
|
|
33
|
-
const PONDER_STATUS =
|
|
36
|
+
const PONDER_STATUS = getPonderStatusTableSchema(namespace);
|
|
34
37
|
return db.select().from(PONDER_STATUS);
|
|
35
38
|
}
|
|
36
39
|
async function queryPonderMeta(namespace, db) {
|
|
37
|
-
const PONDER_META =
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
}
|
|
40
|
+
const PONDER_META = getPonderMetaTableSchema(namespace);
|
|
41
|
+
const [ponderAppMeta] = await db.select({ value: PONDER_META.value }).from(PONDER_META).where(eq(PONDER_META.key, "app")).limit(1);
|
|
42
|
+
if (!ponderAppMeta) {
|
|
43
|
+
throw new Error("Ponder metadata not found");
|
|
44
|
+
}
|
|
45
|
+
return ponderAppMeta.value;
|
|
42
46
|
}
|
|
43
47
|
|
|
44
48
|
// src/prometheus-metrics.ts
|
|
@@ -197,87 +201,131 @@ function ponderMetadata({
|
|
|
197
201
|
app,
|
|
198
202
|
db,
|
|
199
203
|
env,
|
|
200
|
-
|
|
204
|
+
query,
|
|
201
205
|
publicClients
|
|
202
206
|
}) {
|
|
203
207
|
return async function ponderMetadataMiddleware(ctx) {
|
|
204
208
|
const indexedChainIds = Object.keys(publicClients).map(Number);
|
|
205
209
|
const ponderStatus = await queryPonderStatus(env.DATABASE_SCHEMA, db);
|
|
206
|
-
const
|
|
207
|
-
const
|
|
208
|
-
const networkIndexingStatus = {};
|
|
210
|
+
const metrics = PrometheusMetrics.parse(await query.prometheusMetrics());
|
|
211
|
+
const networkIndexingStatusByChainId = {};
|
|
209
212
|
for (const indexedChainId of indexedChainIds) {
|
|
210
213
|
const publicClient = publicClients[indexedChainId];
|
|
211
214
|
if (!publicClient) {
|
|
212
|
-
throw new
|
|
215
|
+
throw new HTTPException(500, {
|
|
216
|
+
message: `No public client found for chainId ${indexedChainId}`
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
const fetchBlockMetadata = async (blockNumber) => {
|
|
220
|
+
const block = await publicClient.getBlock({
|
|
221
|
+
blockNumber: BigInt(blockNumber)
|
|
222
|
+
});
|
|
223
|
+
if (!block) {
|
|
224
|
+
throw new Error(
|
|
225
|
+
`Failed to fetch block metadata for block number ${blockNumber} with chain ID ${indexedChainId}`
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
return {
|
|
229
|
+
number: Number(block.number),
|
|
230
|
+
timestamp: Number(block.timestamp)
|
|
231
|
+
};
|
|
232
|
+
};
|
|
233
|
+
const latestSafeBlockData = await publicClient.getBlock();
|
|
234
|
+
if (!latestSafeBlockData) {
|
|
235
|
+
throw new HTTPException(500, {
|
|
236
|
+
message: `Failed to fetch latest safe block for chainId ${indexedChainId}`
|
|
237
|
+
});
|
|
213
238
|
}
|
|
214
|
-
const latestSafeBlock =
|
|
239
|
+
const latestSafeBlock = {
|
|
240
|
+
number: Number(latestSafeBlockData.number),
|
|
241
|
+
timestamp: Number(latestSafeBlockData.timestamp)
|
|
242
|
+
};
|
|
215
243
|
const network = indexedChainId.toString();
|
|
216
|
-
const
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
244
|
+
const lastSyncedBlockHeight = metrics.getValue("ponder_sync_block", {
|
|
245
|
+
network
|
|
246
|
+
});
|
|
247
|
+
let lastSyncedBlock = null;
|
|
248
|
+
if (lastSyncedBlockHeight) {
|
|
249
|
+
try {
|
|
250
|
+
lastSyncedBlock = await fetchBlockMetadata(lastSyncedBlockHeight);
|
|
251
|
+
} catch (error) {
|
|
252
|
+
console.error("Failed to fetch block metadata for last synced block", error);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
const ponderStatusForNetwork = ponderStatus.find(
|
|
256
|
+
(ponderStatusEntry) => ponderStatusEntry.network_name === network
|
|
257
|
+
);
|
|
258
|
+
let lastIndexedBlock = null;
|
|
259
|
+
if (ponderStatusForNetwork) {
|
|
260
|
+
lastIndexedBlock = ponderBlockInfoToBlockMetadata(ponderStatusForNetwork);
|
|
261
|
+
}
|
|
262
|
+
networkIndexingStatusByChainId[indexedChainId] = {
|
|
263
|
+
lastSyncedBlock,
|
|
235
264
|
lastIndexedBlock,
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
isQueued: lastIndexedBlock === null
|
|
265
|
+
latestSafeBlock,
|
|
266
|
+
firstBlockToIndex: await query.firstBlockToIndexByChainId(indexedChainId, publicClient)
|
|
239
267
|
};
|
|
240
268
|
}
|
|
241
|
-
|
|
269
|
+
let ponderAppBuildId;
|
|
270
|
+
try {
|
|
271
|
+
ponderAppBuildId = (await queryPonderMeta(env.DATABASE_SCHEMA, db)).build_id;
|
|
272
|
+
} catch (error) {
|
|
273
|
+
console.error("Failed to fetch ponder metadata", error);
|
|
274
|
+
}
|
|
275
|
+
let ensRainbowVersionInfo = void 0;
|
|
276
|
+
if (query.ensRainbowVersion) {
|
|
277
|
+
try {
|
|
278
|
+
ensRainbowVersionInfo = await query.ensRainbowVersion();
|
|
279
|
+
} catch (error) {
|
|
280
|
+
console.error("Failed to fetch ENSRainbow version", error);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
const response = {
|
|
242
284
|
app,
|
|
243
|
-
// application dependencies version
|
|
244
285
|
deps: {
|
|
245
|
-
ponder: metrics.getLabel("ponder_version_info", "version"),
|
|
246
|
-
nodejs: metrics.getLabel("nodejs_version_info", "version")
|
|
286
|
+
ponder: formatTextMetricValue(metrics.getLabel("ponder_version_info", "version")),
|
|
287
|
+
nodejs: formatTextMetricValue(metrics.getLabel("nodejs_version_info", "version"))
|
|
247
288
|
},
|
|
248
|
-
// application environment variables
|
|
249
289
|
env,
|
|
250
|
-
// application runtime information
|
|
251
290
|
runtime: {
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
// tableNames: meta?.table_names,
|
|
256
|
-
networkIndexingStatus
|
|
291
|
+
codebaseBuildId: formatTextMetricValue(ponderAppBuildId),
|
|
292
|
+
networkIndexingStatusByChainId,
|
|
293
|
+
ensRainbow: ensRainbowVersionInfo
|
|
257
294
|
}
|
|
258
|
-
}
|
|
295
|
+
};
|
|
296
|
+
validateResponse(response);
|
|
297
|
+
return ctx.json(response);
|
|
259
298
|
};
|
|
260
299
|
}
|
|
261
|
-
function
|
|
262
|
-
|
|
263
|
-
|
|
300
|
+
function validateResponse(response) {
|
|
301
|
+
const { networkIndexingStatusByChainId } = response.runtime;
|
|
302
|
+
if (Object.keys(networkIndexingStatusByChainId).length === 0) {
|
|
303
|
+
throw new HTTPException(500, {
|
|
304
|
+
message: "No network indexing status found"
|
|
305
|
+
});
|
|
264
306
|
}
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
}
|
|
307
|
+
if (Object.values(networkIndexingStatusByChainId).some((n) => n.firstBlockToIndex === null)) {
|
|
308
|
+
throw new HTTPException(500, {
|
|
309
|
+
message: "Failed to fetch first block to index for some networks"
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
function formatTextMetricValue(value) {
|
|
314
|
+
return value ?? "unknown";
|
|
270
315
|
}
|
|
271
|
-
function
|
|
272
|
-
if (!
|
|
316
|
+
function ponderBlockInfoToBlockMetadata(block) {
|
|
317
|
+
if (!block) {
|
|
318
|
+
return null;
|
|
319
|
+
}
|
|
320
|
+
if (!block.block_number || !block.block_timestamp) {
|
|
273
321
|
return null;
|
|
274
322
|
}
|
|
275
|
-
return
|
|
323
|
+
return {
|
|
276
324
|
number: block.block_number,
|
|
277
325
|
timestamp: block.block_timestamp
|
|
278
|
-
}
|
|
326
|
+
};
|
|
279
327
|
}
|
|
280
328
|
export {
|
|
281
329
|
ponderMetadata
|
|
282
330
|
};
|
|
283
|
-
//# sourceMappingURL=
|
|
331
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/middleware.ts","../src/db-helpers.ts","../src/prometheus-metrics.ts"],"sourcesContent":["import type { EnsRainbow } from \"@ensnode/ensrainbow-sdk\";\nimport { MiddlewareHandler } from \"hono\";\nimport { HTTPException } from \"hono/http-exception\";\n\nimport { queryPonderMeta, queryPonderStatus } from \"./db-helpers\";\nimport { PrometheusMetrics } from \"./prometheus-metrics\";\nimport type {\n PonderEnvVarsInfo,\n PonderMetadataMiddlewareOptions,\n PonderMetadataMiddlewareResponse,\n} from \"./types/api\";\nimport type { BlockInfo, NetworkIndexingStatus, PonderBlockStatus } from \"./types/common\";\n\n/**\n * Ponder Metadata types definition.\n */\ninterface PonderMetadataModule {\n /** Application info */\n AppInfo: {\n /** Application name */\n name: string;\n /** Application version */\n version: string;\n };\n\n /** Environment Variables info */\n EnvVars: {\n /** Database schema */\n DATABASE_SCHEMA: string;\n } & PonderEnvVarsInfo;\n\n /** Runtime info */\n RuntimeInfo: {\n /**\n * Application build id\n * https://github.com/ponder-sh/ponder/blob/626e524/packages/core/src/build/index.ts#L425-L431\n **/\n codebaseBuildId: string;\n\n /** Network indexing status by chain ID */\n networkIndexingStatusByChainId: Record<number, NetworkIndexingStatus>;\n\n /** ENSRainbow version info */\n ensRainbow?: EnsRainbow.VersionInfo;\n };\n}\n\nexport type MetadataMiddlewareResponse = PonderMetadataMiddlewareResponse<\n PonderMetadataModule[\"AppInfo\"],\n PonderMetadataModule[\"EnvVars\"],\n PonderMetadataModule[\"RuntimeInfo\"]\n>;\n\nexport function ponderMetadata<\n AppInfo extends PonderMetadataModule[\"AppInfo\"],\n EnvVars extends PonderMetadataModule[\"EnvVars\"],\n>({\n app,\n db,\n env,\n query,\n publicClients,\n}: PonderMetadataMiddlewareOptions<AppInfo, EnvVars>): MiddlewareHandler {\n return async function ponderMetadataMiddleware(ctx) {\n const indexedChainIds = Object.keys(publicClients).map(Number);\n\n const ponderStatus = await queryPonderStatus(env.DATABASE_SCHEMA, db);\n const metrics = PrometheusMetrics.parse(await query.prometheusMetrics());\n\n const networkIndexingStatusByChainId: Record<number, NetworkIndexingStatus> = {};\n\n for (const indexedChainId of indexedChainIds) {\n const publicClient = publicClients[indexedChainId];\n\n if (!publicClient) {\n throw new HTTPException(500, {\n message: `No public client found for chainId ${indexedChainId}`,\n });\n }\n\n /**\n * Fetches block metadata from blockchain network for a given block number.\n * @param blockNumber\n * @returns block metadata\n * @throws {Error} if failed to fetch block metadata from blockchain network\n */\n const fetchBlockMetadata = async (blockNumber: number): Promise<BlockInfo> => {\n const block = await publicClient.getBlock({\n blockNumber: BigInt(blockNumber),\n });\n\n if (!block) {\n throw new Error(\n `Failed to fetch block metadata for block number ${blockNumber} with chain ID ${indexedChainId}`,\n );\n }\n\n return {\n number: Number(block.number),\n timestamp: Number(block.timestamp),\n } satisfies BlockInfo;\n };\n\n const latestSafeBlockData = await publicClient.getBlock();\n\n if (!latestSafeBlockData) {\n throw new HTTPException(500, {\n message: `Failed to fetch latest safe block for chainId ${indexedChainId}`,\n });\n }\n\n // mapping latest safe block\n const latestSafeBlock = {\n number: Number(latestSafeBlockData.number),\n timestamp: Number(latestSafeBlockData.timestamp),\n } satisfies BlockInfo;\n\n // mapping chain id to its string representation for metric queries\n const network = indexedChainId.toString();\n\n // mapping last synced block if available\n const lastSyncedBlockHeight = metrics.getValue(\"ponder_sync_block\", {\n network,\n });\n let lastSyncedBlock: BlockInfo | null = null;\n if (lastSyncedBlockHeight) {\n try {\n lastSyncedBlock = await fetchBlockMetadata(lastSyncedBlockHeight);\n } catch (error) {\n console.error(\"Failed to fetch block metadata for last synced block\", error);\n }\n }\n\n // mapping ponder status for current network\n const ponderStatusForNetwork = ponderStatus.find(\n (ponderStatusEntry) => ponderStatusEntry.network_name === network,\n );\n\n // mapping last indexed block if available\n let lastIndexedBlock: BlockInfo | null = null;\n if (ponderStatusForNetwork) {\n lastIndexedBlock = ponderBlockInfoToBlockMetadata(ponderStatusForNetwork);\n }\n\n networkIndexingStatusByChainId[indexedChainId] = {\n lastSyncedBlock,\n lastIndexedBlock,\n latestSafeBlock,\n firstBlockToIndex: await query.firstBlockToIndexByChainId(indexedChainId, publicClient),\n } satisfies NetworkIndexingStatus;\n }\n\n // mapping ponder app build id if available\n let ponderAppBuildId: string | undefined;\n try {\n ponderAppBuildId = (await queryPonderMeta(env.DATABASE_SCHEMA, db)).build_id;\n } catch (error) {\n console.error(\"Failed to fetch ponder metadata\", error);\n }\n\n // fetch ENSRainbow version if available\n let ensRainbowVersionInfo = undefined;\n if (query.ensRainbowVersion) {\n try {\n ensRainbowVersionInfo = await query.ensRainbowVersion();\n } catch (error) {\n console.error(\"Failed to fetch ENSRainbow version\", error);\n }\n }\n\n const response = {\n app,\n deps: {\n ponder: formatTextMetricValue(metrics.getLabel(\"ponder_version_info\", \"version\")),\n nodejs: formatTextMetricValue(metrics.getLabel(\"nodejs_version_info\", \"version\")),\n },\n env,\n runtime: {\n codebaseBuildId: formatTextMetricValue(ponderAppBuildId),\n networkIndexingStatusByChainId,\n ensRainbow: ensRainbowVersionInfo,\n },\n } satisfies MetadataMiddlewareResponse;\n\n // validate if response is in correct state\n validateResponse(response);\n\n return ctx.json(response);\n };\n}\n\n/**\n * Validates the metadata middleware response to ensure correct state.\n *\n * @param response The response to validate\n * @throws {HTTPException} if the response is in an invalid state\n */\nfunction validateResponse(response: MetadataMiddlewareResponse): void {\n const { networkIndexingStatusByChainId } = response.runtime;\n\n if (Object.keys(networkIndexingStatusByChainId).length === 0) {\n throw new HTTPException(500, {\n message: \"No network indexing status found\",\n });\n }\n\n if (Object.values(networkIndexingStatusByChainId).some((n) => n.firstBlockToIndex === null)) {\n throw new HTTPException(500, {\n message: \"Failed to fetch first block to index for some networks\",\n });\n }\n}\n\n/**\n * Formats a text metric value.\n * @param value\n * @returns\n */\nfunction formatTextMetricValue(value?: string): string {\n return value ?? \"unknown\";\n}\n\n/**\n * Converts a Ponder block status to a block info object.\n **/\nfunction ponderBlockInfoToBlockMetadata(block: PonderBlockStatus | undefined): BlockInfo | null {\n if (!block) {\n return null;\n }\n\n if (!block.block_number || !block.block_timestamp) {\n return null;\n }\n\n return {\n number: block.block_number,\n timestamp: block.block_timestamp,\n };\n}\n","import { pgSchema, pgTable } from \"drizzle-orm/pg-core\";\nimport { type ReadonlyDrizzle, eq } from \"ponder\";\n\n/**\n * Internal ponder metadata type.\n * Copied from https://github.com/ponder-sh/ponder/blob/32634897bf65e92a85dc4cccdaba70c9425d90f3/packages/core/src/database/index.ts#L94-L102\n */\ntype PonderAppMeta = {\n is_locked: 0 | 1;\n is_dev: 0 | 1;\n heartbeat_at: number;\n build_id: string;\n checkpoint: string;\n table_names: Array<string>;\n version: string;\n};\n\n/**\n * Get DB schema for _ponder_meta table.\n * Akin to https://github.com/ponder-sh/ponder/blob/32634897bf65e92a85dc4cccdaba70c9425d90f3/packages/core/src/database/index.ts#L129-L141\n *\n * @param databaseNamespace A namespace for the database.\n * @returns A table schema for _ponder_meta table.\n * */\nconst getPonderMetaTableSchema = (databaseNamespace: string) => {\n if (databaseNamespace === \"public\") {\n return pgTable(\"_ponder_meta\", (t) => ({\n key: t.text().primaryKey().$type<\"app\">(),\n value: t.jsonb().$type<PonderAppMeta>().notNull(),\n }));\n }\n\n return pgSchema(databaseNamespace).table(\"_ponder_meta\", (t) => ({\n key: t.text().primaryKey().$type<\"app\">(),\n value: t.jsonb().$type<PonderAppMeta>().notNull(),\n }));\n};\n\n/**\n * Get DB schema for _ponder_status table.\n * Akin to https://github.com/ponder-sh/ponder/blob/32634897bf65e92a85dc4cccdaba70c9425d90f3/packages/core/src/database/index.ts#L143-L159\n *\n * @param databaseNamespace A namespace for the database.\n * @returns A table schema for _ponder_status table.\n */\nconst getPonderStatusTableSchema = (databaseNamespace: string) => {\n if (databaseNamespace === \"public\") {\n return pgTable(\"_ponder_status\", (t) => ({\n network_name: t.text().primaryKey(),\n block_number: t.bigint({ mode: \"number\" }),\n block_timestamp: t.bigint({ mode: \"number\" }),\n ready: t.boolean().notNull(),\n }));\n }\n\n return pgSchema(databaseNamespace).table(\"_ponder_status\", (t) => ({\n network_name: t.text().primaryKey(),\n block_number: t.bigint({ mode: \"number\" }),\n block_timestamp: t.bigint({ mode: \"number\" }),\n ready: t.boolean().notNull(),\n }));\n};\n\ntype PonderStatusTableSchema = ReturnType<typeof getPonderStatusTableSchema>;\n\n/**\n * Get a list of ponder status entries for each network.\n *\n * @param namespace A namespace for the database (e.g. \"public\").\n * @param db Drizzle DB Client instance.\n * @returns a list of ponder status entries for each network.\n */\nexport async function queryPonderStatus(\n namespace: string,\n db: ReadonlyDrizzle<Record<string, unknown>>,\n): Promise<Array<PonderStatusTableSchema[\"$inferSelect\"]>> {\n const PONDER_STATUS = getPonderStatusTableSchema(namespace);\n\n return db.select().from(PONDER_STATUS);\n}\n\ntype PonderMetaTableSchema = ReturnType<typeof getPonderMetaTableSchema>;\n\n/**\n * Get ponder metadata for the app.\n *\n * @param namespace A namespace for the database (e.g. \"public\").\n * @param db Drizzle DB Client instance.\n * @returns ponder metadata for the app.\n * @throws Error if ponder metadata not found.\n */\nexport async function queryPonderMeta(\n namespace: string,\n db: ReadonlyDrizzle<Record<string, unknown>>,\n): Promise<PonderMetaTableSchema[\"$inferSelect\"][\"value\"]> {\n const PONDER_META = getPonderMetaTableSchema(namespace);\n\n const [ponderAppMeta] = await db\n .select({ value: PONDER_META.value })\n .from(PONDER_META)\n .where(eq(PONDER_META.key, \"app\"))\n .limit(1);\n\n if (!ponderAppMeta) {\n throw new Error(\"Ponder metadata not found\");\n }\n\n return ponderAppMeta.value;\n}\n","import parsePrometheusTextFormat, { type PrometheusMetric } from \"parse-prometheus-text-format\";\n// Ensures local declaration file is available to downstream consumers\nimport \"./types/parse-prometheus-text-format\";\n\ninterface ParsedPrometheusMetric extends Omit<PrometheusMetric, \"metrics\"> {\n metrics: Array<{\n value: number;\n labels?: Record<string, string>;\n }>;\n}\n\n/**\n * Converts Prometheus text format to JSON format compatible with prom2json\n * @param text Raw Prometheus metric text\n * @returns Array of metrics in prom2json compatible format\n * @example\n * ```ts\n * const metrics = parsePrometheusText(`\n * # HELP ponder_version_info Ponder version information\n * # TYPE ponder_version_info gauge\n * ponder_version_info{version=\"0.9.18\",major=\"0\",minor=\"9\",patch=\"18\"} 1\n * `);\n * // Returns:\n * // [{\n * // name: \"ponder_version_info\",\n * // help: \"Ponder version information\",\n * // type: \"gauge\",\n * // metrics: [{\n * // value: 1,\n * // labels: { version: \"0.9.18\", major: \"0\", minor: \"9\", patch: \"18\" }\n * // }]\n * // }]\n * ```\n */\nexport function parsePrometheusText(text: string): Array<ParsedPrometheusMetric> {\n return parsePrometheusTextFormat(text).map((metric) => ({\n name: metric.name,\n help: metric.help || \"\",\n type: metric.type.toLowerCase(),\n metrics: metric.metrics.map((m) => ({\n value: Number(m.value),\n ...(m.labels && Object.keys(m.labels).length > 0 ? { labels: m.labels } : {}),\n })),\n }));\n}\n\nexport class PrometheusMetrics {\n private constructor(private readonly metrics: Array<ParsedPrometheusMetric>) {}\n\n static parse(maybePrometheusMetricsText: string): PrometheusMetrics {\n return new PrometheusMetrics(parsePrometheusText(maybePrometheusMetricsText));\n }\n\n /**\n * Gets all metrics of a specific name\n * @param name Metric name\n * @returns Array of metrics or undefined if not found\n * @example\n * ```ts\n * const metrics = parser.get('ponder_historical_total_indexing_seconds');\n * // Returns: [\n * // { value: 251224935, labels: { network: \"1\" } },\n * // { value: 251224935, labels: { network: \"8453\" } }\n * // ]\n * ```\n */\n get(name: string): Array<{ value: number; labels?: Record<string, string> }> | undefined {\n const metric = this.metrics.find((m) => m.name === name);\n return metric?.metrics;\n }\n\n /**\n * Gets a single metric value, optionally filtered by labels\n * @param name Metric name\n * @param labelFilter Optional label key-value pairs to match\n * @returns Metric value or undefined if not found\n * @example\n * ```ts\n * // Get simple value\n * parser.getValue('ponder_historical_start_timestamp_seconds') // Returns: 1740391265\n *\n * // Get value with label filter\n * parser.getValue('ponder_historical_total_indexing_seconds', { network: '1' }) // Returns: 251224935\n * ```\n */\n getValue(name: string, labelFilter?: Record<string, string>): number | undefined {\n const metrics = this.get(name);\n\n if (!metrics || metrics.length === 0) {\n return undefined;\n }\n\n if (!labelFilter) {\n return metrics[0]?.value;\n }\n\n const metric = metrics.find(\n (m) => m.labels && Object.entries(labelFilter).every(([k, v]) => m.labels?.[k] === v),\n );\n\n return metric?.value;\n }\n\n /**\n * Gets a label value from a metric\n * @param name Metric name\n * @param label Label name to retrieve\n * @returns Label value or undefined if not found\n * @example\n * ```ts\n * parser.getLabel('ponder_version_info', 'version') // Returns: \"0.9.18\"\n * parser.getLabel('ponder_settings_info', 'ordering') // Returns: \"omnichain\"\n * ```\n */\n getLabel(name: string, label: string): string | undefined {\n return this.getLabels(name, label)[0];\n }\n\n /**\n * Gets all unique label values for a metric\n * @param name Metric name\n * @param label Label name to retrieve\n * @returns Array of unique label values\n * @example\n * ```ts\n * // Get all network IDs\n * parser.getLabels('ponder_historical_total_indexing_seconds', 'network')\n * // Returns: ['1', '8453']\n * ```\n */\n getLabels(name: string, label: string): string[] {\n const metrics = this.get(name);\n\n if (!metrics) return [];\n\n return [\n ...new Set(metrics.map((m) => m.labels?.[label]).filter((v): v is string => v !== undefined)),\n ];\n }\n\n /**\n * Gets help text for a metric\n * @param name Metric name\n * @returns Help text or undefined if not found\n * @example\n * ```ts\n * parser.getHelp('ponder_historical_start_timestamp_seconds')\n * // Returns: \"Timestamp at which historical indexing started\"\n * ```\n */\n getHelp(name: string): string | undefined {\n return this.metrics.find((m) => m.name === name)?.help;\n }\n\n /**\n * Gets metric type\n * @param name Metric name\n * @returns Metric type or undefined if not found\n * @example\n * ```ts\n * parser.getType('ponder_version_info') // Returns: \"gauge\"\n * parser.getType('ponder_postgres_query_total') // Returns: \"counter\"\n * ```\n */\n getType(name: string): string | undefined {\n return this.metrics.find((m) => m.name === name)?.type;\n }\n\n /**\n * Gets all metric names\n * @returns Array of metric names\n * @example\n * ```ts\n * parser.getMetricNames()\n * // Returns: [\n * // 'ponder_version_info',\n * // 'ponder_settings_info',\n * // 'ponder_historical_start_timestamp_seconds',\n * // 'ponder_historical_total_indexing_seconds'\n * // ]\n * ```\n */\n getMetricNames(): string[] {\n return this.metrics.map((m) => m.name);\n }\n}\n"],"mappings":"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","names":["_a"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ensnode/ponder-metadata",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "A Hono middleware for making Ponder app metadata available to clients.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -13,36 +13,37 @@
|
|
|
13
13
|
"keywords": [
|
|
14
14
|
"Ponder"
|
|
15
15
|
],
|
|
16
|
-
"exports": {
|
|
17
|
-
"./middleware": {
|
|
18
|
-
"types": "./dist/middleware.d.ts",
|
|
19
|
-
"default": "./dist/middleware.js"
|
|
20
|
-
}
|
|
21
|
-
},
|
|
22
16
|
"files": [
|
|
23
17
|
"dist"
|
|
24
18
|
],
|
|
19
|
+
"exports": {
|
|
20
|
+
".": {
|
|
21
|
+
"types": "./dist/index.d.ts",
|
|
22
|
+
"default": "./dist/index.js"
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
25
|
"publishConfig": {
|
|
26
26
|
"access": "public"
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
29
|
"drizzle-orm": "^0.39.3",
|
|
30
30
|
"parse-prometheus-text-format": "^1.1.1",
|
|
31
|
-
"viem": "^2.22.13"
|
|
31
|
+
"viem": "^2.22.13",
|
|
32
|
+
"@ensnode/ensrainbow-sdk": "0.7.0"
|
|
32
33
|
},
|
|
33
34
|
"devDependencies": {
|
|
34
35
|
"@biomejs/biome": "^1.9.4",
|
|
35
|
-
"@ensnode/shared-configs": "",
|
|
36
36
|
"@types/node": "^20.10.0",
|
|
37
37
|
"hono": "^4.6.14",
|
|
38
|
-
"ponder": "^0.9.
|
|
38
|
+
"ponder": "^0.9.27",
|
|
39
39
|
"tsup": "^8.3.6",
|
|
40
40
|
"typescript": "^5.7.3",
|
|
41
|
-
"vitest": "^3.
|
|
41
|
+
"vitest": "^3.1.1",
|
|
42
|
+
"@ensnode/shared-configs": "0.7.0"
|
|
42
43
|
},
|
|
43
44
|
"peerDependencies": {
|
|
44
45
|
"hono": "^4.6.14",
|
|
45
|
-
"ponder": "^0.9.
|
|
46
|
+
"ponder": "^0.9.27"
|
|
46
47
|
},
|
|
47
48
|
"scripts": {
|
|
48
49
|
"prepublish": "tsup",
|
|
@@ -51,7 +52,7 @@
|
|
|
51
52
|
"lint": "biome check --write",
|
|
52
53
|
"lint:ci": "biome ci"
|
|
53
54
|
},
|
|
54
|
-
"main": "./dist/
|
|
55
|
-
"module": "./dist/
|
|
56
|
-
"types": "./dist/
|
|
55
|
+
"main": "./dist/index.js",
|
|
56
|
+
"module": "./dist/index.mjs",
|
|
57
|
+
"types": "./dist/index.d.ts"
|
|
57
58
|
}
|
package/dist/middleware.d.ts
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { MiddlewareHandler } from 'hono';
|
|
2
|
-
import { ReadonlyDrizzle } from 'ponder';
|
|
3
|
-
import { PublicClient } from 'viem';
|
|
4
|
-
|
|
5
|
-
declare function ponderMetadata({ app, db, env, fetchPrometheusMetrics, publicClients, }: {
|
|
6
|
-
db: ReadonlyDrizzle<Record<string, unknown>>;
|
|
7
|
-
app: {
|
|
8
|
-
name: string;
|
|
9
|
-
version: string;
|
|
10
|
-
};
|
|
11
|
-
env: {
|
|
12
|
-
DATABASE_SCHEMA: string;
|
|
13
|
-
} & Record<string, unknown>;
|
|
14
|
-
fetchPrometheusMetrics: () => Promise<string>;
|
|
15
|
-
publicClients: Record<number, PublicClient>;
|
|
16
|
-
}): MiddlewareHandler;
|
|
17
|
-
|
|
18
|
-
export { ponderMetadata };
|
package/dist/middleware.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/db-helpers.ts","../src/prometheus-metrics.ts","../src/middleware.ts"],"sourcesContent":["import { pgSchema, pgTable } from \"drizzle-orm/pg-core\";\nimport { type ReadonlyDrizzle, eq } from \"ponder\";\n\ntype NamespaceBuild = string;\n\ntype PonderApp = {\n is_locked: 0 | 1;\n is_dev: 0 | 1;\n heartbeat_at: number;\n build_id: string;\n checkpoint: string;\n table_names: string[];\n version: string;\n};\n\nconst getPonderMeta = (namespace: NamespaceBuild) => {\n if (namespace === \"public\") {\n return pgTable(\"_ponder_meta\", (t) => ({\n key: t.text().primaryKey().$type<\"app\">(),\n value: t.jsonb().$type<PonderApp>().notNull(),\n }));\n }\n\n return pgSchema(namespace).table(\"_ponder_meta\", (t) => ({\n key: t.text().primaryKey().$type<\"app\">(),\n value: t.jsonb().$type<PonderApp>().notNull(),\n }));\n};\n\nconst getPonderStatus = (namespace: NamespaceBuild) => {\n if (namespace === \"public\") {\n return pgTable(\"_ponder_status\", (t) => ({\n network_name: t.text().primaryKey(),\n block_number: t.bigint({ mode: \"number\" }),\n block_timestamp: t.bigint({ mode: \"number\" }),\n ready: t.boolean().notNull(),\n }));\n }\n\n return pgSchema(namespace).table(\"_ponder_status\", (t) => ({\n network_name: t.text().primaryKey(),\n block_number: t.bigint({ mode: \"number\" }),\n block_timestamp: t.bigint({ mode: \"number\" }),\n ready: t.boolean().notNull(),\n }));\n};\n\nexport async function queryPonderStatus(\n namespace: string,\n db: ReadonlyDrizzle<Record<string, unknown>>,\n) {\n const PONDER_STATUS = getPonderStatus(namespace);\n\n return db.select().from(PONDER_STATUS);\n}\n\nexport async function queryPonderMeta(\n namespace: string,\n db: ReadonlyDrizzle<Record<string, unknown>>,\n) {\n const PONDER_META = getPonderMeta(namespace);\n\n return db\n .select({ value: PONDER_META.value })\n .from(PONDER_META)\n .where(eq(PONDER_META.key, \"app\"))\n .limit(1)\n .then((result: any) => result[0]?.value);\n}\n","import parsePrometheusTextFormat from \"parse-prometheus-text-format\";\nimport type { PrometheusMetric } from \"parse-prometheus-text-format\";\n// Ensures local declaration file is available to downstream consumers\nimport \"./types/parse-prometheus-text-format\";\n\ninterface ParsedPrometheusMetric extends Omit<PrometheusMetric, \"metrics\"> {\n metrics: Array<{\n value: number;\n labels?: Record<string, string>;\n }>;\n}\n\n/**\n * Converts Prometheus text format to JSON format compatible with prom2json\n * @param text Raw Prometheus metric text\n * @returns Array of metrics in prom2json compatible format\n * @example\n * ```ts\n * const metrics = parsePrometheusText(`\n * # HELP ponder_version_info Ponder version information\n * # TYPE ponder_version_info gauge\n * ponder_version_info{version=\"0.9.18\",major=\"0\",minor=\"9\",patch=\"18\"} 1\n * `);\n * // Returns:\n * // [{\n * // name: \"ponder_version_info\",\n * // help: \"Ponder version information\",\n * // type: \"gauge\",\n * // metrics: [{\n * // value: 1,\n * // labels: { version: \"0.9.18\", major: \"0\", minor: \"9\", patch: \"18\" }\n * // }]\n * // }]\n * ```\n */\nexport function parsePrometheusText(text: string): Array<ParsedPrometheusMetric> {\n return parsePrometheusTextFormat(text).map((metric) => ({\n name: metric.name,\n help: metric.help || \"\",\n type: metric.type.toLowerCase(),\n metrics: metric.metrics.map((m) => ({\n value: Number(m.value),\n ...(m.labels && Object.keys(m.labels).length > 0 ? { labels: m.labels } : {}),\n })),\n }));\n}\n\nexport class PrometheusMetrics {\n private constructor(private readonly metrics: Array<ParsedPrometheusMetric>) {}\n\n static parse(maybePrometheusMetricsText: string): PrometheusMetrics {\n return new PrometheusMetrics(parsePrometheusText(maybePrometheusMetricsText));\n }\n\n /**\n * Gets all metrics of a specific name\n * @param name Metric name\n * @returns Array of metrics or undefined if not found\n * @example\n * ```ts\n * const metrics = parser.get('ponder_historical_total_indexing_seconds');\n * // Returns: [\n * // { value: 251224935, labels: { network: \"1\" } },\n * // { value: 251224935, labels: { network: \"8453\" } }\n * // ]\n * ```\n */\n get(name: string): Array<{ value: number; labels?: Record<string, string> }> | undefined {\n const metric = this.metrics.find((m) => m.name === name);\n return metric?.metrics;\n }\n\n /**\n * Gets a single metric value, optionally filtered by labels\n * @param name Metric name\n * @param labelFilter Optional label key-value pairs to match\n * @returns Metric value or undefined if not found\n * @example\n * ```ts\n * // Get simple value\n * parser.getValue('ponder_historical_start_timestamp_seconds') // Returns: 1740391265\n *\n * // Get value with label filter\n * parser.getValue('ponder_historical_total_indexing_seconds', { network: '1' }) // Returns: 251224935\n * ```\n */\n getValue(name: string, labelFilter?: Record<string, string>): number | undefined {\n const metrics = this.get(name);\n\n if (!metrics || metrics.length === 0) {\n return undefined;\n }\n\n if (!labelFilter) {\n return metrics[0]?.value;\n }\n\n const metric = metrics.find(\n (m) => m.labels && Object.entries(labelFilter).every(([k, v]) => m.labels?.[k] === v),\n );\n\n return metric?.value;\n }\n\n /**\n * Gets a label value from a metric\n * @param name Metric name\n * @param label Label name to retrieve\n * @returns Label value or undefined if not found\n * @example\n * ```ts\n * parser.getLabel('ponder_version_info', 'version') // Returns: \"0.9.18\"\n * parser.getLabel('ponder_settings_info', 'ordering') // Returns: \"omnichain\"\n * ```\n */\n getLabel(name: string, label: string): string | undefined {\n return this.getLabels(name, label)[0];\n }\n\n /**\n * Gets all unique label values for a metric\n * @param name Metric name\n * @param label Label name to retrieve\n * @returns Array of unique label values\n * @example\n * ```ts\n * // Get all network IDs\n * parser.getLabels('ponder_historical_total_indexing_seconds', 'network')\n * // Returns: ['1', '8453']\n * ```\n */\n getLabels(name: string, label: string): string[] {\n const metrics = this.get(name);\n\n if (!metrics) return [];\n\n return [\n ...new Set(metrics.map((m) => m.labels?.[label]).filter((v): v is string => v !== undefined)),\n ];\n }\n\n /**\n * Gets help text for a metric\n * @param name Metric name\n * @returns Help text or undefined if not found\n * @example\n * ```ts\n * parser.getHelp('ponder_historical_start_timestamp_seconds')\n * // Returns: \"Timestamp at which historical indexing started\"\n * ```\n */\n getHelp(name: string): string | undefined {\n return this.metrics.find((m) => m.name === name)?.help;\n }\n\n /**\n * Gets metric type\n * @param name Metric name\n * @returns Metric type or undefined if not found\n * @example\n * ```ts\n * parser.getType('ponder_version_info') // Returns: \"gauge\"\n * parser.getType('ponder_postgres_query_total') // Returns: \"counter\"\n * ```\n */\n getType(name: string): string | undefined {\n return this.metrics.find((m) => m.name === name)?.type;\n }\n\n /**\n * Gets all metric names\n * @returns Array of metric names\n * @example\n * ```ts\n * parser.getMetricNames()\n * // Returns: [\n * // 'ponder_version_info',\n * // 'ponder_settings_info',\n * // 'ponder_historical_start_timestamp_seconds',\n * // 'ponder_historical_total_indexing_seconds'\n * // ]\n * ```\n */\n getMetricNames(): string[] {\n return this.metrics.map((m) => m.name);\n }\n}\n","import { MiddlewareHandler } from \"hono\";\nimport { ReadonlyDrizzle } from \"ponder\";\nimport { PublicClient } from \"viem\";\nimport { queryPonderMeta, queryPonderStatus } from \"./db-helpers\";\nimport { PrometheusMetrics } from \"./prometheus-metrics\";\nimport type { BlockMetadata, NetworkIndexingStatus } from \"./types/common\";\n\nexport function ponderMetadata({\n app,\n db,\n env,\n fetchPrometheusMetrics,\n publicClients,\n}: {\n db: ReadonlyDrizzle<Record<string, unknown>>;\n app: {\n name: string;\n version: string;\n };\n env: {\n DATABASE_SCHEMA: string;\n } & Record<string, unknown>;\n fetchPrometheusMetrics: () => Promise<string>;\n publicClients: Record<number, PublicClient>;\n}): MiddlewareHandler {\n return async function ponderMetadataMiddleware(ctx) {\n const indexedChainIds = Object.keys(publicClients).map(Number);\n\n const ponderStatus = await queryPonderStatus(env.DATABASE_SCHEMA, db);\n const ponderMeta = await queryPonderMeta(env.DATABASE_SCHEMA, db);\n const metrics = PrometheusMetrics.parse(await fetchPrometheusMetrics());\n\n const networkIndexingStatus: Record<string, NetworkIndexingStatus> = {};\n\n for (const indexedChainId of indexedChainIds) {\n const publicClient = publicClients[indexedChainId];\n\n if (!publicClient) {\n throw new Error(`No public client found for chainId ${indexedChainId}`);\n }\n\n const latestSafeBlock = await publicClient.getBlock();\n\n const network = indexedChainId.toString();\n const ponderStatusForNetwork = ponderStatus.find((s) => s.network_name === network);\n const lastIndexedBlock = mapPonderStatusBlockToBlockMetadata(ponderStatusForNetwork);\n\n networkIndexingStatus[network] = {\n totalBlocksCount:\n metrics.getValue(\"ponder_historical_total_blocks\", {\n network,\n }) ?? null,\n cachedBlocksCount:\n metrics.getValue(\"ponder_historical_cached_blocks\", {\n network,\n }) ?? null,\n lastSyncedBlock: blockInfo({\n number:\n metrics.getValue(\"ponder_sync_block\", {\n network,\n }) ?? 0,\n timestamp: 0,\n }),\n latestSafeBlock: blockInfo({\n number: Number(latestSafeBlock.number),\n timestamp: Number(latestSafeBlock.timestamp),\n }),\n lastIndexedBlock,\n isRealtime: Boolean(metrics.getValue(\"ponder_sync_is_realtime\", { network })),\n isComplete: Boolean(metrics.getValue(\"ponder_sync_is_complete\", { network })),\n isQueued: lastIndexedBlock === null,\n } satisfies NetworkIndexingStatus;\n }\n\n return ctx.json({\n app,\n // application dependencies version\n deps: {\n ponder: metrics.getLabel(\"ponder_version_info\", \"version\"),\n nodejs: metrics.getLabel(\"nodejs_version_info\", \"version\"),\n },\n // application environment variables\n env,\n // application runtime information\n runtime: {\n // application build id\n // https://github.com/ponder-sh/ponder/blob/626e524/packages/core/src/build/index.ts#L425-L431\n codebaseBuildId: ponderMeta?.build_id,\n // tableNames: meta?.table_names,\n networkIndexingStatus,\n },\n });\n };\n}\n\nfunction blockInfo(block: {\n number: number | null;\n timestamp: number | null;\n}): BlockMetadata | null {\n if (!block.number) {\n return null;\n }\n\n return {\n height: block.number,\n timestamp: block.timestamp ?? 0,\n utc: block.timestamp ? new Date(block.timestamp * 1000).toISOString() : \"\",\n };\n}\n\nfunction mapPonderStatusBlockToBlockMetadata(\n block:\n | {\n block_number: number | null;\n block_timestamp: number | null;\n }\n | undefined,\n): BlockMetadata | null {\n if (!block?.block_number) {\n return null;\n }\n\n return blockInfo({\n number: block.block_number,\n timestamp: block.block_timestamp,\n });\n}\n"],"mappings":"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","names":["_a"]}
|