@secondlayer/subgraphs 7.4.0 → 7.5.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.js +3 -3
- package/dist/index.js.map +1 -1
- package/dist/runtime/replay.js +3 -3
- package/dist/runtime/replay.js.map +1 -1
- package/dist/schema/index.js +3 -3
- package/dist/schema/index.js.map +1 -1
- package/dist/testing/index.js +174 -14
- package/dist/testing/index.js.map +5 -4
- package/dist/verification.js +3 -3
- package/dist/verification.js.map +1 -1
- package/dist/verify/index.js +198 -38
- package/dist/verify/index.js.map +5 -4
- package/package.json +2 -2
package/dist/testing/index.js
CHANGED
|
@@ -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.
|
|
206
|
+
version: "7.5.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.
|
|
266
|
+
"@secondlayer/shared": "^13.4.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
|
|
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
|
|
2350
|
-
import { sql as
|
|
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 =
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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=
|
|
3369
|
+
//# debugId=1C6775556709A2BD64756E2164756E21
|
|
3210
3370
|
//# sourceMappingURL=index.js.map
|