@ensnode/ponder-metadata 0.0.0-next-20260102143513

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 NameHash
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,3 @@
1
+ # Ponder Metadata API
2
+
3
+ Ponder Metadata API is a Hono middleware for making Ponder app metadata available to clients.
@@ -0,0 +1,365 @@
1
+ import * as ponder from 'ponder';
2
+ import { ReadonlyDrizzle } from 'ponder';
3
+ import { MiddlewareHandler } from 'hono';
4
+ import { EnsRainbow } from '@ensnode/ensrainbow-sdk';
5
+ import { PublicClient } from 'viem';
6
+
7
+ /**
8
+ * Internal ponder metadata type.
9
+ * Copied from https://github.com/ponder-sh/ponder/blob/32634897bf65e92a85dc4cccdaba70c9425d90f3/packages/core/src/database/index.ts#L94-L102
10
+ */
11
+ type PonderAppMeta = {
12
+ is_locked: 0 | 1;
13
+ is_dev: 0 | 1;
14
+ heartbeat_at: number;
15
+ build_id: string;
16
+ table_names: Array<string>;
17
+ version: string;
18
+ is_ready: 0 | 1;
19
+ };
20
+ /**
21
+ * Get DB schema for _ponder_meta table.
22
+ * Akin to https://github.com/ponder-sh/ponder/blob/32634897bf65e92a85dc4cccdaba70c9425d90f3/packages/core/src/database/index.ts#L129-L141
23
+ *
24
+ * @param databaseNamespace A namespace for the database.
25
+ * @returns A table schema for _ponder_meta table.
26
+ * */
27
+ declare const getPonderMetaTableSchema: (databaseNamespace: string) => ponder.PgTableWithColumns<{
28
+ name: "_ponder_meta";
29
+ schema: undefined;
30
+ columns: {
31
+ key: ponder.PgColumn<{
32
+ name: "key";
33
+ tableName: "_ponder_meta";
34
+ dataType: "string";
35
+ columnType: "PgText";
36
+ data: "app";
37
+ driverParam: string;
38
+ notNull: true;
39
+ hasDefault: false;
40
+ isPrimaryKey: true;
41
+ isAutoincrement: false;
42
+ hasRuntimeDefault: false;
43
+ enumValues: [string, ...string[]];
44
+ baseColumn: never;
45
+ identity: undefined;
46
+ generated: undefined;
47
+ }, {}, {
48
+ $type: "app";
49
+ }>;
50
+ value: ponder.PgColumn<{
51
+ name: "value";
52
+ tableName: "_ponder_meta";
53
+ dataType: "json";
54
+ columnType: "PgJsonb";
55
+ data: PonderAppMeta;
56
+ driverParam: unknown;
57
+ notNull: true;
58
+ hasDefault: false;
59
+ isPrimaryKey: false;
60
+ isAutoincrement: false;
61
+ hasRuntimeDefault: false;
62
+ enumValues: undefined;
63
+ baseColumn: never;
64
+ identity: undefined;
65
+ generated: undefined;
66
+ }, {}, {
67
+ $type: PonderAppMeta;
68
+ }>;
69
+ };
70
+ dialect: "pg";
71
+ }> | ponder.PgTableWithColumns<{
72
+ name: "_ponder_meta";
73
+ schema: string;
74
+ columns: {
75
+ key: ponder.PgColumn<{
76
+ name: "key";
77
+ tableName: "_ponder_meta";
78
+ dataType: "string";
79
+ columnType: "PgText";
80
+ data: "app";
81
+ driverParam: string;
82
+ notNull: true;
83
+ hasDefault: false;
84
+ isPrimaryKey: true;
85
+ isAutoincrement: false;
86
+ hasRuntimeDefault: false;
87
+ enumValues: [string, ...string[]];
88
+ baseColumn: never;
89
+ identity: undefined;
90
+ generated: undefined;
91
+ }, {}, {
92
+ $type: "app";
93
+ }>;
94
+ value: ponder.PgColumn<{
95
+ name: "value";
96
+ tableName: "_ponder_meta";
97
+ dataType: "json";
98
+ columnType: "PgJsonb";
99
+ data: PonderAppMeta;
100
+ driverParam: unknown;
101
+ notNull: true;
102
+ hasDefault: false;
103
+ isPrimaryKey: false;
104
+ isAutoincrement: false;
105
+ hasRuntimeDefault: false;
106
+ enumValues: undefined;
107
+ baseColumn: never;
108
+ identity: undefined;
109
+ generated: undefined;
110
+ }, {}, {
111
+ $type: PonderAppMeta;
112
+ }>;
113
+ };
114
+ dialect: "pg";
115
+ }>;
116
+ type PonderMetaTableSchema = ReturnType<typeof getPonderMetaTableSchema>;
117
+ /**
118
+ * Get ponder metadata for the app.
119
+ *
120
+ * @param namespace A namespace for the database (e.g. "public").
121
+ * @param db Drizzle DB Client instance.
122
+ * @returns ponder metadata for the app.
123
+ * @throws Error if ponder metadata not found.
124
+ */
125
+ declare function queryPonderMeta(namespace: string, db: ReadonlyDrizzle<Record<string, unknown>>): Promise<PonderMetaTableSchema["$inferSelect"]["value"]>;
126
+
127
+ /**
128
+ * Basic information about a block.
129
+ */
130
+ interface BlockInfo {
131
+ /** block number */
132
+ number: number;
133
+ /** block unix timestamp */
134
+ timestamp: number;
135
+ }
136
+ /**
137
+ * Ponder Status type
138
+ *
139
+ * It's a type of value returned by the `GET /status` endpoint on ponder server.
140
+ *
141
+ * Akin to:
142
+ * https://github.com/ponder-sh/ponder/blob/8c012a3/packages/client/src/index.ts#L13-L18
143
+ */
144
+ interface PonderStatus {
145
+ [chainName: string]: {
146
+ /** @var id Chain ID */
147
+ id: number;
148
+ /** @var block Last Indexed Block data */
149
+ block: BlockInfo;
150
+ };
151
+ }
152
+ /**
153
+ * Indexing status for a chain.
154
+ */
155
+ interface ChainIndexingStatus {
156
+ /** Chain ID of the indexed chain */
157
+ chainId: number;
158
+ /**
159
+ * First block required to be indexed during the historical sync.
160
+ */
161
+ firstBlockToIndex: BlockInfo;
162
+ /**
163
+ * Latest block synced into indexer's RPC cache.
164
+ */
165
+ lastSyncedBlock: BlockInfo | null;
166
+ /**
167
+ * Last block processed & indexed by the indexer.
168
+ */
169
+ lastIndexedBlock: BlockInfo | null;
170
+ /**
171
+ * Latest safe block available on the chain.
172
+ */
173
+ latestSafeBlock: BlockInfo;
174
+ }
175
+
176
+ type PonderEnvVarsInfo = Record<string, unknown>;
177
+ /**
178
+ * Helper type which describes public clients grouped by chain name
179
+ */
180
+ type PublicClientsByChainName<ChainName extends string = string> = Record<ChainName, PublicClient>;
181
+ interface PonderMetadataMiddlewareOptions<AppInfo, EnvVars extends PonderEnvVarsInfo> {
182
+ /** Database access object (readonly Drizzle) */
183
+ db: ReadonlyDrizzle<Record<string, unknown>>;
184
+ /** Application info */
185
+ app: AppInfo;
186
+ /** Environment settings info */
187
+ env: EnvVars;
188
+ /** Query methods */
189
+ query: {
190
+ /** Fetches Ponder Status object for Ponder application */
191
+ ponderStatus(): Promise<PonderStatus>;
192
+ /** Fetches prometheus metrics for Ponder application */
193
+ prometheusMetrics(): Promise<string>;
194
+ /** Fetches the first block do be indexed for a requested chain ID */
195
+ firstBlockToIndexByChainId(chainId: number, publicClient: PublicClient): Promise<BlockInfo>;
196
+ /** Fetches ENSRainbow version information */
197
+ ensRainbowVersion?(): Promise<EnsRainbow.VersionInfo>;
198
+ };
199
+ /** Public clients for fetching data from each chain */
200
+ publicClients: PublicClientsByChainName;
201
+ }
202
+ interface PonderMetadataMiddlewareResponse<AppInfo, EnvVarsInfo extends PonderEnvVarsInfo, RuntimeInfo> {
203
+ /** Application info */
204
+ app: AppInfo;
205
+ /** Dependencies info */
206
+ deps: {
207
+ /** Ponder application version */
208
+ ponder: string;
209
+ /** Node.js runtime version */
210
+ nodejs: string;
211
+ };
212
+ /** Environment settings info */
213
+ env: EnvVarsInfo;
214
+ /** Runtime status info */
215
+ runtime: RuntimeInfo;
216
+ }
217
+
218
+ /**
219
+ * Ponder Metadata types definition.
220
+ */
221
+ interface PonderMetadataModule {
222
+ /** Application info */
223
+ AppInfo: {
224
+ /** Application name */
225
+ name: string;
226
+ /** Application version */
227
+ version: string;
228
+ };
229
+ /** Environment Variables info */
230
+ EnvVars: {
231
+ /** Database schema */
232
+ DATABASE_SCHEMA: string;
233
+ } & PonderEnvVarsInfo;
234
+ /** Runtime info */
235
+ RuntimeInfo: {
236
+ /**
237
+ * Application build id
238
+ * https://github.com/ponder-sh/ponder/blob/626e524/packages/core/src/build/index.ts#L425-L431
239
+ **/
240
+ codebaseBuildId: string;
241
+ /** Chain indexing statuses by chain ID */
242
+ chainIndexingStatuses: {
243
+ [chainId: number]: ChainIndexingStatus;
244
+ };
245
+ /** ENSRainbow version info */
246
+ ensRainbow?: EnsRainbow.VersionInfo;
247
+ };
248
+ }
249
+ type MetadataMiddlewareResponse = PonderMetadataMiddlewareResponse<PonderMetadataModule["AppInfo"], PonderMetadataModule["EnvVars"], PonderMetadataModule["RuntimeInfo"]>;
250
+ declare function ponderMetadata<AppInfo extends PonderMetadataModule["AppInfo"], EnvVars extends PonderMetadataModule["EnvVars"]>({ app, db, env, query, publicClients, }: PonderMetadataMiddlewareOptions<AppInfo, EnvVars>): MiddlewareHandler;
251
+
252
+ declare module "parse-prometheus-text-format" {
253
+ interface PrometheusMetric {
254
+ name: string;
255
+ help: string;
256
+ type: string;
257
+ metrics: Array<{
258
+ value: string;
259
+ labels?: Record<string, string>;
260
+ }>;
261
+ }
262
+ export default function parsePrometheusTextFormat(text: string): Array<PrometheusMetric>;
263
+ }
264
+
265
+ declare class PrometheusMetrics {
266
+ private readonly metrics;
267
+ private constructor();
268
+ static parse(maybePrometheusMetricsText: string): PrometheusMetrics;
269
+ /**
270
+ * Gets all metrics of a specific name
271
+ * @param name Metric name
272
+ * @returns Array of metrics or undefined if not found
273
+ * @example
274
+ * ```ts
275
+ * const metrics = parser.get('ponder_historical_total_indexing_seconds');
276
+ * // Returns: [
277
+ * // { value: 251224935, labels: { network: "1" } },
278
+ * // { value: 251224935, labels: { network: "8453" } }
279
+ * // ]
280
+ * ```
281
+ */
282
+ get(name: string): Array<{
283
+ value: number;
284
+ labels?: Record<string, string>;
285
+ }> | undefined;
286
+ /**
287
+ * Gets a single metric value, optionally filtered by labels
288
+ * @param name Metric name
289
+ * @param labelFilter Optional label key-value pairs to match
290
+ * @returns Metric value or undefined if not found
291
+ * @example
292
+ * ```ts
293
+ * // Get simple value
294
+ * parser.getValue('ponder_historical_start_timestamp_seconds') // Returns: 1740391265
295
+ *
296
+ * // Get value with label filter
297
+ * parser.getValue('ponder_historical_total_indexing_seconds', { network: '1' }) // Returns: 251224935
298
+ * ```
299
+ */
300
+ getValue(name: string, labelFilter?: Record<string, string>): number | undefined;
301
+ /**
302
+ * Gets a label value from a metric
303
+ * @param name Metric name
304
+ * @param label Label name to retrieve
305
+ * @returns Label value or undefined if not found
306
+ * @example
307
+ * ```ts
308
+ * parser.getLabel('ponder_version_info', 'version') // Returns: "0.9.18"
309
+ * parser.getLabel('ponder_settings_info', 'ordering') // Returns: "omnichain"
310
+ * ```
311
+ */
312
+ getLabel(name: string, label: string): string | undefined;
313
+ /**
314
+ * Gets all unique label values for a metric
315
+ * @param name Metric name
316
+ * @param label Label name to retrieve
317
+ * @returns Array of unique label values
318
+ * @example
319
+ * ```ts
320
+ * // Get all network IDs
321
+ * parser.getLabels('ponder_historical_total_indexing_seconds', 'network')
322
+ * // Returns: ['1', '8453']
323
+ * ```
324
+ */
325
+ getLabels(name: string, label: string): string[];
326
+ /**
327
+ * Gets help text for a metric
328
+ * @param name Metric name
329
+ * @returns Help text or undefined if not found
330
+ * @example
331
+ * ```ts
332
+ * parser.getHelp('ponder_historical_start_timestamp_seconds')
333
+ * // Returns: "Timestamp at which historical indexing started"
334
+ * ```
335
+ */
336
+ getHelp(name: string): string | undefined;
337
+ /**
338
+ * Gets metric type
339
+ * @param name Metric name
340
+ * @returns Metric type or undefined if not found
341
+ * @example
342
+ * ```ts
343
+ * parser.getType('ponder_version_info') // Returns: "gauge"
344
+ * parser.getType('ponder_postgres_query_total') // Returns: "counter"
345
+ * ```
346
+ */
347
+ getType(name: string): string | undefined;
348
+ /**
349
+ * Gets all metric names
350
+ * @returns Array of metric names
351
+ * @example
352
+ * ```ts
353
+ * parser.getMetricNames()
354
+ * // Returns: [
355
+ * // 'ponder_version_info',
356
+ * // 'ponder_settings_info',
357
+ * // 'ponder_historical_start_timestamp_seconds',
358
+ * // 'ponder_historical_total_indexing_seconds'
359
+ * // ]
360
+ * ```
361
+ */
362
+ getMetricNames(): string[];
363
+ }
364
+
365
+ export { type BlockInfo, type ChainIndexingStatus, type MetadataMiddlewareResponse, type PonderMetadataMiddlewareResponse, type PonderStatus, PrometheusMetrics, ponderMetadata, queryPonderMeta };
package/dist/index.js ADDED
@@ -0,0 +1,309 @@
1
+ // src/db-helpers.ts
2
+ import { pgSchema, pgTable } from "drizzle-orm/pg-core";
3
+ import { eq } from "ponder";
4
+ var getPonderMetaTableSchema = (databaseNamespace) => {
5
+ if (databaseNamespace === "public") {
6
+ return pgTable("_ponder_meta", (t) => ({
7
+ key: t.text().primaryKey().$type(),
8
+ value: t.jsonb().$type().notNull()
9
+ }));
10
+ }
11
+ return pgSchema(databaseNamespace).table("_ponder_meta", (t) => ({
12
+ key: t.text().primaryKey().$type(),
13
+ value: t.jsonb().$type().notNull()
14
+ }));
15
+ };
16
+ async function queryPonderMeta(namespace, db) {
17
+ const PONDER_META = getPonderMetaTableSchema(namespace);
18
+ const [ponderAppMeta] = await db.select({ value: PONDER_META.value }).from(PONDER_META).where(eq(PONDER_META.key, "app")).limit(1);
19
+ if (!ponderAppMeta) {
20
+ throw new Error("Ponder metadata not found");
21
+ }
22
+ return ponderAppMeta.value;
23
+ }
24
+
25
+ // src/middleware.ts
26
+ import { HTTPException } from "hono/http-exception";
27
+
28
+ // src/prometheus-metrics.ts
29
+ import parsePrometheusTextFormat from "parse-prometheus-text-format";
30
+ function parsePrometheusText(text) {
31
+ return parsePrometheusTextFormat(text).map((metric) => ({
32
+ name: metric.name,
33
+ help: metric.help || "",
34
+ type: metric.type.toLowerCase(),
35
+ metrics: metric.metrics.map((m) => ({
36
+ value: Number(m.value),
37
+ ...m.labels && Object.keys(m.labels).length > 0 ? { labels: m.labels } : {}
38
+ }))
39
+ }));
40
+ }
41
+ var PrometheusMetrics = class _PrometheusMetrics {
42
+ constructor(metrics) {
43
+ this.metrics = metrics;
44
+ }
45
+ static parse(maybePrometheusMetricsText) {
46
+ return new _PrometheusMetrics(parsePrometheusText(maybePrometheusMetricsText));
47
+ }
48
+ /**
49
+ * Gets all metrics of a specific name
50
+ * @param name Metric name
51
+ * @returns Array of metrics or undefined if not found
52
+ * @example
53
+ * ```ts
54
+ * const metrics = parser.get('ponder_historical_total_indexing_seconds');
55
+ * // Returns: [
56
+ * // { value: 251224935, labels: { network: "1" } },
57
+ * // { value: 251224935, labels: { network: "8453" } }
58
+ * // ]
59
+ * ```
60
+ */
61
+ get(name) {
62
+ const metric = this.metrics.find((m) => m.name === name);
63
+ return metric == null ? void 0 : metric.metrics;
64
+ }
65
+ /**
66
+ * Gets a single metric value, optionally filtered by labels
67
+ * @param name Metric name
68
+ * @param labelFilter Optional label key-value pairs to match
69
+ * @returns Metric value or undefined if not found
70
+ * @example
71
+ * ```ts
72
+ * // Get simple value
73
+ * parser.getValue('ponder_historical_start_timestamp_seconds') // Returns: 1740391265
74
+ *
75
+ * // Get value with label filter
76
+ * parser.getValue('ponder_historical_total_indexing_seconds', { network: '1' }) // Returns: 251224935
77
+ * ```
78
+ */
79
+ getValue(name, labelFilter) {
80
+ var _a;
81
+ const metrics = this.get(name);
82
+ if (!metrics || metrics.length === 0) {
83
+ return void 0;
84
+ }
85
+ if (!labelFilter) {
86
+ return (_a = metrics[0]) == null ? void 0 : _a.value;
87
+ }
88
+ const metric = metrics.find(
89
+ (m) => m.labels && Object.entries(labelFilter).every(([k, v]) => {
90
+ var _a2;
91
+ return ((_a2 = m.labels) == null ? void 0 : _a2[k]) === v;
92
+ })
93
+ );
94
+ return metric == null ? void 0 : metric.value;
95
+ }
96
+ /**
97
+ * Gets a label value from a metric
98
+ * @param name Metric name
99
+ * @param label Label name to retrieve
100
+ * @returns Label value or undefined if not found
101
+ * @example
102
+ * ```ts
103
+ * parser.getLabel('ponder_version_info', 'version') // Returns: "0.9.18"
104
+ * parser.getLabel('ponder_settings_info', 'ordering') // Returns: "omnichain"
105
+ * ```
106
+ */
107
+ getLabel(name, label) {
108
+ return this.getLabels(name, label)[0];
109
+ }
110
+ /**
111
+ * Gets all unique label values for a metric
112
+ * @param name Metric name
113
+ * @param label Label name to retrieve
114
+ * @returns Array of unique label values
115
+ * @example
116
+ * ```ts
117
+ * // Get all network IDs
118
+ * parser.getLabels('ponder_historical_total_indexing_seconds', 'network')
119
+ * // Returns: ['1', '8453']
120
+ * ```
121
+ */
122
+ getLabels(name, label) {
123
+ const metrics = this.get(name);
124
+ if (!metrics) return [];
125
+ return [
126
+ ...new Set(metrics.map((m) => {
127
+ var _a;
128
+ return (_a = m.labels) == null ? void 0 : _a[label];
129
+ }).filter((v) => v !== void 0))
130
+ ];
131
+ }
132
+ /**
133
+ * Gets help text for a metric
134
+ * @param name Metric name
135
+ * @returns Help text or undefined if not found
136
+ * @example
137
+ * ```ts
138
+ * parser.getHelp('ponder_historical_start_timestamp_seconds')
139
+ * // Returns: "Timestamp at which historical indexing started"
140
+ * ```
141
+ */
142
+ getHelp(name) {
143
+ var _a;
144
+ return (_a = this.metrics.find((m) => m.name === name)) == null ? void 0 : _a.help;
145
+ }
146
+ /**
147
+ * Gets metric type
148
+ * @param name Metric name
149
+ * @returns Metric type or undefined if not found
150
+ * @example
151
+ * ```ts
152
+ * parser.getType('ponder_version_info') // Returns: "gauge"
153
+ * parser.getType('ponder_postgres_query_total') // Returns: "counter"
154
+ * ```
155
+ */
156
+ getType(name) {
157
+ var _a;
158
+ return (_a = this.metrics.find((m) => m.name === name)) == null ? void 0 : _a.type;
159
+ }
160
+ /**
161
+ * Gets all metric names
162
+ * @returns Array of metric names
163
+ * @example
164
+ * ```ts
165
+ * parser.getMetricNames()
166
+ * // Returns: [
167
+ * // 'ponder_version_info',
168
+ * // 'ponder_settings_info',
169
+ * // 'ponder_historical_start_timestamp_seconds',
170
+ * // 'ponder_historical_total_indexing_seconds'
171
+ * // ]
172
+ * ```
173
+ */
174
+ getMetricNames() {
175
+ return this.metrics.map((m) => m.name);
176
+ }
177
+ };
178
+
179
+ // src/middleware.ts
180
+ function ponderMetadata({
181
+ app,
182
+ db,
183
+ env,
184
+ query,
185
+ publicClients
186
+ }) {
187
+ return async function ponderMetadataMiddleware(ctx) {
188
+ const indexedChainNames = Object.keys(publicClients);
189
+ const ponderStatus = await query.ponderStatus();
190
+ const metrics = PrometheusMetrics.parse(await query.prometheusMetrics());
191
+ const chainIndexingStatuses = {};
192
+ for (const indexedChainName of indexedChainNames) {
193
+ const publicClient = publicClients[indexedChainName];
194
+ if (!publicClient || typeof publicClient.chain === "undefined") {
195
+ throw new HTTPException(500, {
196
+ message: `No public client found for "${indexedChainName}" chain name`
197
+ });
198
+ }
199
+ const publicClientChainId = publicClient.chain.id;
200
+ const fetchBlockMetadata = async (blockNumber) => {
201
+ const block = await publicClient.getBlock({
202
+ blockNumber: BigInt(blockNumber)
203
+ });
204
+ if (!block) {
205
+ throw new Error(
206
+ `Failed to fetch block metadata for block number ${blockNumber} on chain ID "${publicClientChainId}"`
207
+ );
208
+ }
209
+ return {
210
+ number: Number(block.number),
211
+ timestamp: Number(block.timestamp)
212
+ };
213
+ };
214
+ const latestSafeBlockData = await publicClient.getBlock();
215
+ if (!latestSafeBlockData) {
216
+ throw new HTTPException(500, {
217
+ message: `Failed to fetch latest safe block for chain ID "${publicClientChainId}"`
218
+ });
219
+ }
220
+ const latestSafeBlock = {
221
+ number: Number(latestSafeBlockData.number),
222
+ timestamp: Number(latestSafeBlockData.timestamp)
223
+ };
224
+ const chain = indexedChainName;
225
+ const lastSyncedBlockHeight = metrics.getValue("ponder_sync_block", {
226
+ chain
227
+ });
228
+ let lastSyncedBlock = null;
229
+ if (lastSyncedBlockHeight) {
230
+ try {
231
+ lastSyncedBlock = await fetchBlockMetadata(lastSyncedBlockHeight);
232
+ } catch (error) {
233
+ console.error("Failed to fetch block metadata for last synced block", error);
234
+ }
235
+ }
236
+ const firstBlockToIndex = await query.firstBlockToIndexByChainId(
237
+ publicClientChainId,
238
+ publicClient
239
+ );
240
+ const ponderStatusForChain = Object.values(ponderStatus).find(
241
+ (ponderStatusEntry) => ponderStatusEntry.id === publicClientChainId
242
+ );
243
+ let lastIndexedBlock = null;
244
+ if (ponderStatusForChain) {
245
+ if (firstBlockToIndex.number < ponderStatusForChain.block.number) {
246
+ lastIndexedBlock = ponderStatusForChain.block;
247
+ }
248
+ }
249
+ chainIndexingStatuses[publicClientChainId] = {
250
+ chainId: publicClientChainId,
251
+ lastSyncedBlock,
252
+ lastIndexedBlock,
253
+ latestSafeBlock,
254
+ firstBlockToIndex
255
+ };
256
+ }
257
+ let ponderAppBuildId;
258
+ try {
259
+ ponderAppBuildId = (await queryPonderMeta(env.DATABASE_SCHEMA, db)).build_id;
260
+ } catch (error) {
261
+ console.error("Failed to fetch ponder metadata", error);
262
+ }
263
+ let ensRainbowVersionInfo;
264
+ if (query.ensRainbowVersion) {
265
+ try {
266
+ ensRainbowVersionInfo = await query.ensRainbowVersion();
267
+ } catch (error) {
268
+ console.error("Failed to fetch ENSRainbow version", error);
269
+ }
270
+ }
271
+ const response = {
272
+ app,
273
+ deps: {
274
+ ponder: formatTextMetricValue(metrics.getLabel("ponder_version_info", "version")),
275
+ nodejs: formatTextMetricValue(metrics.getLabel("nodejs_version_info", "version"))
276
+ },
277
+ env,
278
+ runtime: {
279
+ codebaseBuildId: formatTextMetricValue(ponderAppBuildId),
280
+ chainIndexingStatuses,
281
+ ensRainbow: ensRainbowVersionInfo
282
+ }
283
+ };
284
+ validateResponse(response);
285
+ return ctx.json(response);
286
+ };
287
+ }
288
+ function validateResponse(response) {
289
+ const { chainIndexingStatuses } = response.runtime;
290
+ if (Object.keys(chainIndexingStatuses).length === 0) {
291
+ throw new HTTPException(500, {
292
+ message: "No chain indexing status found"
293
+ });
294
+ }
295
+ if (Object.values(chainIndexingStatuses).some((n) => n.firstBlockToIndex === null)) {
296
+ throw new HTTPException(500, {
297
+ message: "Failed to fetch first block to index for some chains"
298
+ });
299
+ }
300
+ }
301
+ function formatTextMetricValue(value) {
302
+ return value ?? "unknown";
303
+ }
304
+ export {
305
+ PrometheusMetrics,
306
+ ponderMetadata,
307
+ queryPonderMeta
308
+ };
309
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/db-helpers.ts","../src/middleware.ts","../src/prometheus-metrics.ts"],"sourcesContent":["import { pgSchema, pgTable } from \"drizzle-orm/pg-core\";\nimport { eq, type ReadonlyDrizzle } 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 table_names: Array<string>;\n version: string;\n is_ready: 0 | 1;\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\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 type { MiddlewareHandler } from \"hono\";\nimport { HTTPException } from \"hono/http-exception\";\n\nimport type { EnsRainbow } from \"@ensnode/ensrainbow-sdk\";\n\nimport { queryPonderMeta } from \"./db-helpers\";\nimport { PrometheusMetrics } from \"./prometheus-metrics\";\nimport type {\n PonderEnvVarsInfo,\n PonderMetadataMiddlewareOptions,\n PonderMetadataMiddlewareResponse,\n} from \"./types/api\";\nimport type { BlockInfo, ChainIndexingStatus } 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 /** Chain indexing statuses by chain ID */\n chainIndexingStatuses: { [chainId: number]: ChainIndexingStatus };\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 indexedChainNames = Object.keys(publicClients);\n\n const ponderStatus = await query.ponderStatus();\n\n const metrics = PrometheusMetrics.parse(await query.prometheusMetrics());\n\n const chainIndexingStatuses: Record<number, ChainIndexingStatus> = {};\n\n for (const indexedChainName of indexedChainNames) {\n const publicClient = publicClients[indexedChainName];\n\n if (!publicClient || typeof publicClient.chain === \"undefined\") {\n throw new HTTPException(500, {\n message: `No public client found for \"${indexedChainName}\" chain name`,\n });\n }\n\n const publicClientChainId = publicClient.chain.id;\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} on chain ID \"${publicClientChainId}\"`,\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 chain ID \"${publicClientChainId}\"`,\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 indexed chain name to its metric representation for metric queries\n const chain = indexedChainName;\n\n // mapping last synced block if available\n const lastSyncedBlockHeight = metrics.getValue(\"ponder_sync_block\", {\n chain,\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 const firstBlockToIndex = await query.firstBlockToIndexByChainId(\n publicClientChainId,\n publicClient,\n );\n\n // mapping ponder status for current chain\n const ponderStatusForChain = Object.values(ponderStatus).find(\n (ponderStatusEntry) => ponderStatusEntry.id === publicClientChainId,\n );\n\n // mapping last indexed block if available\n let lastIndexedBlock: BlockInfo | null = null;\n if (ponderStatusForChain) {\n // Since Ponder 0.11, the `block` value in the PonderStatus object\n // is always provided. It represents either the very first block to be indexed\n // or the last block that has been indexed.\n //\n // We compare the first block to be indexed (from ponder.config.ts)\n // with the block value from the PonderStatus object (from `GET /status` response).\n // We only set the `lastIndexedBlock` value if the `block` from the PonderStatus object\n // is not the same as the `firstBlockToIndex`.\n if (firstBlockToIndex.number < ponderStatusForChain.block.number) {\n lastIndexedBlock = ponderStatusForChain.block;\n }\n }\n\n chainIndexingStatuses[publicClientChainId] = {\n chainId: publicClientChainId,\n lastSyncedBlock,\n lastIndexedBlock,\n latestSafeBlock,\n firstBlockToIndex,\n } satisfies ChainIndexingStatus;\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: EnsRainbow.VersionInfo | 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 chainIndexingStatuses,\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 { chainIndexingStatuses } = response.runtime;\n\n if (Object.keys(chainIndexingStatuses).length === 0) {\n throw new HTTPException(500, {\n message: \"No chain indexing status found\",\n });\n }\n\n if (Object.values(chainIndexingStatuses).some((n) => n.firstBlockToIndex === null)) {\n throw new HTTPException(500, {\n message: \"Failed to fetch first block to index for some chains\",\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","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":";AAAA,SAAS,UAAU,eAAe;AAClC,SAAS,UAAgC;AAuBzC,IAAM,2BAA2B,CAAC,sBAA8B;AAC9D,MAAI,sBAAsB,UAAU;AAClC,WAAO,QAAQ,gBAAgB,CAAC,OAAO;AAAA,MACrC,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,MAAa;AAAA,MACxC,OAAO,EAAE,MAAM,EAAE,MAAqB,EAAE,QAAQ;AAAA,IAClD,EAAE;AAAA,EACJ;AAEA,SAAO,SAAS,iBAAiB,EAAE,MAAM,gBAAgB,CAAC,OAAO;AAAA,IAC/D,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,MAAa;AAAA,IACxC,OAAO,EAAE,MAAM,EAAE,MAAqB,EAAE,QAAQ;AAAA,EAClD,EAAE;AACJ;AAYA,eAAsB,gBACpB,WACA,IACyD;AACzD,QAAM,cAAc,yBAAyB,SAAS;AAEtD,QAAM,CAAC,aAAa,IAAI,MAAM,GAC3B,OAAO,EAAE,OAAO,YAAY,MAAM,CAAC,EACnC,KAAK,WAAW,EAChB,MAAM,GAAG,YAAY,KAAK,KAAK,CAAC,EAChC,MAAM,CAAC;AAEV,MAAI,CAAC,eAAe;AAClB,UAAM,IAAI,MAAM,2BAA2B;AAAA,EAC7C;AAEA,SAAO,cAAc;AACvB;;;AChEA,SAAS,qBAAqB;;;ACD9B,OAAO,+BAA0D;AAkC1D,SAAS,oBAAoB,MAA6C;AAC/E,SAAO,0BAA0B,IAAI,EAAE,IAAI,CAAC,YAAY;AAAA,IACtD,MAAM,OAAO;AAAA,IACb,MAAM,OAAO,QAAQ;AAAA,IACrB,MAAM,OAAO,KAAK,YAAY;AAAA,IAC9B,SAAS,OAAO,QAAQ,IAAI,CAAC,OAAO;AAAA,MAClC,OAAO,OAAO,EAAE,KAAK;AAAA,MACrB,GAAI,EAAE,UAAU,OAAO,KAAK,EAAE,MAAM,EAAE,SAAS,IAAI,EAAE,QAAQ,EAAE,OAAO,IAAI,CAAC;AAAA,IAC7E,EAAE;AAAA,EACJ,EAAE;AACJ;AAEO,IAAM,oBAAN,MAAM,mBAAkB;AAAA,EACrB,YAA6B,SAAwC;AAAxC;AAAA,EAAyC;AAAA,EAE9E,OAAO,MAAM,4BAAuD;AAClE,WAAO,IAAI,mBAAkB,oBAAoB,0BAA0B,CAAC;AAAA,EAC9E;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAeA,IAAI,MAAqF;AACvF,UAAM,SAAS,KAAK,QAAQ,KAAK,CAAC,MAAM,EAAE,SAAS,IAAI;AACvD,WAAO,iCAAQ;AAAA,EACjB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAgBA,SAAS,MAAc,aAA0D;AArFnF;AAsFI,UAAM,UAAU,KAAK,IAAI,IAAI;AAE7B,QAAI,CAAC,WAAW,QAAQ,WAAW,GAAG;AACpC,aAAO;AAAA,IACT;AAEA,QAAI,CAAC,aAAa;AAChB,cAAO,aAAQ,CAAC,MAAT,mBAAY;AAAA,IACrB;AAEA,UAAM,SAAS,QAAQ;AAAA,MACrB,CAAC,MAAM,EAAE,UAAU,OAAO,QAAQ,WAAW,EAAE,MAAM,CAAC,CAAC,GAAG,CAAC,MAAG;AAjGpE,YAAAA;AAiGuE,iBAAAA,MAAA,EAAE,WAAF,gBAAAA,IAAW,QAAO;AAAA,OAAC;AAAA,IACtF;AAEA,WAAO,iCAAQ;AAAA,EACjB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,SAAS,MAAc,OAAmC;AACxD,WAAO,KAAK,UAAU,MAAM,KAAK,EAAE,CAAC;AAAA,EACtC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcA,UAAU,MAAc,OAAyB;AAC/C,UAAM,UAAU,KAAK,IAAI,IAAI;AAE7B,QAAI,CAAC,QAAS,QAAO,CAAC;AAEtB,WAAO;AAAA,MACL,GAAG,IAAI,IAAI,QAAQ,IAAI,CAAC,MAAG;AAxIjC;AAwIoC,uBAAE,WAAF,mBAAW;AAAA,OAAM,EAAE,OAAO,CAAC,MAAmB,MAAM,MAAS,CAAC;AAAA,IAC9F;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,QAAQ,MAAkC;AAtJ5C;AAuJI,YAAO,UAAK,QAAQ,KAAK,CAAC,MAAM,EAAE,SAAS,IAAI,MAAxC,mBAA2C;AAAA,EACpD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,QAAQ,MAAkC;AApK5C;AAqKI,YAAO,UAAK,QAAQ,KAAK,CAAC,MAAM,EAAE,SAAS,IAAI,MAAxC,mBAA2C;AAAA,EACpD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAgBA,iBAA2B;AACzB,WAAO,KAAK,QAAQ,IAAI,CAAC,MAAM,EAAE,IAAI;AAAA,EACvC;AACF;;;ADnIO,SAAS,eAGd;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAyE;AACvE,SAAO,eAAe,yBAAyB,KAAK;AAClD,UAAM,oBAAoB,OAAO,KAAK,aAAa;AAEnD,UAAM,eAAe,MAAM,MAAM,aAAa;AAE9C,UAAM,UAAU,kBAAkB,MAAM,MAAM,MAAM,kBAAkB,CAAC;AAEvE,UAAM,wBAA6D,CAAC;AAEpE,eAAW,oBAAoB,mBAAmB;AAChD,YAAM,eAAe,cAAc,gBAAgB;AAEnD,UAAI,CAAC,gBAAgB,OAAO,aAAa,UAAU,aAAa;AAC9D,cAAM,IAAI,cAAc,KAAK;AAAA,UAC3B,SAAS,+BAA+B,gBAAgB;AAAA,QAC1D,CAAC;AAAA,MACH;AAEA,YAAM,sBAAsB,aAAa,MAAM;AAQ/C,YAAM,qBAAqB,OAAO,gBAA4C;AAC5E,cAAM,QAAQ,MAAM,aAAa,SAAS;AAAA,UACxC,aAAa,OAAO,WAAW;AAAA,QACjC,CAAC;AAED,YAAI,CAAC,OAAO;AACV,gBAAM,IAAI;AAAA,YACR,mDAAmD,WAAW,iBAAiB,mBAAmB;AAAA,UACpG;AAAA,QACF;AAEA,eAAO;AAAA,UACL,QAAQ,OAAO,MAAM,MAAM;AAAA,UAC3B,WAAW,OAAO,MAAM,SAAS;AAAA,QACnC;AAAA,MACF;AAEA,YAAM,sBAAsB,MAAM,aAAa,SAAS;AAExD,UAAI,CAAC,qBAAqB;AACxB,cAAM,IAAI,cAAc,KAAK;AAAA,UAC3B,SAAS,mDAAmD,mBAAmB;AAAA,QACjF,CAAC;AAAA,MACH;AAGA,YAAM,kBAAkB;AAAA,QACtB,QAAQ,OAAO,oBAAoB,MAAM;AAAA,QACzC,WAAW,OAAO,oBAAoB,SAAS;AAAA,MACjD;AAGA,YAAM,QAAQ;AAGd,YAAM,wBAAwB,QAAQ,SAAS,qBAAqB;AAAA,QAClE;AAAA,MACF,CAAC;AACD,UAAI,kBAAoC;AACxC,UAAI,uBAAuB;AACzB,YAAI;AACF,4BAAkB,MAAM,mBAAmB,qBAAqB;AAAA,QAClE,SAAS,OAAO;AACd,kBAAQ,MAAM,wDAAwD,KAAK;AAAA,QAC7E;AAAA,MACF;AAEA,YAAM,oBAAoB,MAAM,MAAM;AAAA,QACpC;AAAA,QACA;AAAA,MACF;AAGA,YAAM,uBAAuB,OAAO,OAAO,YAAY,EAAE;AAAA,QACvD,CAAC,sBAAsB,kBAAkB,OAAO;AAAA,MAClD;AAGA,UAAI,mBAAqC;AACzC,UAAI,sBAAsB;AASxB,YAAI,kBAAkB,SAAS,qBAAqB,MAAM,QAAQ;AAChE,6BAAmB,qBAAqB;AAAA,QAC1C;AAAA,MACF;AAEA,4BAAsB,mBAAmB,IAAI;AAAA,QAC3C,SAAS;AAAA,QACT;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAGA,QAAI;AACJ,QAAI;AACF,0BAAoB,MAAM,gBAAgB,IAAI,iBAAiB,EAAE,GAAG;AAAA,IACtE,SAAS,OAAO;AACd,cAAQ,MAAM,mCAAmC,KAAK;AAAA,IACxD;AAGA,QAAI;AACJ,QAAI,MAAM,mBAAmB;AAC3B,UAAI;AACF,gCAAwB,MAAM,MAAM,kBAAkB;AAAA,MACxD,SAAS,OAAO;AACd,gBAAQ,MAAM,sCAAsC,KAAK;AAAA,MAC3D;AAAA,IACF;AAEA,UAAM,WAAW;AAAA,MACf;AAAA,MACA,MAAM;AAAA,QACJ,QAAQ,sBAAsB,QAAQ,SAAS,uBAAuB,SAAS,CAAC;AAAA,QAChF,QAAQ,sBAAsB,QAAQ,SAAS,uBAAuB,SAAS,CAAC;AAAA,MAClF;AAAA,MACA;AAAA,MACA,SAAS;AAAA,QACP,iBAAiB,sBAAsB,gBAAgB;AAAA,QACvD;AAAA,QACA,YAAY;AAAA,MACd;AAAA,IACF;AAGA,qBAAiB,QAAQ;AAEzB,WAAO,IAAI,KAAK,QAAQ;AAAA,EAC1B;AACF;AAQA,SAAS,iBAAiB,UAA4C;AACpE,QAAM,EAAE,sBAAsB,IAAI,SAAS;AAE3C,MAAI,OAAO,KAAK,qBAAqB,EAAE,WAAW,GAAG;AACnD,UAAM,IAAI,cAAc,KAAK;AAAA,MAC3B,SAAS;AAAA,IACX,CAAC;AAAA,EACH;AAEA,MAAI,OAAO,OAAO,qBAAqB,EAAE,KAAK,CAAC,MAAM,EAAE,sBAAsB,IAAI,GAAG;AAClF,UAAM,IAAI,cAAc,KAAK;AAAA,MAC3B,SAAS;AAAA,IACX,CAAC;AAAA,EACH;AACF;AAOA,SAAS,sBAAsB,OAAwB;AACrD,SAAO,SAAS;AAClB;","names":["_a"]}
package/package.json ADDED
@@ -0,0 +1,57 @@
1
+ {
2
+ "name": "@ensnode/ponder-metadata",
3
+ "version": "0.0.0-next-20260102143513",
4
+ "type": "module",
5
+ "description": "A Hono middleware for making Ponder app metadata available to clients.",
6
+ "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/namehash/ensnode.git",
10
+ "directory": "packages/ponder-metadata-api"
11
+ },
12
+ "homepage": "https://github.com/namehash/ensnode/tree/main/packages/ponder-metadata-api",
13
+ "keywords": [
14
+ "Ponder"
15
+ ],
16
+ "files": [
17
+ "dist"
18
+ ],
19
+ "exports": {
20
+ ".": {
21
+ "types": "./dist/index.d.ts",
22
+ "default": "./dist/index.js"
23
+ }
24
+ },
25
+ "publishConfig": {
26
+ "access": "public"
27
+ },
28
+ "dependencies": {
29
+ "drizzle-orm": "=0.41.0",
30
+ "parse-prometheus-text-format": "^1.1.1",
31
+ "viem": "^2.22.13",
32
+ "@ensnode/ensrainbow-sdk": "0.0.0-next-20260102143513"
33
+ },
34
+ "devDependencies": {
35
+ "@types/node": "22.18.13",
36
+ "hono": "^4.10.2",
37
+ "ponder": "0.13.16",
38
+ "tsup": "^8.3.6",
39
+ "typescript": "^5.7.3",
40
+ "vitest": "^4.0.2",
41
+ "@ensnode/shared-configs": "0.0.0-next-20260102143513"
42
+ },
43
+ "peerDependencies": {
44
+ "hono": "^4.10.2",
45
+ "ponder": "0.13.16"
46
+ },
47
+ "scripts": {
48
+ "prepublish": "tsup",
49
+ "typecheck": "tsc --noEmit",
50
+ "test": "vitest",
51
+ "lint": "biome check --write .",
52
+ "lint:ci": "biome ci"
53
+ },
54
+ "main": "./dist/index.js",
55
+ "module": "./dist/index.mjs",
56
+ "types": "./dist/index.d.ts"
57
+ }