@secondlayer/subgraphs 3.2.1 → 3.3.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 (36) hide show
  1. package/README.md +81 -0
  2. package/dist/src/index.d.ts +71 -9
  3. package/dist/src/index.js +461 -54
  4. package/dist/src/index.js.map +11 -9
  5. package/dist/src/runtime/block-processor.d.ts +37 -9
  6. package/dist/src/runtime/block-processor.js +127 -37
  7. package/dist/src/runtime/block-processor.js.map +5 -5
  8. package/dist/src/runtime/catchup.d.ts +34 -8
  9. package/dist/src/runtime/catchup.js +125 -36
  10. package/dist/src/runtime/catchup.js.map +5 -5
  11. package/dist/src/runtime/context.d.ts +27 -1
  12. package/dist/src/runtime/context.js +13 -2
  13. package/dist/src/runtime/context.js.map +3 -3
  14. package/dist/src/runtime/processor.js +143 -39
  15. package/dist/src/runtime/processor.js.map +8 -8
  16. package/dist/src/runtime/reindex.d.ts +34 -8
  17. package/dist/src/runtime/reindex.js +131 -36
  18. package/dist/src/runtime/reindex.js.map +6 -6
  19. package/dist/src/runtime/reorg.d.ts +34 -8
  20. package/dist/src/runtime/reorg.js +131 -39
  21. package/dist/src/runtime/reorg.js.map +6 -6
  22. package/dist/src/runtime/runner.d.ts +43 -9
  23. package/dist/src/runtime/source-matcher.d.ts +19 -10
  24. package/dist/src/runtime/source-matcher.js +26 -6
  25. package/dist/src/runtime/source-matcher.js.map +3 -3
  26. package/dist/src/schema/index.d.ts +42 -8
  27. package/dist/src/schema/index.js +57 -19
  28. package/dist/src/schema/index.js.map +5 -5
  29. package/dist/src/service.js +143 -39
  30. package/dist/src/service.js.map +8 -8
  31. package/dist/src/triggers/index.d.ts +16 -8
  32. package/dist/src/types.d.ts +35 -9
  33. package/dist/src/validate.d.ts +34 -8
  34. package/dist/src/validate.js +10 -3
  35. package/dist/src/validate.js.map +3 -3
  36. package/package.json +3 -3
