@secondlayer/subgraphs 4.1.4 → 4.2.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.
Files changed (41) hide show
  1. package/dist/src/index.d.ts +91 -31
  2. package/dist/src/index.js +3319 -3067
  3. package/dist/src/index.js.map +11 -9
  4. package/dist/src/runtime/block-processor.d.ts +72 -23
  5. package/dist/src/runtime/block-processor.js +867 -400
  6. package/dist/src/runtime/block-processor.js.map +9 -6
  7. package/dist/src/runtime/catchup.d.ts +72 -23
  8. package/dist/src/runtime/catchup.js +878 -411
  9. package/dist/src/runtime/catchup.js.map +9 -6
  10. package/dist/src/runtime/processor.js +943 -475
  11. package/dist/src/runtime/processor.js.map +10 -7
  12. package/dist/src/runtime/reindex.d.ts +72 -23
  13. package/dist/src/runtime/reindex.js +911 -444
  14. package/dist/src/runtime/reindex.js.map +9 -6
  15. package/dist/src/runtime/reorg.d.ts +72 -23
  16. package/dist/src/runtime/reorg.js +890 -422
  17. package/dist/src/runtime/reorg.js.map +10 -7
  18. package/dist/src/runtime/replay.js +12 -6
  19. package/dist/src/runtime/replay.js.map +4 -4
  20. package/dist/src/runtime/runner.d.ts +72 -23
  21. package/dist/src/runtime/runner.js +757 -28
  22. package/dist/src/runtime/runner.js.map +9 -5
  23. package/dist/src/runtime/source-matcher.d.ts +80 -24
  24. package/dist/src/runtime/source-matcher.js +12 -5
  25. package/dist/src/runtime/source-matcher.js.map +3 -3
  26. package/dist/src/schema/index.d.ts +72 -23
  27. package/dist/src/schema/index.js +115 -1
  28. package/dist/src/schema/index.js.map +3 -3
  29. package/dist/src/service.js +983 -515
  30. package/dist/src/service.js.map +10 -7
  31. package/dist/src/testing/index.d.ts +195 -24
  32. package/dist/src/testing/index.js +1049 -30
  33. package/dist/src/testing/index.js.map +10 -5
  34. package/dist/src/triggers/index.d.ts +83 -24
  35. package/dist/src/triggers/index.js +5 -2
  36. package/dist/src/triggers/index.js.map +3 -3
  37. package/dist/src/types.d.ts +73 -24
  38. package/dist/src/validate.d.ts +72 -23
  39. package/dist/src/validate.js +115 -1
  40. package/dist/src/validate.js.map +3 -3
  41. package/package.json +2 -2
@@ -942,15 +942,679 @@ function decodeFunctionArgs(args) {
942
942
  return args.map(decodeClarityValue);
943
943
  }
944
944
 
945
+ // src/runtime/source-matcher.ts
946
+ var patternCache = new Map;
947
+ function matchAny(value, pattern) {
948
+ return Array.isArray(pattern) ? pattern.some((p) => matchPattern(value, p)) : matchPattern(value, pattern);
949
+ }
950
+ function matchPattern(value, pattern) {
951
+ if (!pattern.includes("*"))
952
+ return value === pattern;
953
+ let re = patternCache.get(pattern);
954
+ if (!re) {
955
+ const regex = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
956
+ re = new RegExp(`^${regex}$`);
957
+ patternCache.set(pattern, re);
958
+ }
959
+ return re.test(value);
960
+ }
961
+ var EMPTY_SET = new Set;
962
+ function factoryAllows(filter, contractId, factoryContracts) {
963
+ if (!filter.factory)
964
+ return true;
965
+ if (!contractId)
966
+ return false;
967
+ return (factoryContracts.get(filter.factory.from) ?? EMPTY_SET).has(contractId);
968
+ }
969
+ function readPath(payload, path) {
970
+ let cur = payload;
971
+ for (const key of path.split(".")) {
972
+ if (cur === null || typeof cur !== "object")
973
+ return;
974
+ cur = cur[key];
975
+ }
976
+ return cur;
977
+ }
978
+ function traitAllows(filter, contractId, traitContracts) {
979
+ const trait = filter.trait;
980
+ if (!trait)
981
+ return true;
982
+ if (!contractId)
983
+ return false;
984
+ return (traitContracts.get(trait) ?? EMPTY_SET).has(contractId);
985
+ }
986
+ function assetContract(assetId) {
987
+ return assetId?.split("::")[0];
988
+ }
989
+ function printContractId(data) {
990
+ if (!data)
991
+ return;
992
+ const id = (typeof data.contract_identifier === "string" ? data.contract_identifier : undefined) ?? (typeof data.contract_id === "string" ? data.contract_id : undefined);
993
+ return id || undefined;
994
+ }
995
+ function matchFilter(filter, transactions, eventsByTx, traitContracts, factoryContracts = new Map) {
996
+ const results = [];
997
+ switch (filter.type) {
998
+ case "stx_transfer":
999
+ case "stx_mint":
1000
+ case "stx_burn":
1001
+ case "stx_lock": {
1002
+ const eventType = `${filter.type}_event`;
1003
+ for (const tx of transactions) {
1004
+ const txEvents = eventsByTx.get(tx.tx_id) ?? [];
1005
+ const matched = txEvents.filter((e) => e.type === eventType);
1006
+ if (matched.length === 0)
1007
+ continue;
1008
+ const filtered = matched.filter((e) => {
1009
+ const data = e.data;
1010
+ if (!data)
1011
+ return false;
1012
+ if ("sender" in filter && filter.sender) {
1013
+ if (!matchPattern(data.sender, filter.sender))
1014
+ return false;
1015
+ }
1016
+ if ("recipient" in filter && filter.recipient) {
1017
+ if (!matchPattern(data.recipient, filter.recipient))
1018
+ return false;
1019
+ }
1020
+ if ("lockedAddress" in filter && filter.lockedAddress) {
1021
+ if (!matchPattern(data.locked_address, filter.lockedAddress))
1022
+ return false;
1023
+ }
1024
+ if ("minAmount" in filter && filter.minAmount !== undefined) {
1025
+ const amount = BigInt(data.amount ?? data.locked_amount ?? "0");
1026
+ if (amount < filter.minAmount)
1027
+ return false;
1028
+ }
1029
+ if ("maxAmount" in filter && filter.maxAmount !== undefined) {
1030
+ const amount = BigInt(data.amount ?? "0");
1031
+ if (amount > filter.maxAmount)
1032
+ return false;
1033
+ }
1034
+ return true;
1035
+ });
1036
+ if (filtered.length > 0) {
1037
+ results.push({ tx, events: filtered });
1038
+ }
1039
+ }
1040
+ break;
1041
+ }
1042
+ case "ft_transfer":
1043
+ case "ft_mint":
1044
+ case "ft_burn": {
1045
+ const eventType = `${filter.type}_event`;
1046
+ for (const tx of transactions) {
1047
+ const txEvents = eventsByTx.get(tx.tx_id) ?? [];
1048
+ const matched = txEvents.filter((e) => {
1049
+ if (e.type !== eventType)
1050
+ return false;
1051
+ const data = e.data;
1052
+ if (!data)
1053
+ return false;
1054
+ if (filter.assetIdentifier) {
1055
+ const assetId = data.asset_identifier;
1056
+ if (!assetId || !matchPattern(assetId, filter.assetIdentifier))
1057
+ return false;
1058
+ }
1059
+ if (!traitAllows(filter, assetContract(data.asset_identifier), traitContracts))
1060
+ return false;
1061
+ if ("sender" in filter && filter.sender) {
1062
+ if (!matchPattern(data.sender, filter.sender))
1063
+ return false;
1064
+ }
1065
+ if ("recipient" in filter && filter.recipient) {
1066
+ if (!matchPattern(data.recipient, filter.recipient))
1067
+ return false;
1068
+ }
1069
+ if (filter.minAmount !== undefined) {
1070
+ const amount = BigInt(data.amount ?? "0");
1071
+ if (amount < filter.minAmount)
1072
+ return false;
1073
+ }
1074
+ return true;
1075
+ });
1076
+ if (matched.length > 0) {
1077
+ results.push({ tx, events: matched });
1078
+ }
1079
+ }
1080
+ break;
1081
+ }
1082
+ case "nft_transfer":
1083
+ case "nft_mint":
1084
+ case "nft_burn": {
1085
+ const eventType = `${filter.type}_event`;
1086
+ for (const tx of transactions) {
1087
+ const txEvents = eventsByTx.get(tx.tx_id) ?? [];
1088
+ const matched = txEvents.filter((e) => {
1089
+ if (e.type !== eventType)
1090
+ return false;
1091
+ const data = e.data;
1092
+ if (!data)
1093
+ return false;
1094
+ if (filter.assetIdentifier) {
1095
+ const assetId = data.asset_identifier;
1096
+ if (!assetId || !matchPattern(assetId, filter.assetIdentifier))
1097
+ return false;
1098
+ }
1099
+ if (!traitAllows(filter, assetContract(data.asset_identifier), traitContracts))
1100
+ return false;
1101
+ if ("sender" in filter && filter.sender) {
1102
+ if (!matchPattern(data.sender, filter.sender))
1103
+ return false;
1104
+ }
1105
+ if ("recipient" in filter && filter.recipient) {
1106
+ if (!matchPattern(data.recipient, filter.recipient))
1107
+ return false;
1108
+ }
1109
+ return true;
1110
+ });
1111
+ if (matched.length > 0) {
1112
+ results.push({ tx, events: matched });
1113
+ }
1114
+ }
1115
+ break;
1116
+ }
1117
+ case "contract_call": {
1118
+ for (const tx of transactions) {
1119
+ if (tx.type !== "contract_call")
1120
+ continue;
1121
+ if (filter.contractId) {
1122
+ if (!tx.contract_id || !matchAny(tx.contract_id, filter.contractId))
1123
+ continue;
1124
+ }
1125
+ if (!factoryAllows(filter, tx.contract_id, factoryContracts))
1126
+ continue;
1127
+ if (filter.functionName) {
1128
+ if (!tx.function_name || !matchPattern(tx.function_name, filter.functionName))
1129
+ continue;
1130
+ }
1131
+ if (filter.caller) {
1132
+ if (!matchPattern(tx.sender, filter.caller))
1133
+ continue;
1134
+ }
1135
+ if (!traitAllows(filter, tx.contract_id, traitContracts))
1136
+ continue;
1137
+ const txEvents = eventsByTx.get(tx.tx_id) ?? [];
1138
+ results.push({ tx, events: txEvents });
1139
+ }
1140
+ break;
1141
+ }
1142
+ case "contract_deploy": {
1143
+ for (const tx of transactions) {
1144
+ if (tx.type !== "smart_contract")
1145
+ continue;
1146
+ if (filter.deployer) {
1147
+ if (!matchPattern(tx.sender, filter.deployer))
1148
+ continue;
1149
+ }
1150
+ if (filter.contractName) {
1151
+ const name = tx.contract_id?.split(".")[1] ?? "";
1152
+ if (!matchPattern(name, filter.contractName))
1153
+ continue;
1154
+ }
1155
+ const txEvents = eventsByTx.get(tx.tx_id) ?? [];
1156
+ results.push({ tx, events: txEvents });
1157
+ }
1158
+ break;
1159
+ }
1160
+ case "print_event": {
1161
+ for (const tx of transactions) {
1162
+ const txEvents = eventsByTx.get(tx.tx_id) ?? [];
1163
+ const matched = txEvents.filter((e) => {
1164
+ if (e.type !== "smart_contract_event" && e.type !== "contract_event")
1165
+ return false;
1166
+ const data = e.data;
1167
+ if (!data)
1168
+ return false;
1169
+ if (data.topic !== "print")
1170
+ return false;
1171
+ const contractId = printContractId(data);
1172
+ if (filter.contractId) {
1173
+ if (!contractId || !matchAny(contractId, filter.contractId))
1174
+ return false;
1175
+ }
1176
+ if (!traitAllows(filter, contractId, traitContracts))
1177
+ return false;
1178
+ if (!factoryAllows(filter, contractId, factoryContracts))
1179
+ return false;
1180
+ return true;
1181
+ });
1182
+ if (matched.length > 0) {
1183
+ results.push({ tx, events: matched });
1184
+ }
1185
+ }
1186
+ break;
1187
+ }
1188
+ }
1189
+ return results;
1190
+ }
1191
+ function matchSources(sources, transactions, events, traitContracts = new Map, factoryContracts = new Map) {
1192
+ const eventsByTx = new Map;
1193
+ for (const event of events) {
1194
+ const list = eventsByTx.get(event.tx_id) ?? [];
1195
+ list.push(event);
1196
+ eventsByTx.set(event.tx_id, list);
1197
+ }
1198
+ const seen = new Set;
1199
+ const results = [];
1200
+ for (const [sourceName, filter] of Object.entries(sources)) {
1201
+ const matches = matchFilter(filter, transactions, eventsByTx, traitContracts, factoryContracts);
1202
+ for (const match of matches) {
1203
+ const dedupeKey = `${match.tx.tx_id}:${sourceName}`;
1204
+ if (!seen.has(dedupeKey)) {
1205
+ seen.add(dedupeKey);
1206
+ results.push({ ...match, sourceName });
1207
+ }
1208
+ }
1209
+ }
1210
+ return results;
1211
+ }
1212
+
945
1213
  // src/runtime/runner.ts
946
1214
  import { getErrorMessage } from "@secondlayer/shared";
947
- import { logger as logger3 } from "@secondlayer/shared/logger";
1215
+ import { logger as logger4 } from "@secondlayer/shared/logger";
948
1216
  import {
949
1217
  clarityValueToJS,
950
- deserializeCV as deserializeCV3,
1218
+ deserializeCV as deserializeCV4,
951
1219
  toCamelCase as toCamelCase2
952
1220
  } from "@secondlayer/stacks/clarity";
953
1221
 
