@secondlayer/subgraphs 3.17.0 → 3.19.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.
@@ -2302,7 +2302,7 @@ class StatsAccumulator {
2302
2302
  }
2303
2303
 
2304
2304
  // src/runtime/reindex.ts
2305
- import { getErrorMessage as getErrorMessage2 } from "@secondlayer/shared";
2305
+ import { getErrorMessage as getErrorMessage3 } from "@secondlayer/shared";
2306
2306
  import { getRawClient, getTargetDb as getTargetDb2 } from "@secondlayer/shared/db";
2307
2307
  import {
2308
2308
  recordGapBatch,
@@ -2316,7 +2316,58 @@ import {
2316
2316
  recordSubgraphProcessed as recordSubgraphProcessed2,
2317
2317
  updateSubgraphStatus as updateSubgraphStatus2
2318
2318
  } from "@secondlayer/shared/db/queries/subgraphs";
2319
+ import { logger as logger7 } from "@secondlayer/shared/logger";
2320
+
2321
+ // src/runtime/reindex-notify.ts
2322
+ import { getErrorMessage as getErrorMessage2 } from "@secondlayer/shared";
2319
2323
  import { logger as logger6 } from "@secondlayer/shared/logger";
2324
+ async function notifyReindexComplete(db, subgraphName, stats) {
2325
+ try {
2326
+ const subgraph = await db.selectFrom("subgraphs").select(["account_id"]).where("name", "=", subgraphName).executeTakeFirst();
2327
+ if (!subgraph)
2328
+ return;
2329
+ const account = await db.selectFrom("accounts").select(["email", "notify_reindex_complete"]).where("id", "=", subgraph.account_id).executeTakeFirst();
2330
+ if (!account?.email || !account.notify_reindex_complete)
2331
+ return;
2332
+ const resendKey = process.env.RESEND_API_KEY;
2333
+ if (!resendKey) {
2334
+ logger6.warn("RESEND_API_KEY unset — skipping reindex-complete email", {
2335
+ subgraph: subgraphName
2336
+ });
2337
+ return;
2338
+ }
2339
+ const from = process.env.EMAIL_FROM ?? "Secondlayer <noreply@secondlayer.tools>";
2340
+ const body = stats.errors > 0 ? `Your subgraph "${subgraphName}" finished reindexing — ${stats.blocks.toLocaleString()} blocks, ${stats.events.toLocaleString()} events, ${stats.errors.toLocaleString()} errors. Check the dashboard for details.` : `Your subgraph "${subgraphName}" finished reindexing — ${stats.blocks.toLocaleString()} blocks, ${stats.events.toLocaleString()} events processed, no errors. It's live now.`;
2341
+ const res = await fetch("https://api.resend.com/emails", {
2342
+ method: "POST",
2343
+ headers: {
2344
+ Authorization: `Bearer ${resendKey}`,
2345
+ "Content-Type": "application/json"
2346
+ },
2347
+ body: JSON.stringify({
2348
+ from,
2349
+ to: [account.email],
2350
+ subject: `Reindex complete: ${subgraphName}`,
2351
+ text: body
2352
+ })
2353
+ });
2354
+ if (!res.ok) {
2355
+ const text = await res.text().catch(() => "");
2356
+ logger6.warn("reindex-complete email failed", {
2357
+ subgraph: subgraphName,
2358
+ status: res.status,
2359
+ body: text.slice(0, 200)
2360
+ });
2361
+ }
2362
+ } catch (err) {
2363
+ logger6.warn("reindex-complete email threw", {
2364
+ subgraph: subgraphName,
2365
+ error: getErrorMessage2(err)
2366
+ });
2367
+ }
2368
+ }
2369
+
2370
+ // src/runtime/reindex.ts
2320
2371
  var LOG_INTERVAL = 1000;
2321
2372
  var HEALTH_FLUSH_INTERVAL = 1000;
2322
2373
  var PROGRESS_FLUSH_INTERVAL_MS = 5000;
@@ -2421,7 +2472,7 @@ async function processBlockRange(def, opts) {
2421
2472
  while (currentHeight <= toBlock) {
2422
2473
  if (opts.signal?.aborted) {
2423
2474
  aborted = true;
2424
- logger6.info("Block processing aborted", {
2475
+ logger7.info("Block processing aborted", {
2425
2476
  subgraph: subgraphName,
2426
2477
  currentBlock: currentHeight,
2427
2478
  reason: String(opts.signal.reason ?? "unknown")
@@ -2469,8 +2520,8 @@ async function processBlockRange(def, opts) {
2469
2520
  preloaded: blockData
2470
2521
  });
2471
2522
  } catch (err) {
2472
- const errorMsg = getErrorMessage2(err);
2473
- logger6.error("Block processing failed persistently", {
2523
+ const errorMsg = getErrorMessage3(err);
2524
+ logger7.error("Block processing failed persistently", {
2474
2525
  subgraph: subgraphName,
2475
2526
  blockHeight: height,
2476
2527
  error: errorMsg
@@ -2516,7 +2567,7 @@ async function processBlockRange(def, opts) {
2516
2567
  lastProgressFlushAt = now;
2517
2568
  }
2518
2569
  if (blocksProcessed % LOG_INTERVAL === 0) {
2519
- logger6.info(`${status === "reindexing" ? "Reindex" : "Backfill"} progress`, {
2570
+ logger7.info(`${status === "reindexing" ? "Reindex" : "Backfill"} progress`, {
2520
2571
  subgraph: subgraphName,
2521
2572
  processed: blocksProcessed,
2522
2573
  total: totalBlocks,
@@ -2536,7 +2587,7 @@ async function processBlockRange(def, opts) {
2536
2587
  if (batchFailedBlocks.length > 0 && opts.subgraphId) {
2537
2588
  const gaps = coalesceFailedBlocks(batchFailedBlocks);
2538
2589
  await recordGapBatch(targetDb, opts.subgraphId, subgraphName, gaps).catch((err) => {
2539
- logger6.warn("Failed to record subgraph gaps", {
2590
+ logger7.warn("Failed to record subgraph gaps", {
2540
2591
  subgraph: subgraphName,
2541
2592
  error: err instanceof Error ? err.message : String(err)
2542
2593
  });
@@ -2553,7 +2604,7 @@ async function processBlockRange(def, opts) {
2553
2604
  } else {
2554
2605
  await updateSubgraphStatus2(targetDb, subgraphName, status, jumpTo - 1);
2555
2606
  }
2556
- logger6.info("Sparse skip", {
2607
+ logger7.info("Sparse skip", {
2557
2608
  subgraph: subgraphName,
2558
2609
  from: batchEnd + 1,
2559
2610
  to: jumpTo - 1,
@@ -2593,11 +2644,11 @@ async function reindexSubgraph(def, opts) {
2593
2644
  const subgraphName = def.name;
2594
2645
  const schemaName = opts?.schemaName ?? pgSchemaName(subgraphName);
2595
2646
  await updateSubgraphStatus2(targetDb, subgraphName, "reindexing");
2596
- logger6.info("Reindex starting", { subgraph: subgraphName });
2647
+ logger7.info("Reindex starting", { subgraph: subgraphName });
2597
2648
  try {
2598
2649
  const { fromBlock, toBlock } = await resolveBlockRange(source, def, opts);
2599
2650
  if (fromBlock > toBlock) {
2600
- logger6.info("No blocks to reindex", {
2651
+ logger7.info("No blocks to reindex", {
2601
2652
  subgraph: subgraphName,
2602
2653
  fromBlock,
2603
2654
  toBlock
@@ -2611,7 +2662,7 @@ async function reindexSubgraph(def, opts) {
2611
2662
  await client.unsafe(stmt);
2612
2663
  }
2613
2664
  await updateSubgraphStatus2(targetDb, subgraphName, "reindexing", Math.max(0, fromBlock - 1));
2614
- logger6.info("Schema recreated for reindex", {
2665
+ logger7.info("Schema recreated for reindex", {
2615
2666
  subgraph: subgraphName,
2616
2667
  cursorResetTo: Math.max(0, fromBlock - 1)
2617
2668
  });
@@ -2625,7 +2676,7 @@ async function reindexSubgraph(def, opts) {
2625
2676
  last_error_at: null,
2626
2677
  updated_at: new Date
2627
2678
  }).where("name", "=", subgraphName).execute();
2628
- logger6.info("Reindexing blocks", {
2679
+ logger7.info("Reindexing blocks", {
2629
2680
  subgraph: subgraphName,
2630
2681
  fromBlock,
2631
2682
  toBlock,
@@ -2647,9 +2698,9 @@ async function reindexSubgraph(def, opts) {
2647
2698
  if (reason === "user-cancelled") {
2648
2699
  await updateSubgraphStatus2(targetDb, subgraphName, "active");
2649
2700
  await clearReindexMetadata(targetDb, subgraphName);
2650
- logger6.info("Reindex cancelled by user", { subgraph: subgraphName });
2701
+ logger7.info("Reindex cancelled by user", { subgraph: subgraphName });
2651
2702
  } else {
2652
- logger6.info("Reindex interrupted by shutdown, will resume", {
2703
+ logger7.info("Reindex interrupted by shutdown, will resume", {
2653
2704
  subgraph: subgraphName
2654
2705
  });
2655
2706
  }
@@ -2657,17 +2708,22 @@ async function reindexSubgraph(def, opts) {
2657
2708
  }
2658
2709
  await updateSubgraphStatus2(targetDb, subgraphName, "active", toBlock);
2659
2710
  await clearReindexMetadata(targetDb, subgraphName);
2660
- logger6.info("Reindex complete", {
2711
+ logger7.info("Reindex complete", {
2661
2712
  subgraph: subgraphName,
2662
2713
  blocks: result.blocksProcessed,
2663
2714
  events: result.totalEventsProcessed,
2664
2715
  errors: result.totalErrors
2665
2716
  });
2717
+ notifyReindexComplete(targetDb, subgraphName, {
2718
+ blocks: result.blocksProcessed,
2719
+ events: result.totalEventsProcessed,
2720
+ errors: result.totalErrors
2721
+ });
2666
2722
  return { processed: result.blocksProcessed };
2667
2723
  } catch (err) {
2668
- logger6.error("Reindex failed", {
2724
+ logger7.error("Reindex failed", {
2669
2725
  subgraph: subgraphName,
2670
- error: getErrorMessage2(err)
2726
+ error: getErrorMessage3(err)
2671
2727
  });
2672
2728
  await updateSubgraphStatus2(targetDb, subgraphName, "error");
2673
2729
  throw err;
@@ -2687,7 +2743,7 @@ async function resumeReindex(def, opts) {
2687
2743
  const fromBlock = resolveReindexResumeBlock(row);
2688
2744
  const toBlock = Number(row.reindex_to_block);
2689
2745
  if (fromBlock == null) {
2690
- logger6.info("No reindex metadata, starting fresh reindex", {
2746
+ logger7.info("No reindex metadata, starting fresh reindex", {
2691
2747
  subgraph: subgraphName
2692
2748
  });
2693
2749
  return reindexSubgraph(def, {
@@ -2696,12 +2752,12 @@ async function resumeReindex(def, opts) {
2696
2752
  });
2697
2753
  }
2698
2754
  if (fromBlock > toBlock) {
2699
- logger6.info("Resume: no remaining blocks", { subgraph: subgraphName });
2755
+ logger7.info("Resume: no remaining blocks", { subgraph: subgraphName });
2700
2756
  await updateSubgraphStatus2(targetDb, subgraphName, "active", toBlock);
2701
2757
  await clearReindexMetadata(targetDb, subgraphName);
2702
2758
  return { processed: 0 };
2703
2759
  }
2704
- logger6.info("Resuming reindex", {
2760
+ logger7.info("Resuming reindex", {
2705
2761
  subgraph: subgraphName,
2706
2762
  fromBlock,
2707
2763
  toBlock,
@@ -2723,9 +2779,9 @@ async function resumeReindex(def, opts) {
2723
2779
  if (reason === "user-cancelled") {
2724
2780
  await updateSubgraphStatus2(targetDb, subgraphName, "active");
2725
2781
  await clearReindexMetadata(targetDb, subgraphName);
2726
- logger6.info("Resume cancelled by user", { subgraph: subgraphName });
2782
+ logger7.info("Resume cancelled by user", { subgraph: subgraphName });
2727
2783
  } else {
2728
- logger6.info("Resume interrupted by shutdown, will resume again", {
2784
+ logger7.info("Resume interrupted by shutdown, will resume again", {
2729
2785
  subgraph: subgraphName
2730
2786
  });
2731
2787
  }
@@ -2733,15 +2789,15 @@ async function resumeReindex(def, opts) {
2733
2789
  }
2734
2790
  await updateSubgraphStatus2(targetDb, subgraphName, "active", toBlock);
2735
2791
  await clearReindexMetadata(targetDb, subgraphName);
2736
- logger6.info("Resumed reindex complete", {
2792
+ logger7.info("Resumed reindex complete", {
2737
2793
  subgraph: subgraphName,
2738
2794
  blocks: result.blocksProcessed
2739
2795
  });
2740
2796
  return { processed: result.blocksProcessed };
2741
2797
  } catch (err) {
2742
- logger6.error("Resumed reindex failed", {
2798
+ logger7.error("Resumed reindex failed", {
2743
2799
  subgraph: subgraphName,
2744
- error: getErrorMessage2(err)
2800
+ error: getErrorMessage3(err)
2745
2801
  });
2746
2802
  await updateSubgraphStatus2(targetDb, subgraphName, "error");
2747
2803
  throw err;
@@ -2750,7 +2806,7 @@ async function resumeReindex(def, opts) {
2750
2806
  async function backfillSubgraph(def, opts) {
2751
2807
  const targetDb = getTargetDb2();
2752
2808
  const subgraphName = def.name;
2753
- logger6.info("Backfill starting", {
2809
+ logger7.info("Backfill starting", {
2754
2810
  subgraph: subgraphName,
2755
2811
  from: opts.fromBlock,
2756
2812
  to: opts.toBlock
@@ -2768,17 +2824,17 @@ async function backfillSubgraph(def, opts) {
2768
2824
  signal: opts.signal
2769
2825
  });
2770
2826
  if (result.aborted) {
2771
- logger6.info("Backfill aborted", { subgraph: subgraphName });
2827
+ logger7.info("Backfill aborted", { subgraph: subgraphName });
2772
2828
  return { processed: result.blocksProcessed };
2773
2829
  }
2774
2830
  const resolved = await resolveGaps(targetDb, subgraphName, opts.fromBlock, opts.toBlock).catch(() => 0);
2775
2831
  if (resolved > 0) {
2776
- logger6.info("Resolved subgraph gaps via backfill", {
2832
+ logger7.info("Resolved subgraph gaps via backfill", {
2777
2833
  subgraph: subgraphName,
2778
2834
  resolved
2779
2835
  });
2780
2836
  }
2781
- logger6.info("Backfill complete", {
2837
+ logger7.info("Backfill complete", {
2782
2838
  subgraph: subgraphName,
2783
2839
  blocks: result.blocksProcessed,
2784
2840
  events: result.totalEventsProcessed,
@@ -2786,9 +2842,9 @@ async function backfillSubgraph(def, opts) {
2786
2842
  });
2787
2843
  return { processed: result.blocksProcessed };
2788
2844
  } catch (err) {
2789
- logger6.error("Backfill failed", {
2845
+ logger7.error("Backfill failed", {
2790
2846
  subgraph: subgraphName,
2791
- error: getErrorMessage2(err)
2847
+ error: getErrorMessage3(err)
2792
2848
  });
2793
2849
  throw err;
2794
2850
  }
@@ -2802,5 +2858,5 @@ export {
2802
2858
  backfillSubgraph
2803
2859
  };
2804
2860
 
2805
- //# debugId=C5F839761BB60C3664756E2164756E21
2861
+ //# debugId=316D16E7196F1A6A64756E2164756E21
2806
2862
  //# sourceMappingURL=reindex.js.map