@secondlayer/subgraphs 6.3.3 → 6.3.5

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
@@ -2,6 +2,8 @@
2
2
 
3
3
  Typed on-chain indexing for Stacks. Declare event filters + column schema with `defineSubgraph()`; the runtime decodes blocks, matches filters, runs your handlers inside a transactional context, and exposes the result as a Postgres schema you query over REST or SQL.
4
4
 
5
+ Run subgraphs on your own instance, or hosted on `api.secondlayer.tools` with `secondlayer subgraphs deploy` and your account key (`SECONDLAYER_API_KEY`). Hosted subgraphs are private to your account and limited to event filters and `contract_call`/`contract_deploy` sources (`422 SOURCE_NOT_HOSTABLE` otherwise); set `startBlock`. Subgraph webhooks are self-host only.
6
+
5
7
  Subgraph rows fan out to HTTP subscribers through a post-flush outbox emitter — signed Standard Webhooks POSTs with retries, circuit breaker, and replay.
6
8
 
7
9
  ## Install
package/dist/index.d.ts CHANGED
@@ -1197,7 +1197,15 @@ type SparseProbeTarget = {
1197
1197
  * a contract_call/contract_deploy source matches transactions, which the
1198
1198
  * event probe can't see. */
1199
1199
  declare function canSparseScan(subgraph: SubgraphDefinition): boolean;
1200
- /** Probe targets for a subgraph's filters: decoded type + contract scope when\\n* the filter pins one (assetIdentifier "SP….contract::asset" or contractId). */
1200
+ /**
1201
+ * The (decoded event type, contract scope) pairs a subgraph's filters can
1202
+ * match. One function feeds BOTH the sparse probe and the event walks, so a
1203
+ * new contract-pinning filter field is added here once.
1204
+ *
1205
+ * Per event type: if ANY filter for it is unscoped, a single unscoped target
1206
+ * covers everything (today's behavior); otherwise one target per distinct
1207
+ * pinned contract.
1208
+ */
1201
1209
  declare function sparseProbeTargets(subgraph: SubgraphDefinition): SparseProbeTarget[];
1202
1210
  /**
1203
1211
  * streams-index eligibility: every source must be a known event-type or
package/dist/index.js CHANGED
@@ -1979,18 +1979,44 @@ function canSparseScan(subgraph) {
1979
1979
  return false;
1980
1980
  return filters.every((f) => Boolean(EVENT_FILTER_TO_INDEX_TYPE[f.type]));
1981
1981
  }
1982
+ var MAX_PINNED_CONTRACTS = 20;
1983
+ function pinnedContracts(filter) {
1984
+ const f = filter;
1985
+ if (f.trait || f.factory)
1986
+ return null;
1987
+ const ids = Array.isArray(f.contractId) ? [...f.contractId] : f.contractId ? [f.contractId] : f.assetIdentifier ? [f.assetIdentifier.split("::")[0]] : [];
1988
+ if (ids.length === 0 || ids.length > MAX_PINNED_CONTRACTS)
1989
+ return null;
1990
+ if (ids.some((id) => !id || id.includes("*")))
1991
+ return null;
1992
+ return ids;
1993
+ }
1982
1994
  function sparseProbeTargets(subgraph) {
1983
- const targets = new Map;
1995
+ const byType = new Map;
1984
1996
  for (const f of sourceFilters(subgraph)) {
1985
1997
  const eventType = EVENT_FILTER_TO_INDEX_TYPE[f.type];
1986
1998
  if (!eventType)
1987
1999
  continue;
1988
- const scoped = f;
1989
- const contractId = scoped.contractId ?? scoped.assetIdentifier?.split("::")[0];
1990
- const key = `${eventType}|${contractId ?? ""}`;
1991
- targets.set(key, { eventType, ...contractId ? { contractId } : {} });
2000
+ const pinned = pinnedContracts(f);
2001
+ const prev = byType.get(eventType);
2002
+ if (!pinned || prev === "all") {
2003
+ byType.set(eventType, "all");
2004
+ continue;
2005
+ }
2006
+ const set = prev ?? new Set;
2007
+ for (const id of pinned)
2008
+ set.add(id);
2009
+ byType.set(eventType, set);
1992
2010
  }
1993
- return [...targets.values()];
2011
+ const targets = [];
2012
+ for (const [eventType, scope] of byType) {
2013
+ if (scope === "all")
2014
+ targets.push({ eventType });
2015
+ else
2016
+ for (const contractId of scope)
2017
+ targets.push({ eventType, contractId });
2018
+ }
2019
+ return targets;
1994
2020
  }
1995
2021
 
1996
2022
  class PostgresBlockSource {
@@ -2069,6 +2095,16 @@ function isStreamsIndexEligible(subgraph) {
2069
2095
  }
2070
2096
  return true;
2071
2097
  }
2098
+ function dedupeEvents(rows) {
2099
+ const seen = new Set;
2100
+ return rows.filter((e) => {
2101
+ const key = `${e.event_type}|${e.block_height}|${e.event_index}`;
2102
+ if (seen.has(key))
2103
+ return false;
2104
+ seen.add(key);
2105
+ return true;
2106
+ });
2107
+ }
2072
2108
 
2073
2109
  class PublicApiBlockSource {
2074
2110
  http;
@@ -2081,6 +2117,13 @@ class PublicApiBlockSource {
2081
2117
  this.probeTargets = probeTargets;
2082
2118
  this.needsTransactions = needsTransactions;
2083
2119
  }
2120
+ walkTargets() {
2121
+ const scoped = this.needsTransactions ? undefined : this.probeTargets;
2122
+ return this.eventTypes.flatMap((eventType) => {
2123
+ const own = scoped?.filter((t) => t.eventType === eventType);
2124
+ return own?.length ? own : [{ eventType }];
2125
+ });
2126
+ }
2084
2127
  async nextDataHeight(afterHeight, untilHeight) {
2085
2128
  if (!this.probeTargets?.length)
2086
2129
  return afterHeight + 1;
@@ -2102,8 +2145,9 @@ class PublicApiBlockSource {
2102
2145
  const [blocks, txRows, eventLists] = await Promise.all([
2103
2146
  this.http.walkBlocks(fromHeight, toHeight),
2104
2147
  this.needsTransactions ? this.http.walkTransactions(fromHeight, toHeight) : Promise.resolve([]),
2105
- Promise.all(this.eventTypes.map((t) => this.http.walkEvents(t, fromHeight, toHeight, withTx)))
2148
+ Promise.all(this.walkTargets().map((t) => this.http.walkEvents(t.eventType, fromHeight, toHeight, withTx, t.contractId)))
2106
2149
  ]);
2150
+ const events = dedupeEvents(eventLists.flat());
2107
2151
  const map = new Map;
2108
2152
  for (const b of blocks) {
2109
2153
  map.set(b.block_height, {
@@ -2112,21 +2156,19 @@ class PublicApiBlockSource {
2112
2156
  events: []
2113
2157
  });
2114
2158
  }
2115
- const txs = this.needsTransactions ? txRows.map(reconstructTransaction) : synthesizeTxsFromEvents(eventLists.flat());
2159
+ const txs = this.needsTransactions ? txRows.map(reconstructTransaction) : synthesizeTxsFromEvents(events);
2116
2160
  for (const t of txs) {
2117
2161
  map.get(t.block_height)?.txs.push(t);
2118
2162
  }
2119
- for (const list of eventLists) {
2120
- for (const e of list) {
2121
- const bd = map.get(e.block_height);
2122
- if (!bd)
2123
- continue;
2124
- if (VM_INDEX_EVENT_TYPES.has(e.event_type)) {
2125
- bd.vmEvents ??= [];
2126
- bd.vmEvents.push(reconstructEvent(e));
2127
- } else {
2128
- bd.events.push(reconstructEvent(e));
2129
- }
2163
+ for (const e of events) {
2164
+ const bd = map.get(e.block_height);
2165
+ if (!bd)
2166
+ continue;
2167
+ if (VM_INDEX_EVENT_TYPES.has(e.event_type)) {
2168
+ bd.vmEvents ??= [];
2169
+ bd.vmEvents.push(reconstructEvent(e));
2170
+ } else {
2171
+ bd.events.push(reconstructEvent(e));
2130
2172
  }
2131
2173
  }
2132
2174
  for (const bd of map.values()) {
@@ -2165,5 +2207,5 @@ export {
2165
2207
  validateSubgraphDefinition
2166
2208
  };
2167
2209
 
2168
- //# debugId=171BB1FAA9B94DE264756E2164756E21
2210
+ //# debugId=9E0F3F4652CCD15164756E2164756E21
2169
2211
  //# sourceMappingURL=index.js.map