@secondlayer/subgraphs 4.1.1 → 4.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/src/index.js CHANGED
@@ -2290,6 +2290,8 @@ function indexEventTypesForFilterTypes(filterTypes) {
2290
2290
  return [...types];
2291
2291
  }
2292
2292
  function referencedIndexEventTypes(subgraph) {
2293
+ if (!subgraph.sources)
2294
+ return [];
2293
2295
  return indexEventTypesForFilterTypes(sourceFilters(subgraph).map((f) => f.type));
2294
2296
  }
2295
2297
  function isStreamsIndexEligible(subgraph) {
@@ -3407,6 +3409,63 @@ function startCatchUpLeader(opts = {}) {
3407
3409
  });
3408
3410
  }
3409
3411
 
3412
+ // src/runtime/decoder-bound.ts
3413
+ import { decodeStreamsCursor, isEmptyRangeCursor } from "@secondlayer/shared";
3414
+ import { getSourceDb as getSourceDb3 } from "@secondlayer/shared/db";
3415
+ function decoderNameForEventType(indexEventType) {
3416
+ return `decode.${indexEventType}.v1`;
3417
+ }
3418
+ function decoderNamesForIndexEventTypes(eventTypes) {
3419
+ return eventTypes.map(decoderNameForEventType);
3420
+ }
3421
+ function decoderNamesForSubgraph(subgraph) {
3422
+ return decoderNamesForIndexEventTypes(referencedIndexEventTypes(subgraph));
3423
+ }
3424
+ function committedHeight(cursor) {
3425
+ if (!cursor)
3426
+ return null;
3427
+ try {
3428
+ const decoded = decodeStreamsCursor(cursor);
3429
+ if (isEmptyRangeCursor(decoded))
3430
+ return decoded.block_height;
3431
+ return Math.max(0, decoded.block_height - 1);
3432
+ } catch {
3433
+ return null;
3434
+ }
3435
+ }
3436
+ async function decoderBoundTip(decoderNames, opts) {
3437
+ if (decoderNames.length === 0)
3438
+ return { kind: "unbounded" };
3439
+ const sourceDb = opts?.sourceDb ?? getSourceDb3();
3440
+ const rows = await sourceDb.selectFrom("decoder_checkpoints").select(["decoder_name", "last_cursor"]).where("decoder_name", "in", decoderNames).execute();
3441
+ const byName = new Map(rows.map((r) => [r.decoder_name, r.last_cursor]));
3442
+ const missing = [];
3443
+ const heights = [];
3444
+ for (const name of decoderNames) {
3445
+ const height = committedHeight(byName.get(name));
3446
+ if (height === null)
3447
+ missing.push(name);
3448
+ else
3449
+ heights.push(height);
3450
+ }
3451
+ if (missing.length > 0)
3452
+ return { kind: "stall", missing };
3453
+ return { kind: "height", height: Math.min(...heights) };
3454
+ }
3455
+ async function boundSourceTip(rawTip, decoderNames, opts) {
3456
+ const bound = await decoderBoundTip(decoderNames, opts);
3457
+ if (bound.kind === "stall")
3458
+ return { ok: false, missing: bound.missing };
3459
+ if (bound.kind === "unbounded") {
3460
+ return { ok: true, tip: rawTip, floor: null };
3461
+ }
3462
+ return {
3463
+ ok: true,
3464
+ tip: Math.min(rawTip, bound.height),
3465
+ floor: bound.height
3466
+ };
3467
+ }
3468
+
3410
3469
  // src/runtime/catchup.ts
3411
3470
  var LOG_INTERVAL = 1000;
3412
3471
  var STANDARD_CATCHUP_BATCH_CONFIG = {
@@ -3517,7 +3576,28 @@ async function catchUpSubgraph(subgraph, subgraphName) {
3517
3576
  if (!subgraphRow)
3518
3577
  return 0;
3519
3578
  const lastProcessedBlock = Number(subgraphRow.last_processed_block);
3520
- const chainTip = await source.getTip();
3579
+ const rawTip = await source.getTip();
3580
+ if (rawTip <= 0)
3581
+ return 0;
3582
+ const bound = await boundSourceTip(rawTip, decoderNamesForSubgraph(subgraph));
3583
+ if (!bound.ok) {
3584
+ logger8.warn("Subgraph catch-up stalled: missing decoder checkpoint", {
3585
+ event: "subgraph_catchup_decoder_stall",
3586
+ subgraph: subgraphName,
3587
+ missing: bound.missing
3588
+ });
3589
+ return 0;
3590
+ }
3591
+ if (bound.floor !== null && bound.floor < rawTip) {
3592
+ logger8.debug("Subgraph catch-up tip bounded by decoder progress", {
3593
+ event: "subgraph_catchup_decoder_floor",
3594
+ subgraph: subgraphName,
3595
+ rawTip,
3596
+ floor: bound.floor,
3597
+ tip: bound.tip
3598
+ });
3599
+ }
3600
+ const chainTip = bound.tip;
3521
3601
  if (chainTip <= 0 || lastProcessedBlock >= chainTip)
3522
3602
  return 0;
3523
3603
  const subgraphStart = Number(subgraphRow.start_block) || 1;
@@ -3980,7 +4060,26 @@ async function processBlockRange(def, opts) {
3980
4060
  async function resolveBlockRange(source, def, opts) {
3981
4061
  const definitionStart = def.startBlock ?? 1;
3982
4062
  const fromBlock = opts?.startBlockFloor != null ? Math.max(definitionStart, opts.startBlockFloor) : definitionStart;
3983
- return { fromBlock, toBlock: await source.getTip() };
4063
+ const rawTip = await source.getTip();
4064
+ const bound = await boundSourceTip(rawTip, decoderNamesForSubgraph(def));
4065
+ if (!bound.ok) {
4066
+ logger10.warn("Reindex stalled: missing decoder checkpoint", {
4067
+ event: "subgraph_reindex_decoder_stall",
4068
+ subgraph: def.name,
4069
+ missing: bound.missing
4070
+ });
4071
+ throw new Error(`Reindex stalled: missing decoder checkpoint (${bound.missing.join(", ")})`);
4072
+ }
4073
+ if (bound.floor !== null && bound.floor < rawTip) {
4074
+ logger10.debug("Reindex tip bounded by decoder progress", {
4075
+ event: "subgraph_reindex_decoder_floor",
4076
+ subgraph: def.name,
4077
+ rawTip,
4078
+ floor: bound.floor,
4079
+ tip: bound.tip
4080
+ });
4081
+ }
4082
+ return { fromBlock, toBlock: bound.tip };
3984
4083
  }
3985
4084
  async function clearReindexMetadata(db, subgraphName) {
3986
4085
  await db.updateTable("subgraphs").set({ reindex_from_block: null, reindex_to_block: null }).where("name", "=", subgraphName).execute();
@@ -5438,5 +5537,5 @@ export {
5438
5537
  ChainReadError
5439
5538
  };
5440
5539
 
5441
- //# debugId=E71414D5C2925EDA64756E2164756E21
5540
+ //# debugId=514A04CE6E76132964756E2164756E21
5442
5541
  //# sourceMappingURL=index.js.map