@secondlayer/subgraphs 1.2.0 → 1.2.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/README.md CHANGED
@@ -12,6 +12,10 @@ bun add @secondlayer/subgraphs
12
12
 
13
13
  ## Quick Start
14
14
 
15
+ For the full hosted beta loop, including project setup, fast deploys with
16
+ `--start-block`, querying, and subscriptions, start with
17
+ [QUICKSTART.md](QUICKSTART.md).
18
+
15
19
  ```typescript
16
20
  import { defineSubgraph } from "@secondlayer/subgraphs";
17
21
 
@@ -57,6 +61,7 @@ Deploy via CLI (`sl subgraphs deploy path/to/definition.ts`), SDK (`sl.subgraphs
57
61
  | `./types` | All schema + filter + handler types (`SubgraphDefinition`, `SubgraphFilter`, `StxTransferFilter`, etc.) |
58
62
  | `./schema` | Generator + deployer internals |
59
63
  | `./validate` | Shape + filter validation for deploys |
64
+ | `./triggers` | Typed `on.*` helpers for all `SubgraphFilter` variants |
60
65
  | `./runtime/source-matcher` | Pure fn: match txs+events against a `SubgraphFilter` — used by the processor hot path |
61
66
  | `./runtime/replay` | `replaySubscription({ accountId, subscriptionId, fromBlock, toBlock })` — re-enqueue historical rows as outbox entries |
62
67
 
@@ -84,6 +89,10 @@ Delivery bodies and response previews land in `subscription_deliveries`. Rows wh
84
89
  | `SECONDLAYER_EMIT_OUTBOX` | `true` | Set `false` to bypass outbox emission on every block (kill-switch). |
85
90
  | `SECONDLAYER_ALLOW_PRIVATE_EGRESS` | `false` | Allow the emitter to deliver to private IP ranges (localhost, 10/8, 172.16/12, 192.168/16, link-local, v6 mapped). Leave off in production. |
86
91
  | `SECONDLAYER_SECRETS_KEY` | — | 32-byte hex key for the AES-GCM envelope around subscription signing secrets. OSS mode auto-generates + persists to `.env.local`. |
92
+ | `TENANT_PLAN` | unset | Dedicated hosting plan injected by the provisioner. `hobby` uses smaller reindex batches; unset and paid plans use standard batches. |
93
+ | `SUBGRAPH_REINDEX_BATCH_SIZE` | plan-based | Override the default historical block batch size used by reindex/backfill. |
94
+ | `SUBGRAPH_REINDEX_MIN_BATCH_SIZE` | plan-based | Override the adaptive lower bound for reindex/backfill batches. |
95
+ | `SUBGRAPH_REINDEX_MAX_BATCH_SIZE` | plan-based | Override the adaptive upper bound for reindex/backfill batches. |
87
96
 
88
97
  ## Postgres + pool mode
89
98
 
package/dist/src/index.js CHANGED
@@ -582,7 +582,8 @@ function buildEventPayload(filter, tx, event) {
582
582
  tx: txMeta
583
583
  };