@@ -18,12 +18,14 @@ class SubgraphContext {
18
18
  pgSchemaName;
19
19
  subgraphSchema;
20
20
  ops = [];
21
- constructor(db, pgSchemaName, subgraphSchema, block, tx) {
21
+ byo;
22
+ constructor(db, pgSchemaName, subgraphSchema, block, tx, byo = false) {
22
23
  this.db = db;
23
24
  this.pgSchemaName = pgSchemaName;
24
25
  this.subgraphSchema = subgraphSchema;
25
26
  this.block = block;
26
27
  this._tx = tx;
28
+ this.byo = byo;
27
29
  }
28
30
  get tx() {
29
31
  return this._tx;
@@ -218,6 +220,15 @@ class SubgraphContext {
218
220
  }
219
221
  buildStatements(ops) {
220
222
  const statements = [];
223
+ if (this.byo) {
224
+ const insertTables = new Set;
225
+ for (const op of ops)
226
+ if (op.kind === "insert")
227
+ insertTables.add(op.table);
228
+ for (const table of insertTables) {
229
+ statements.push(`DELETE FROM "${this.pgSchemaName}"."${table}" WHERE "_block_height" = ${this.block.height}`);
230
+ }
231
+ }
221
232
  let currentBatch = null;
222
233
  let currentBatchKey = "";
223
234
  const flushInsertBatch = () => {
@@ -711,7 +722,19 @@ function matchPattern(value, pattern) {
711
722
  }
712
723
  return re.test(value);
713
724
  }
714
- function matchFilter(filter, transactions, eventsByTx) {
725
+ var EMPTY_SET = new Set;
726
+ function traitAllows(filter, contractId, traitContracts) {
727
+ const trait = filter.trait;
728
+ if (!trait)
729
+ return true;
730
+ if (!contractId)
731
+ return false;
732
+ return (traitContracts.get(trait) ?? EMPTY_SET).has(contractId);
733
+ }
734
+ function assetContract(assetId) {
735
+ return assetId?.split("::")[0];
736
+ }
737
+ function matchFilter(filter, transactions, eventsByTx, traitContracts) {
715
738
  const results = [];
716
739
  switch (filter.type) {
717
740
  case "stx_transfer":
@@ -775,6 +798,8 @@ function matchFilter(filter, transactions, eventsByTx) {
775
798
  if (!assetId || !matchPattern(assetId, filter.assetIdentifier))
776
799
  return false;
777
800
  }
801
+ if (!traitAllows(filter, assetContract(data.asset_identifier), traitContracts))
802
+ return false;
778
803
  if ("sender" in filter && filter.sender) {
779
804
  if (!matchPattern(data.sender, filter.sender))
780
805
  return false;
@@ -813,6 +838,8 @@ function matchFilter(filter, transactions, eventsByTx) {
813
838
  if (!assetId || !matchPattern(assetId, filter.assetIdentifier))
814
839
  return false;
815
840
  }
841
+ if (!traitAllows(filter, assetContract(data.asset_identifier), traitContracts))
842
+ return false;
816
843
  if ("sender" in filter && filter.sender) {
817
844
  if (!matchPattern(data.sender, filter.sender))
818
845
  return false;
@@ -845,6 +872,8 @@ function matchFilter(filter, transactions, eventsByTx) {
845
872
  if (!matchPattern(tx.sender, filter.caller))
846
873
  continue;
847
874
  }
875
+ if (!traitAllows(filter, tx.contract_id, traitContracts))
876
+ continue;
848
877
  const txEvents = eventsByTx.get(tx.tx_id) ?? [];
849
878
  results.push({ tx, events: txEvents });
850
879
  }
@@ -879,11 +908,13 @@ function matchFilter(filter, transactions, eventsByTx) {
879
908
  return false;
880
909
  if (data.topic !== "print")
881
910
  return false;
911
+ const printContractId = data.contract_identifier ?? data.contract_id;
882
912
  if (filter.contractId) {
883
- const contractId = data.contract_identifier ?? data.contract_id;
884
- if (!contractId || !matchPattern(contractId, filter.contractId))
913
+ if (!printContractId || !matchPattern(printContractId, filter.contractId))
885
914
  return false;
886
915
  }
916
+ if (!traitAllows(filter, printContractId, traitContracts))
917
+ return false;
887
918
  return true;
888
919
  });
889
920
  if (matched.length > 0) {
@@ -895,7 +926,7 @@ function matchFilter(filter, transactions, eventsByTx) {
895
926
  }
896
927
  return results;
897
928
  }
898
- function matchSources(sources, transactions, events) {
929
+ function matchSources(sources, transactions, events, traitContracts = new Map) {
899
930
  const eventsByTx = new Map;
900
931
  for (const event of events) {
901
932
  const list = eventsByTx.get(event.tx_id) ?? [];
@@ -905,7 +936,7 @@ function matchSources(sources, transactions, events) {
905
936
  const seen = new Set;
906
937
  const results = [];
907
938
  for (const [sourceName, filter] of Object.entries(sources)) {
908
- const matches = matchFilter(filter, transactions, eventsByTx);
939
+ const matches = matchFilter(filter, transactions, eventsByTx, traitContracts);
909
940
  for (const match of matches) {
910
941
  const dedupeKey = `${match.tx.tx_id}:${sourceName}`;
911
942
  if (!seen.has(dedupeKey)) {
@@ -922,8 +953,11 @@ import {
922
953
  getSourceDb,
923
954
  getTargetDb
924
955
  } from "@secondlayer/shared/db";
956
+ import { resolveTraitContractIds } from "@secondlayer/shared/db/queries/contracts";
925
957
  import {
958
+ isByoSubgraph,
926
959
  recordSubgraphProcessed,
960
+ resolveSubgraphDb,
927
961
  updateSubgraphStatus
928
962
  } from "@secondlayer/shared/db/queries/subgraphs";
929
963
  import { logger as logger4 } from "@secondlayer/shared/logger";
@@ -1161,7 +1195,38 @@ async function refreshMatcher(db) {
1161
1195
  }
1162
1196
 
1163
1197
  // src/runtime/block-processor.ts
1164
- var schemaNameCache = new Map;
1198
+ var routeCache = new Map;
1199
+ async function resolveRoute(subgraphName, targetDb) {
1200
+ const cached = routeCache.get(subgraphName);
1201
+ if (cached)
1202
+ return cached;
1203
+ const row = await targetDb.selectFrom("subgraphs").selectAll().where("name", "=", subgraphName).executeTakeFirst();
1204
+ const byo = row ? isByoSubgraph(row) : false;
1205
+ const route = {
1206
+ schemaName: row?.schema_name ?? pgSchemaName(subgraphName),
1207
+ dataDb: row && byo ? resolveSubgraphDb(row) : targetDb,
1208
+ byo
1209
+ };
1210
+ routeCache.set(subgraphName, route);
1211
+ return route;
1212
+ }
1213
+ function invalidateSubgraphRoute(subgraphName) {
1214
+ routeCache.delete(subgraphName);
1215
+ }
1216
+ async function resolveTraitContracts(subgraph, blockHeight, db) {
1217
+ const traits = new Set;
1218
+ for (const source of Object.values(subgraph.sources)) {
1219
+ const trait = source.trait;
1220
+ if (trait)
1221
+ traits.add(trait);
1222
+ }
1223
+ const resolved = new Map;
1224
+ for (const trait of traits) {
1225
+ const ids = await resolveTraitContractIds(db, trait, blockHeight);
1226
+ resolved.set(trait, new Set(ids));
1227
+ }
1228
+ return resolved;
1229
+ }
1165
1230
  async function processBlock(subgraph, subgraphName, blockHeight, opts) {
1166
1231
  const sourceDb = getSourceDb();
1167
1232
  const targetDb = getTargetDb();
@@ -1201,7 +1266,8 @@ async function processBlock(subgraph, subgraphName, blockHeight, opts) {
1201
1266
  txs = await sourceDb.selectFrom("transactions").selectAll().where("block_height", "=", blockHeight).execute();
1202
1267
  evts = await sourceDb.selectFrom("events").selectAll().where("block_height", "=", blockHeight).execute();
1203
1268
  }
1204
- const matched = matchSources(subgraph.sources, txs, evts);
1269
+ const traitContracts = await resolveTraitContracts(subgraph, blockHeight, targetDb);
1270
+ const matched = matchSources(subgraph.sources, txs, evts, traitContracts);
1205
1271
  result.matched = matched.length;
1206
1272
  if (matched.length === 0) {
1207
1273
  if (!opts?.skipProgressUpdate) {
@@ -1209,12 +1275,8 @@ async function processBlock(subgraph, subgraphName, blockHeight, opts) {
1209
1275
  }
1210
1276
  return result;
1211
1277
  }
1212
- let schemaName = schemaNameCache.get(subgraphName);
1213
- if (!schemaName) {
1214
- const subgraphRecord = await targetDb.selectFrom("subgraphs").select("schema_name").where("name", "=", subgraphName).executeTakeFirst();
1215
- schemaName = subgraphRecord?.schema_name ?? pgSchemaName(subgraphName);
1216
- schemaNameCache.set(subgraphName, schemaName);
1217
- }
1278
+ const route = await resolveRoute(subgraphName, targetDb);
1279
+ const schemaName = route.schemaName;
1218
1280
  const blockMeta = {
1219
1281
  height: block.height,
1220
1282
  hash: block.hash,
@@ -1229,30 +1291,57 @@ async function processBlock(subgraph, subgraphName, blockHeight, opts) {
1229
1291
  };
1230
1292
  let handlerMs = 0;
1231
1293
  let flushMs = 0;
1232
- await targetDb.transaction().execute(async (tx) => {
1233
- const ctx = new SubgraphContext(tx, schemaName, subgraph.schema, blockMeta, initialTx);
1234
- const handlerStart = performance.now();
1235
- const runResult = await runHandlers(subgraph, matched, ctx);
1236
- handlerMs = performance.now() - handlerStart;
1294
+ const applyProgress = async (tx, rr) => {
1295
+ if (opts?.skipProgressUpdate)
1296
+ return;
1297
+ const status = rr.errors > 0 && rr.processed === 0 ? "error" : "active";
1298
+ await updateSubgraphStatus(tx, subgraphName, status, blockHeight);
1299
+ if (rr.processed > 0 || rr.errors > 0) {
1300
+ const lastError = rr.errors > 0 ? `${rr.errors} error(s) at block ${blockHeight}` : undefined;
1301
+ await recordSubgraphProcessed(tx, subgraphName, rr.processed, rr.errors, lastError);
1302
+ }
1303
+ };
1304
+ if (route.byo) {
1305
+ let runResult = { processed: 0, errors: 0 };
1306
+ let manifest;
1307
+ await route.dataDb.transaction().execute(async (tx) => {
1308
+ const ctx = new SubgraphContext(tx, schemaName, subgraph.schema, blockMeta, initialTx, true);
1309
+ const handlerStart = performance.now();
1310
+ runResult = await runHandlers(subgraph, matched, ctx);
1311
+ handlerMs = performance.now() - handlerStart;
1312
+ if (ctx.pendingOps > 0) {
1313
+ const flushStart = performance.now();
1314
+ manifest = await ctx.flush();
1315
+ flushMs = performance.now() - flushStart;
1316
+ }
1317
+ });
1237
1318
  result.processed = runResult.processed;
1238
1319
  result.errors = runResult.errors;
1239
- if (ctx.pendingOps > 0) {
1240
- const flushStart = performance.now();
1241
- const manifest = await ctx.flush();
1242
- if (manifest.count > 0) {
1320
+ await targetDb.transaction().execute(async (tx) => {
1321
+ if (manifest && manifest.count > 0) {
1243
1322
  await emitSubscriptionOutbox(tx, subgraphName, manifest, matcher, block.height);
1244
1323
  }
1245
- flushMs = performance.now() - flushStart;
1246
- }
1247
- if (!opts?.skipProgressUpdate) {
1248
- const status = runResult.errors > 0 && runResult.processed === 0 ? "error" : "active";
1249
- await updateSubgraphStatus(tx, subgraphName, status, blockHeight);
1250
- }
1251
- if (!opts?.skipProgressUpdate && (runResult.processed > 0 || runResult.errors > 0)) {
1252
- const lastError = runResult.errors > 0 ? `${runResult.errors} error(s) at block ${blockHeight}` : undefined;
1253
- await recordSubgraphProcessed(tx, subgraphName, runResult.processed, runResult.errors, lastError);
1254
- }
1255
- });
1324
+ await applyProgress(tx, runResult);
1325
+ });
1326
+ } else {
1327
+ await targetDb.transaction().execute(async (tx) => {
1328
+ const ctx = new SubgraphContext(tx, schemaName, subgraph.schema, blockMeta, initialTx);
1329
+ const handlerStart = performance.now();
1330
+ const runResult = await runHandlers(subgraph, matched, ctx);
1331
+ handlerMs = performance.now() - handlerStart;
1332
+ result.processed = runResult.processed;
1333
+ result.errors = runResult.errors;
1334
+ if (ctx.pendingOps > 0) {
1335
+ const flushStart = performance.now();
1336
+ const manifest = await ctx.flush();
1337
+ if (manifest.count > 0) {
1338
+ await emitSubscriptionOutbox(tx, subgraphName, manifest, matcher, block.height);
1339
+ }
1340
+ flushMs = performance.now() - flushStart;
1341
+ }
1342
+ await applyProgress(tx, runResult);
1343
+ });
1344
+ }
1256
1345
  const totalMs = performance.now() - blockStart;
1257
1346
  result.timing = {
1258
1347
  totalMs: Math.round(totalMs),
@@ -1263,7 +1352,7 @@ async function processBlock(subgraph, subgraphName, blockHeight, opts) {
1263
1352
  try {
1264
1353
  const tables = Object.keys(subgraph.schema);
1265
1354
  for (const table of tables) {
1266
- const { rows } = await sql3.raw(`SELECT n_live_tup AS count FROM pg_stat_user_tables WHERE schemaname = '${schemaName}' AND relname = '${table}'`).execute(targetDb);
1355
+ 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);
1267
1356
  const count = Number(rows[0]?.count ?? 0);
1268
1357
  if (count >= 1e7) {
1269
1358
  logger4.warn("Subgraph table exceeds 10M rows (estimate)", {
@@ -1590,5 +1679,5 @@ export {
1590
1679
  catchUpSubgraph
1591
1680
  };
1592
1681
 
1593
- //# debugId=BC0A838C52409B1064756E2164756E21
1682
+ //# debugId=3947491E2917E90B64756E2164756E21
1594
1683
  //# sourceMappingURL=catchup.js.map