@secondlayer/subgraphs 3.25.1 → 3.26.1

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.
@@ -459,9 +459,28 @@ declare function filterFieldsByType(): Array<{
459
459
  type: string
460
460
  fields: string[]
461
461
  }>;
462
+ /** A (decoded event type, optional contract scope) pair the sparse probe
463
+ * checks. Contract scoping is what makes token-subgraph reindexes leap over
464
+ * everything that isn't their token. */
465
+ type SparseProbeTarget = {
466
+ eventType: string
467
+ contractId?: string
468
+ };
469
+ /** Sparse scanning is sound only when EVERY source is an event-type filter —
470
+ * a contract_call/contract_deploy source matches transactions, which the
471
+ * event probe can't see. */
472
+ declare function canSparseScan(subgraph: SubgraphDefinition): boolean;
473
+ /** Probe targets for a subgraph's filters: decoded type + contract scope when\\n* the filter pins one (assetIdentifier "SP….contract::asset" or contractId). */
474
+ declare function sparseProbeTargets(subgraph: SubgraphDefinition): SparseProbeTarget[];
462
475
  interface ReindexOptions {
463
- fromBlock?: number;
464
- toBlock?: number;
476
+ /**
477
+ * Policy floor on the walk's first block — NOT a caller-supplied range.
478
+ * Free-tier accounts may not reindex below their registered `start_block`
479
+ * (see `resolveGenesisPolicy` on the API side); this raises the walk start
480
+ * and can never lower it. There is deliberately no `toBlock` counterpart:
481
+ * a reindex always runs to chain tip. See {@link resolveBlockRange}.
482
+ */
483
+ startBlockFloor?: number;
465
484
  schemaName?: string;
466
485
  /** Op row to receive processed_events on each progress flush. */
467
486
  operationId?: string;
@@ -470,6 +489,13 @@ interface ReindexOptions {
470
489
  /**
471
490
  * Reindex a subgraph by dropping its tables, recreating them, and reprocessing
472
491
  * all historical blocks through the handler pipeline.
492
+ *
493
+ * Always whole-subgraph: `[start_block, chain tip]`. There is no ranged
494
+ * reindex — the drop cannot be scoped to a range without re-running delta
495
+ * handlers (`ctx.increment` / `ctx.update` / `ctx.patchOrInsert`) over rows
496
+ * that were never removed, which is the exact double-count hazard `backfill`
497
+ * already refuses. Use `backfillSubgraph` for ranged, additive work.
498
+ *
473
499
  * Supports cancellation via AbortSignal — on shutdown abort, status stays
474
500
  * "reindexing" for auto-resume; on user cancel, status resets to "active".
475
501
  */
@@ -1076,10 +1102,10 @@ declare function generateIndexSchema(target: IndexCodegenTarget, opts?: IndexCod
1076
1102
  /** Index tables available to codegen, for help text and validation. */
1077
1103
  declare const INDEX_CODEGEN_TABLES: readonly string[];
1078
1104
  import { pgSchemaName } from "@secondlayer/shared/db/queries/subgraphs";
1079
- import { Database as Database2 } from "@secondlayer/shared/db";
1105
+ import { Database as Database3 } from "@secondlayer/shared/db";
1080
1106
  import { ByoBreakingChangeDetails } from "@secondlayer/shared/errors";
1081
- import { Kysely as Kysely2 } from "kysely";
1082
- type AnyDb = Kysely2<Database2>;
1107
+ import { Kysely as Kysely3 } from "kysely";
1108
+ type AnyDb = Kysely3<Database3>;
1083
1109
  interface TableDiff {
1084
1110
  /** Tables added to the schema */
1085
1111
  addedTables: string[];
@@ -1200,17 +1226,4 @@ declare function deploySchema(db: AnyDb, def: SubgraphDefinition, handlerPath: s
1200
1226
  /** Non-fatal caveats the caller should surface (e.g. handler grafts at tip). */
1201
1227
  warnings?: string[]
1202
1228
  }>;
1203
- /** A (decoded event type, optional contract scope) pair the sparse probe
1204
- * checks. Contract scoping is what makes token-subgraph reindexes leap over
1205
- * everything that isn't their token. */
1206
- type SparseProbeTarget = {
1207
- eventType: string
1208
- contractId?: string
1209
- };
1210
- /** Sparse scanning is sound only when EVERY source is an event-type filter —
1211
- * a contract_call/contract_deploy source matches transactions, which the
1212
- * event probe can't see. */
1213
- declare function canSparseScan(subgraph: SubgraphDefinition): boolean;
1214
- /** Probe targets for a subgraph's filters: decoded type + contract scope when\\n* the filter pins one (assetIdentifier "SP….contract::asset" or contractId). */
1215
- declare function sparseProbeTargets(subgraph: SubgraphDefinition): SparseProbeTarget[];
1216
1229
  export { validateSubgraphDefinition, validatePrintPayload, sparseProbeTargets, resumeReindex, renderDeployPlan, reindexSubgraph, readContractAt, pgSchemaName, inferPrintTopics, hasBreakingChanges, generateSubgraphSQL, generatePrismaSchema, generateKyselySchema, generateIndexSchema, generateDrizzleSchema, filterFieldsByType, diffSchema, deploySchema, defineSubgraph, defineSchema, canSparseScan, camelizeDataKey, backfillSubgraph, WriteRow, WhereInput, TypedSubgraphDefinition, TypedSubgraphContext, TypedHandlers, TxMeta, TableDiff, SystemRow, SubscribeOptions, SubgraphTableClient, SubgraphTable, SubgraphSchema, SubgraphHandler, SubgraphFilter, SubgraphDefinition, SubgraphContext, SubgraphColumn, StxTransferPayload, StxTransferFilter, StxMintPayload, StxMintFilter, StxLockPayload, StxLockFilter, StxBurnPayload, StxBurnFilter, SparseProbeTarget, RowValue, ReindexOptions, PrismaGenOptions, PrintValidationResult, PrintSample, PrintFieldToTS, PrintField, PrintEventPayload, PrintEventFor, PrintEventFilter, PrintDataOf, NftTransferPayload, NftTransferFilter, NftMintPayload, NftMintFilter, NftBurnPayload, NftBurnFilter, KyselyGenOptions, InferredTopicSchema, InferredPrintField, InferTableRow, InferSubgraphClient, InferContext, InferColumnType, IndexCodegenTarget, IndexCodegenOptions, INDEX_CODEGEN_TABLES, GeneratedSQL, FtTransferPayload, FtTransferFilter, FtMintPayload, FtMintFilter, FtBurnPayload, FtBurnFilter, FindManyOptions, FactoryScope, EventForFilter, ErasedChainReadClient, DrizzleGenOptions, DeployPlan, DeployDiff, ContractDeployPayload, ContractDeployFilter, ContractCallPayload, ContractCallFilter, ContractCallEvent, ComputedValue, ComparisonFilter, ColumnType, ColumnToTS, ColumnDiff, ChainReadOptions, ChainReadMethods, ChainReadError, ChainReadClient, ChainReadCacheMode, ByoMigrationPlan, ByoBreakingChangeError, AnyEvent, AggregateSpec, AggregateResult };
package/dist/src/index.js CHANGED
@@ -820,14 +820,10 @@ class SubgraphContext {
820
820
  this.ops.length = 0;
821
821
  const statements = this.buildStatements(opsToFlush);
822
822
  if ("isTransaction" in this.db) {
823
- for (const stmt of statements) {
824
- await sql.raw(stmt).execute(this.db);
825
- }
823
+ await this.execStatements(this.db, statements);
826
824
  } else {
827
825
  await this.db.transaction().execute(async (tx) => {
828
- for (const stmt of statements) {
829
- await sql.raw(stmt).execute(tx);
830
- }
826
+ await this.execStatements(tx, statements);
831
827
  });
832
828
  }
833
829
  const writes = opsToFlush.map((op, rowIndex) => {
@@ -846,6 +842,25 @@ class SubgraphContext {
846
842
  });
847
843
  return { count: opsToFlush.length, writes };
848
844
  }
845
+ async execStatements(executor, statements) {
846
+ const affected = (r) => r.numAffectedRows ?? 0n;
847
+ for (const stmt of statements) {
848
+ if (typeof stmt === "string") {
849
+ await sql.raw(stmt).execute(executor);
850
+ continue;
851
+ }
852
+ const updated = await sql.raw(stmt.update).execute(executor);
853
+ if (affected(updated) > 0n)
854
+ continue;
855
+ const inserted = await sql.raw(stmt.insert).execute(executor);
856
+ if (affected(inserted) > 0n)
857
+ continue;
858
+ const retried = await sql.raw(stmt.update).execute(executor);
859
+ if (affected(retried) === 0n) {
860
+ throw new Error(`increment could not be applied to ${this.pgSchemaName}.${stmt.table} where ${stmt.keyPred}: row neither updated nor inserted`);
861
+ }
862
+ }
863
+ }
849
864
  prepareInsert(op) {
850
865
  const upsertKeys = op.data._upsert_keys;
851
866
  const data = { ...op.data };
@@ -925,7 +940,6 @@ class SubgraphContext {
925
940
  for (const r of batch.rows.values()) {
926
941
  const keyPred = batch.keyCols.map((k) => `"${k}" = ${escapeLiteral(r.keys[k])}`).join(" AND ");
927
942
  const updSet = batch.deltaCols.map((c) => `"${c}" = COALESCE("${c}", 0) + (${String(r.deltas[c] ?? 0n)})`).join(", ");
928
- statements.push(`UPDATE ${qualifiedTable} SET ${updSet} WHERE ${keyPred}`);
929
943
  const insertVals = [
930
944
  ...batch.keyCols.map((k) => escapeLiteral(r.keys[k])),
931
945
  ...batch.deltaCols.map((c) => String(r.deltas[c] ?? 0n)),
@@ -933,7 +947,13 @@ class SubgraphContext {
933
947
  escapeLiteral(r.meta.txId),
934
948
  "NOW()"
935
949
  ];
936
- statements.push(`INSERT INTO ${qualifiedTable} (${cols.map((c) => `"${c}"`).join(", ")}) ` + `SELECT ${insertVals.join(", ")} ` + `WHERE NOT EXISTS (SELECT 1 FROM ${qualifiedTable} WHERE ${keyPred})`);
950
+ statements.push({
951
+ kind: "increment",
952
+ table: batch.table,
953
+ keyPred,
954
+ update: `UPDATE ${qualifiedTable} SET ${updSet} WHERE ${keyPred}`,
955
+ insert: `INSERT INTO ${qualifiedTable} (${cols.map((c) => `"${c}"`).join(", ")}) ` + `SELECT ${insertVals.join(", ")} ` + `WHERE NOT EXISTS (SELECT 1 FROM ${qualifiedTable} WHERE ${keyPred})`
956
+ });
937
957
  }
938
958
  incBatch = null;
939
959
  incBatchKey = "";
@@ -3568,9 +3588,9 @@ async function processBlockRange(def, opts) {
3568
3588
  return { blocksProcessed, totalEventsProcessed, totalErrors, aborted };
3569
3589
  }
3570
3590
  async function resolveBlockRange(source, def, opts) {
3571
- const fromBlock = opts?.fromBlock ?? def.startBlock ?? 1;
3572
- const toBlock = opts?.toBlock != null ? opts.toBlock : await source.getTip();
3573
- return { fromBlock, toBlock };
3591
+ const definitionStart = def.startBlock ?? 1;
3592
+ const fromBlock = opts?.startBlockFloor != null ? Math.max(definitionStart, opts.startBlockFloor) : definitionStart;
3593
+ return { fromBlock, toBlock: await source.getTip() };
3574
3594
  }
3575
3595
  async function clearReindexMetadata(db, subgraphName) {
3576
3596
  await db.updateTable("subgraphs").set({ reindex_from_block: null, reindex_to_block: null }).where("name", "=", subgraphName).execute();
@@ -5086,5 +5106,5 @@ export {
5086
5106
  ByoBreakingChangeError
5087
5107
  };
5088
5108
 
5089
- //# debugId=8CAA9C7AD1208F2564756E2164756E21
5109
+ //# debugId=7F2918CDE1AF5BC064756E2164756E21
5090
5110
  //# sourceMappingURL=index.js.map