584
584
  case "print_event": {
585
- const rawValue = decoded.value;
585
+ const decodedRawValue = decoded.raw_value;
586
+ const rawValue = decodedRawValue && typeof decodedRawValue === "object" && !Array.isArray(decodedRawValue) ? decodedRawValue : decoded.value;
586
587
  const clarityObj = rawValue && typeof rawValue === "object" && !Array.isArray(rawValue) ? rawValue : null;
587
588
  const topic = clarityObj?.topic ? String(clarityObj.topic) : decoded.topic ?? "";
588
589
  const { topic: _, ...rest } = clarityObj ?? {};
@@ -893,7 +894,7 @@ function matchFilter(filter, transactions, eventsByTx) {
893
894
  for (const tx of transactions) {
894
895
  const txEvents = eventsByTx.get(tx.tx_id) ?? [];
895
896
  const matched = txEvents.filter((e) => {
896
- if (e.type !== "smart_contract_event")
897
+ if (e.type !== "smart_contract_event" && e.type !== "contract_event")
897
898
  return false;
898
899
  const data = e.data;
899
900
  if (!data)
@@ -1508,9 +1509,44 @@ function avgEventsPerBlock(batch) {
1508
1509
 
1509
1510
  // src/runtime/reindex.ts
1510
1511
  var LOG_INTERVAL = 1000;
1511
- var DEFAULT_BATCH_SIZE = 500;
1512
- var MIN_BATCH_SIZE = 100;
1513
- var MAX_BATCH_SIZE = 1000;
1512
+ var HOBBY_REINDEX_BATCH_CONFIG = {
1513
+ defaultBatchSize: 50,
1514
+ minBatchSize: 25,
1515
+ maxBatchSize: 100
1516
+ };
1517
+ var STANDARD_REINDEX_BATCH_CONFIG = {
1518
+ defaultBatchSize: 500,
1519
+ minBatchSize: 100,
1520
+ maxBatchSize: 1000
1521
+ };
1522
+ function initialReindexProgressBlock(fromBlock) {
1523
+ return Math.max(0, fromBlock - 1);
1524
+ }
1525
+ function resolveReindexResumeBlock(row) {
1526
+ if (row.reindex_from_block == null || row.reindex_to_block == null) {
1527
+ return null;
1528
+ }
1529
+ const lastProcessedBlock = Number(row.last_processed_block);
1530
+ const reindexFromBlock = Number(row.reindex_from_block);
1531
+ return Math.max(lastProcessedBlock + 1, reindexFromBlock);
1532
+ }
1533
+ function parsePositiveInt(value) {
1534
+ if (value == null || value.trim() === "")
1535
+ return;
1536
+ const parsed = Number.parseInt(value, 10);
1537
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : undefined;
1538
+ }
1539
+ function resolveReindexBatchConfig(env = process.env) {
1540
+ const base = env.TENANT_PLAN?.trim().toLowerCase() === "hobby" ? HOBBY_REINDEX_BATCH_CONFIG : STANDARD_REINDEX_BATCH_CONFIG;
1541
+ const minBatchSize = parsePositiveInt(env.SUBGRAPH_REINDEX_MIN_BATCH_SIZE) ?? base.minBatchSize;
1542
+ const maxBatchSize = parsePositiveInt(env.SUBGRAPH_REINDEX_MAX_BATCH_SIZE) ?? base.maxBatchSize;
1543
+ const defaultBatchSize = parsePositiveInt(env.SUBGRAPH_REINDEX_BATCH_SIZE) ?? base.defaultBatchSize;
1544
+ return {
1545
+ minBatchSize,
1546
+ maxBatchSize,
1547
+ defaultBatchSize: Math.min(Math.max(defaultBatchSize, minBatchSize), maxBatchSize)
1548
+ };
1549
+ }
1514
1550
  function coalesceFailedBlocks(blocks) {
1515
1551
  if (blocks.length === 0)
1516
1552
  return [];
@@ -1543,7 +1579,8 @@ async function processBlockRange(def, opts) {
1543
1579
  let blocksProcessed = 0;
1544
1580
  let totalEventsProcessed = 0;
1545
1581
  let totalErrors = 0;
1546
- let batchSize = DEFAULT_BATCH_SIZE;
1582
+ const batchConfig = resolveReindexBatchConfig();
1583
+ let batchSize = batchConfig.defaultBatchSize;
1547
1584
  let currentHeight = fromBlock;
1548
1585
  let aborted = false;
1549
1586
  let nextBatchEnd = Math.min(currentHeight + batchSize - 1, toBlock);
@@ -1626,9 +1663,9 @@ async function processBlockRange(def, opts) {
1626
1663
  }
1627
1664
  const avg = avgEventsPerBlock(batch);
1628
1665
  if (avg > 50)
1629
- batchSize = Math.max(Math.round(batchSize * 0.5), MIN_BATCH_SIZE);
1666
+ batchSize = Math.max(Math.round(batchSize * 0.5), batchConfig.minBatchSize);
1630
1667
  else if (avg < 10)
1631
- batchSize = Math.min(Math.round(batchSize * 1.5), MAX_BATCH_SIZE);
1668
+ batchSize = Math.min(Math.round(batchSize * 1.5), batchConfig.maxBatchSize);
1632
1669
  currentHeight = batchEnd + 1;
1633
1670
  }
1634
1671
  await stats.flush(targetDb);
@@ -1667,13 +1704,18 @@ async function reindexSubgraph(def, opts) {
1667
1704
  await updateSubgraphStatus2(targetDb, subgraphName, "active", 0);
1668
1705
  return { processed: 0 };
1669
1706
  }
1670
- await targetDb.updateTable("subgraphs").set({ reindex_from_block: fromBlock, reindex_to_block: toBlock }).where("name", "=", subgraphName).execute();
1671
1707
  await client.unsafe(`DROP SCHEMA IF EXISTS "${schemaName}" CASCADE`);
1672
1708
  const { statements } = generateSubgraphSQL(def, schemaName);
1673
1709
  for (const stmt of statements) {
1674
1710
  await client.unsafe(stmt);
1675
1711
  }
1676
1712
  logger5.info("Schema recreated for reindex", { subgraph: subgraphName });
1713
+ await targetDb.updateTable("subgraphs").set({
1714
+ last_processed_block: initialReindexProgressBlock(fromBlock),
1715
+ reindex_from_block: fromBlock,
1716
+ reindex_to_block: toBlock,
1717
+ updated_at: new Date
1718
+ }).where("name", "=", subgraphName).execute();
1677
1719
  logger5.info("Reindexing blocks", {
1678
1720
  subgraph: subgraphName,
1679
1721
  fromBlock,
@@ -1736,7 +1778,9 @@ async function resumeReindex(def, opts) {
1736
1778
  ]).where("name", "=", subgraphName).executeTakeFirst();
1737
1779
  if (!row)
1738
1780
  throw new Error(`Subgraph "${subgraphName}" not found`);
1739
- if (row.reindex_from_block == null || row.reindex_to_block == null) {
1781
+ const fromBlock = resolveReindexResumeBlock(row);
1782
+ const toBlock = Number(row.reindex_to_block);
1783
+ if (fromBlock == null) {
1740
1784
  logger5.info("No reindex metadata, starting fresh reindex", {
1741
1785
  subgraph: subgraphName
1742
1786
  });
@@ -1745,8 +1789,6 @@ async function resumeReindex(def, opts) {
1745
1789
  signal: opts.signal
1746
1790
  });
1747
1791
  }
1748
- const fromBlock = Math.max(row.last_processed_block + 1, row.reindex_from_block);
1749
- const toBlock = row.reindex_to_block;
1750
1792
  if (fromBlock > toBlock) {
1751
1793
  logger5.info("Resume: no remaining blocks", { subgraph: subgraphName });
1752
1794
  await updateSubgraphStatus2(targetDb, subgraphName, "active", toBlock);
@@ -2089,5 +2131,5 @@ export {
2089
2131
  backfillSubgraph
2090
2132
  };
2091
2133
 
2092
- //# debugId=E6D35F38651A38BA64756E2164756E21
2134
+ //# debugId=A62D0D17F8163B5364756E2164756E21
2093
2135
  //# sourceMappingURL=index.js.map