1222
+ // src/print-schema.ts
1223
+ import {
1224
+ deserializeCV as deserializeCV3
1225
+ } from "@secondlayer/stacks/clarity";
1226
+ function camelizeDataKey(str) {
1227
+ return str.replace(/-([a-z0-9])/g, (_, c) => c.toUpperCase());
1228
+ }
1229
+ function camelizeKeys(obj) {
1230
+ if (obj === null || obj === undefined)
1231
+ return obj;
1232
+ if (typeof obj !== "object")
1233
+ return obj;
1234
+ if (Array.isArray(obj))
1235
+ return obj.map(camelizeKeys);
1236
+ const result = {};
1237
+ for (const [k, v] of Object.entries(obj)) {
1238
+ result[camelizeDataKey(k)] = camelizeKeys(v);
1239
+ }
1240
+ return result;
1241
+ }
1242
+ function cvToTree(cv) {
1243
+ switch (cv.type) {
1244
+ case "uint":
1245
+ case "int":
1246
+ return { kind: cv.type };
1247
+ case "true":
1248
+ case "false":
1249
+ return { kind: "bool" };
1250
+ case "address":
1251
+ case "contract":
1252
+ return { kind: "principal" };
1253
+ case "buffer":
1254
+ return { kind: "buffer", len: cv.value.length / 2 };
1255
+ case "ascii":
1256
+ return { kind: "ascii", len: cv.value.length };
1257
+ case "utf8":
1258
+ return { kind: "utf8", len: new TextEncoder().encode(cv.value).length };
1259
+ case "none":
1260
+ return { kind: "optional", inner: null };
1261
+ case "some":
1262
+ return { kind: "optional", inner: cvToTree(cv.value) };
1263
+ case "ok":
1264
+ return { kind: "response", ok: cvToTree(cv.value), err: null };
1265
+ case "err":
1266
+ return { kind: "response", ok: null, err: cvToTree(cv.value) };
1267
+ case "list": {
1268
+ let inner = null;
1269
+ for (const el of cv.value) {
1270
+ const t = cvToTree(el);
1271
+ inner = inner ? unify(inner, t) : t;
1272
+ }
1273
+ return { kind: "list", inner };
1274
+ }
1275
+ case "tuple": {
1276
+ const entries = new Map;
1277
+ for (const [k, v] of Object.entries(cv.value)) {
1278
+ entries.set(k, { tree: cvToTree(v), present: 1 });
1279
+ }
1280
+ return { kind: "tuple", count: 1, entries };
1281
+ }
1282
+ }
1283
+ }
1284
+ function unifyNullable(a, b) {
1285
+ if (!a)
1286
+ return b;
1287
+ if (!b)
1288
+ return a;
1289
+ return unify(a, b);
1290
+ }
1291
+ var UNION_KIND_ORDER = {
1292
+ uint: 0,
1293
+ int: 1,
1294
+ bool: 2,
1295
+ principal: 3,
1296
+ buffer: 4,
1297
+ ascii: 5,
1298
+ utf8: 6,
1299
+ list: 7,
1300
+ tuple: 8,
1301
+ response: 9
1302
+ };
1303
+ function makeUnion(members) {
1304
+ const sorted = [...members].sort((a, b) => (UNION_KIND_ORDER[a.kind] ?? 99) - (UNION_KIND_ORDER[b.kind] ?? 99));
1305
+ return { kind: "union", members: sorted };
1306
+ }
1307
+ function unify(a, b) {
1308
+ if (a.kind === "optional" || b.kind === "optional") {
1309
+ const ai = a.kind === "optional" ? a.inner : a;
1310
+ const bi = b.kind === "optional" ? b.inner : b;
1311
+ return { kind: "optional", inner: unifyNullable(ai, bi) };
1312
+ }
1313
+ if (a.kind === "union")
1314
+ return unionAdd(a.members, b);
1315
+ if (b.kind === "union")
1316
+ return unionAdd(b.members, a);
1317
+ if (a.kind !== b.kind)
1318
+ return makeUnion([a, b]);
1319
+ switch (a.kind) {
1320
+ case "uint":
1321
+ case "int":
1322
+ case "bool":
1323
+ case "principal":
1324
+ return a;
1325
+ case "buffer":
1326
+ case "ascii":
1327
+ case "utf8":
1328
+ return { kind: a.kind, len: Math.max(a.len, b.len) };
1329
+ case "list":
1330
+ return {
1331
+ kind: "list",
1332
+ inner: unifyNullable(a.inner, b.inner)
1333
+ };
1334
+ case "response": {
1335
+ const rb = b;
1336
+ return {
1337
+ kind: "response",
1338
+ ok: unifyNullable(a.ok, rb.ok),
1339
+ err: unifyNullable(a.err, rb.err)
1340
+ };
1341
+ }
1342
+ case "tuple": {
1343
+ const tb = b;
1344
+ const entries = new Map([...a.entries].map(([k, e]) => [k, { ...e }]));
1345
+ for (const [k, e] of tb.entries) {
1346
+ const existing = entries.get(k);
1347
+ entries.set(k, existing ? {
1348
+ tree: unify(existing.tree, e.tree),
1349
+ present: existing.present + e.present
1350
+ } : { ...e });
1351
+ }
1352
+ return { kind: "tuple", count: a.count + tb.count, entries };
1353
+ }
1354
+ }
1355
+ }
1356
+ function unionAdd(members, t) {
1357
+ if (t.kind === "union") {
1358
+ let acc = { kind: "union", members };
1359
+ for (const m of t.members)
1360
+ acc = unify(acc, m);
1361
+ return acc;
1362
+ }
1363
+ const next = [...members];
1364
+ for (let i = 0;i < next.length; i++) {
1365
+ const member = next[i];
1366
+ if (!member)
1367
+ continue;
1368
+ const merged = unify(member, t);
1369
+ if (merged.kind !== "union") {
1370
+ next[i] = merged;
1371
+ return makeUnion(next);
1372
+ }
1373
+ }
1374
+ next.push(t);
1375
+ return makeUnion(next);
1376
+ }
1377
+ function wrapOptional(t) {
1378
+ return t.kind === "optional" ? t : { kind: "optional", inner: t };
1379
+ }
1380
+ function renderClarity(t) {
1381
+ if (!t)
1382
+ return "?";
1383
+ switch (t.kind) {
1384
+ case "uint":
1385
+ case "int":
1386
+ case "bool":
1387
+ case "principal":
1388
+ return t.kind;
1389
+ case "buffer":
1390
+ return `(buff ${t.len})`;
1391
+ case "ascii":
1392
+ return `(string-ascii ${t.len})`;
1393
+ case "utf8":
1394
+ return `(string-utf8 ${t.len})`;
1395
+ case "optional":
1396
+ return `(optional ${renderClarity(t.inner)})`;
1397
+ case "list":
1398
+ return `(list ${renderClarity(t.inner)})`;
1399
+ case "response":
1400
+ return `(response ${renderClarity(t.ok)} ${renderClarity(t.err)})`;
1401
+ case "tuple": {
1402
+ const parts = [...t.entries].map(([k, e]) => {
1403
+ const tree = e.present < t.count ? wrapOptional(e.tree) : e.tree;
1404
+ return `(${k} ${renderClarity(tree)})`;
1405
+ });
1406
+ return `(tuple ${parts.join(" ")})`;
1407
+ }
1408
+ case "union":
1409
+ return t.members.map(renderClarity).join(" | ");
1410
+ }
1411
+ }
1412
+ function renderTs(t) {
1413
+ if (!t)
1414
+ return "unknown";
1415
+ switch (t.kind) {
1416
+ case "uint":
1417
+ case "int":
1418
+ return "bigint";
1419
+ case "bool":
1420
+ return "boolean";
1421
+ case "principal":
1422
+ case "buffer":
1423
+ case "ascii":
1424
+ case "utf8":
1425
+ return "string";
1426
+ case "optional":
1427
+ return t.inner ? `${renderTs(t.inner)} | null` : "unknown | null";
1428
+ case "list": {
1429
+ const inner = renderTs(t.inner);
1430
+ return inner.includes(" | ") ? `(${inner})[]` : `${inner}[]`;
1431
+ }
1432
+ case "response": {
1433
+ const sides = [...new Set([renderTs(t.ok), renderTs(t.err)])];
1434
+ return sides.join(" | ");
1435
+ }
1436
+ case "tuple": {
1437
+ const parts = [...t.entries].map(([k, e]) => {
1438
+ const opt = e.present < t.count ? "?" : "";
1439
+ return `${camelizeDataKey(k)}${opt}: ${renderTs(e.tree)}`;
1440
+ });
1441
+ return `{ ${parts.join("; ")} }`;
1442
+ }
1443
+ case "union":
1444
+ return [...new Set(t.members.map((m) => renderTs(m)))].join(" | ");
1445
+ }
1446
+ }
1447
+ function toColumnType(t) {
1448
+ if (!t)
1449
+ return "jsonb";
1450
+ switch (t.kind) {
1451
+ case "uint":
1452
+ return "uint";
1453
+ case "int":
1454
+ return "int";
1455
+ case "bool":
1456
+ return "boolean";
1457
+ case "principal":
1458
+ return "principal";
1459
+ case "buffer":
1460
+ case "ascii":
1461
+ case "utf8":
1462
+ return "text";
1463
+ case "list":
1464
+ case "tuple":
1465
+ return "jsonb";
1466
+ case "optional":
1467
+ return toColumnType(t.inner);
1468
+ case "response":
1469
+ return t.ok ? toColumnType(t.ok) : "jsonb";
1470
+ case "union":
1471
+ return "jsonb";
1472
+ }
1473
+ }
1474
+ var MAX_DECODED_PER_TOPIC_NEWEST = 75;
1475
+ var MAX_DECODED_PER_TOPIC_OLDEST = 25;
1476
+ function inferPrintTopics(samples) {
1477
+ const groups = new Map;
1478
+ for (const s of samples) {
1479
+ const group = groups.get(s.topic);
1480
+ if (group)
1481
+ group.push(s);
1482
+ else
1483
+ groups.set(s.topic, [s]);
1484
+ }
1485
+ const out = [];
1486
+ for (const [topic, rows] of groups) {
1487
+ let first = Number.POSITIVE_INFINITY;
1488
+ let last = Number.NEGATIVE_INFINITY;
1489
+ for (const r of rows) {
1490
+ if (r.blockHeight < first)
1491
+ first = r.blockHeight;
1492
+ if (r.blockHeight > last)
1493
+ last = r.blockHeight;
1494
+ }
1495
+ const withHex = [...rows].filter((r) => r.rawHex !== null).sort((a, b) => b.blockHeight - a.blockHeight);
1496
+ const budget = MAX_DECODED_PER_TOPIC_NEWEST + MAX_DECODED_PER_TOPIC_OLDEST;
1497
+ const picked = withHex.length <= budget ? withHex : [
1498
+ ...withHex.slice(0, MAX_DECODED_PER_TOPIC_NEWEST),
1499
+ ...withHex.slice(-MAX_DECODED_PER_TOPIC_OLDEST)
1500
+ ];
1501
+ const tuples = [];
1502
+ let decoded = 0;
1503
+ for (const p of picked) {
1504
+ try {
1505
+ const cv = deserializeCV3(p.rawHex);
1506
+ decoded++;
1507
+ if (cv.type === "tuple")
1508
+ tuples.push(cv);
1509
+ } catch {}
1510
+ }
1511
+ const nonTuple = decoded > 0 && tuples.length === 0;
1512
+ const fields = [];
1513
+ if (!nonTuple) {
1514
+ const stats = new Map;
1515
+ for (const t of tuples) {
1516
+ for (const [key, value] of Object.entries(t.value)) {
1517
+ if (key === "topic")
1518
+ continue;
1519
+ const tree = cvToTree(value);
1520
+ const existing = stats.get(key);
1521
+ if (existing) {
1522
+ existing.present++;
1523
+ existing.tree = unify(existing.tree, tree);
1524
+ if (value.type === "none")
1525
+ existing.noneCount++;
1526
+ if (value.type === "none" || value.type === "some") {
1527
+ existing.optionalSeen = true;
1528
+ }
1529
+ } else {
1530
+ stats.set(key, {
1531
+ tree,
1532
+ present: 1,
1533
+ noneCount: value.type === "none" ? 1 : 0,
1534
+ optionalSeen: value.type === "none" || value.type === "some"
1535
+ });
1536
+ }
1537
+ }
1538
+ }
1539
+ for (const [name, st] of [...stats].sort(([a], [b]) => a.localeCompare(b))) {
1540
+ const field = {
1541
+ name,
1542
+ camel_name: camelizeDataKey(name),
1543
+ clarity_type: renderClarity(st.tree),
1544
+ ts_type: renderTs(st.tree),
1545
+ column_type: toColumnType(st.tree),
1546
+ always_present: st.present === tuples.length
1547
+ };
1548
+ if (st.optionalSeen) {
1549
+ field.optional_some_rate = (st.present - st.noneCount) / st.present;
1550
+ }
1551
+ fields.push(field);
1552
+ }
1553
+ }
1554
+ out.push({
1555
+ topic,
1556
+ count: rows.length,
1557
+ first_height: first,
1558
+ last_height: last,
1559
+ non_tuple: nonTuple,
1560
+ fields
1561
+ });
1562
+ }
1563
+ return out.sort((a, b) => b.count - a.count);
1564
+ }
1565
+
1566
+ // src/runtime/materialize.ts
1567
+ function resolveColumn(col, event, tx, block) {
1568
+ if ("fromTx" in col) {
1569
+ return { ok: true, value: tx[col.fromTx] };
1570
+ }
1571
+ if ("fromBlock" in col) {
1572
+ return { ok: true, value: block[col.fromBlock] };
1573
+ }
1574
+ const data = event.data;
1575
+ const dataObj = data !== null && typeof data === "object" && !Array.isArray(data) ? data : undefined;
1576
+ if (dataObj && Object.prototype.hasOwnProperty.call(dataObj, col.from)) {
1577
+ return { ok: true, value: dataObj[col.from] };
1578
+ }
1579
+ if (col.from === "topic" && "topic" in event) {
1580
+ return { ok: true, value: event.topic };
1581
+ }
1582
+ if (Object.prototype.hasOwnProperty.call(event, col.from)) {
1583
+ return { ok: true, value: event[col.from] };
1584
+ }
1585
+ return { ok: false, missing: col.from };
1586
+ }
1587
+ function buildMaterializeRow(spec, event, ctx, tableColumns) {
1588
+ const row = {};
1589
+ for (const [colName, mapping] of Object.entries(spec.columns)) {
1590
+ const resolved = resolveColumn(mapping, event, ctx.tx, ctx.block);
1591
+ if (!resolved.ok) {
1592
+ const colDef = tableColumns[colName];
1593
+ if (colDef?.nullable) {
1594
+ row[colName] = null;
1595
+ continue;
1596
+ }
1597
+ return {
1598
+ ok: false,
1599
+ reason: `required materialize field "${resolved.missing}" missing for column "${colName}"`
1600
+ };
1601
+ }
1602
+ row[colName] = resolved.value ?? null;
1603
+ }
1604
+ return { ok: true, row };
1605
+ }
1606
+ function applyMaterializeInsert(spec, event, ctx, schema) {
1607
+ const table = schema[spec.table];
1608
+ if (!table) {
1609
+ return { ok: false, reason: `materialize table "${spec.table}" missing` };
1610
+ }
1611
+ const built = buildMaterializeRow(spec, event, ctx, table.columns);
1612
+ if (!built.ok)
1613
+ return built;
1614
+ ctx.insert(spec.table, built.row);
1615
+ return { ok: true };
1616
+ }
1617
+
954
1618
  // src/runtime/print-validate.ts
