@secondlayer/subgraphs 3.23.2 → 3.24.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.
package/dist/src/index.js CHANGED
@@ -226,6 +226,11 @@ import { createHash } from "node:crypto";
226
226
 
227
227
  // src/schema/utils.ts
228
228
  import { pgSchemaName } from "@secondlayer/shared/db/queries/subgraphs";
229
+ function quotePgIdent(name) {
230
+ if (/^[a-z_][a-z0-9_]*$/i.test(name))
231
+ return name;
232
+ return `"${name.replace(/"/g, '""')}"`;
233
+ }
229
234
 
230
235
  // src/schema/generator.ts
231
236
  var TYPE_MAP = {
@@ -250,7 +255,7 @@ function tableNeedsTrgm(tableDef) {
250
255
  return Object.values(tableDef.columns).some((col) => col.search);
251
256
  }
252
257
  function emitTableDDL(schemaName, tableName, tableDef) {
253
- const qualifiedName = `${schemaName}.${tableName}`;
258
+ const qualifiedName = `${quotePgIdent(schemaName)}.${quotePgIdent(tableName)}`;
254
259
  const statements = [];
255
260
  const columnDefs = [
256
261
  "_id BIGSERIAL PRIMARY KEY",
@@ -261,12 +266,12 @@ function emitTableDDL(schemaName, tableName, tableDef) {
261
266
  for (const [colName, col] of Object.entries(tableDef.columns)) {
262
267
  const sqlType = TYPE_MAP[col.type];
263
268
  const nullable = col.nullable ? "" : " NOT NULL";
264
- let colDef = `${colName} ${sqlType}${nullable}`;
269
+ let colDef = `${quotePgIdent(colName)} ${sqlType}${nullable}`;
265
270
  if (col.default !== undefined) {
266
271
  colDef += ` DEFAULT ${escapeLiteralDefault(col.default)}`;
267
272
  }
268
273
  if (col.type === "uint") {
269
- colDef += ` CHECK (${colName} >= 0)`;
274
+ colDef += ` CHECK (${quotePgIdent(colName)} >= 0)`;
270
275
  }
271
276
  columnDefs.push(colDef);
272
277
  }
@@ -274,30 +279,30 @@ function emitTableDDL(schemaName, tableName, tableDef) {
274
279
  ${columnDefs.join(`,
275
280
  `)}
276
281
  )`);
277
- statements.push(`CREATE INDEX IF NOT EXISTS idx_${schemaName}_${tableName}_block_height ON ${qualifiedName} (_block_height)`);
278
- statements.push(`CREATE INDEX IF NOT EXISTS idx_${schemaName}_${tableName}_tx_id ON ${qualifiedName} (_tx_id)`);
282
+ statements.push(`CREATE INDEX IF NOT EXISTS ${quotePgIdent(`idx_${schemaName}_${tableName}_block_height`)} ON ${qualifiedName} (_block_height)`);
283
+ statements.push(`CREATE INDEX IF NOT EXISTS ${quotePgIdent(`idx_${schemaName}_${tableName}_tx_id`)} ON ${qualifiedName} (_tx_id)`);
279
284
  for (const [colName, col] of Object.entries(tableDef.columns)) {
280
285
  if (col.indexed) {
281
- statements.push(`CREATE INDEX IF NOT EXISTS idx_${schemaName}_${tableName}_${colName} ON ${qualifiedName} (${colName})`);
286
+ statements.push(`CREATE INDEX IF NOT EXISTS ${quotePgIdent(`idx_${schemaName}_${tableName}_${colName}`)} ON ${qualifiedName} (${quotePgIdent(colName)})`);
282
287
  }
283
288
  }
284
289
  for (const [colName, col] of Object.entries(tableDef.columns)) {
285
290
  if (col.search) {
286
- statements.push(`CREATE INDEX IF NOT EXISTS idx_${schemaName}_${tableName}_${colName}_trgm ON ${qualifiedName} USING gin (${colName} gin_trgm_ops)`);
291
+ statements.push(`CREATE INDEX IF NOT EXISTS ${quotePgIdent(`idx_${schemaName}_${tableName}_${colName}_trgm`)} ON ${qualifiedName} USING gin (${quotePgIdent(colName)} gin_trgm_ops)`);
287
292
  }
288
293
  }
289
294
  if (tableDef.indexes) {
290
295
  for (let i = 0;i < tableDef.indexes.length; i++) {
291
296
  const cols = tableDef.indexes[i];
292
- const idxName = `idx_${schemaName}_${tableName}_composite_${i}`;
293
- statements.push(`CREATE INDEX IF NOT EXISTS ${idxName} ON ${qualifiedName} (${cols.join(", ")})`);
297
+ const idxName = quotePgIdent(`idx_${schemaName}_${tableName}_composite_${i}`);
298
+ statements.push(`CREATE INDEX IF NOT EXISTS ${idxName} ON ${qualifiedName} (${cols.map(quotePgIdent).join(", ")})`);
294
299
  }
295
300
  }
296
301
  if (tableDef.uniqueKeys) {
297
302
  for (let i = 0;i < tableDef.uniqueKeys.length; i++) {
298
303
  const cols = tableDef.uniqueKeys[i];
299
- const constraintName = `uq_${schemaName}_${tableName}_${cols.join("_")}`;
300
- statements.push(`ALTER TABLE ${qualifiedName} ADD CONSTRAINT ${constraintName} UNIQUE (${cols.join(", ")})`);
304
+ const constraintName = quotePgIdent(`uq_${schemaName}_${tableName}_${cols.join("_")}`);
305
+ statements.push(`ALTER TABLE ${qualifiedName} ADD CONSTRAINT ${constraintName} UNIQUE (${cols.map(quotePgIdent).join(", ")})`);
301
306
  }
302
307
  }
303
308
  return statements;
@@ -329,8 +334,8 @@ function emitFactoryDDL(schemaName) {
329
334
  }
330
335
  function emitForeignKeyDDL(schemaName, tableName, tableDef) {
331
336
  return (tableDef.relations ?? []).map((rel) => {
332
- const constraintName = `fk_${schemaName}_${tableName}_${rel.name}`;
333
- return `ALTER TABLE ${schemaName}.${tableName} ADD CONSTRAINT ${constraintName} ` + `FOREIGN KEY (${rel.fields.join(", ")}) ` + `REFERENCES ${schemaName}.${rel.references} (${rel.referencedColumns.join(", ")})`;
337
+ const constraintName = quotePgIdent(`fk_${schemaName}_${tableName}_${rel.name}`);
338
+ return `ALTER TABLE ${quotePgIdent(schemaName)}.${quotePgIdent(tableName)} ADD CONSTRAINT ${constraintName} ` + `FOREIGN KEY (${rel.fields.map(quotePgIdent).join(", ")}) ` + `REFERENCES ${quotePgIdent(schemaName)}.${quotePgIdent(rel.references)} (${rel.referencedColumns.map(quotePgIdent).join(", ")})`;
334
339
  });
335
340
  }
336
341
  function generateSubgraphSQL(def, schemaNameOverride) {
@@ -1842,9 +1847,11 @@ import { resolveTraitContractIds } from "@secondlayer/shared/db/queries/contract
1842
1847
  import { advanceOperationCursor } from "@secondlayer/shared/db/queries/subgraph-operations";
1843
1848
  import {
1844
1849
  isByoSubgraph,
1850
+ recordLiveError,
1845
1851
  recordLiveProgress,
1846
1852
  recordSubgraphProcessed,
1847
1853
  resolveSubgraphDb,
1854
+ rewindLiveProgress,
1848
1855
  updateSubgraphStatus
1849
1856
  } from "@secondlayer/shared/db/queries/subgraphs";
1850
1857
  import { logger as logger6 } from "@secondlayer/shared/logger";
@@ -2552,10 +2559,20 @@ class CursorRaceLostError extends Error {
2552
2559
  this.name = "CursorRaceLostError";
2553
2560
  }
2554
2561
  }
2562
+
2563
+ class LiveCursorRaceLostError extends Error {
2564
+ constructor(subgraphName, height) {
2565
+ super(`live cursor race lost for ${subgraphName} at block ${height}`);
2566
+ this.name = "LiveCursorRaceLostError";
2567
+ }
2568
+ }
2555
2569
  function opCursorMode(opts) {
2556
2570
  const ap = opts?.atomicProgress;
2557
2571
  return ap && "operationId" in ap ? ap : undefined;
2558
2572
  }
2573
+ function isReorgRewind(opts) {
2574
+ return opts?.reorgRewind === true;
2575
+ }
2559
2576
  function statusMode(opts) {
2560
2577
  const ap = opts?.atomicProgress;
2561
2578
  return ap && "status" in ap ? ap : undefined;
@@ -2625,7 +2642,11 @@ async function processBlock(subgraph, subgraphName, blockHeight, opts) {
2625
2642
  result.matched = matched.length;
2626
2643
  if (matched.length === 0) {
2627
2644
  if (!opts?.skipProgressUpdate) {
2628
- await recordLiveProgress(targetDb, subgraphName, blockHeight);
2645
+ if (isReorgRewind(opts)) {
2646
+ await rewindLiveProgress(targetDb, subgraphName, blockHeight);
2647
+ } else {
2648
+ await recordLiveProgress(targetDb, subgraphName, blockHeight);
2649
+ }
2629
2650
  }
2630
2651
  return result;
2631
2652
  }
@@ -2644,13 +2665,23 @@ async function processBlock(subgraph, subgraphName, blockHeight, opts) {
2644
2665
  };
2645
2666
  let handlerMs = 0;
2646
2667
  let flushMs = 0;
2647
- const applyProgress = async (tx, rr) => {
2668
+ const applyProgress = async (tx, rr, flushedWrites) => {
2648
2669
  if (opts?.skipProgressUpdate)
2649
2670
  return;
2671
+ const rewind = isReorgRewind(opts);
2650
2672
  if (rr.errors > 0 && rr.processed === 0) {
2651
- await updateSubgraphStatus(tx, subgraphName, "error", blockHeight);
2673
+ if (rewind) {
2674
+ await updateSubgraphStatus(tx, subgraphName, "error", blockHeight);
2675
+ } else {
2676
+ await recordLiveError(tx, subgraphName, blockHeight);
2677
+ }
2678
+ } else if (rewind) {
2679
+ await rewindLiveProgress(tx, subgraphName, blockHeight);
2652
2680
  } else {
2653
- await recordLiveProgress(tx, subgraphName, blockHeight);
2681
+ const advanced = await recordLiveProgress(tx, subgraphName, blockHeight);
2682
+ if (!advanced && flushedWrites && !route.byo) {
2683
+ throw new LiveCursorRaceLostError(subgraphName, blockHeight);
2684
+ }
2654
2685
  }
2655
2686
  if (rr.processed > 0 || rr.errors > 0) {
2656
2687
  const lastError = rr.errors > 0 ? `${rr.errors} error(s) at block ${blockHeight}` : undefined;
@@ -2699,13 +2730,15 @@ async function processBlock(subgraph, subgraphName, blockHeight, opts) {
2699
2730
  } else if (byoOm && manifest && manifest.count > 0) {
2700
2731
  await advanceOperationCursor(tx, byoOm.operationId, blockHeight);
2701
2732
  }
2702
- await applyProgress(tx, runResult);
2733
+ await applyProgress(tx, runResult, manifest ? manifest.count > 0 : false);
2703
2734
  });
2704
2735
  } else {
2705
2736
  try {
2706
2737
  await targetDb.transaction().execute(async (tx) => {
2707
2738
  const opMode = opCursorMode(opts);
2708
- if (statusMode(opts)) {
2739
+ const sm = statusMode(opts);
2740
+ const rewind = isReorgRewind(opts);
2741
+ if (sm) {
2709
2742
  const row = await tx.selectFrom("subgraphs").select("last_processed_block").where("name", "=", subgraphName).executeTakeFirst();
2710
2743
  if (row && Number(row.last_processed_block) >= blockHeight) {
2711
2744
  result.skipped = true;
@@ -2717,6 +2750,12 @@ async function processBlock(subgraph, subgraphName, blockHeight, opts) {
2717
2750
  result.skipped = true;
2718
2751
  return;
2719
2752
  }
2753
+ } else if (!rewind) {
2754
+ const row = await tx.selectFrom("subgraphs").select("last_processed_block").where("name", "=", subgraphName).executeTakeFirst();
2755
+ if (row && Number(row.last_processed_block) >= blockHeight) {
2756
+ result.skipped = true;
2757
+ return;
2758
+ }
2720
2759
  }
2721
2760
  const ctx = new SubgraphContext(tx, schemaName, subgraph.schema, blockMeta, initialTx, false, journalEnabled(opts));
2722
2761
  const handlerStart = performance.now();
@@ -2734,7 +2773,6 @@ async function processBlock(subgraph, subgraphName, blockHeight, opts) {
2734
2773
  }
2735
2774
  flushMs = performance.now() - flushStart;
2736
2775
  }
2737
- const sm = statusMode(opts);
2738
2776
  if (sm && flushedWrites) {
2739
2777
  await updateSubgraphStatus(tx, subgraphName, sm.status, blockHeight);
2740
2778
  } else if (opMode && flushedWrites) {
@@ -2743,10 +2781,10 @@ async function processBlock(subgraph, subgraphName, blockHeight, opts) {
2743
2781
  throw new CursorRaceLostError(opMode.operationId, blockHeight);
2744
2782
  }
2745
2783
  }
2746
- await applyProgress(tx, runResult);
2784
+ await applyProgress(tx, runResult, flushedWrites);
2747
2785
  });
2748
2786
  } catch (err) {
2749
- if (err instanceof CursorRaceLostError) {
2787
+ if (err instanceof CursorRaceLostError || err instanceof LiveCursorRaceLostError) {
2750
2788
  logger6.warn("cursor race lost — block already covered", {
2751
2789
  subgraph: subgraphName,
2752
2790
  blockHeight,
@@ -2759,11 +2797,13 @@ async function processBlock(subgraph, subgraphName, blockHeight, opts) {
2759
2797
  }
2760
2798
  }
2761
2799
  const totalMs = performance.now() - blockStart;
2762
- result.timing = {
2763
- totalMs: Math.round(totalMs),
2764
- handlerMs: Math.round(handlerMs),
2765
- flushMs: Math.round(flushMs)
2766
- };
2800
+ if (!result.skipped) {
2801
+ result.timing = {
2802
+ totalMs: Math.round(totalMs),
2803
+ handlerMs: Math.round(handlerMs),
2804
+ flushMs: Math.round(flushMs)
2805
+ };
2806
+ }
2767
2807
  if (blockHeight % 1000 === 0) {
2768
2808
  try {
2769
2809
  const tables = Object.keys(subgraph.schema);
@@ -4701,5 +4741,5 @@ export {
4701
4741
  ByoBreakingChangeError
4702
4742
  };
4703
4743
 
4704
- //# debugId=EA579C34B6DF559E64756E2164756E21
4744
+ //# debugId=CE8974B47137FD3C64756E2164756E21
4705
4745
  //# sourceMappingURL=index.js.map