@secondlayer/subgraphs 7.4.0 → 7.6.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.
@@ -203,7 +203,7 @@ import { createHash as createHash2 } from "node:crypto";
203
203
  // package.json
204
204
  var package_default = {
205
205
  name: "@secondlayer/subgraphs",
206
- version: "7.4.0",
206
+ version: "7.6.0",
207
207
  description: "Describe the Stacks data you want as one TypeScript file; it fills itself and survives reorgs.",
208
208
  license: "MIT",
209
209
  repository: {
@@ -263,7 +263,7 @@ var package_default = {
263
263
  prepublishOnly: "bun run build"
264
264
  },
265
265
  dependencies: {
266
- "@secondlayer/shared": "^13.3.0",
266
+ "@secondlayer/shared": "^13.5.0",
267
267
  "@secondlayer/stacks": "^6.4.0",
268
268
  "@secondlayer/verify": "^0.6.0",
269
269
  esbuild: "^0.28.2",
@@ -1535,7 +1535,7 @@ function sameValue(a, b) {
1535
1535
 
1536
1536
  // src/runtime/runner.ts
1537
1537
  import { getErrorMessage as getErrorMessage2 } from "@secondlayer/shared";
1538
- import { logger as logger4 } from "@secondlayer/shared/logger";
1538
+ import { logger as logger5 } from "@secondlayer/shared/logger";
1539
1539
  import {
1540
1540
  clarityValueToJS,
1541
1541
  deserializeCV as deserializeCV4,
@@ -1914,6 +1914,158 @@ function decodeEventData(data) {
1914
1914
  return data;
1915
1915
  }
1916
1916
 
1917
+ // src/runtime/error-groups.ts
1918
+ import { createHash as createHash3 } from "node:crypto";
1919
+ import { getTargetDb } from "@secondlayer/shared/db";
1920
+ import { logger as logger3 } from "@secondlayer/shared/logger";
1921
+ import { sql as sql2 } from "kysely";
1922
+ var ERROR_GROUPS_CAP_PER_SUBGRAPH = 50;
1923
+ var MESSAGE_MAX_CHARS = 500;
1924
+ function normalizeMessage(message) {
1925
+ return message.replace(/0x[0-9a-f]+/gi, "<hex>").replace(/(["'`])(?:(?!\1).)*\1/g, "<str>").replace(/\d+/g, "<n>");
1926
+ }
1927
+ function errorFingerprint(input) {
1928
+ return createHash3("sha1").update([
1929
+ input.sourceName,
1930
+ input.eventType ?? "",
1931
+ normalizeMessage(input.message),
1932
+ input.frame ?? ""
1933
+ ].join("|")).digest("hex");
1934
+ }
1935
+ function topFrame(err) {
1936
+ if (!(err instanceof Error) || !err.stack)
1937
+ return null;
1938
+ for (const line of err.stack.split(`
1939
+ `).slice(1)) {
1940
+ const match = /([^/\\\s()]+):(\d+):\d+\)?\s*$/.exec(line);
1941
+ if (match)
1942
+ return `${match[1]}:${match[2]}`;
1943
+ }
1944
+ return null;
1945
+ }
1946
+ var FLUSH_INTERVAL_MS = 5000;
1947
+ var MAX_PENDING_KEYS = 1000;
1948
+ var pending = new Map;
1949
+ var droppedKeys = 0;
1950
+ var flushTimer = null;
1951
+ function startFlusher() {
1952
+ if (flushTimer)
1953
+ return;
1954
+ flushTimer = setInterval(() => {
1955
+ flushHandlerErrors();
1956
+ }, FLUSH_INTERVAL_MS);
1957
+ flushTimer.unref();
1958
+ }
1959
+ async function flushHandlerErrors() {
1960
+ if (pending.size === 0 && droppedKeys === 0)
1961
+ return;
1962
+ const batch = pending;
1963
+ const dropped = droppedKeys;
1964
+ pending = new Map;
1965
+ droppedKeys = 0;
1966
+ if (dropped > 0) {
1967
+ logger3.warn("Handler error groups dropped: too many distinct errors", {
1968
+ dropped,
1969
+ cap: MAX_PENDING_KEYS
1970
+ });
1971
+ }
1972
+ if (batch.size === 0)
1973
+ return;
1974
+ try {
1975
+ const db = getTargetDb();
1976
+ const subgraphs = new Set;
1977
+ for (const group of batch.values()) {
1978
+ subgraphs.add(group.subgraphName);
1979
+ await db.insertInto("subgraph_error_groups").values({
1980
+ subgraph_name: group.subgraphName,
1981
+ fingerprint: group.fingerprint,
1982
+ source_name: group.sourceName,
1983
+ event_type: group.eventType,
1984
+ message: group.message,
1985
+ frame: group.frame,
1986
+ count: group.count,
1987
+ sample_tx_id: group.sampleTxId,
1988
+ sample_block_height: group.sampleBlockHeight,
1989
+ first_seen: group.firstSeenAt,
1990
+ last_seen: group.lastSeenAt
1991
+ }).onConflict((oc) => oc.columns(["subgraph_name", "fingerprint"]).doUpdateSet({
1992
+ count: sql2`subgraph_error_groups.count + ${group.count}`,
1993
+ last_seen: group.lastSeenAt,
1994
+ sample_tx_id: group.sampleTxId,
1995
+ sample_block_height: group.sampleBlockHeight
1996
+ })).execute();
1997
+ }
1998
+ for (const subgraphName of subgraphs) {
1999
+ await sql2`
2000
+ DELETE FROM subgraph_error_groups
2001
+ WHERE id IN (
2002
+ SELECT id FROM subgraph_error_groups
2003
+ WHERE subgraph_name = ${subgraphName}
2004
+ ORDER BY last_seen DESC, id
2005
+ OFFSET ${ERROR_GROUPS_CAP_PER_SUBGRAPH}
2006
+ )
2007
+ `.execute(db);
2008
+ }
2009
+ } catch (err) {
2010
+ logger3.warn("Failed to flush handler error groups", {
2011
+ error: err instanceof Error ? err.message : String(err)
2012
+ });
2013
+ }
2014
+ }
2015
+ var recorderOverride = null;
2016
+ function recordHandlerError(input) {
2017
+ try {
2018
+ if (recorderOverride) {
2019
+ recorderOverride(input).catch((err) => {
2020
+ logger3.warn("Failed to record handler error group", {
2021
+ subgraph: input.subgraphName,
2022
+ error: err instanceof Error ? err.message : String(err)
2023
+ });
2024
+ });
2025
+ return;
2026
+ }
2027
+ const message = (input.err instanceof Error ? input.err.message : String(input.err)).slice(0, MESSAGE_MAX_CHARS);
2028
+ const frame = topFrame(input.err);
2029
+ const fingerprint = errorFingerprint({
2030
+ sourceName: input.sourceName,
2031
+ eventType: input.eventType,
2032
+ message,
2033
+ frame
2034
+ });
2035
+ const key = `${input.subgraphName}|${fingerprint}`;
2036
+ const now = new Date;
2037
+ const existing = pending.get(key);
2038
+ if (existing) {
2039
+ existing.count++;
2040
+ existing.sampleTxId = input.txId;
2041
+ existing.sampleBlockHeight = input.blockHeight;
2042
+ existing.lastSeenAt = now;
2043
+ } else if (pending.size >= MAX_PENDING_KEYS) {
2044
+ droppedKeys++;
2045
+ } else {
2046
+ pending.set(key, {
2047
+ subgraphName: input.subgraphName,
2048
+ fingerprint,
2049
+ sourceName: input.sourceName,
2050
+ eventType: input.eventType,
2051
+ message,
2052
+ frame,
2053
+ count: 1,
2054
+ sampleTxId: input.txId,
2055
+ sampleBlockHeight: input.blockHeight,
2056
+ firstSeenAt: now,
2057
+ lastSeenAt: now
2058
+ });
2059
+ }
2060
+ startFlusher();
2061
+ } catch (err) {
2062
+ logger3.warn("Failed to record handler error group", {
2063
+ subgraph: input.subgraphName,
2064
+ error: err instanceof Error ? err.message : String(err)
2065
+ });
2066
+ }
2067
+ }
2068
+
1917
2069
  // src/runtime/print-validate.ts
1918
2070
  var COLUMN_TYPES = new Set([
1919
2071
  "text",
@@ -2345,9 +2497,9 @@ function matchSources(sources, transactions, events, traitContracts = new Map, f
2345
2497
  }
2346
2498
 
2347
2499
  // src/runtime/violations.ts
2348
- import { getTargetDb } from "@secondlayer/shared/db";
2349
- import { logger as logger3 } from "@secondlayer/shared/logger";
2350
- import { sql as sql2 } from "kysely";
2500
+ import { getTargetDb as getTargetDb2 } from "@secondlayer/shared/db";
2501
+ import { logger as logger4 } from "@secondlayer/shared/logger";
2502
+ import { sql as sql3 } from "kysely";
2351
2503
  var VIOLATIONS_CAP_PER_SUBGRAPH = 100;
2352
2504
  function jsonSafe2(value) {
2353
2505
  if (typeof value === "bigint")
@@ -2367,7 +2519,7 @@ function jsonSafe2(value) {
2367
2519
  }
2368
2520
  async function defaultRecordPrintViolation(input) {
2369
2521
  try {
2370
- const db = getTargetDb();
2522
+ const db = getTargetDb2();
2371
2523
  const sample = jsonSafe2(input.samplePayload);
2372
2524
  await db.insertInto("subgraph_violations").values({
2373
2525
  subgraph_name: input.subgraphName,
@@ -2377,7 +2529,7 @@ async function defaultRecordPrintViolation(input) {
2377
2529
  reason: input.reason,
2378
2530
  sample_payload: sample
2379
2531
  }).execute();
2380
- await sql2`
2532
+ await sql3`
2381
2533
  DELETE FROM subgraph_violations
2382
2534
  WHERE id IN (
2383
2535
  SELECT id FROM subgraph_violations
@@ -2387,7 +2539,7 @@ async function defaultRecordPrintViolation(input) {
2387
2539
  )
2388
2540
  `.execute(db);
2389
2541
  } catch (err) {
2390
- logger3.warn("Failed to record print violation", {
2542
+ logger4.warn("Failed to record print violation", {
2391
2543
  subgraph: input.subgraphName,
2392
2544
  error: err instanceof Error ? err.message : String(err)
2393
2545
  });
@@ -2729,7 +2881,7 @@ async function runHandlers(subgraph, matched, ctx, opts) {
2729
2881
  units.sort((a, b) => (a.tx.tx_index ?? 0) - (b.tx.tx_index ?? 0) || clockRank(a.event) - clockRank(b.event) || (a.event?.event_index ?? -1) - (b.event?.event_index ?? -1));
2730
2882
  for (const { tx, event, sourceName } of units) {
2731
2883
  if (errors >= threshold) {
2732
- logger4.error("Subgraph error threshold reached, skipping remaining events", {
2884
+ logger5.error("Subgraph error threshold reached, skipping remaining events", {
2733
2885
  subgraph: subgraph.name,
2734
2886
  errors,
2735
2887
  threshold
@@ -2744,7 +2896,7 @@ async function runHandlers(subgraph, matched, ctx, opts) {
2744
2896
  const filter = filterLookup.get(sourceName);
2745
2897
  const handler = subgraph.handlers[sourceName] ?? null;
2746
2898
  if (!handler) {
2747
- logger4.warn("No handler found for source", {
2899
+ logger5.warn("No handler found for source", {
2748
2900
  subgraph: subgraph.name,
2749
2901
  sourceName,
2750
2902
  txId: tx.tx_id
@@ -2801,7 +2953,7 @@ async function runHandlers(subgraph, matched, ctx, opts) {
2801
2953
  const verdict = validatePrintPayload(filter.prints, printPayload.topic ?? "", printPayload.data ?? {});
2802
2954
  if (!verdict.ok) {
2803
2955
  skipped++;
2804
- logger4.warn("Print payload does not match declared prints schema", {
2956
+ logger5.warn("Print payload does not match declared prints schema", {
2805
2957
  subgraph: subgraph.name,
2806
2958
  sourceName,
2807
2959
  topic: printPayload.topic,
@@ -2829,7 +2981,15 @@ async function runHandlers(subgraph, matched, ctx, opts) {
2829
2981
  throw err;
2830
2982
  ctx.rollbackTo(checkpoint);
2831
2983
  errors++;
2832
- logger4.error("Subgraph handler error", {
2984
+ recordHandlerError({
2985
+ subgraphName: subgraph.name,
2986
+ sourceName,
2987
+ eventType: event?.type ?? null,
2988
+ txId: tx.tx_id,
2989
+ blockHeight: ctx.block.height,
2990
+ err
2991
+ });
2992
+ logger5.error("Subgraph handler error", {
2833
2993
  subgraph: subgraph.name,
2834
2994
  sourceName,
2835
2995
  txId: tx.tx_id,
@@ -3206,5 +3366,5 @@ export {
3206
3366
  toHandlerPayload
3207
3367
  };
3208
3368
 
3209
- //# debugId=9370182C1798E38564756E2164756E21
3369
+ //# debugId=7B58E8FEA54194CE64756E2164756E21
3210
3370
  //# sourceMappingURL=index.js.map