@secondlayer/subgraphs 4.1.5 → 4.2.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 (37) hide show
  1. package/dist/src/index.d.ts +87 -29
  2. package/dist/src/index.js +376 -128
  3. package/dist/src/index.js.map +9 -7
  4. package/dist/src/runtime/block-processor.d.ts +72 -23
  5. package/dist/src/runtime/block-processor.js +186 -53
  6. package/dist/src/runtime/block-processor.js.map +7 -5
  7. package/dist/src/runtime/catchup.d.ts +72 -23
  8. package/dist/src/runtime/catchup.js +201 -68
  9. package/dist/src/runtime/catchup.js.map +7 -5
  10. package/dist/src/runtime/processor.js +281 -147
  11. package/dist/src/runtime/processor.js.map +8 -6
  12. package/dist/src/runtime/reindex.d.ts +72 -23
  13. package/dist/src/runtime/reindex.js +238 -105
  14. package/dist/src/runtime/reindex.js.map +7 -5
  15. package/dist/src/runtime/reorg.d.ts +72 -23
  16. package/dist/src/runtime/reorg.js +215 -81
  17. package/dist/src/runtime/reorg.js.map +8 -6
  18. package/dist/src/runtime/runner.d.ts +72 -23
  19. package/dist/src/runtime/runner.js +156 -23
  20. package/dist/src/runtime/runner.js.map +8 -6
  21. package/dist/src/runtime/source-matcher.d.ts +72 -23
  22. package/dist/src/schema/index.d.ts +72 -23
  23. package/dist/src/schema/index.js +115 -1
  24. package/dist/src/schema/index.js.map +3 -3
  25. package/dist/src/service.js +286 -152
  26. package/dist/src/service.js.map +8 -6
  27. package/dist/src/testing/index.d.ts +195 -24
  28. package/dist/src/testing/index.js +448 -25
  29. package/dist/src/testing/index.js.map +9 -6
  30. package/dist/src/triggers/index.d.ts +83 -24
  31. package/dist/src/triggers/index.js +5 -2
  32. package/dist/src/triggers/index.js.map +3 -3
  33. package/dist/src/types.d.ts +73 -24
  34. package/dist/src/validate.d.ts +72 -23
  35. package/dist/src/validate.js +115 -1
  36. package/dist/src/validate.js.map +3 -3
  37. package/package.json +2 -2