955
1619
  var COLUMN_TYPES = new Set([
956
1620
  "text",
@@ -1030,24 +1694,62 @@ function validatePrintPayload(prints, topic, data) {
1030
1694
  return matchesTopic(declared, data);
1031
1695
  }
1032
1696
 
1033
- // src/runtime/runner.ts
1034
- var DEFAULT_ERROR_THRESHOLD = 50;
1035
- function camelCase(str) {
1036
- return str.replace(/-([a-z0-9])/g, (_, c) => c.toUpperCase());
1697
+ // src/runtime/violations.ts
1698
+ import { getTargetDb } from "@secondlayer/shared/db";
1699
+ import { logger as logger3 } from "@secondlayer/shared/logger";
1700
+ import { sql as sql2 } from "kysely";
1701
+ var VIOLATIONS_CAP_PER_SUBGRAPH = 100;
1702
+ function jsonSafe2(value) {
1703
+ if (typeof value === "bigint")
1704
+ return value.toString();
1705
+ if (value === null || value === undefined)
1706
+ return value;
1707
+ if (Array.isArray(value))
1708
+ return value.map(jsonSafe2);
1709
+ if (typeof value === "object") {
1710
+ const out = {};
1711
+ for (const [k, v] of Object.entries(value)) {
1712
+ out[k] = jsonSafe2(v);
1713
+ }
1714
+ return out;
1715
+ }
1716
+ return value;
1037
1717
  }
1038
- function camelizeKeys(obj) {
1039
- if (obj === null || obj === undefined)
1040
- return obj;
1041
- if (typeof obj !== "object")
1042
- return obj;
1043
- if (Array.isArray(obj))
1044
- return obj.map(camelizeKeys);
1045
- const result = {};
1046
- for (const [k, v] of Object.entries(obj)) {
1047
- result[camelCase(k)] = camelizeKeys(v);
1718
+ async function defaultRecordPrintViolation(input) {
1719
+ try {
1720
+ const db = getTargetDb();
1721
+ const sample = jsonSafe2(input.samplePayload);
1722
+ await db.insertInto("subgraph_violations").values({
1723
+ subgraph_name: input.subgraphName,
1724
+ source_name: input.sourceName,
1725
+ block_height: input.blockHeight,
1726
+ tx_id: input.txId,
1727
+ reason: input.reason,
1728
+ sample_payload: sample
1729
+ }).execute();
1730
+ await sql2`
1731
+ DELETE FROM subgraph_violations
1732
+ WHERE id IN (
1733
+ SELECT id FROM subgraph_violations
1734
+ WHERE subgraph_name = ${input.subgraphName}
1735
+ ORDER BY seen_at DESC
1736
+ OFFSET ${VIOLATIONS_CAP_PER_SUBGRAPH}
1737
+ )
1738
+ `.execute(db);
1739
+ } catch (err) {
1740
+ logger3.warn("Failed to record print violation", {
1741
+ subgraph: input.subgraphName,
1742
+ error: err instanceof Error ? err.message : String(err)
1743
+ });
1048
1744
  }
1049
- return result;
1050
1745
  }
1746
+ var recorder = defaultRecordPrintViolation;
1747
+ async function recordPrintViolation(input) {
1748
+ await recorder(input);
1749
+ }
1750
+
1751
+ // src/runtime/runner.ts
1752
+ var DEFAULT_ERROR_THRESHOLD = 50;
1051
1753
  function decodeFunctionArgs2(args) {
1052
1754
  let parsed = args;
1053
1755
  if (typeof parsed === "string") {
@@ -1111,7 +1813,7 @@ function buildContractCallInput(filter, tx) {
1111
1813
  return;
1112
1814
  try {
1113
1815
  const clean = hex.startsWith("0x") ? hex.slice(2) : hex;
1114
- input[toCamelCase2(arg.name)] = decodeArg(arg.type, deserializeCV3(clean));
1816
+ input[toCamelCase2(arg.name)] = decodeArg(arg.type, deserializeCV4(clean));
1115
1817
  } catch {}
1116
1818
  });
1117
1819
  return input;
@@ -1234,7 +1936,7 @@ function buildEventPayload(filter, tx, event) {
1234
1936
  const { topic: _, ...rest } = clarityObj ?? {};
1235
1937
  const data = Object.keys(rest).length > 0 ? camelizeKeys(rest) : rawValue && typeof rawValue !== "object" ? rawValue : {};
1236
1938
  return {
1237
- contractId: decoded.contract_identifier ?? tx.contract_id ?? "",
1939
+ contractId: printContractId(decoded) ?? tx.contract_id ?? "",
1238
1940
  topic,
1239
1941
  data: data ?? {},
1240
1942
  tx: txMeta
@@ -1295,363 +1997,128 @@ async function runHandlers(subgraph, matched, ctx, opts) {
1295
1997
  units.sort((a, b) => (a.tx.tx_index ?? 0) - (b.tx.tx_index ?? 0) || (a.event?.event_index ?? -1) - (b.event?.event_index ?? -1));
1296
1998
  for (const { tx, event, sourceName } of units) {
1297
1999
  if (errors >= threshold) {
1298
- logger3.error("Subgraph error threshold reached, skipping remaining events", {
2000
+ logger4.error("Subgraph error threshold reached, skipping remaining events", {
1299
2001
  subgraph: subgraph.name,
1300
2002
  errors,
1301
2003
  threshold
1302
2004
  });
1303
2005
  return { processed, errors, ...skipped > 0 ? { skipped } : {} };
1304
2006
  }
2007
+ const filter = filterLookup.get(sourceName);
2008
+ const materialize = filter && "materialize" in filter && filter.materialize !== undefined ? filter.materialize : undefined;
1305
2009
  const handler = subgraph.handlers[sourceName] ?? subgraph.handlers["*"] ?? null;
1306
- if (!handler) {
1307
- logger3.warn("No handler found for source", {
2010
+ if (!handler && !materialize) {
2011
+ logger4.warn("No handler found for source", {
1308
2012
  subgraph: subgraph.name,
1309
2013
  sourceName,
1310
2014
  txId: tx.tx_id
1311
- });
1312
- continue;
1313
- }
1314
- ctx.setTx({
1315
- txId: tx.tx_id,
1316
- sender: tx.sender,
1317
- type: tx.type,
1318
- status: tx.status,
1319
- contractId: tx.contract_id ?? null,
1320
- functionName: tx.function_name ?? null
1321
- });
1322
- const filter = filterLookup.get(sourceName);
1323
- const checkpoint = ctx.opsCheckpoint();
1324
- try {
1325
- let payload;
1326
- if (event === null) {
1327
- payload = filter ? buildEventPayload(filter, tx, null) : {
1328
- tx: {
1329
- txId: tx.tx_id,
1330
- sender: tx.sender,
1331
- type: tx.type,
1332
- status: tx.status,
1333
- contractId: tx.contract_id,
1334
- functionName: tx.function_name
1335
- }
1336
- };
1337
- } else if (filter) {
1338
- payload = buildEventPayload(filter, tx, event);
1339
- } else {
1340
- const decoded = decodeEventData(event.data);
1341
- payload = {
1342
- ...decoded,
1343
- _eventId: event.id,
1344
- _eventType: event.type,
1345
- _eventIndex: event.event_index,
1346
- tx: {
1347
- txId: tx.tx_id,
1348
- sender: tx.sender,
1349
- type: tx.type,
1350
- status: tx.status,
1351
- contractId: tx.contract_id,
1352
- functionName: tx.function_name
1353
- }
1354
- };
1355
- }
1356
- if (event !== null && filter?.type === "print_event" && filter.topic && payload.topic !== filter.topic) {
1357
- continue;
1358
- }
1359
- if (event !== null && filter?.type === "print_event" && filter.prints) {
1360
- const printPayload = payload;
1361
- const verdict = validatePrintPayload(filter.prints, printPayload.topic ?? "", printPayload.data ?? {});
1362
- if (!verdict.ok) {
1363
- skipped++;
1364
- logger3.warn("Print payload does not match declared prints schema", {
1365
- subgraph: subgraph.name,
1366
- sourceName,
1367
- topic: printPayload.topic,
1368
- txId: tx.tx_id,
1369
- reason: verdict.reason
1370
- });
1371
- continue;
1372
- }
1373
- }
1374
- await handler(payload, ctx);
1375
- processed++;
1376
- } catch (err) {
1377
- ctx.rollbackTo(checkpoint);
1378
- errors++;
1379
- logger3.error("Subgraph handler error", {
1380
- subgraph: subgraph.name,
1381
- sourceName,
1382
- txId: tx.tx_id,
1383
- ...event !== null ? { eventId: event.id, eventType: event.type } : {},
1384
- error: getErrorMessage(err)
1385
- });
1386
- }
1387
- }
1388
- return { processed, errors, ...skipped > 0 ? { skipped } : {} };
1389
- }
1390
-
1391
- // src/runtime/source-matcher.ts
1392
- var patternCache = new Map;
1393
- function matchAny(value, pattern) {
1394
- return Array.isArray(pattern) ? pattern.some((p) => matchPattern(value, p)) : matchPattern(value, pattern);
1395
- }
1396
- function matchPattern(value, pattern) {
1397
- if (!pattern.includes("*"))
1398
- return value === pattern;
1399
- let re = patternCache.get(pattern);
1400
- if (!re) {
1401
- const regex = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
1402
- re = new RegExp(`^${regex}$`);
1403
- patternCache.set(pattern, re);
1404
- }
1405
- return re.test(value);
1406
- }
1407
- var EMPTY_SET = new Set;
1408
- function factoryAllows(filter, contractId, factoryContracts) {
1409
- if (!filter.factory)
1410
- return true;
1411
- if (!contractId)
1412
- return false;
1413
- return (factoryContracts.get(filter.factory.from) ?? EMPTY_SET).has(contractId);
1414
- }
1415
- function readPath(payload, path) {
1416
- let cur = payload;
1417
- for (const key of path.split(".")) {
1418
- if (cur === null || typeof cur !== "object")
1419
- return;
1420
- cur = cur[key];
1421
- }
1422
- return cur;
1423
- }
1424
- function traitAllows(filter, contractId, traitContracts) {
1425
- const trait = filter.trait;
1426
- if (!trait)
1427
- return true;
1428
- if (!contractId)
1429
- return false;
1430
- return (traitContracts.get(trait) ?? EMPTY_SET).has(contractId);
1431
- }
1432
- function assetContract(assetId) {
1433
- return assetId?.split("::")[0];
1434
- }
1435
- function matchFilter(filter, transactions, eventsByTx, traitContracts, factoryContracts = new Map) {
1436
- const results = [];
1437
- switch (filter.type) {
1438
- case "stx_transfer":
1439
- case "stx_mint":
1440
- case "stx_burn":
1441
- case "stx_lock": {
1442
- const eventType = `${filter.type}_event`;
1443
- for (const tx of transactions) {
1444
- const txEvents = eventsByTx.get(tx.tx_id) ?? [];
1445
- const matched = txEvents.filter((e) => e.type === eventType);
1446
- if (matched.length === 0)
1447
- continue;
1448
- const filtered = matched.filter((e) => {
1449
- const data = e.data;
1450
- if (!data)
1451
- return false;
1452
- if ("sender" in filter && filter.sender) {
1453
- if (!matchPattern(data.sender, filter.sender))
1454
- return false;
1455
- }
1456
- if ("recipient" in filter && filter.recipient) {
1457
- if (!matchPattern(data.recipient, filter.recipient))
1458
- return false;
1459
- }
1460
- if ("lockedAddress" in filter && filter.lockedAddress) {
1461
- if (!matchPattern(data.locked_address, filter.lockedAddress))
1462
- return false;
1463
- }
1464
- if ("minAmount" in filter && filter.minAmount !== undefined) {
1465
- const amount = BigInt(data.amount ?? data.locked_amount ?? "0");
1466
- if (amount < filter.minAmount)
1467
- return false;
1468
- }
1469
- if ("maxAmount" in filter && filter.maxAmount !== undefined) {
1470
- const amount = BigInt(data.amount ?? "0");
1471
- if (amount > filter.maxAmount)
1472
- return false;
1473
- }
1474
- return true;
1475
- });
1476
- if (filtered.length > 0) {
1477
- results.push({ tx, events: filtered });
1478
- }
1479
- }
1480
- break;
2015
+ });
2016
+ continue;
1481
2017
  }
1482
- case "ft_transfer":
1483
- case "ft_mint":
1484
- case "ft_burn": {
1485
- const eventType = `${filter.type}_event`;
1486
- for (const tx of transactions) {
1487
- const txEvents = eventsByTx.get(tx.tx_id) ?? [];
1488
- const matched = txEvents.filter((e) => {
1489
- if (e.type !== eventType)
1490
- return false;
1491
- const data = e.data;
1492
- if (!data)
1493
- return false;
1494
- if (filter.assetIdentifier) {
1495
- const assetId = data.asset_identifier;
1496
- if (!assetId || !matchPattern(assetId, filter.assetIdentifier))
1497
- return false;
1498
- }
1499
- if (!traitAllows(filter, assetContract(data.asset_identifier), traitContracts))
1500
- return false;
1501
- if ("sender" in filter && filter.sender) {
1502
- if (!matchPattern(data.sender, filter.sender))
1503
- return false;
1504
- }
1505
- if ("recipient" in filter && filter.recipient) {
1506
- if (!matchPattern(data.recipient, filter.recipient))
1507
- return false;
2018
+ ctx.setTx({
2019
+ txId: tx.tx_id,
2020
+ sender: tx.sender,
2021
+ type: tx.type,
2022
+ status: tx.status,
2023
+ contractId: tx.contract_id ?? null,
2024
+ functionName: tx.function_name ?? null
2025
+ });
2026
+ const checkpoint = ctx.opsCheckpoint();
2027
+ try {
2028
+ let payload;
2029
+ if (event === null) {
2030
+ payload = filter ? buildEventPayload(filter, tx, null) : {
2031
+ tx: {
2032
+ txId: tx.tx_id,
2033
+ sender: tx.sender,
2034
+ type: tx.type,
2035
+ status: tx.status,
2036
+ contractId: tx.contract_id,
2037
+ functionName: tx.function_name
1508
2038
  }
1509
- if (filter.minAmount !== undefined) {
1510
- const amount = BigInt(data.amount ?? "0");
1511
- if (amount < filter.minAmount)
1512
- return false;
2039
+ };
2040
+ } else if (filter) {
2041
+ payload = buildEventPayload(filter, tx, event);
2042
+ } else {
2043
+ const decoded = decodeEventData(event.data);
2044
+ payload = {
2045
+ ...decoded,
2046
+ _eventId: event.id,
2047
+ _eventType: event.type,
2048
+ _eventIndex: event.event_index,
2049
+ tx: {
2050
+ txId: tx.tx_id,
2051
+ sender: tx.sender,
2052
+ type: tx.type,
2053
+ status: tx.status,
2054
+ contractId: tx.contract_id,
2055
+ functionName: tx.function_name
1513
2056
  }
1514
- return true;
1515
- });
1516
- if (matched.length > 0) {
1517
- results.push({ tx, events: matched });
1518
- }
2057
+ };
1519
2058
  }
1520
- break;
1521
- }
1522
- case "nft_transfer":
1523
- case "nft_mint":
1524
- case "nft_burn": {
1525
- const eventType = `${filter.type}_event`;
1526
- for (const tx of transactions) {
1527
- const txEvents = eventsByTx.get(tx.tx_id) ?? [];
1528
- const matched = txEvents.filter((e) => {
1529
- if (e.type !== eventType)
1530
- return false;
1531
- const data = e.data;
1532
- if (!data)
1533
- return false;
1534
- if (filter.assetIdentifier) {
1535
- const assetId = data.asset_identifier;
1536
- if (!assetId || !matchPattern(assetId, filter.assetIdentifier))
1537
- return false;
1538
- }
1539
- if (!traitAllows(filter, assetContract(data.asset_identifier), traitContracts))
1540
- return false;
1541
- if ("sender" in filter && filter.sender) {
1542
- if (!matchPattern(data.sender, filter.sender))
1543
- return false;
1544
- }
1545
- if ("recipient" in filter && filter.recipient) {
1546
- if (!matchPattern(data.recipient, filter.recipient))
1547
- return false;
1548
- }
1549
- return true;
1550
- });
1551
- if (matched.length > 0) {
1552
- results.push({ tx, events: matched });
1553
- }
2059
+ if (event !== null && filter?.type === "print_event" && filter.topic && payload.topic !== filter.topic) {
2060
+ continue;
1554
2061
  }
1555
- break;
1556
- }
1557
- case "contract_call": {
1558
- for (const tx of transactions) {
1559
- if (tx.type !== "contract_call")
1560
- continue;
1561
- if (filter.contractId) {
1562
- if (!tx.contract_id || !matchAny(tx.contract_id, filter.contractId))
1563
- continue;
1564
- }
1565
- if (!factoryAllows(filter, tx.contract_id, factoryContracts))
2062
+ if (event !== null && filter?.type === "print_event" && filter.prints) {
2063
+ const printPayload = payload;
2064
+ const verdict = validatePrintPayload(filter.prints, printPayload.topic ?? "", printPayload.data ?? {});
2065
+ if (!verdict.ok) {
2066
+ skipped++;
2067
+ logger4.warn("Print payload does not match declared prints schema", {
2068
+ subgraph: subgraph.name,
2069
+ sourceName,
2070
+ topic: printPayload.topic,
2071
+ txId: tx.tx_id,
2072
+ reason: verdict.reason
2073
+ });
2074
+ recordPrintViolation({
2075
+ subgraphName: subgraph.name,
2076
+ sourceName,
2077
+ blockHeight: ctx.block.height,
2078
+ txId: tx.tx_id,
2079
+ reason: verdict.reason,
2080
+ samplePayload: {
2081
+ topic: printPayload.topic ?? "",
2082
+ data: printPayload.data ?? {}
2083
+ }
2084
+ });
1566
2085
  continue;
1567
- if (filter.functionName) {
1568
- if (!tx.function_name || !matchPattern(tx.function_name, filter.functionName))
1569
- continue;
1570
- }
1571
- if (filter.caller) {
1572
- if (!matchPattern(tx.sender, filter.caller))
1573
- continue;
1574
2086
  }
1575
- if (!traitAllows(filter, tx.contract_id, traitContracts))
1576
- continue;
1577
- const txEvents = eventsByTx.get(tx.tx_id) ?? [];
1578
- results.push({ tx, events: txEvents });
1579
2087
  }
1580
- break;
1581
- }
1582
- case "contract_deploy": {
1583
- for (const tx of transactions) {
1584
- if (tx.type !== "smart_contract")
2088
+ if (materialize) {
2089
+ const result = applyMaterializeInsert(materialize, payload, ctx, subgraph.schema);
2090
+ if (!result.ok) {
2091
+ skipped++;
2092
+ logger4.warn("Materialize skipped: required field missing", {
2093
+ subgraph: subgraph.name,
2094
+ sourceName,
2095
+ txId: tx.tx_id,
2096
+ reason: result.reason
2097
+ });
1585
2098
  continue;
1586
- if (filter.deployer) {
1587
- if (!matchPattern(tx.sender, filter.deployer))
1588
- continue;
1589
- }
1590
- if (filter.contractName) {
1591
- const name = tx.contract_id?.split(".")[1] ?? "";
1592
- if (!matchPattern(name, filter.contractName))
1593
- continue;
1594
- }
1595
- const txEvents = eventsByTx.get(tx.tx_id) ?? [];
1596
- results.push({ tx, events: txEvents });
1597
- }
1598
- break;
1599
- }
1600
- case "print_event": {
1601
- for (const tx of transactions) {
1602
- const txEvents = eventsByTx.get(tx.tx_id) ?? [];
1603
- const matched = txEvents.filter((e) => {
1604
- if (e.type !== "smart_contract_event" && e.type !== "contract_event")
1605
- return false;
1606
- const data = e.data;
1607
- if (!data)
1608
- return false;
1609
- if (data.topic !== "print")
1610
- return false;
1611
- const printContractId = data.contract_identifier ?? data.contract_id;
1612
- if (filter.contractId) {
1613
- if (!printContractId || !matchAny(printContractId, filter.contractId))
1614
- return false;
1615
- }
1616
- if (!traitAllows(filter, printContractId, traitContracts))
1617
- return false;
1618
- if (!factoryAllows(filter, printContractId, factoryContracts))
1619
- return false;
1620
- return true;
1621
- });
1622
- if (matched.length > 0) {
1623
- results.push({ tx, events: matched });
1624
2099
  }
2100
+ processed++;
2101
+ } else if (handler) {
2102
+ await handler(payload, ctx);
2103
+ processed++;
1625
2104
  }
1626
- break;
1627
- }
1628
- }
1629
- return results;
1630
- }
1631
- function matchSources(sources, transactions, events, traitContracts = new Map, factoryContracts = new Map) {
1632
- const eventsByTx = new Map;
1633
- for (const event of events) {
1634
- const list = eventsByTx.get(event.tx_id) ?? [];
1635
- list.push(event);
1636
- eventsByTx.set(event.tx_id, list);
1637
- }
1638
- const seen = new Set;
1639
- const results = [];
1640
- for (const [sourceName, filter] of Object.entries(sources)) {
1641
- const matches = matchFilter(filter, transactions, eventsByTx, traitContracts, factoryContracts);
1642
- for (const match of matches) {
1643
- const dedupeKey = `${match.tx.tx_id}:${sourceName}`;
1644
- if (!seen.has(dedupeKey)) {
1645
- seen.add(dedupeKey);
1646
- results.push({ ...match, sourceName });
1647
- }
2105
+ } catch (err) {
2106
+ ctx.rollbackTo(checkpoint);
2107
+ errors++;
2108
+ logger4.error("Subgraph handler error", {
2109
+ subgraph: subgraph.name,
2110
+ sourceName,
2111
+ txId: tx.tx_id,
2112
+ ...event !== null ? { eventId: event.id, eventType: event.type } : {},
2113
+ error: getErrorMessage(err)
2114
+ });
1648
2115
  }
1649
2116
  }
1650
- return results;
2117
+ return { processed, errors, ...skipped > 0 ? { skipped } : {} };
1651
2118
  }
1652
2119
 
1653
2120
  // src/runtime/block-processor.ts
1654
- import { getTargetDb } from "@secondlayer/shared/db";
2121
+ import { getTargetDb as getTargetDb2 } from "@secondlayer/shared/db";
1655
2122
  import { resolveTraitContractIds } from "@secondlayer/shared/db/queries/contracts";
1656
2123
  import { recordGapBatch } from "@secondlayer/shared/db/queries/subgraph-gaps";
1657
2124
  import { advanceOperationCursor } from "@secondlayer/shared/db/queries/subgraph-operations";
@@ -1662,15 +2129,15 @@ import {
1662
2129
  rewindLiveProgress,
1663
2130
  updateSubgraphStatus
1664
2131
  } from "@secondlayer/shared/db/queries/subgraphs";
1665
- import { logger as logger7 } from "@secondlayer/shared/logger";
1666
- import { sql as sql3 } from "kysely";
2132
+ import { logger as logger8 } from "@secondlayer/shared/logger";
2133
+ import { sql as sql4 } from "kysely";
1667
2134
 
1668
2135
  // src/runtime/block-source.ts
1669
2136
  import { getSourceDb as getSourceDb2 } from "@secondlayer/shared/db";
1670
2137
  import {
1671
2138
  createInternalIndexHttpClient
1672
2139
  } from "@secondlayer/shared/index-http";
1673
- import { logger as logger4 } from "@secondlayer/shared/logger";
2140
+ import { logger as logger5 } from "@secondlayer/shared/logger";
1674
2141
 
1675
2142
  // src/runtime/batch-loader.ts
1676
2143
  async function loadBlockRange(db, fromHeight, toHeight) {
@@ -1990,7 +2457,7 @@ function reconstructEvent(e) {
1990
2457
  type: "contract_event",
1991
2458
  data: {
1992
2459
  topic: e.payload.topic,
1993
- contract_identifier: e.contract_id,
2460
+ contract_id: e.contract_id,
1994
2461
  value: e.payload.value,
1995
2462
  raw_value: e.payload.raw_value
1996
2463
  }
@@ -2157,7 +2624,7 @@ class FallbackBlockSource {
2157
2624
  try {
2158
2625
  return await this.primary.getTip();
2159
2626
  } catch (err) {
2160
- logger4.warn("block source primary getTip failed — using DB tap", {
2627
+ logger5.warn("block source primary getTip failed — using DB tap", {
2161
2628
  error: err instanceof Error ? err.message : String(err)
2162
2629
  });
2163
2630
  return this.fallback.getTip();
@@ -2167,7 +2634,7 @@ class FallbackBlockSource {
2167
2634
  try {
2168
2635
  return await this.primary.loadBlockRange(fromHeight, toHeight);
2169
2636
  } catch (err) {
2170
- logger4.warn("block source primary loadBlockRange failed — using DB tap", {
2637
+ logger5.warn("block source primary loadBlockRange failed — using DB tap", {
2171
2638
  from: fromHeight,
2172
2639
  to: toHeight,
2173
2640
  error: err instanceof Error ? err.message : String(err)
@@ -2204,7 +2671,7 @@ function resolveBlockSource(subgraph) {
2204
2671
  return new FallbackBlockSource(new PublicApiBlockSource(buildHttpClient(), referencedIndexEventTypes(subgraph), canSparseScan(subgraph) ? sparseProbeTargets(subgraph) : undefined, needsTransactionData(subgraph)), postgresBlockSource);
2205
2672
  }
2206
2673
  if (process.env.SUBGRAPH_SOURCE === "streams-index" && subgraph) {
2207
- logger4.debug("Subgraph not streams-index eligible, using DB tap", {
2674
+ logger5.debug("Subgraph not streams-index eligible, using DB tap", {
2208
2675
  subgraph: subgraph.name
2209
2676
  });
2210
2677
  }
@@ -2213,7 +2680,7 @@ function resolveBlockSource(subgraph) {
2213
2680
 
2214
2681
  // src/runtime/outbox-emit.ts
2215
2682
  import { createHash as createHash2 } from "node:crypto";
2216
- import { logger as logger5 } from "@secondlayer/shared/logger";
2683
+ import { logger as logger6 } from "@secondlayer/shared/logger";
2217
2684
  var loggedKillSwitch = false;
2218
2685
  var OP_VERB = {
2219
2686
  insert: "created",
@@ -2237,7 +2704,7 @@ function stableStringify(obj) {
2237
2704
  async function emitSubscriptionOutbox(tx, subgraphName, manifest, matcher, blockHeight) {
2238
2705
  if (!isEmitOutboxEnabled()) {
2239
2706
  if (!loggedKillSwitch) {
2240
- logger5.warn("SECONDLAYER_EMIT_OUTBOX=false — outbox emission bypassed");
2707
+ logger6.warn("SECONDLAYER_EMIT_OUTBOX=false — outbox emission bypassed");
2241
2708
  loggedKillSwitch = true;
2242
2709
  }
2243
2710
  return 0;
@@ -2282,7 +2749,7 @@ function sandboxEnabled(subgraph) {
2282
2749
  // src/runtime/sandbox/host.ts
2283
2750
  import { tmpdir } from "node:os";
2284
2751
  import { fileURLToPath } from "node:url";
2285
- import { logger as logger6 } from "@secondlayer/shared/logger";
2752
+ import { logger as logger7 } from "@secondlayer/shared/logger";
2286
2753
 
2287
2754
  // src/runtime/sandbox/bundle.ts
2288
2755
  import esbuild from "esbuild";
@@ -2362,7 +2829,7 @@ function spawnSandboxProcess() {
2362
2829
  break;
2363
2830
  const text = decoder.decode(value, { stream: true });
2364
2831
  if (text.trim()) {
2365
- logger6.warn("Sandbox subprocess stderr", { stderr: text.trim() });
2832
+ logger7.warn("Sandbox subprocess stderr", { stderr: text.trim() });
2366
2833
  }
2367
2834
  }
2368
2835
  } catch {}
@@ -2420,7 +2887,7 @@ async function ensureSandboxProcess(subgraphName, version, handlerCode) {
2420
2887
  try {
2421
2888
  await initSandboxProcess(existing.proc, subgraphName, version, handlerCode);
2422
2889
  existing.version = version;
2423
- logger6.info("Sandbox subprocess handler reloaded", {
2890
+ logger7.info("Sandbox subprocess handler reloaded", {
2424
2891
  subgraph: subgraphName,
2425
2892
  version
2426
2893
  });
@@ -2428,7 +2895,7 @@ async function ensureSandboxProcess(subgraphName, version, handlerCode) {
2428
2895
  } catch (err) {
2429
2896
  existing.proc.kill();
2430
2897
  pool.delete(subgraphName);
2431
- logger6.warn("Sandbox subprocess re-init failed; respawning", {
2898
+ logger7.warn("Sandbox subprocess re-init failed; respawning", {
2432
2899
  subgraph: subgraphName,
2433
2900
  error: err instanceof Error ? err.message : String(err)
2434
2901
  });
@@ -2511,7 +2978,7 @@ async function runBlockThroughProcess(entry, subgraphName, hostCtx, block, match
2511
2978
  return result;
2512
2979
  } catch (err) {
2513
2980
  evictSandboxWorker(subgraphName);
2514
- logger6.warn("Sandbox subprocess run failed; subprocess evicted", {
2981
+ logger7.warn("Sandbox subprocess run failed; subprocess evicted", {
2515
2982
  subgraph: subgraphName,
2516
2983
  blockHeight: block.height,
2517
2984
  error: err instanceof Error ? err.message : String(err)
@@ -2582,7 +3049,7 @@ async function answerRead(hostCtx, msg) {
2582
3049
 
2583
3050
  // src/runtime/subscription-state.ts
2584
3051
  import { listSubscriptions } from "@secondlayer/shared/db/queries/subscriptions";
2585
- import { sql as sql2 } from "kysely";
3052
+ import { sql as sql3 } from "kysely";
2586
3053
 
2587
3054
  // src/runtime/emitter-matcher.ts
2588
3055
  function isPrimitive(v) {
@@ -2746,7 +3213,7 @@ class SubscriptionMatcher {
2746
3213
  // src/runtime/subscription-state.ts
2747
3214
  var matcher = new SubscriptionMatcher;
2748
3215
  async function refreshMatcher(db) {
2749
- const rows = await sql2`
3216
+ const rows = await sql3`
2750
3217
  SELECT * FROM subscriptions WHERE status = 'active'
2751
3218
  `.execute(db);
2752
3219
  matcher.setAll(rows.rows);
@@ -2768,7 +3235,7 @@ async function resolveRoute(subgraphName, targetDb) {
2768
3235
  version: row?.version ?? ""
2769
3236
  };
2770
3237
  routeCache.set(subgraphName, route);
2771
- logger7.info("subgraph handler dispatch path resolved", {
3238
+ logger8.info("subgraph handler dispatch path resolved", {
2772
3239
  subgraph: subgraphName,
2773
3240
  path: sandboxEnabled({ sandbox_workers: route.sandboxWorkers }) ? "sandbox" : "in-process"
2774
3241
  });
@@ -2792,8 +3259,8 @@ async function resolveFactoryContracts(subgraph, blockHeight, schemaName, db, tx
2792
3259
  for (const [discoveringSource, factory] of factories) {
2793
3260
  const known = new Set;
2794
3261
  try {
2795
- const rows = await sql3`
2796
- SELECT address FROM ${sql3.raw(`"${schemaName}"."_factory_addresses"`)}
3262
+ const rows = await sql4`
3263
+ SELECT address FROM ${sql4.raw(`"${schemaName}"."_factory_addresses"`)}
2797
3264
  WHERE source_name = ${discoveringSource} AND block_height <= ${blockHeight}
2798
3265
  `.execute(db);
2799
3266
  for (const row of rows.rows)
@@ -2821,8 +3288,8 @@ async function persistFactoryDiscoveries(schemaName, db, blockHeight, discovered
2821
3288
  if (discovered.length === 0)
2822
3289
  return;
2823
3290
  for (const { sourceName, address } of discovered) {
2824
- await sql3`
2825
- INSERT INTO ${sql3.raw(`"${schemaName}"."_factory_addresses"`)}
3291
+ await sql4`
3292
+ INSERT INTO ${sql4.raw(`"${schemaName}"."_factory_addresses"`)}
2826
3293
  (source_name, address, block_height)
2827
3294
  VALUES (${sourceName}, ${address}, ${blockHeight})
2828
3295
  ON CONFLICT (source_name, address) DO NOTHING
@@ -2882,7 +3349,7 @@ async function processBlockWithRetry(subgraph, subgraphName, blockHeight, opts,
2882
3349
  const delay = retryDelaysMs[attempt];
2883
3350
  if (delay === undefined)
2884
3351
  break;
2885
- logger7.warn("Block processing failed, retrying", {
3352
+ logger8.warn("Block processing failed, retrying", {
2886
3353
  subgraph: subgraphName,
2887
3354
  blockHeight,
2888
3355
  attempt: attempt + 1,
@@ -2895,7 +3362,7 @@ async function processBlockWithRetry(subgraph, subgraphName, blockHeight, opts,
2895
3362
  throw lastError;
2896
3363
  }
2897
3364
  async function processBlock(subgraph, subgraphName, blockHeight, opts) {
2898
- const targetDb = getTargetDb();
3365
+ const targetDb = getTargetDb2();
2899
3366
  const blockStart = performance.now();
2900
3367
  const result = {
2901
3368
  blockHeight,
@@ -2914,7 +3381,7 @@ async function processBlock(subgraph, subgraphName, blockHeight, opts) {
2914
3381
  } else {
2915
3382
  const data = (await resolveBlockSource(subgraph).loadBlockRange(blockHeight, blockHeight)).get(blockHeight);
2916
3383
  if (!data) {
2917
- logger7.debug("Block not found or non-canonical for subgraph processing", {
3384
+ logger8.debug("Block not found or non-canonical for subgraph processing", {
2918
3385
  subgraph: subgraphName,
2919
3386
  blockHeight
2920
3387
  });
@@ -2980,7 +3447,7 @@ async function processBlock(subgraph, subgraphName, blockHeight, opts) {
2980
3447
  { start: blockHeight, end: blockHeight, reason: "processing_error" }
2981
3448
  ]);
2982
3449
  } else {
2983
- logger7.warn("Block errors not recorded as a gap — no subgraphId", {
3450
+ logger8.warn("Block errors not recorded as a gap — no subgraphId", {
2984
3451
  subgraph: subgraphName,
2985
3452
  blockHeight,
2986
3453
  errors: rr.errors
@@ -3053,7 +3520,7 @@ async function processBlock(subgraph, subgraphName, blockHeight, opts) {
3053
3520
  });
3054
3521
  } catch (err) {
3055
3522
  if (err instanceof CursorRaceLostError || err instanceof LiveCursorRaceLostError) {
3056
- logger7.warn("cursor race lost — block already covered", {
3523
+ logger8.warn("cursor race lost — block already covered", {
3057
3524
  subgraph: subgraphName,
3058
3525
  blockHeight,
3059
3526
  error: err.message
@@ -3078,10 +3545,10 @@ async function processBlock(subgraph, subgraphName, blockHeight, opts) {
3078
3545
  try {
3079
3546
  const tables = Object.keys(subgraph.schema);
3080
3547
  for (const table of tables) {
3081
- const { rows } = await sql3.raw(`SELECT n_live_tup AS count FROM pg_stat_user_tables WHERE schemaname = '${schemaName}' AND relname = '${table}'`).execute(route.dataDb);
3548
+ const { rows } = await sql4.raw(`SELECT n_live_tup AS count FROM pg_stat_user_tables WHERE schemaname = '${schemaName}' AND relname = '${table}'`).execute(route.dataDb);
3082
3549
  const count = Number(rows[0]?.count ?? 0);
3083
3550
  if (count >= 1e7) {
3084
- logger7.warn("Subgraph table exceeds 10M rows (estimate)", {
3551
+ logger8.warn("Subgraph table exceeds 10M rows (estimate)", {
3085
3552
  subgraph: subgraphName,
3086
3553
  table,
3087
3554
  count
@@ -3089,13 +3556,13 @@ async function processBlock(subgraph, subgraphName, blockHeight, opts) {
3089
3556
  }
3090
3557
  }
3091
3558
  } catch (err) {
3092
- logger7.debug("Row count sample failed", {
3559
+ logger8.debug("Row count sample failed", {
3093
3560
  subgraph: subgraphName,
3094
3561
  error: err instanceof Error ? err.message : String(err)
3095
3562
  });
3096
3563
  }
3097
3564
  if (journalEnabled(opts)) {
3098
- await sql3.raw(`DELETE FROM "${schemaName}"."_journal" WHERE "block_height" < ${blockHeight - JOURNAL_RETENTION_BLOCKS}`).execute(route.dataDb).catch(() => {});
3565
+ await sql4.raw(`DELETE FROM "${schemaName}"."_journal" WHERE "block_height" < ${blockHeight - JOURNAL_RETENTION_BLOCKS}`).execute(route.dataDb).catch(() => {});
3099
3566
  }
3100
3567
  }
3101
3568
  return result;
@@ -3165,9 +3632,9 @@ class StatsAccumulator {
3165
3632
  }
3166
3633
 
3167
3634
  // src/runtime/catchup.ts
3168
- import { getTargetDb as getTargetDb2, sql as sql4 } from "@secondlayer/shared/db";
3635
+ import { getTargetDb as getTargetDb3, sql as sql5 } from "@secondlayer/shared/db";
3169
3636
  import { getSubgraph } from "@secondlayer/shared/db/queries/subgraphs";
3170
- import { logger as logger8 } from "@secondlayer/shared/logger";
3637
+ import { logger as logger9 } from "@secondlayer/shared/logger";
3171
3638
 
3172
3639
  // src/runtime/catchup-leader.ts
3173
3640
  import {
@@ -3297,8 +3764,8 @@ async function withSubgraphBlockLock(name, fn) {
3297
3764
  }
3298
3765
  }
3299
3766
  async function withSubgraphBlockAdvisoryLock(name, fn) {
3300
- return getTargetDb2().transaction().execute(async (lockTx) => {
3301
- await sql4`SELECT pg_advisory_xact_lock(hashtext(${`subgraph-block:${name}`}))`.execute(lockTx);
3767
+ return getTargetDb3().transaction().execute(async (lockTx) => {
3768
+ await sql5`SELECT pg_advisory_xact_lock(hashtext(${`subgraph-block:${name}`}))`.execute(lockTx);
3302
3769
  return fn();
3303
3770
  });
3304
3771
  }
@@ -3359,7 +3826,7 @@ async function catchUpSubgraph(subgraph, subgraphName) {
3359
3826
  catchingUp.add(subgraphName);
3360
3827
  try {
3361
3828
  const source = resolveBlockSource(subgraph);
3362
- const targetDb = getTargetDb2();
3829
+ const targetDb = getTargetDb3();
3363
3830
  const subgraphRow = await getSubgraph(targetDb, subgraphName);
3364
3831
  if (!subgraphRow)
3365
3832
  return 0;
@@ -3371,7 +3838,7 @@ async function catchUpSubgraph(subgraph, subgraphName) {
3371
3838
  if (usesDecodedIndexPlane(subgraph)) {
3372
3839
  const bound = await boundSourceTip(rawTip, decoderNamesForSubgraph(subgraph));
3373
3840
  if (!bound.ok) {
3374
- logger8.warn("Subgraph catch-up stalled: missing decoder checkpoint", {
3841
+ logger9.warn("Subgraph catch-up stalled: missing decoder checkpoint", {
3375
3842
  event: "subgraph_catchup_decoder_stall",
3376
3843
  subgraph: subgraphName,
3377
3844
  missing: bound.missing
@@ -3379,7 +3846,7 @@ async function catchUpSubgraph(subgraph, subgraphName) {
3379
3846
  return 0;
3380
3847
  }
3381
3848
  if (bound.floor !== null && bound.floor < rawTip) {
3382
- logger8.debug("Subgraph catch-up tip bounded by decoder progress", {
3849
+ logger9.debug("Subgraph catch-up tip bounded by decoder progress", {
3383
3850
  event: "subgraph_catchup_decoder_floor",
3384
3851
  subgraph: subgraphName,
3385
3852
  rawTip,
@@ -3394,7 +3861,7 @@ async function catchUpSubgraph(subgraph, subgraphName) {
3394
3861
  const subgraphStart = Number(subgraphRow.start_block) || 1;
3395
3862
  const startBlock = Math.max(lastProcessedBlock + 1, subgraphStart);
3396
3863
  const totalBlocks = chainTip - lastProcessedBlock;
3397
- logger8.info("Subgraph catch-up starting", {
3864
+ logger9.info("Subgraph catch-up starting", {
3398
3865
  subgraph: subgraphName,
3399
3866
  from: startBlock,
3400
3867
  to: chainTip,
@@ -3410,7 +3877,7 @@ async function catchUpSubgraph(subgraph, subgraphName) {
3410
3877
  let nextBatchPromise = batchConfig.prefetch ? source.loadBlockRange(currentHeight, prefetchedBatchEnd) : undefined;
3411
3878
  while (currentHeight <= chainTip) {
3412
3879
  if (!isCatchUpLeader()) {
3413
- logger8.info("Lost catch-up leadership, stopping catch-up", {
3880
+ logger9.info("Lost catch-up leadership, stopping catch-up", {
3414
3881
  subgraph: subgraphName,
3415
3882
  processed
3416
3883
  });
@@ -3418,7 +3885,7 @@ async function catchUpSubgraph(subgraph, subgraphName) {
3418
3885
  }
3419
3886
  const currentRow = await getSubgraph(targetDb, subgraphName);
3420
3887
  if (!currentRow || currentRow.status !== "active") {
3421
- logger8.info("Subgraph status changed, stopping catch-up", {
3888
+ logger9.info("Subgraph status changed, stopping catch-up", {
3422
3889
  subgraph: subgraphName,
3423
3890
  status: currentRow?.status ?? "deleted"
3424
3891
  });
@@ -3447,7 +3914,7 @@ async function catchUpSubgraph(subgraph, subgraphName) {
3447
3914
  blockData = (await source.loadBlockRange(height, height)).get(height);
3448
3915
  }
3449
3916
  if (!blockData) {
3450
- logger8.warn("Block missing during catch-up, deferring to next tick", {
3917
+ logger9.warn("Block missing during catch-up, deferring to next tick", {
3451
3918
  subgraph: subgraphName,
3452
3919
  blockHeight: height
3453
3920
  });
@@ -3468,12 +3935,12 @@ async function catchUpSubgraph(subgraph, subgraphName) {
3468
3935
  });
3469
3936
  } catch (err) {
3470
3937
  if (err instanceof ReorgEpochChangedError) {
3471
- logger8.info("Reorg detected mid catch-up, aborting walk without writing further progress", { subgraph: subgraphName, blockHeight: height });
3938
+ logger9.info("Reorg detected mid catch-up, aborting walk without writing further progress", { subgraph: subgraphName, blockHeight: height });
3472
3939
  stopCatchup = true;
3473
3940
  break;
3474
3941
  }
3475
3942
  const errorMsg = err instanceof Error ? err.message : String(err);
3476
- logger8.error("Block processing failed persistently during catch-up", {
3943
+ logger9.error("Block processing failed persistently during catch-up", {
3477
3944
  subgraph: subgraphName,
3478
3945
  blockHeight: height,
3479
3946
  error: errorMsg
@@ -3492,7 +3959,7 @@ async function catchUpSubgraph(subgraph, subgraphName) {
3492
3959
  }
3493
3960
  }
3494
3961
  if (processed % LOG_INTERVAL === 0) {
3495
- logger8.info("Subgraph catch-up progress", {
3962
+ logger9.info("Subgraph catch-up progress", {
3496
3963
  subgraph: subgraphName,
3497
3964
  processed,
3498
3965
  total: totalBlocks,
@@ -3508,7 +3975,7 @@ async function catchUpSubgraph(subgraph, subgraphName) {
3508
3975
  currentHeight = batchEnd + 1;
3509
3976
  }
3510
3977
  await stats.flush(targetDb);
3511
- logger8.info("Subgraph catch-up complete", {
3978
+ logger9.info("Subgraph catch-up complete", {
3512
3979
  subgraph: subgraphName,
3513
3980
  processed
3514
3981
  });
@@ -3520,7 +3987,7 @@ async function catchUpSubgraph(subgraph, subgraphName) {
3520
3987
 
3521
3988
  // src/runtime/reindex.ts
3522
3989
  import { getErrorMessage as getErrorMessage3 } from "@secondlayer/shared";
3523
- import { getRawClient, getTargetDb as getTargetDb3 } from "@secondlayer/shared/db";
3990
+ import { getRawClient, getTargetDb as getTargetDb4 } from "@secondlayer/shared/db";
3524
3991
  import {
3525
3992
  recordGapBatch as recordGapBatch2,
3526
3993
  resolveGaps
@@ -3533,11 +4000,11 @@ import {
3533
4000
  recordSubgraphProcessed as recordSubgraphProcessed2,
3534
4001
  updateSubgraphStatus as updateSubgraphStatus2
3535
4002
  } from "@secondlayer/shared/db/queries/subgraphs";
3536
- import { logger as logger10 } from "@secondlayer/shared/logger";
4003
+ import { logger as logger11 } from "@secondlayer/shared/logger";
3537
4004
 
3538
4005
  // src/runtime/reindex-notify.ts
3539
4006
  import { getErrorMessage as getErrorMessage2 } from "@secondlayer/shared";
3540
- import { logger as logger9 } from "@secondlayer/shared/logger";
4007
+ import { logger as logger10 } from "@secondlayer/shared/logger";
3541
4008
  async function notifyReindexComplete(db, subgraphName, stats) {
3542
4009
  try {
3543
4010
  const subgraph = await db.selectFrom("subgraphs").select(["account_id"]).where("name", "=", subgraphName).executeTakeFirst();
@@ -3548,7 +4015,7 @@ async function notifyReindexComplete(db, subgraphName, stats) {
3548
4015
  return;
3549
4016
  const resendKey = process.env.RESEND_API_KEY;
3550
4017
  if (!resendKey) {
3551
- logger9.warn("RESEND_API_KEY unset — skipping reindex-complete email", {
4018
+ logger10.warn("RESEND_API_KEY unset — skipping reindex-complete email", {
3552
4019
  subgraph: subgraphName
3553
4020
  });
3554
4021
  return;
@@ -3570,14 +4037,14 @@ async function notifyReindexComplete(db, subgraphName, stats) {
3570
4037
  });
3571
4038
  if (!res.ok) {
3572
4039
  const text = await res.text().catch(() => "");
3573
- logger9.warn("reindex-complete email failed", {
4040
+ logger10.warn("reindex-complete email failed", {
3574
4041
  subgraph: subgraphName,
3575
4042
  status: res.status,
3576
4043
  body: text.slice(0, 200)
3577
4044
  });
3578
4045
  }
3579
4046
  } catch (err) {
3580
- logger9.warn("reindex-complete email threw", {
4047
+ logger10.warn("reindex-complete email threw", {
3581
4048
  subgraph: subgraphName,
3582
4049
  error: getErrorMessage2(err)
3583
4050
  });
@@ -3646,7 +4113,7 @@ function coalesceFailedBlocks(blocks) {
3646
4113
  }
3647
4114
  async function processBlockRange(def, opts) {
3648
4115
  const source = resolveBlockSource(def);
3649
- const targetDb = getTargetDb3();
4116
+ const targetDb = getTargetDb4();
3650
4117
  const subgraphName = def.name;
3651
4118
  const { fromBlock, toBlock, status } = opts;
3652
4119
  const totalBlocks = toBlock - fromBlock + 1;
@@ -3689,7 +4156,7 @@ async function processBlockRange(def, opts) {
3689
4156
  while (currentHeight <= toBlock) {
3690
4157
  if (opts.signal?.aborted) {
3691
4158
  aborted = true;
3692
- logger10.info("Block processing aborted", {
4159
+ logger11.info("Block processing aborted", {
3693
4160
  subgraph: subgraphName,
3694
4161
  currentBlock: currentHeight,
3695
4162
  reason: String(opts.signal.reason ?? "unknown")
@@ -3740,7 +4207,7 @@ async function processBlockRange(def, opts) {
3740
4207
  result = mayWrite ? await withSubgraphBlockWriteLock(subgraphName, runBlock) : await runBlock();
3741
4208
  } catch (err) {
3742
4209
  const errorMsg = getErrorMessage3(err);
3743
- logger10.error("Block processing failed persistently", {
4210
+ logger11.error("Block processing failed persistently", {
3744
4211
  subgraph: subgraphName,
3745
4212
  blockHeight: height,
3746
4213
  error: errorMsg
@@ -3786,7 +4253,7 @@ async function processBlockRange(def, opts) {
3786
4253
  lastProgressFlushAt = now;
3787
4254
  }
3788
4255
  if (blocksProcessed % LOG_INTERVAL2 === 0) {
3789
- logger10.info(`${status === "reindexing" ? "Reindex" : "Backfill"} progress`, {
4256
+ logger11.info(`${status === "reindexing" ? "Reindex" : "Backfill"} progress`, {
3790
4257
  subgraph: subgraphName,
3791
4258
  processed: blocksProcessed,
3792
4259
  total: totalBlocks,
@@ -3806,7 +4273,7 @@ async function processBlockRange(def, opts) {
3806
4273
  if (batchFailedBlocks.length > 0 && opts.subgraphId) {
3807
4274
  const gaps = coalesceFailedBlocks(batchFailedBlocks);
3808
4275
  await recordGapBatch2(targetDb, opts.subgraphId, subgraphName, gaps).catch((err) => {
3809
- logger10.warn("Failed to record subgraph gaps", {
4276
+ logger11.warn("Failed to record subgraph gaps", {
3810
4277
  subgraph: subgraphName,
3811
4278
  error: err instanceof Error ? err.message : String(err)
3812
4279
  });
@@ -3823,7 +4290,7 @@ async function processBlockRange(def, opts) {
3823
4290
  } else {
3824
4291
  await updateSubgraphStatus2(targetDb, subgraphName, status, jumpTo - 1);
3825
4292
  }
3826
- logger10.info("Sparse skip", {
4293
+ logger11.info("Sparse skip", {
3827
4294
  subgraph: subgraphName,
3828
4295
  from: batchEnd + 1,
3829
4296
  to: jumpTo - 1,
@@ -3857,7 +4324,7 @@ async function resolveBlockRange(source, def, opts) {
3857
4324
  }
3858
4325
  const bound = await boundSourceTip(rawTip, decoderNamesForSubgraph(def));
3859
4326
  if (!bound.ok) {
3860
- logger10.warn("Reindex stalled: missing decoder checkpoint", {
4327
+ logger11.warn("Reindex stalled: missing decoder checkpoint", {
3861
4328
  event: "subgraph_reindex_decoder_stall",
3862
4329
  subgraph: def.name,
3863
4330
  missing: bound.missing
@@ -3865,7 +4332,7 @@ async function resolveBlockRange(source, def, opts) {
3865
4332
  throw new Error(`Reindex stalled: missing decoder checkpoint (${bound.missing.join(", ")})`);
3866
4333
  }
3867
4334
  if (bound.floor !== null && bound.floor < rawTip) {
3868
- logger10.debug("Reindex tip bounded by decoder progress", {
4335
+ logger11.debug("Reindex tip bounded by decoder progress", {
3869
4336
  event: "subgraph_reindex_decoder_floor",
3870
4337
  subgraph: def.name,
3871
4338
  rawTip,
@@ -3880,16 +4347,16 @@ async function clearReindexMetadata(db, subgraphName) {
3880
4347
  }
3881
4348
  async function reindexSubgraph(def, opts) {
3882
4349
  const source = resolveBlockSource(def);
3883
- const targetDb = getTargetDb3();
4350
+ const targetDb = getTargetDb4();
3884
4351
  const client = getRawClient("target");
3885
4352
  const subgraphName = def.name;
3886
4353
  const schemaName = opts?.schemaName ?? pgSchemaName(subgraphName);
3887
4354
  await updateSubgraphStatus2(targetDb, subgraphName, "reindexing");
3888
- logger10.info("Reindex starting", { subgraph: subgraphName });
4355
+ logger11.info("Reindex starting", { subgraph: subgraphName });
3889
4356
  try {
3890
4357
  const { fromBlock, toBlock } = await resolveBlockRange(source, def, opts);
3891
4358
  if (fromBlock > toBlock) {
3892
- logger10.info("No blocks to reindex", {
4359
+ logger11.info("No blocks to reindex", {
3893
4360
  subgraph: subgraphName,
3894
4361
  fromBlock,
3895
4362
  toBlock
@@ -3903,7 +4370,7 @@ async function reindexSubgraph(def, opts) {
3903
4370
  await client.unsafe(stmt);
3904
4371
  }
3905
4372
  await updateSubgraphStatus2(targetDb, subgraphName, "reindexing", Math.max(0, fromBlock - 1));
3906
- logger10.info("Schema recreated for reindex", {
4373
+ logger11.info("Schema recreated for reindex", {
3907
4374
  subgraph: subgraphName,
3908
4375
  cursorResetTo: Math.max(0, fromBlock - 1)
3909
4376
  });
@@ -3917,7 +4384,7 @@ async function reindexSubgraph(def, opts) {
3917
4384
  last_error_at: null,
3918
4385
  updated_at: new Date
3919
4386
  }).where("name", "=", subgraphName).execute();
3920
- logger10.info("Reindexing blocks", {
4387
+ logger11.info("Reindexing blocks", {
3921
4388
  subgraph: subgraphName,
3922
4389
  fromBlock,
3923
4390
  toBlock,
@@ -3939,9 +4406,9 @@ async function reindexSubgraph(def, opts) {
3939
4406
  if (reason === "user-cancelled") {
3940
4407
  await updateSubgraphStatus2(targetDb, subgraphName, "active");
3941
4408
  await clearReindexMetadata(targetDb, subgraphName);
3942
- logger10.info("Reindex cancelled by user", { subgraph: subgraphName });
4409
+ logger11.info("Reindex cancelled by user", { subgraph: subgraphName });
3943
4410
  } else {
3944
- logger10.info("Reindex interrupted by shutdown, will resume", {
4411
+ logger11.info("Reindex interrupted by shutdown, will resume", {
3945
4412
  subgraph: subgraphName
3946
4413
  });
3947
4414
  }
@@ -3949,7 +4416,7 @@ async function reindexSubgraph(def, opts) {
3949
4416
  }
3950
4417
  await updateSubgraphStatus2(targetDb, subgraphName, "active", toBlock);
3951
4418
  await clearReindexMetadata(targetDb, subgraphName);
3952
- logger10.info("Reindex complete", {
4419
+ logger11.info("Reindex complete", {
3953
4420
  subgraph: subgraphName,
3954
4421
  blocks: result.blocksProcessed,
3955
4422
  events: result.totalEventsProcessed,
@@ -3962,7 +4429,7 @@ async function reindexSubgraph(def, opts) {
3962
4429
  });
3963
4430
  return { processed: result.blocksProcessed };
3964
4431
  } catch (err) {
3965
- logger10.error("Reindex failed", {
4432
+ logger11.error("Reindex failed", {
3966
4433
  subgraph: subgraphName,
3967
4434
  error: getErrorMessage3(err)
3968
4435
  });
@@ -3971,7 +4438,7 @@ async function reindexSubgraph(def, opts) {
3971
4438
  }
3972
4439
  }
3973
4440
  async function resumeReindex(def, opts) {
3974
- const targetDb = getTargetDb3();
4441
+ const targetDb = getTargetDb4();
3975
4442
  const subgraphName = def.name;
3976
4443
  const row = await targetDb.selectFrom("subgraphs").select([
3977
4444
  "id",
@@ -3984,7 +4451,7 @@ async function resumeReindex(def, opts) {
3984
4451
  const fromBlock = resolveReindexResumeBlock(row);
3985
4452
  const toBlock = Number(row.reindex_to_block);
3986
4453
  if (fromBlock == null) {
3987
- logger10.info("No reindex metadata, starting fresh reindex", {
4454
+ logger11.info("No reindex metadata, starting fresh reindex", {
3988
4455
  subgraph: subgraphName
3989
4456
  });
3990
4457
  return reindexSubgraph(def, {
@@ -3993,12 +4460,12 @@ async function resumeReindex(def, opts) {
3993
4460
  });
3994
4461
  }
3995
4462
  if (fromBlock > toBlock) {
3996
- logger10.info("Resume: no remaining blocks", { subgraph: subgraphName });
4463
+ logger11.info("Resume: no remaining blocks", { subgraph: subgraphName });
3997
4464
  await updateSubgraphStatus2(targetDb, subgraphName, "active", toBlock);
3998
4465
  await clearReindexMetadata(targetDb, subgraphName);
3999
4466
  return { processed: 0 };
4000
4467
  }
4001
- logger10.info("Resuming reindex", {
4468
+ logger11.info("Resuming reindex", {
4002
4469
  subgraph: subgraphName,
4003
4470
  fromBlock,
4004
4471
  toBlock,
@@ -4020,9 +4487,9 @@ async function resumeReindex(def, opts) {
4020
4487
  if (reason === "user-cancelled") {
4021
4488
  await updateSubgraphStatus2(targetDb, subgraphName, "active");
4022
4489
  await clearReindexMetadata(targetDb, subgraphName);
4023
- logger10.info("Resume cancelled by user", { subgraph: subgraphName });
4490
+ logger11.info("Resume cancelled by user", { subgraph: subgraphName });
4024
4491
  } else {
4025
- logger10.info("Resume interrupted by shutdown, will resume again", {
4492
+ logger11.info("Resume interrupted by shutdown, will resume again", {
4026
4493
  subgraph: subgraphName
4027
4494
  });
4028
4495
  }
@@ -4030,13 +4497,13 @@ async function resumeReindex(def, opts) {
4030
4497
  }
4031
4498
  await updateSubgraphStatus2(targetDb, subgraphName, "active", toBlock);
4032
4499
  await clearReindexMetadata(targetDb, subgraphName);
4033
- logger10.info("Resumed reindex complete", {
4500
+ logger11.info("Resumed reindex complete", {
4034
4501
  subgraph: subgraphName,
4035
4502
  blocks: result.blocksProcessed
4036
4503
  });
4037
4504
  return { processed: result.blocksProcessed };
4038
4505
  } catch (err) {
4039
- logger10.error("Resumed reindex failed", {
4506
+ logger11.error("Resumed reindex failed", {
4040
4507
  subgraph: subgraphName,
4041
4508
  error: getErrorMessage3(err)
4042
4509
  });
@@ -4045,9 +4512,9 @@ async function resumeReindex(def, opts) {
4045
4512
  }
4046
4513
  }
4047
4514
  async function backfillSubgraph(def, opts) {
4048
- const targetDb = getTargetDb3();
4515
+ const targetDb = getTargetDb4();
4049
4516
  const subgraphName = def.name;
4050
- logger10.info("Backfill starting", {
4517
+ logger11.info("Backfill starting", {
4051
4518
  subgraph: subgraphName,
4052
4519
  from: opts.fromBlock,
4053
4520
  to: opts.toBlock
@@ -4065,17 +4532,17 @@ async function backfillSubgraph(def, opts) {
4065
4532
  signal: opts.signal
4066
4533
  });
4067
4534
  if (result.aborted) {
4068
- logger10.info("Backfill aborted", { subgraph: subgraphName });
4535
+ logger11.info("Backfill aborted", { subgraph: subgraphName });
4069
4536
  return { processed: result.blocksProcessed };
4070
4537
  }
4071
4538
  const resolved = await resolveGaps(targetDb, subgraphName, opts.fromBlock, opts.toBlock).catch(() => 0);
4072
4539
  if (resolved > 0) {
4073
- logger10.info("Resolved subgraph gaps via backfill", {
4540
+ logger11.info("Resolved subgraph gaps via backfill", {
4074
4541
  subgraph: subgraphName,
4075
4542
  resolved
4076
4543
  });
4077
4544
  }
4078
- logger10.info("Backfill complete", {
4545
+ logger11.info("Backfill complete", {
4079
4546
  subgraph: subgraphName,
4080
4547
  blocks: result.blocksProcessed,
4081
4548
  events: result.totalEventsProcessed,
@@ -4083,7 +4550,7 @@ async function backfillSubgraph(def, opts) {
4083
4550
  });
4084
4551
  return { processed: result.blocksProcessed };
4085
4552
  } catch (err) {
4086
- logger10.error("Backfill failed", {
4553
+ logger11.error("Backfill failed", {
4087
4554
  subgraph: subgraphName,
4088
4555
  error: getErrorMessage3(err)
4089
4556
  });
@@ -4093,16 +4560,16 @@ async function backfillSubgraph(def, opts) {
4093
4560
 
4094
4561
  // src/runtime/reorg.ts
4095
4562
  import { getErrorMessage as getErrorMessage4 } from "@secondlayer/shared";
4096
- import { getRawClient as getRawClient2, getTargetDb as getTargetDb4 } from "@secondlayer/shared/db";
4563
+ import { getRawClient as getRawClient2, getTargetDb as getTargetDb5 } from "@secondlayer/shared/db";
4097
4564
  import { listSubgraphs } from "@secondlayer/shared/db/queries/subgraphs";
4098
- import { logger as logger11 } from "@secondlayer/shared/logger";
4099
- import { sql as sql5 } from "kysely";
4565
+ import { logger as logger12 } from "@secondlayer/shared/logger";
4566
+ import { sql as sql6 } from "kysely";
4100
4567
  async function handleSubgraphReorg(blockHeight, loadSubgraphDef) {
4101
- const targetDb = getTargetDb4();
4568
+ const targetDb = getTargetDb5();
4102
4569
  const activeSubgraphs = (await listSubgraphs(targetDb)).filter((v) => v.status === "active");
4103
4570
  if (activeSubgraphs.length === 0)
4104
4571
  return;
4105
- logger11.info("Propagating reorg to subgraphs", {
4572
+ logger12.info("Propagating reorg to subgraphs", {
4106
4573
  blockHeight,
4107
4574
  subgraphCount: activeSubgraphs.length
4108
4575
  });
@@ -4113,13 +4580,13 @@ async function handleSubgraphReorg(blockHeight, loadSubgraphDef) {
4113
4580
  continue;
4114
4581
  bumpReorgEpoch(sg.name);
4115
4582
  await withSubgraphBlockLock(sg.name, async () => {
4116
- await getTargetDb4().transaction().execute(async (lockTx) => {
4117
- await sql5`SELECT pg_advisory_xact_lock(hashtext(${`subgraph-reorg:${sg.name}`}))`.execute(lockTx);
4583
+ await getTargetDb5().transaction().execute(async (lockTx) => {
4584
+ await sql6`SELECT pg_advisory_xact_lock(hashtext(${`subgraph-reorg:${sg.name}`}))`.execute(lockTx);
4118
4585
  await reorgOneSubgraph(sg, schema, blockHeight, loadSubgraphDef);
4119
4586
  });
4120
4587
  });
4121
4588
  } catch (err) {
4122
- logger11.error("Subgraph reorg handling failed", {
4589
+ logger12.error("Subgraph reorg handling failed", {
4123
4590
  subgraph: sg.name,
4124
4591
  blockHeight,
4125
4592
  error: getErrorMessage4(err)
@@ -4128,7 +4595,7 @@ async function handleSubgraphReorg(blockHeight, loadSubgraphDef) {
4128
4595
  }
4129
4596
  }
4130
4597
  async function reorgOneSubgraph(sg, schema, blockHeight, loadSubgraphDef) {
4131
- const targetDb = getTargetDb4();
4598
+ const targetDb = getTargetDb5();
4132
4599
  const schemaName = sg.schema_name ?? pgSchemaName(sg.name);
4133
4600
  const client = getRawClient2("target");
4134
4601
  const tableNames = Object.keys(schema);
@@ -4157,7 +4624,7 @@ async function reorgOneSubgraph(sg, schema, blockHeight, loadSubgraphDef) {
4157
4624
  await tx.unsafe(`DELETE FROM "${schemaName}"."_journal" WHERE block_height >= $1`, [blockHeight]);
4158
4625
  });
4159
4626
  } else {
4160
- logger11.warn("Subgraph has no revert journal — falling back to height delete (accumulator rows may lose history)", { subgraph: sg.name, blockHeight });
4627
+ logger12.warn("Subgraph has no revert journal — falling back to height delete (accumulator rows may lose history)", { subgraph: sg.name, blockHeight });
4161
4628
  for (const tableName of tableNames) {
4162
4629
  await client.unsafe(`DELETE FROM "${schemaName}"."${tableName}" WHERE "_block_height" >= $1`, [blockHeight]);
4163
4630
  }
@@ -4167,6 +4634,7 @@ async function reorgOneSubgraph(sg, schema, blockHeight, loadSubgraphDef) {
4167
4634
  DELETE FROM "${schemaName}"."_factory_addresses" WHERE block_height >= ${Number(blockHeight)};
4168
4635
  END IF;
4169
4636
  END $$`);
4637
+ await targetDb.deleteFrom("subgraph_violations").where("subgraph_name", "=", sg.name).where("block_height", ">=", blockHeight).execute();
4170
4638
  for (const [tableName, rows] of Object.entries(revertedByTable)) {
4171
4639
  if (rows.length === 0)
4172
4640
  continue;
@@ -4200,7 +4668,7 @@ async function reorgOneSubgraph(sg, schema, blockHeight, loadSubgraphDef) {
4200
4668
  }).as("row_pk"),
4201
4669
  eb2.val("pending").as("status")
4202
4670
  ]).where("subgraph_name", "=", sg.name).where("table_name", "=", tableName).where("status", "=", "active")).onConflict((oc) => oc.columns(["subscription_id", "dedup_key"]).doNothing()).execute().catch((err) => {
4203
- logger11.error("Failed to emit revert event for subscriptions", {
4671
+ logger12.error("Failed to emit revert event for subscriptions", {
4204
4672
  event: "reorg_revert_emit_dropped",
4205
4673
  subgraph: sg.name,
4206
4674
  table: tableName,
@@ -4209,14 +4677,14 @@ async function reorgOneSubgraph(sg, schema, blockHeight, loadSubgraphDef) {
4209
4677
  });
4210
4678
  });
4211
4679
  }
4212
- logger11.info("Subgraph reorg cleanup done", {
4680
+ logger12.info("Subgraph reorg cleanup done", {
4213
4681
  subgraph: sg.name,
4214
4682
  blockHeight,
4215
4683
  tablesAffected: Object.keys(revertedByTable).length
4216
4684
  });
4217
4685
  const def = await loadSubgraphDef(sg);
4218
4686
  await processBlock(def, sg.name, blockHeight, { reorgRewind: true });
4219
- logger11.info("Subgraph reorg reprocessed", {
4687
+ logger12.info("Subgraph reorg reprocessed", {
4220
4688
  subgraph: sg.name,
4221
4689
  blockHeight
4222
4690
  });
@@ -4228,7 +4696,7 @@ import { hostname } from "node:os";
4228
4696
  import { resolve } from "node:path";
4229
4697
  import { pathToFileURL } from "node:url";
4230
4698
  import { getErrorMessage as getErrorMessage6 } from "@secondlayer/shared";
4231
- import { getTargetDb as getTargetDb5 } from "@secondlayer/shared/db";
4699
+ import { getTargetDb as getTargetDb6 } from "@secondlayer/shared/db";
4232
4700
  import {
4233
4701
  cancelSubgraphOperation,
4234
4702
  claimSubgraphOperation,
@@ -4245,7 +4713,7 @@ import {
4245
4713
  pgSchemaName as pgSchemaName2,
4246
4714
  updateSubgraphStatus as updateSubgraphStatus3
4247
4715
  } from "@secondlayer/shared/db/queries/subgraphs";
4248
- import { logger as logger13 } from "@secondlayer/shared/logger";
4716
+ import { logger as logger14 } from "@secondlayer/shared/logger";
4249
4717
  import {
4250
4718
  listen,
4251
4719
  sourceListenerUrl,
@@ -4257,7 +4725,7 @@ import { getErrorMessage as getErrorMessage5 } from "@secondlayer/shared";
4257
4725
  import {
4258
4726
  createInternalIndexHttpClient as createInternalIndexHttpClient2
4259
4727
  } from "@secondlayer/shared/index-http";
4260
- import { logger as logger12 } from "@secondlayer/shared/logger";
4728
+ import { logger as logger13 } from "@secondlayer/shared/logger";
4261
4729
  var POLL_MS = Number(process.env.SUBGRAPH_REORG_POLL_MS) || 15000;
4262
4730
  var STARTUP_MARGIN_MS = 60 * 60 * 1000;
4263
4731
  async function pollReorgsOnce(http2, cursor, onReorg, handled) {
@@ -4265,13 +4733,13 @@ async function pollReorgsOnce(http2, cursor, onReorg, handled) {
4265
4733
  const sorted = [...reorgs].sort((a, b) => a.fork_point_height - b.fork_point_height);
4266
4734
  for (const r of sorted) {
4267
4735
  if (handled && r.id && handled.has(r.id)) {
4268
- logger12.info("Streams reorg already handled — skipping", {
4736
+ logger13.info("Streams reorg already handled — skipping", {
4269
4737
  forkPointHeight: r.fork_point_height,
4270
4738
  reorgId: r.id
4271
4739
  });
4272
4740
  continue;
4273
4741
  }
4274
- logger12.info("Streams reorg — rewinding", {
4742
+ logger13.info("Streams reorg — rewinding", {
4275
4743
  forkPointHeight: r.fork_point_height
4276
4744
  });
4277
4745
  await onReorg(r.fork_point_height);
@@ -4294,7 +4762,7 @@ function startStreamsReorgPoll(onReorg) {
4294
4762
  try {
4295
4763
  since = await pollReorgsOnce(http2, since, onReorg, handled);
4296
4764
  } catch (err) {
4297
- logger12.error("Streams reorg poll failed", {
4765
+ logger13.error("Streams reorg poll failed", {
4298
4766
  error: getErrorMessage5(err)
4299
4767
  });
4300
4768
  }
@@ -4302,7 +4770,7 @@ function startStreamsReorgPoll(onReorg) {
4302
4770
  timer = setTimeout(tick, POLL_MS);
4303
4771
  };
4304
4772
  timer = setTimeout(tick, POLL_MS);
4305
- logger12.info("Streams reorg poll started", { pollMs: POLL_MS });
4773
+ logger13.info("Streams reorg poll started", { pollMs: POLL_MS });
4306
4774
  return () => {
4307
4775
  running = false;
4308
4776
  if (timer)
@@ -4333,7 +4801,7 @@ async function catchUpAll(subgraphs, db, concurrency) {
4333
4801
  if (isHandlerNotFoundError(err)) {
4334
4802
  await updateSubgraphStatus3(db, sg.name, "error");
4335
4803
  }
4336
- logger13.error("Subgraph catch-up failed", {
4804
+ logger14.error("Subgraph catch-up failed", {
4337
4805
  subgraph: sg.name,
4338
4806
  error: msg
4339
4807
  });
@@ -4373,7 +4841,7 @@ async function loadSubgraphDefinition(sg) {
4373
4841
  definitionCache.set(sg.name, def);
4374
4842
  if (prevVersion && prevVersion !== sg.version) {
4375
4843
  invalidateSubgraphRoute(sg.name);
4376
- logger13.info("Subgraph handler reloaded", {
4844
+ logger14.info("Subgraph handler reloaded", {
4377
4845
  subgraph: sg.name,
4378
4846
  from: prevVersion,
4379
4847
  to: sg.version
@@ -4392,7 +4860,7 @@ function cleanupCaches(active) {
4392
4860
  }
4393
4861
  }
4394
4862
  async function synthesizeLegacyReindexOperations() {
4395
- const db = getTargetDb5();
4863
+ const db = getTargetDb6();
4396
4864
  const stale = (await listSubgraphs2(db)).filter((sg) => sg.status === "reindexing");
4397
4865
  for (const sg of stale) {
4398
4866
  const active = await findActiveSubgraphOperation(db, sg.id);
@@ -4407,7 +4875,7 @@ async function synthesizeLegacyReindexOperations() {
4407
4875
  fromBlock: sg.reindex_from_block == null ? undefined : Number(sg.reindex_from_block),
4408
4876
  toBlock: sg.reindex_to_block == null ? undefined : Number(sg.reindex_to_block)
4409
4877
  });
4410
- logger13.info("Queued legacy reindex resume operation", {
4878
+ logger14.info("Queued legacy reindex resume operation", {
4411
4879
  subgraph: sg.name
4412
4880
  });
4413
4881
  } catch (err) {
@@ -4421,7 +4889,7 @@ async function runSubgraphOperation(operation, signal) {
4421
4889
  if (operation.cancel_requested) {
4422
4890
  return 0;
4423
4891
  }
4424
- const db = getTargetDb5();
4892
+ const db = getTargetDb6();
4425
4893
  const subgraph = await db.selectFrom("subgraphs").selectAll().where("id", "=", operation.subgraph_id).executeTakeFirst();
4426
4894
  if (!subgraph)
4427
4895
  throw new Error(`Subgraph not found: ${operation.subgraph_id}`);
@@ -4460,13 +4928,13 @@ async function runSubgraphOperation(operation, signal) {
4460
4928
  }
4461
4929
  async function startSubgraphOperationRunner(opts) {
4462
4930
  const concurrency = opts?.concurrency ?? DEFAULT_OPERATION_CONCURRENCY;
4463
- const db = getTargetDb5();
4931
+ const db = getTargetDb6();
4464
4932
  const lockedBy = `${hostname()}:${process.pid}:${randomUUID()}`;
4465
4933
  const active = new Map;
4466
4934
  const activeRuns = new Map;
4467
4935
  let running = true;
4468
4936
  let draining = false;
4469
- logger13.info("Starting subgraph operation runner", { concurrency, lockedBy });
4937
+ logger14.info("Starting subgraph operation runner", { concurrency, lockedBy });
4470
4938
  try {
4471
4939
  const stranded = await db.selectFrom("subgraphs").select(["id", "name", "account_id"]).where("status", "=", "reindexing").where("reindex_from_block", "is not", null).where("reindex_to_block", "is not", null).where(({ not, exists, selectFrom }) => not(exists(selectFrom("subgraph_operations").select("id").whereRef("subgraph_id", "=", "subgraphs.id").where("status", "in", ["queued", "running"])))).execute();
4472
4940
  for (const row of stranded) {
@@ -4477,14 +4945,14 @@ async function startSubgraphOperationRunner(opts) {
4477
4945
  accountId: row.account_id,
4478
4946
  kind: "reindex"
4479
4947
  });
4480
- logger13.info("Re-enqueued stranded reindex", { subgraph: row.name });
4948
+ logger14.info("Re-enqueued stranded reindex", { subgraph: row.name });
4481
4949
  } catch (err) {
4482
4950
  if (!isActiveSubgraphOperationConflict(err))
4483
4951
  throw err;
4484
4952
  }
4485
4953
  }
4486
4954
  } catch (err) {
4487
- logger13.warn("Stranded-reindex sweep failed", {
4955
+ logger14.warn("Stranded-reindex sweep failed", {
4488
4956
  error: getErrorMessage6(err)
4489
4957
  });
4490
4958
  }
@@ -4495,7 +4963,7 @@ async function startSubgraphOperationRunner(opts) {
4495
4963
  if (!running)
4496
4964
  return;
4497
4965
  heartbeatSubgraphOperation(db, operation.id, lockedBy).catch((err) => {
4498
- logger13.warn("Subgraph operation heartbeat failed", {
4966
+ logger14.warn("Subgraph operation heartbeat failed", {
4499
4967
  operationId: operation.id,
4500
4968
  error: getErrorMessage6(err)
4501
4969
  });
@@ -4507,7 +4975,7 @@ async function startSubgraphOperationRunner(opts) {
4507
4975
  controller.abort("user-cancelled");
4508
4976
  }
4509
4977
  }).catch((err) => {
4510
- logger13.warn("Subgraph operation cancel poll failed", {
4978
+ logger14.warn("Subgraph operation cancel poll failed", {
4511
4979
  operationId: operation.id,
4512
4980
  error: getErrorMessage6(err)
4513
4981
  });
@@ -4524,14 +4992,14 @@ async function startSubgraphOperationRunner(opts) {
4524
4992
  const reason = String(controller.signal.reason ?? "");
4525
4993
  if (controller.signal.aborted && reason === "user-cancelled") {
4526
4994
  await cancelSubgraphOperation(db, operation.id, lockedBy, processed);
4527
- logger13.info("Subgraph operation cancelled", {
4995
+ logger14.info("Subgraph operation cancelled", {
4528
4996
  operationId: operation.id,
4529
4997
  subgraph: operation.subgraph_name
4530
4998
  });
4531
4999
  return;
4532
5000
  }
4533
5001
  if (controller.signal.aborted) {
4534
- logger13.info("Subgraph operation interrupted", {
5002
+ logger14.info("Subgraph operation interrupted", {
4535
5003
  operationId: operation.id,
4536
5004
  subgraph: operation.subgraph_name,
4537
5005
  reason
@@ -4539,7 +5007,7 @@ async function startSubgraphOperationRunner(opts) {
4539
5007
  return;
4540
5008
  }
4541
5009
  await completeSubgraphOperation(db, operation.id, lockedBy, processed);
4542
- logger13.info("Subgraph operation completed", {
5010
+ logger14.info("Subgraph operation completed", {
4543
5011
  operationId: operation.id,
4544
5012
  subgraph: operation.subgraph_name,
4545
5013
  processed
@@ -4547,7 +5015,7 @@ async function startSubgraphOperationRunner(opts) {
4547
5015
  } catch (err) {
4548
5016
  const reason = String(controller.signal.reason ?? "");
4549
5017
  if (controller.signal.aborted && reason === "shutdown") {
4550
- logger13.info("Subgraph operation interrupted by shutdown", {
5018
+ logger14.info("Subgraph operation interrupted by shutdown", {
4551
5019
  operationId: operation.id,
4552
5020
  subgraph: operation.subgraph_name
4553
5021
  });
@@ -4558,7 +5026,7 @@ async function startSubgraphOperationRunner(opts) {
4558
5026
  return;
4559
5027
  }
4560
5028
  await failSubgraphOperation(db, operation.id, lockedBy, getErrorMessage6(err), processed);
4561
- logger13.error("Subgraph operation failed", {
5029
+ logger14.error("Subgraph operation failed", {
4562
5030
  operationId: operation.id,
4563
5031
  subgraph: operation.subgraph_name,
4564
5032
  error: getErrorMessage6(err)
@@ -4605,20 +5073,20 @@ async function startSubgraphOperationRunner(opts) {
4605
5073
  controller.abort("shutdown");
4606
5074
  }
4607
5075
  await Promise.allSettled(activeRuns.values());
4608
- logger13.info("Subgraph operation runner stopped");
5076
+ logger14.info("Subgraph operation runner stopped");
4609
5077
  };
4610
5078
  }
4611
5079
  async function startSubgraphProcessor(opts) {
4612
5080
  const concurrency = opts?.concurrency ?? DEFAULT_CONCURRENCY;
4613
5081
  let running = true;
4614
- logger13.info("Starting subgraph processor", { concurrency });
5082
+ logger14.info("Starting subgraph processor", { concurrency });
4615
5083
  const stopOperations = await startSubgraphOperationRunner({
4616
5084
  concurrency: Number.parseInt(process.env.SUBGRAPH_OPERATION_CONCURRENCY ?? String(DEFAULT_OPERATION_CONCURRENCY))
4617
5085
  });
4618
5086
  const runCatchUp = async () => {
4619
5087
  if (!running || !isCatchUpLeader())
4620
5088
  return;
4621
- const db = getTargetDb5();
5089
+ const db = getTargetDb6();
4622
5090
  const subgraphs = (await listSubgraphs2(db)).filter((v) => v.status === "active");
4623
5091
  cleanupCaches(subgraphs);
4624
5092
  await catchUpAll(subgraphs, db, concurrency);
@@ -4637,7 +5105,7 @@ async function startSubgraphProcessor(opts) {
4637
5105
  await handleSubgraphReorg(blockHeight, loadSubgraphDefinition);
4638
5106
  }
4639
5107
  } catch (err) {
4640
- logger13.error("Subgraph reorg handling failed", {
5108
+ logger14.error("Subgraph reorg handling failed", {
4641
5109
  error: getErrorMessage6(err)
4642
5110
  });
4643
5111
  }
@@ -4646,7 +5114,7 @@ async function startSubgraphProcessor(opts) {
4646
5114
  runCatchUp();
4647
5115
  }, POLL_INTERVAL_MS);
4648
5116
  const stopStreamsReorgPoll = process.env.SUBGRAPH_SOURCE === "streams-index" ? startStreamsReorgPoll((forkHeight) => handleSubgraphReorg(forkHeight, loadSubgraphDefinition)) : undefined;
4649
- logger13.info("Subgraph processor ready");
5117
+ logger14.info("Subgraph processor ready");
4650
5118
  return async () => {
4651
5119
  running = false;
4652
5120
  clearInterval(pollInterval);
@@ -4655,7 +5123,7 @@ async function startSubgraphProcessor(opts) {
4655
5123
  await stopReorgListening();
4656
5124
  stopStreamsReorgPoll?.();
4657
5125
  await stopOperations();
4658
- logger13.info("Subgraph processor stopped");
5126
+ logger14.info("Subgraph processor stopped");
4659
5127
  };
4660
5128
  }
4661
5129
  export {
@@ -4663,5 +5131,5 @@ export {
4663
5131
  startSubgraphOperationRunner
4664
5132
  };
4665
5133
 
4666
- //# debugId=0FA31971C86CC61564756E2164756E21
5134
+ //# debugId=9AD13E77BAE4758164756E2164756E21
4667
5135
  //# sourceMappingURL=processor.js.map