@@ -1212,7 +1212,7 @@ function matchSources(sources, transactions, events, traitContracts = new Map, f
1212
1212
 
1213
1213
  // src/runtime/runner.ts
1214
1214
  import { getErrorMessage } from "@secondlayer/shared";
1215
- import { logger as logger3 } from "@secondlayer/shared/logger";
1215
+ import { logger as logger4 } from "@secondlayer/shared/logger";
1216
1216
  import {
1217
1217
  clarityValueToJS,
1218
1218
  deserializeCV as deserializeCV4,
@@ -1226,6 +1226,19 @@ import {
1226
1226
  function camelizeDataKey(str) {
1227
1227
  return str.replace(/-([a-z0-9])/g, (_, c) => c.toUpperCase());
1228
1228
  }
1229
+ function camelizeKeys(obj) {
1230
+ if (obj === null || obj === undefined)
1231
+ return obj;
1232
+ if (typeof obj !== "object")
1233
+ return obj;
1234
+ if (Array.isArray(obj))
1235
+ return obj.map(camelizeKeys);
1236
+ const result = {};
1237
+ for (const [k, v] of Object.entries(obj)) {
1238
+ result[camelizeDataKey(k)] = camelizeKeys(v);
1239
+ }
1240
+ return result;
1241
+ }
1229
1242
  function cvToTree(cv) {
1230
1243
  switch (cv.type) {
1231
1244
  case "uint":
@@ -1550,6 +1563,58 @@ function inferPrintTopics(samples) {
1550
1563
  return out.sort((a, b) => b.count - a.count);
1551
1564
  }
1552
1565
 
1566
+ // src/runtime/materialize.ts
1567
+ function resolveColumn(col, event, tx, block) {
1568
+ if ("fromTx" in col) {
1569
+ return { ok: true, value: tx[col.fromTx] };
1570
+ }
1571
+ if ("fromBlock" in col) {
1572
+ return { ok: true, value: block[col.fromBlock] };
1573
+ }
1574
+ const data = event.data;
1575
+ const dataObj = data !== null && typeof data === "object" && !Array.isArray(data) ? data : undefined;
1576
+ if (dataObj && Object.prototype.hasOwnProperty.call(dataObj, col.from)) {
1577
+ return { ok: true, value: dataObj[col.from] };
1578
+ }
1579
+ if (col.from === "topic" && "topic" in event) {
1580
+ return { ok: true, value: event.topic };
1581
+ }
1582
+ if (Object.prototype.hasOwnProperty.call(event, col.from)) {
1583
+ return { ok: true, value: event[col.from] };
1584
+ }
1585
+ return { ok: false, missing: col.from };
1586
+ }
1587
+ function buildMaterializeRow(spec, event, ctx, tableColumns) {
1588
+ const row = {};
1589
+ for (const [colName, mapping] of Object.entries(spec.columns)) {
1590
+ const resolved = resolveColumn(mapping, event, ctx.tx, ctx.block);
1591
+ if (!resolved.ok) {
1592
+ const colDef = tableColumns[colName];
1593
+ if (colDef?.nullable) {
1594
+ row[colName] = null;
1595
+ continue;
1596
+ }
1597
+ return {
1598
+ ok: false,
1599
+ reason: `required materialize field "${resolved.missing}" missing for column "${colName}"`
1600
+ };
1601
+ }
1602
+ row[colName] = resolved.value ?? null;
1603
+ }
1604
+ return { ok: true, row };
1605
+ }
1606
+ function applyMaterializeInsert(spec, event, ctx, schema) {
1607
+ const table = schema[spec.table];
1608
+ if (!table) {
1609
+ return { ok: false, reason: `materialize table "${spec.table}" missing` };
1610
+ }
1611
+ const built = buildMaterializeRow(spec, event, ctx, table.columns);
1612
+ if (!built.ok)
1613
+ return built;
1614
+ ctx.insert(spec.table, built.row);
1615
+ return { ok: true };
1616
+ }
1617
+
1553
1618
  // src/runtime/print-validate.ts
1554
1619
  var COLUMN_TYPES = new Set([
1555
1620
  "text",
@@ -1629,21 +1694,62 @@ function validatePrintPayload(prints, topic, data) {
1629
1694
  return matchesTopic(declared, data);
1630
1695
  }
1631
1696
 
1632
- // src/runtime/runner.ts
1633
- var DEFAULT_ERROR_THRESHOLD = 50;
1634
- function camelizeKeys(obj) {
1635
- if (obj === null || obj === undefined)
1636
- return obj;
1637
- if (typeof obj !== "object")
1638
- return obj;
1639
- if (Array.isArray(obj))
1640
- return obj.map(camelizeKeys);
1641
- const result = {};
1642
- for (const [k, v] of Object.entries(obj)) {
1643
- result[camelizeDataKey(k)] = camelizeKeys(v);
1697
+ // src/runtime/violations.ts
1698
+ import { getTargetDb } from "@secondlayer/shared/db";
1699
+ import { logger as logger3 } from "@secondlayer/shared/logger";
1700
+ import { sql as sql2 } from "kysely";
1701
+ var VIOLATIONS_CAP_PER_SUBGRAPH = 100;
1702
+ function jsonSafe2(value) {
1703
+ if (typeof value === "bigint")
1704
+ return value.toString();
1705
+ if (value === null || value === undefined)
1706
+ return value;
1707
+ if (Array.isArray(value))
1708
+ return value.map(jsonSafe2);
1709
+ if (typeof value === "object") {
1710
+ const out = {};
1711
+ for (const [k, v] of Object.entries(value)) {
1712
+ out[k] = jsonSafe2(v);
1713
+ }
1714
+ return out;
1715
+ }
1716
+ return value;
1717
+ }
1718
+ async function defaultRecordPrintViolation(input) {
1719
+ try {
1720
+ const db = getTargetDb();
1721
+ const sample = jsonSafe2(input.samplePayload);
1722
+ await db.insertInto("subgraph_violations").values({
1723
+ subgraph_name: input.subgraphName,
1724
+ source_name: input.sourceName,
1725
+ block_height: input.blockHeight,
1726
+ tx_id: input.txId,
1727
+ reason: input.reason,
1728
+ sample_payload: sample
1729
+ }).execute();
1730
+ await sql2`
1731
+ DELETE FROM subgraph_violations
1732
+ WHERE id IN (
1733
+ SELECT id FROM subgraph_violations
1734
+ WHERE subgraph_name = ${input.subgraphName}
1735
+ ORDER BY seen_at DESC
1736
+ OFFSET ${VIOLATIONS_CAP_PER_SUBGRAPH}
1737
+ )
1738
+ `.execute(db);
1739
+ } catch (err) {
1740
+ logger3.warn("Failed to record print violation", {
1741
+ subgraph: input.subgraphName,
1742
+ error: err instanceof Error ? err.message : String(err)
1743
+ });
1644
1744
  }
1645
- return result;
1646
1745
  }
1746
+ var recorder = defaultRecordPrintViolation;
1747
+ async function recordPrintViolation(input) {
1748
+ await recorder(input);
1749
+ }
1750
+
1751
+ // src/runtime/runner.ts
1752
+ var DEFAULT_ERROR_THRESHOLD = 50;
1647
1753
  function decodeFunctionArgs2(args) {
1648
1754
  let parsed = args;
1649
1755
  if (typeof parsed === "string") {
@@ -1891,16 +1997,18 @@ async function runHandlers(subgraph, matched, ctx, opts) {
1891
1997
  units.sort((a, b) => (a.tx.tx_index ?? 0) - (b.tx.tx_index ?? 0) || (a.event?.event_index ?? -1) - (b.event?.event_index ?? -1));
1892
1998
  for (const { tx, event, sourceName } of units) {
1893
1999
  if (errors >= threshold) {
1894
- logger3.error("Subgraph error threshold reached, skipping remaining events", {
2000
+ logger4.error("Subgraph error threshold reached, skipping remaining events", {
1895
2001
  subgraph: subgraph.name,
1896
2002
  errors,
1897
2003
  threshold
1898
2004
  });
1899
2005
  return { processed, errors, ...skipped > 0 ? { skipped } : {} };
1900
2006
  }
2007
+ const filter = filterLookup.get(sourceName);
2008
+ const materialize = filter && "materialize" in filter && filter.materialize !== undefined ? filter.materialize : undefined;
1901
2009
  const handler = subgraph.handlers[sourceName] ?? subgraph.handlers["*"] ?? null;
1902
- if (!handler) {
1903
- logger3.warn("No handler found for source", {
2010
+ if (!handler && !materialize) {
2011
+ logger4.warn("No handler found for source", {
1904
2012
  subgraph: subgraph.name,
1905
2013
  sourceName,
1906
2014
  txId: tx.tx_id
@@ -1915,7 +2023,6 @@ async function runHandlers(subgraph, matched, ctx, opts) {
1915
2023
  contractId: tx.contract_id ?? null,
1916
2024
  functionName: tx.function_name ?? null
1917
2025
  });
1918
- const filter = filterLookup.get(sourceName);
1919
2026
  const checkpoint = ctx.opsCheckpoint();
1920
2027
  try {
1921
2028
  let payload;
@@ -1957,22 +2064,48 @@ async function runHandlers(subgraph, matched, ctx, opts) {
1957
2064
  const verdict = validatePrintPayload(filter.prints, printPayload.topic ?? "", printPayload.data ?? {});
1958
2065
  if (!verdict.ok) {
1959
2066
  skipped++;
1960
- logger3.warn("Print payload does not match declared prints schema", {
2067
+ logger4.warn("Print payload does not match declared prints schema", {
1961
2068
  subgraph: subgraph.name,
1962
2069
  sourceName,
1963
2070
  topic: printPayload.topic,
1964
2071
  txId: tx.tx_id,
1965
2072
  reason: verdict.reason
1966
2073
  });
2074
+ recordPrintViolation({
2075
+ subgraphName: subgraph.name,
2076
+ sourceName,
2077
+ blockHeight: ctx.block.height,
2078
+ txId: tx.tx_id,
2079
+ reason: verdict.reason,
2080
+ samplePayload: {
2081
+ topic: printPayload.topic ?? "",
2082
+ data: printPayload.data ?? {}
2083
+ }
2084
+ });
1967
2085
  continue;
1968
2086
  }
1969
2087
  }
1970
- await handler(payload, ctx);
1971
- processed++;
2088
+ if (materialize) {
2089
+ const result = applyMaterializeInsert(materialize, payload, ctx, subgraph.schema);
2090
+ if (!result.ok) {
2091
+ skipped++;
2092
+ logger4.warn("Materialize skipped: required field missing", {
2093
+ subgraph: subgraph.name,
2094
+ sourceName,
2095
+ txId: tx.tx_id,
2096
+ reason: result.reason
2097
+ });
2098
+ continue;
2099
+ }
2100
+ processed++;
2101
+ } else if (handler) {
2102
+ await handler(payload, ctx);
2103
+ processed++;
2104
+ }
1972
2105
  } catch (err) {
1973
2106
  ctx.rollbackTo(checkpoint);
1974
2107
  errors++;
1975
- logger3.error("Subgraph handler error", {
2108
+ logger4.error("Subgraph handler error", {
1976
2109
  subgraph: subgraph.name,
1977
2110
  sourceName,
1978
2111
  txId: tx.tx_id,
@@ -1984,7 +2117,7 @@ async function runHandlers(subgraph, matched, ctx, opts) {
1984
2117
  return { processed, errors, ...skipped > 0 ? { skipped } : {} };
1985
2118
  }
1986
2119
 
1987
- // src/testing/index.ts
2120
+ // src/testing/harness.ts
1988
2121
  import { normalizeAbi as normalizeAbi2, toCamelCase as toCamelCase3 } from "@secondlayer/stacks/clarity";
1989
2122
  var TEST_SCHEMA_NAME = "subgraph_test";
1990
2123
  function defaultBlock(overrides = {}) {
@@ -2052,6 +2185,20 @@ class InMemorySubgraphContext extends SubgraphContext {
2052
2185
  }
2053
2186
  this.ops.length = 0;
2054
2187
  }
2188
+ insertsSince(checkpoint) {
2189
+ const out = [];
2190
+ for (const op of this.ops.slice(Math.max(0, checkpoint))) {
2191
+ if (op.kind !== "insert")
2192
+ continue;
2193
+ const row = { ...op.data };
2194
+ out.push({
2195
+ table: op.table,
2196
+ keys: Object.keys(row).filter((k) => !k.startsWith("_")),
2197
+ row
2198
+ });
2199
+ }
2200
+ return out;
2201
+ }
2055
2202
  }
2056
2203
  function sameValue(a, b) {
2057
2204
  if (a === b)
@@ -2067,7 +2214,10 @@ function createTestContext(schema, options = {}) {
2067
2214
  const ctx = impl;
2068
2215
  ctx.rows = (table) => impl.rowsOf(table);
2069
2216
  ctx.commit = () => impl.commitOps();
2070
- ctx.setTx = (tx) => impl.setTx(defaultTx(tx));
2217
+ ctx.setTx = (tx) => {
2218
+ SubgraphContext.prototype.setTx.call(impl, defaultTx(tx));
2219
+ };
2220
+ ctx.pendingInsertsSince = (checkpoint) => impl.insertsSince(checkpoint);
2071
2221
  return ctx;
2072
2222
  }
2073
2223
  function buildEvent(source, payload, options = {}) {
@@ -2087,11 +2237,284 @@ function buildEvent(source, payload, options = {}) {
2087
2237
  }
2088
2238
  return { ...base, ...payload };
2089
2239
  }
2240
+ // src/testing/run.ts
2241
+ function toHandlerPayload(_filter, row) {
2242
+ if (row.event_type === "print") {
2243
+ const payload = row.payload;
2244
+ return {
2245
+ contractId: row.contract_id ?? "",
2246
+ topic: payload?.topic ?? "",
2247
+ data: camelizeKeys(payload?.value) ?? {}
2248
+ };
2249
+ }
2250
+ const r = row;
2251
+ return {
2252
+ ...r.sender !== undefined ? { sender: r.sender } : {},
2253
+ ...r.recipient !== undefined ? { recipient: r.recipient } : {},
2254
+ ...r.amount !== undefined ? { amount: BigInt(String(r.amount)) } : {},
2255
+ ...r.asset_identifier !== undefined ? { assetIdentifier: r.asset_identifier } : {},
2256
+ ...r.value !== undefined ? { tokenId: r.value } : {}
2257
+ };
2258
+ }
2259
+ function toContractCallPayload(row) {
2260
+ return {
2261
+ type: "contract_call",
2262
+ sender: row.sender,
2263
+ contractId: row.contract_id ?? "",
2264
+ functionName: row.function_name ?? "",
2265
+ args: Array.isArray(row.args) ? row.args : [],
2266
+ result: row.result ?? null,
2267
+ resultHex: row.result_hex ?? null,
2268
+ tx: {
2269
+ txId: row.tx_id,
2270
+ sender: row.sender,
2271
+ type: "contract_call",
2272
+ status: row.status,
2273
+ contractId: row.contract_id ?? null,
2274
+ functionName: row.function_name ?? null
2275
+ }
2276
+ };
2277
+ }
2278
+ function jsonSafe3(value) {
2279
+ if (typeof value === "bigint")
2280
+ return value.toString();
2281
+ if (value === null || value === undefined)
2282
+ return value;
2283
+ if (Array.isArray(value))
2284
+ return value.map(jsonSafe3);
2285
+ if (typeof value === "object") {
2286
+ const out = {};
2287
+ for (const [k, v] of Object.entries(value)) {
2288
+ out[k] = jsonSafe3(v);
2289
+ }
2290
+ return out;
2291
+ }
2292
+ return value;
2293
+ }
2294
+ function dataKeysOf(data) {
2295
+ if (data && typeof data === "object" && !Array.isArray(data)) {
2296
+ return Object.keys(data);
2297
+ }
2298
+ return [];
2299
+ }
2300
+ function rowMeta(row) {
2301
+ const r = row;
2302
+ return {
2303
+ ...typeof r.block_height === "number" ? { blockHeight: r.block_height } : {},
2304
+ ...typeof r.tx_id === "string" ? { txId: r.tx_id } : {}
2305
+ };
2306
+ }
2307
+ function inKeysOf(filter, payload) {
2308
+ if (filter?.type === "contract_call") {
2309
+ return ["functionName", "args", "sender", "contractId"].filter((k) => payload[k] !== undefined);
2310
+ }
2311
+ return dataKeysOf(payload.data);
2312
+ }
2313
+ function unusedInKeysAcross(traces) {
2314
+ if (traces.length === 0)
2315
+ return;
2316
+ const alwaysIn = new Set(traces[0]?.inKeys ?? []);
2317
+ for (const t of traces.slice(1)) {
2318
+ const keys = new Set(t.inKeys);
2319
+ for (const k of [...alwaysIn]) {
2320
+ if (!keys.has(k))
2321
+ alwaysIn.delete(k);
2322
+ }
2323
+ }
2324
+ const outKeys = new Set;
2325
+ for (const t of traces) {
2326
+ for (const o of t.outs) {
2327
+ for (const k of o.keys)
2328
+ outKeys.add(k);
2329
+ }
2330
+ }
2331
+ const outNormalized = new Set([...outKeys].map((k) => k.replace(/_/g, "").toLowerCase()));
2332
+ const unused = [...alwaysIn].filter((k) => !outNormalized.has(k.replace(/_/g, "").toLowerCase()));
2333
+ return unused.length > 0 ? unused : undefined;
2334
+ }
2335
+ async function runSubgraphTest(input) {
2336
+ const { schema, handlers, sources, events, trace } = input;
2337
+ const sourceNames = Object.keys(sources);
2338
+ if (sourceNames.length === 0) {
2339
+ return {
2340
+ ok: false,
2341
+ code: "NO_SOURCES",
2342
+ matched: 0,
2343
+ written: 0,
2344
+ tables: [],
2345
+ hint: "No sources to test."
2346
+ };
2347
+ }
2348
+ const ctx = createTestContext(schema);
2349
+ let matched = 0;
2350
+ let firstEvent;
2351
+ const traces = [];
2352
+ for (const [name, rows] of Object.entries(events)) {
2353
+ const filter = sources[name];
2354
+ const handler = handlers[name] ?? handlers["*"];
2355
+ const materialize = filter && "materialize" in filter && filter.materialize !== undefined ? filter.materialize : undefined;
2356
+ if (typeof handler !== "function" && !materialize)
2357
+ continue;
2358
+ for (const row of rows) {
2359
+ const payload = filter?.type === "contract_call" ? toContractCallPayload(row) : toHandlerPayload(filter, row);
2360
+ if (filter?.type === "print_event" && filter.topic && payload.topic !== filter.topic) {
2361
+ continue;
2362
+ }
2363
+ if (!firstEvent) {
2364
+ firstEvent = {
2365
+ source: name,
2366
+ data: filter?.type === "contract_call" ? {
2367
+ functionName: payload.functionName,
2368
+ args: payload.args
2369
+ } : payload.data
2370
+ };
2371
+ }
2372
+ matched++;
2373
+ const checkpoint = ctx.opsCheckpoint();
2374
+ try {
2375
+ const event = buildEvent(filter, payload);
2376
+ if (materialize) {
2377
+ applyMaterializeInsert(materialize, event, {
2378
+ tx: ctx.tx,
2379
+ block: ctx.block,
2380
+ insert: (table, insertRow) => {
2381
+ ctx.insert(table, insertRow);
2382
+ }
2383
+ }, schema);
2384
+ } else {
2385
+ await handler(event, ctx);
2386
+ }
2387
+ } catch {}
2388
+ if (trace) {
2389
+ const inserts = ctx.pendingInsertsSince(checkpoint);
2390
+ traces.push({
2391
+ source: name,
2392
+ ...rowMeta(row),
2393
+ inKeys: inKeysOf(filter, payload),
2394
+ outs: inserts.map((i) => ({ table: i.table, keys: i.keys }))
2395
+ });
2396
+ }
2397
+ }
2398
+ }
2399
+ const tableNames = Object.keys(schema);
2400
+ const tables = [];
2401
+ let written = 0;
2402
+ for (const table of tableNames) {
2403
+ const tableRows = await ctx.rows(table);
2404
+ written += tableRows.length;
2405
+ const sample = tableRows[0];
2406
+ tables.push({
2407
+ name: table,
2408
+ rows: tableRows.length,
2409
+ ...sample ? { sampleRow: jsonSafe3(sample) } : {}
2410
+ });
2411
+ }
2412
+ const fetched = Object.values(events).reduce((n, r) => n + r.length, 0);
2413
+ const unusedInKeys = trace ? unusedInKeysAcross(traces) : undefined;
2414
+ const traceFields = {
2415
+ ...trace ? { traces } : {},
2416
+ ...unusedInKeys ? { unusedInKeys } : {}
2417
+ };
2418
+ if (fetched === 0) {
2419
+ return {
2420
+ ok: false,
2421
+ code: "NO_EVENTS",
2422
+ matched: 0,
2423
+ written,
2424
+ tables,
2425
+ hint: "No events matched these sources in the given range. Widen the range or check the source filters.",
2426
+ ...traceFields
2427
+ };
2428
+ }
2429
+ if (written === 0) {
2430
+ const keys = dataKeysOf(firstEvent?.data);
2431
+ const keyPart = keys.length > 0 ? ` Observed event.data keys on first event (${firstEvent?.source}): ${keys.join(", ")}.` : firstEvent ? ` First event source: ${firstEvent.source}.` : "";
2432
+ return {
2433
+ ok: false,
2434
+ code: "EMPTY_MAPPING",
2435
+ matched,
2436
+ written: 0,
2437
+ tables,
2438
+ firstEvent: firstEvent ? { ...firstEvent, data: jsonSafe3(firstEvent.data) } : undefined,
2439
+ hint: `${matched} event${matched === 1 ? "" : "s"} matched, but NO rows were written — the shape of the field-mapping bug that ships a 0-row subgraph.${keyPart} Map only observed keys; do not invent fields.`,
2440
+ ...traceFields
2441
+ };
2442
+ }
2443
+ if (trace && traces.some((t) => t.outs.length === 0)) {
2444
+ const empty = traces.find((t) => t.outs.length === 0);
2445
+ const keyPart = empty && empty.inKeys.length > 0 ? ` Empty event (${empty.source}) IN keys: ${empty.inKeys.join(", ")}.` : "";
2446
+ return {
2447
+ ok: false,
2448
+ code: "EMPTY_MAPPING",
2449
+ matched,
2450
+ written,
2451
+ tables,
2452
+ firstEvent: firstEvent ? { ...firstEvent, data: jsonSafe3(firstEvent.data) } : undefined,
2453
+ hint: `At least one matched event wrote 0 rows.${keyPart}`,
2454
+ ...traceFields
2455
+ };
2456
+ }
2457
+ return {
2458
+ ok: true,
2459
+ matched,
2460
+ written,
2461
+ tables,
2462
+ firstEvent: firstEvent ? { ...firstEvent, data: jsonSafe3(firstEvent.data) } : undefined,
2463
+ ...traceFields
2464
+ };
2465
+ }
2466
+ async function probeHandlers(def, samples) {
2467
+ const ctx = createTestContext(def.schema);
2468
+ let matched = 0;
2469
+ let firstEventKeys;
2470
+ for (const sample of samples) {
2471
+ const filter = def.sources[sample.source];
2472
+ if (!filter)
2473
+ continue;
2474
+ const handler = def.handlers[sample.source] ?? def.handlers["*"];
2475
+ const materialize = "materialize" in filter && filter.materialize !== undefined ? filter.materialize : undefined;
2476
+ if (typeof handler !== "function" && !materialize)
2477
+ continue;
2478
+ if (firstEventKeys === undefined) {
2479
+ firstEventKeys = dataKeysOf(sample.event.data);
2480
+ }
2481
+ matched++;
2482
+ try {
2483
+ const event = buildEvent(filter, sample.event);
2484
+ if (materialize) {
2485
+ applyMaterializeInsert(materialize, event, {
2486
+ tx: ctx.tx,
2487
+ block: ctx.block,
2488
+ insert: (table, row) => {
2489
+ ctx.insert(table, row);
2490
+ }
2491
+ }, def.schema);
2492
+ } else {
2493
+ await handler(event, ctx);
2494
+ }
2495
+ } catch {}
2496
+ }
2497
+ const tables = Object.keys(def.schema);
2498
+ let written = 0;
2499
+ for (const table of tables) {
2500
+ written += (await ctx.rows(table)).length;
2501
+ }
2502
+ return {
2503
+ matched,
2504
+ written,
2505
+ tables,
2506
+ ...firstEventKeys !== undefined ? { firstEventKeys } : {}
2507
+ };
2508
+ }
2090
2509
  export {
2510
+ toHandlerPayload,
2511
+ toContractCallPayload,
2512
+ runSubgraphTest,
2513
+ probeHandlers,
2091
2514
  createTestContext,
2092
2515
  buildEventPayload,
2093
2516
  buildEvent
2094
2517
  };
2095
2518
 
2096
- //# debugId=A471864525222B1C64756E2164756E21
2519
+ //# debugId=ABB2AE2A4DA0D8AC64756E2164756E21
2097
2520
  //# sourceMappingURL=index.js.map