@secondlayer/subgraphs 4.4.1 → 5.0.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 (64) hide show
  1. package/README.md +13 -21
  2. package/dist/{src/index.d.ts → index.d.ts} +59 -189
  3. package/dist/index.js +2151 -0
  4. package/dist/index.js.map +24 -0
  5. package/dist/{src/runtime → runtime}/emitter.d.ts +1 -1
  6. package/dist/{src/runtime → runtime}/emitter.js +8 -12
  7. package/dist/{src/runtime → runtime}/emitter.js.map +3 -3
  8. package/dist/{src/runtime → runtime}/replay.d.ts +1 -1
  9. package/dist/{src/runtime → runtime}/replay.js +342 -683
  10. package/dist/{src/runtime → runtime}/replay.js.map +5 -5
  11. package/dist/{src/schema → schema}/index.d.ts +10 -69
  12. package/dist/{src/schema → schema}/index.js +188 -281
  13. package/dist/schema/index.js.map +13 -0
  14. package/dist/{src/testing → testing}/index.d.ts +65 -115
  15. package/dist/{src/testing → testing}/index.js +554 -968
  16. package/dist/testing/index.js.map +21 -0
  17. package/dist/{src/types.d.ts → types.d.ts} +10 -66
  18. package/dist/{src/validate.d.ts → validate.d.ts} +11 -69
  19. package/dist/{src/validate.js → validate.js} +18 -98
  20. package/dist/validate.js.map +10 -0
  21. package/package.json +18 -26
  22. package/dist/src/index.js +0 -6081
  23. package/dist/src/index.js.map +0 -44
  24. package/dist/src/runtime/block-processor.d.ts +0 -559
  25. package/dist/src/runtime/block-processor.js +0 -3810
  26. package/dist/src/runtime/block-processor.js.map +0 -31
  27. package/dist/src/runtime/catchup.d.ts +0 -473
  28. package/dist/src/runtime/catchup.js +0 -4228
  29. package/dist/src/runtime/catchup.js.map +0 -35
  30. package/dist/src/runtime/clarity.d.ts +0 -15
  31. package/dist/src/runtime/clarity.js +0 -41
  32. package/dist/src/runtime/clarity.js.map +0 -10
  33. package/dist/src/runtime/context.d.ts +0 -325
  34. package/dist/src/runtime/context.js +0 -936
  35. package/dist/src/runtime/context.js.map +0 -13
  36. package/dist/src/runtime/processor.d.ts +0 -11
  37. package/dist/src/runtime/processor.js +0 -5372
  38. package/dist/src/runtime/processor.js.map +0 -40
  39. package/dist/src/runtime/reindex.d.ts +0 -539
  40. package/dist/src/runtime/reindex.js +0 -4802
  41. package/dist/src/runtime/reindex.js.map +0 -37
  42. package/dist/src/runtime/reorg.d.ts +0 -432
  43. package/dist/src/runtime/reorg.js +0 -4355
  44. package/dist/src/runtime/reorg.js.map +0 -36
  45. package/dist/src/runtime/runner.d.ts +0 -719
  46. package/dist/src/runtime/runner.js +0 -1332
  47. package/dist/src/runtime/runner.js.map +0 -16
  48. package/dist/src/runtime/source-matcher.d.ts +0 -304
  49. package/dist/src/runtime/source-matcher.js +0 -335
  50. package/dist/src/runtime/source-matcher.js.map +0 -10
  51. package/dist/src/runtime/stats.d.ts +0 -23
  52. package/dist/src/runtime/stats.js +0 -71
  53. package/dist/src/runtime/stats.js.map +0 -10
  54. package/dist/src/schema/index.js.map +0 -13
  55. package/dist/src/service.d.ts +0 -0
  56. package/dist/src/service.js +0 -5398
  57. package/dist/src/service.js.map +0 -41
  58. package/dist/src/testing/index.js.map +0 -22
  59. package/dist/src/triggers/index.d.ts +0 -381
  60. package/dist/src/triggers/index.js +0 -31
  61. package/dist/src/triggers/index.js.map +0 -10
  62. package/dist/src/validate.js.map +0 -10
  63. /package/dist/{src/types.js → types.js} +0 -0
  64. /package/dist/{src/types.js.map → types.js.map} +0 -0
@@ -1,331 +1,3 @@
1
- import { createRequire } from "node:module";
2
- var __require = /* @__PURE__ */ createRequire(import.meta.url);
3
-
4
- // src/runtime/source-matcher.ts
5
- var patternCache = new Map;
6
- function matchAny(value, pattern) {
7
- return Array.isArray(pattern) ? pattern.some((p) => matchPattern(value, p)) : matchPattern(value, pattern);
8
- }
9
- function matchPattern(value, pattern) {
10
- if (!pattern.includes("*"))
11
- return value === pattern;
12
- let re = patternCache.get(pattern);
13
- if (!re) {
14
- const regex = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
15
- re = new RegExp(`^${regex}$`);
16
- patternCache.set(pattern, re);
17
- }
18
- return re.test(value);
19
- }
20
- var EMPTY_SET = new Set;
21
- function factoryAllows(filter, contractId, factoryContracts) {
22
- if (!filter.factory)
23
- return true;
24
- if (!contractId)
25
- return false;
26
- return (factoryContracts.get(filter.factory.from) ?? EMPTY_SET).has(contractId);
27
- }
28
- function readPath(payload, path) {
29
- let cur = payload;
30
- for (const key of path.split(".")) {
31
- if (cur === null || typeof cur !== "object")
32
- return;
33
- cur = cur[key];
34
- }
35
- return cur;
36
- }
37
- function traitAllows(filter, contractId, traitContracts) {
38
- const trait = filter.trait;
39
- if (!trait)
40
- return true;
41
- if (!contractId)
42
- return false;
43
- return (traitContracts.get(trait) ?? EMPTY_SET).has(contractId);
44
- }
45
- function assetContract(assetId) {
46
- return assetId?.split("::")[0];
47
- }
48
- function printContractId(data) {
49
- if (!data)
50
- return;
51
- const id = (typeof data.contract_identifier === "string" ? data.contract_identifier : undefined) ?? (typeof data.contract_id === "string" ? data.contract_id : undefined);
52
- return id || undefined;
53
- }
54
- function matchFilter(filter, transactions, eventsByTx, traitContracts, factoryContracts = new Map, vmEventsByTx = new Map) {
55
- const results = [];
56
- switch (filter.type) {
57
- case "stx_transfer":
58
- case "stx_mint":
59
- case "stx_burn":
60
- case "stx_lock": {
61
- const eventType = `${filter.type}_event`;
62
- for (const tx of transactions) {
63
- const txEvents = eventsByTx.get(tx.tx_id) ?? [];
64
- const matched = txEvents.filter((e) => e.type === eventType);
65
- if (matched.length === 0)
66
- continue;
67
- const filtered = matched.filter((e) => {
68
- const data = e.data;
69
- if (!data)
70
- return false;
71
- if ("sender" in filter && filter.sender) {
72
- if (!matchPattern(data.sender, filter.sender))
73
- return false;
74
- }
75
- if ("recipient" in filter && filter.recipient) {
76
- if (!matchPattern(data.recipient, filter.recipient))
77
- return false;
78
- }
79
- if ("lockedAddress" in filter && filter.lockedAddress) {
80
- if (!matchPattern(data.locked_address, filter.lockedAddress))
81
- return false;
82
- }
83
- if ("minAmount" in filter && filter.minAmount !== undefined) {
84
- const amount = BigInt(data.amount ?? data.locked_amount ?? "0");
85
- if (amount < filter.minAmount)
86
- return false;
87
- }
88
- if ("maxAmount" in filter && filter.maxAmount !== undefined) {
89
- const amount = BigInt(data.amount ?? "0");
90
- if (amount > filter.maxAmount)
91
- return false;
92
- }
93
- return true;
94
- });
95
- if (filtered.length > 0) {
96
- results.push({ tx, events: filtered });
97
- }
98
- }
99
- break;
100
- }
101
- case "ft_transfer":
102
- case "ft_mint":
103
- case "ft_burn": {
104
- const eventType = `${filter.type}_event`;
105
- for (const tx of transactions) {
106
- const txEvents = eventsByTx.get(tx.tx_id) ?? [];
107
- const matched = txEvents.filter((e) => {
108
- if (e.type !== eventType)
109
- return false;
110
- const data = e.data;
111
- if (!data)
112
- return false;
113
- if (filter.assetIdentifier) {
114
- const assetId = data.asset_identifier;
115
- if (!assetId || !matchPattern(assetId, filter.assetIdentifier))
116
- return false;
117
- }
118
- if (!traitAllows(filter, assetContract(data.asset_identifier), traitContracts))
119
- return false;
120
- if ("sender" in filter && filter.sender) {
121
- if (!matchPattern(data.sender, filter.sender))
122
- return false;
123
- }
124
- if ("recipient" in filter && filter.recipient) {
125
- if (!matchPattern(data.recipient, filter.recipient))
126
- return false;
127
- }
128
- if (filter.minAmount !== undefined) {
129
- const amount = BigInt(data.amount ?? "0");
130
- if (amount < filter.minAmount)
131
- return false;
132
- }
133
- return true;
134
- });
135
- if (matched.length > 0) {
136
- results.push({ tx, events: matched });
137
- }
138
- }
139
- break;
140
- }
141
- case "nft_transfer":
142
- case "nft_mint":
143
- case "nft_burn": {
144
- const eventType = `${filter.type}_event`;
145
- for (const tx of transactions) {
146
- const txEvents = eventsByTx.get(tx.tx_id) ?? [];
147
- const matched = txEvents.filter((e) => {
148
- if (e.type !== eventType)
149
- return false;
150
- const data = e.data;
151
- if (!data)
152
- return false;
153
- if (filter.assetIdentifier) {
154
- const assetId = data.asset_identifier;
155
- if (!assetId || !matchPattern(assetId, filter.assetIdentifier))
156
- return false;
157
- }
158
- if (!traitAllows(filter, assetContract(data.asset_identifier), traitContracts))
159
- return false;
160
- if ("sender" in filter && filter.sender) {
161
- if (!matchPattern(data.sender, filter.sender))
162
- return false;
163
- }
164
- if ("recipient" in filter && filter.recipient) {
165
- if (!matchPattern(data.recipient, filter.recipient))
166
- return false;
167
- }
168
- return true;
169
- });
170
- if (matched.length > 0) {
171
- results.push({ tx, events: matched });
172
- }
173
- }
174
- break;
175
- }
176
- case "contract_call": {
177
- for (const tx of transactions) {
178
- if (tx.type !== "contract_call")
179
- continue;
180
- if (filter.contractId) {
181
- if (!tx.contract_id || !matchAny(tx.contract_id, filter.contractId))
182
- continue;
183
- }
184
- if (!factoryAllows(filter, tx.contract_id, factoryContracts))
185
- continue;
186
- if (filter.functionName) {
187
- if (!tx.function_name || !matchPattern(tx.function_name, filter.functionName))
188
- continue;
189
- }
190
- if (filter.caller) {
191
- if (!matchPattern(tx.sender, filter.caller))
192
- continue;
193
- }
194
- if (!traitAllows(filter, tx.contract_id, traitContracts))
195
- continue;
196
- const txEvents = eventsByTx.get(tx.tx_id) ?? [];
197
- results.push({ tx, events: txEvents });
198
- }
199
- break;
200
- }
201
- case "contract_deploy": {
202
- for (const tx of transactions) {
203
- if (tx.type !== "smart_contract")
204
- continue;
205
- if (filter.deployer) {
206
- if (!matchPattern(tx.sender, filter.deployer))
207
- continue;
208
- }
209
- if (filter.contractName) {
210
- const name = tx.contract_id?.split(".")[1] ?? "";
211
- if (!matchPattern(name, filter.contractName))
212
- continue;
213
- }
214
- const txEvents = eventsByTx.get(tx.tx_id) ?? [];
215
- results.push({ tx, events: txEvents });
216
- }
217
- break;
218
- }
219
- case "nested_contract_call":
220
- case "var_set":
221
- case "map_set":
222
- case "map_insert":
223
- case "map_delete": {
224
- for (const tx of transactions) {
225
- const txEvents = vmEventsByTx.get(tx.tx_id) ?? [];
226
- const matched = txEvents.filter((e) => {
227
- if (e.type !== filter.type)
228
- return false;
229
- const data = e.data;
230
- if (!data)
231
- return false;
232
- const contractId = printContractId(data);
233
- if ("contractId" in filter && filter.contractId) {
234
- if (!contractId || !matchAny(contractId, filter.contractId))
235
- return false;
236
- }
237
- if (!traitAllows(filter, contractId, traitContracts))
238
- return false;
239
- if (!factoryAllows(filter, contractId, factoryContracts))
240
- return false;
241
- if (filter.type === "nested_contract_call") {
242
- if (filter.functionName) {
243
- if (typeof data.function_name !== "string" || !matchPattern(data.function_name, filter.functionName))
244
- return false;
245
- }
246
- if (filter.caller) {
247
- if (typeof data.caller !== "string" || !matchPattern(data.caller, filter.caller))
248
- return false;
249
- }
250
- if (filter.sender) {
251
- if (typeof data.sender !== "string" || !matchPattern(data.sender, filter.sender))
252
- return false;
253
- }
254
- }
255
- if (filter.type === "var_set" && filter.varName) {
256
- if (typeof data.var_name !== "string" || !matchPattern(data.var_name, filter.varName))
257
- return false;
258
- }
259
- if ((filter.type === "map_set" || filter.type === "map_insert" || filter.type === "map_delete") && filter.map) {
260
- if (typeof data.map_name !== "string" || !matchPattern(data.map_name, filter.map))
261
- return false;
262
- }
263
- return true;
264
- });
265
- if (matched.length > 0)
266
- results.push({ tx, events: matched });
267
- }
268
- break;
269
- }
270
- case "print_event": {
271
- for (const tx of transactions) {
272
- const txEvents = eventsByTx.get(tx.tx_id) ?? [];
273
- const matched = txEvents.filter((e) => {
274
- if (e.type !== "smart_contract_event" && e.type !== "contract_event")
275
- return false;
276
- const data = e.data;
277
- if (!data)
278
- return false;
279
- if (data.topic !== "print")
280
- return false;
281
- const contractId = printContractId(data);
282
- if (filter.contractId) {
283
- if (!contractId || !matchAny(contractId, filter.contractId))
284
- return false;
285
- }
286
- if (!traitAllows(filter, contractId, traitContracts))
287
- return false;
288
- if (!factoryAllows(filter, contractId, factoryContracts))
289
- return false;
290
- return true;
291
- });
292
- if (matched.length > 0) {
293
- results.push({ tx, events: matched });
294
- }
295
- }
296
- break;
297
- }
298
- }
299
- return results;
300
- }
301
- function matchSources(sources, transactions, events, traitContracts = new Map, factoryContracts = new Map, vmEvents = []) {
302
- const eventsByTx = new Map;
303
- for (const event of events) {
304
- const list = eventsByTx.get(event.tx_id) ?? [];
305
- list.push(event);
306
- eventsByTx.set(event.tx_id, list);
307
- }
308
- const vmEventsByTx = new Map;
309
- for (const event of vmEvents) {
310
- const list = vmEventsByTx.get(event.tx_id) ?? [];
311
- list.push(event);
312
- vmEventsByTx.set(event.tx_id, list);
313
- }
314
- const seen = new Set;
315
- const results = [];
316
- for (const [sourceName, filter] of Object.entries(sources)) {
317
- const matches = matchFilter(filter, transactions, eventsByTx, traitContracts, factoryContracts, vmEventsByTx);
318
- for (const match of matches) {
319
- const dedupeKey = `${match.tx.tx_id}:${sourceName}`;
320
- if (!seen.has(dedupeKey)) {
321
- seen.add(dedupeKey);
322
- results.push({ ...match, sourceName });
323
- }
324
- }
325
- }
326
- return results;
327
- }
328
-
329
1
  // src/runtime/replay.ts
330
2
  import { createHash } from "node:crypto";
331
3
  import { getTargetDb } from "@secondlayer/shared/db";
@@ -408,201 +80,11 @@ async function loadBlockRange(db, fromHeight, toHeight) {
408
80
  }
409
81
  return result;
410
82
  }
411
- function avgEventsPerBlock(batch) {
412
- if (batch.size === 0)
413
- return 0;
414
- let totalEvents = 0;
415
- for (const data of batch.values()) {
416
- totalEvents += data.events.length;
417
- }
418
- return totalEvents / batch.size;
419
- }
420
83
 
421
84
  // src/runtime/observer-http-source.ts
422
85
  import {
423
86
  VM_NODE_TO_STORED_TYPE
424
87
  } from "@secondlayer/shared";
425
- var OBSERVER_HTTP_EXPORT_PATH = "/internal/observer-events";
426
- function stripTrailingSlash(url) {
427
- return url.replace(/\/+$/, "");
428
- }
429
- function blockTimestamp(payload) {
430
- for (const value of [
431
- payload.timestamp,
432
- payload.block_time,
433
- payload.burn_block_time,
434
- payload.burn_block_timestamp
435
- ]) {
436
- if (typeof value === "number" && Number.isFinite(value) && value > 0) {
437
- return value;
438
- }
439
- }
440
- return 0;
441
- }
442
- function eventData(event) {
443
- if (typeof event.type === "string" && event.type in event) {
444
- return event[event.type] ?? {};
445
- }
446
- return {};
447
- }
448
- function mapNewBlockPayloadToBlockData(payload) {
449
- const p = payload;
450
- if (typeof p.block_height !== "number" || !Number.isFinite(p.block_height) || typeof p.index_block_hash !== "string" || p.index_block_hash.length === 0) {
451
- throw new Error("observer /new_block payload missing block_height or index_block_hash");
452
- }
453
- const height = p.block_height;
454
- const block = {
455
- height,
456
- hash: p.block_hash ?? "",
457
- parent_hash: p.parent_block_hash ?? "",
458
- burn_block_height: p.burn_block_height ?? 0,
459
- burn_block_hash: p.burn_block_hash ?? null,
460
- index_block_hash: p.index_block_hash,
461
- timestamp: blockTimestamp(p),
462
- canonical: true,
463
- created_at: new Date(0)
464
- };
465
- const txs = (p.transactions ?? []).map((tx) => {
466
- let contractId = null;
467
- let functionName = null;
468
- if (tx.tx_type === "contract_call" && tx.contract_call) {
469
- contractId = tx.contract_call.contract_id ?? null;
470
- functionName = tx.contract_call.function_name ?? null;
471
- } else if (tx.tx_type === "smart_contract" && tx.smart_contract) {
472
- contractId = tx.smart_contract.contract_id ?? null;
473
- }
474
- return {
475
- tx_id: tx.txid ?? "",
476
- block_height: height,
477
- tx_index: tx.tx_index ?? 0,
478
- type: tx.tx_type ?? "",
479
- sender: tx.sender_address ?? "",
480
- status: tx.status ?? "success",
481
- contract_id: contractId,
482
- function_name: functionName,
483
- function_args: [],
484
- raw_result: tx.raw_result ?? null,
485
- raw_tx: tx.raw_tx ?? "",
486
- created_at: new Date(0)
487
- };
488
- });
489
- const events = (p.events ?? []).map((event) => {
490
- const txId = event.txid ?? "";
491
- const eventIndex = event.event_index ?? 0;
492
- return {
493
- id: `${txId}#${eventIndex}`,
494
- tx_id: txId,
495
- block_height: height,
496
- event_index: eventIndex,
497
- type: event.type ?? "",
498
- data: eventData(event),
499
- created_at: new Date(0)
500
- };
501
- });
502
- const vmEvents = [];
503
- for (const [i, trace] of (p.vm_events ?? []).entries()) {
504
- const nodeType = trace.type;
505
- if (typeof nodeType !== "string" || !(nodeType in VM_NODE_TO_STORED_TYPE))
506
- continue;
507
- const stored = VM_NODE_TO_STORED_TYPE[nodeType];
508
- const body = trace[nodeType];
509
- if (!body || typeof body !== "object")
510
- continue;
511
- const txId = trace.txid ?? "";
512
- const vmIndex = i;
513
- vmEvents.push({
514
- id: vmEventId(txId, vmIndex),
515
- tx_id: txId,
516
- block_height: height,
517
- event_index: vmIndex,
518
- type: stored,
519
- data: body,
520
- created_at: new Date(0),
521
- clock: "vm"
522
- });
523
- }
524
- return { block, txs, events, vmEvents };
525
- }
526
-
527
- class ObserverHttpBlockSource {
528
- baseUrl;
529
- token;
530
- fetchFn;
531
- constructor(opts) {
532
- this.baseUrl = stripTrailingSlash(opts.baseUrl);
533
- this.token = opts.token ?? null;
534
- this.fetchFn = opts.fetch ?? globalThis.fetch.bind(globalThis);
535
- }
536
- headers() {
537
- if (!this.token)
538
- return {};
539
- return { Authorization: `Bearer ${this.token}` };
540
- }
541
- async getTip() {
542
- const res = await this.fetchFn(`${this.baseUrl}${OBSERVER_HTTP_EXPORT_PATH}/tip`, { headers: this.headers() });
543
- if (!res.ok) {
544
- throw new Error(`observer tip HTTP ${res.status}: ${await res.text().catch(() => "")}`);
545
- }
546
- let body;
547
- try {
548
- body = await res.json();
549
- } catch (err) {
550
- throw new Error(`observer tip invalid JSON: ${err instanceof Error ? err.message : String(err)}`);
551
- }
552
- if (typeof body.block_height !== "number" || !Number.isFinite(body.block_height)) {
553
- throw new Error("observer tip missing block_height");
554
- }
555
- return body.block_height;
556
- }
557
- async loadBlockRange(fromHeight, toHeight) {
558
- const result = new Map;
559
- let afterHeight = fromHeight > 0 ? fromHeight - 1 : undefined;
560
- let afterIndexBlockHash;
561
- for (;; ) {
562
- const url = new URL(`${this.baseUrl}${OBSERVER_HTTP_EXPORT_PATH}`);
563
- url.searchParams.set("path", "/new_block");
564
- url.searchParams.set("limit", "100");
565
- if (afterHeight != null) {
566
- url.searchParams.set("after_height", String(afterHeight));
567
- }
568
- if (afterIndexBlockHash) {
569
- url.searchParams.set("after_index_block_hash", afterIndexBlockHash);
570
- }
571
- const res = await this.fetchFn(url.toString(), {
572
- headers: this.headers()
573
- });
574
- if (!res.ok) {
575
- throw new Error(`observer events HTTP ${res.status}: ${await res.text().catch(() => "")}`);
576
- }
577
- let body;
578
- try {
579
- body = await res.json();
580
- } catch (err) {
581
- throw new Error(`observer events invalid JSON: ${err instanceof Error ? err.message : String(err)}`);
582
- }
583
- let pastTo = false;
584
- for (const message of body.events ?? []) {
585
- if (message.path === "/new_burn_block")
586
- continue;
587
- const height = message.payload?.block_height;
588
- if (typeof height === "number" && height > toHeight) {
589
- pastTo = true;
590
- break;
591
- }
592
- const data = mapNewBlockPayloadToBlockData(message.payload);
593
- const h = data.block.height;
594
- if (h >= fromHeight && h <= toHeight) {
595
- result.set(h, data);
596
- }
597
- }
598
- if (pastTo || body.next == null)
599
- break;
600
- afterHeight = body.next.after_height;
601
- afterIndexBlockHash = body.next.after_index_block_hash;
602
- }
603
- return result;
604
- }
605
- }
606
88
 
607
89
  // src/runtime/reconstruct.ts
608
90
  function isoToUnixSeconds(iso) {
@@ -816,9 +298,6 @@ function sourceFilters(subgraph) {
816
298
  const sources = subgraph.sources;
817
299
  return Array.isArray(sources) ? sources : Object.values(sources);
818
300
  }
819
- function needsTransactionData(subgraph) {
820
- return sourceFilters(subgraph).some((f) => TX_SOURCE_TYPES.has(f.type));
821
- }
822
301
  function synthesizeTxsFromEvents(events) {
823
302
  const byId = new Map;
824
303
  for (const e of events) {
@@ -840,25 +319,6 @@ function indexEventTypesForFilterTypes(filterTypes) {
840
319
  }
841
320
  return [...types];
842
321
  }
843
- function referencedIndexEventTypes(subgraph) {
844
- if (!subgraph.sources)
845
- return [];
846
- return indexEventTypesForFilterTypes(sourceFilters(subgraph).map((f) => f.type));
847
- }
848
- function isStreamsIndexEligible(subgraph) {
849
- if (Array.isArray(subgraph.sources))
850
- return false;
851
- const filters = sourceFilters(subgraph);
852
- if (filters.length === 0)
853
- return false;
854
- for (const f of filters) {
855
- const known = EVENT_FILTER_TO_INDEX_TYPE[f.type] || TX_SOURCE_TYPES.has(f.type);
856
- if (!known)
857
- return false;
858
- }
859
- return true;
860
- }
861
-
862
322
  class PublicApiBlockSource {
863
323
  http;
864
324
  eventTypes;
@@ -898,159 +358,358 @@ class PublicApiBlockSource {
898
358
  events: []
899
359
  });
900
360
  }
901
- const txs = this.needsTransactions ? txRows.map(reconstructTransaction) : synthesizeTxsFromEvents(eventLists.flat());
902
- for (const t of txs) {
903
- map.get(t.block_height)?.txs.push(t);
361
+ const txs = this.needsTransactions ? txRows.map(reconstructTransaction) : synthesizeTxsFromEvents(eventLists.flat());
362
+ for (const t of txs) {
363
+ map.get(t.block_height)?.txs.push(t);
364
+ }
365
+ for (const list of eventLists) {
366
+ for (const e of list) {
367
+ const bd = map.get(e.block_height);
368
+ if (!bd)
369
+ continue;
370
+ if (VM_INDEX_EVENT_TYPES.has(e.event_type)) {
371
+ bd.vmEvents ??= [];
372
+ bd.vmEvents.push(reconstructEvent(e));
373
+ } else {
374
+ bd.events.push(reconstructEvent(e));
375
+ }
376
+ }
377
+ }
378
+ for (const bd of map.values()) {
379
+ bd.txs.sort((a, b) => a.tx_index - b.tx_index);
380
+ bd.events.sort((a, b) => a.event_index - b.event_index);
381
+ bd.vmEvents?.sort((a, b) => a.event_index - b.event_index);
382
+ }
383
+ return map;
384
+ }
385
+ }
386
+ var postgresBlockSource = new PostgresBlockSource;
387
+ function buildHttpClient() {
388
+ return createInternalIndexHttpClient();
389
+ }
390
+
391
+ // src/runtime/trigger-evaluator.ts
392
+ import { getSourceDb as getSourceDb3 } from "@secondlayer/shared/db";
393
+ import { resolveTraitContractIds } from "@secondlayer/shared/db/queries/contracts";
394
+
395
+ // src/runtime/decoder-bound.ts
396
+ import { decodeStreamsCursor, isEmptyRangeCursor } from "@secondlayer/shared";
397
+ import { getSourceDb as getSourceDb2 } from "@secondlayer/shared/db";
398
+
399
+ // src/runtime/source-matcher.ts
400
+ var patternCache = new Map;
401
+ function matchAny(value, pattern) {
402
+ return Array.isArray(pattern) ? pattern.some((p) => matchPattern(value, p)) : matchPattern(value, pattern);
403
+ }
404
+ function matchPattern(value, pattern) {
405
+ if (!pattern.includes("*"))
406
+ return value === pattern;
407
+ let re = patternCache.get(pattern);
408
+ if (!re) {
409
+ const regex = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
410
+ re = new RegExp(`^${regex}$`);
411
+ patternCache.set(pattern, re);
412
+ }
413
+ return re.test(value);
414
+ }
415
+ var EMPTY_SET = new Set;
416
+ function factoryAllows(filter, contractId, factoryContracts) {
417
+ if (!filter.factory)
418
+ return true;
419
+ if (!contractId)
420
+ return false;
421
+ return (factoryContracts.get(filter.factory.from) ?? EMPTY_SET).has(contractId);
422
+ }
423
+ function traitAllows(filter, contractId, traitContracts) {
424
+ const trait = filter.trait;
425
+ if (!trait)
426
+ return true;
427
+ if (!contractId)
428
+ return false;
429
+ return (traitContracts.get(trait) ?? EMPTY_SET).has(contractId);
430
+ }
431
+ function assetContract(assetId) {
432
+ return assetId?.split("::")[0];
433
+ }
434
+ function printContractId(data) {
435
+ if (!data)
436
+ return;
437
+ const id = (typeof data.contract_identifier === "string" ? data.contract_identifier : undefined) ?? (typeof data.contract_id === "string" ? data.contract_id : undefined);
438
+ return id || undefined;
439
+ }
440
+ function matchFilter(filter, transactions, eventsByTx, traitContracts, factoryContracts = new Map, vmEventsByTx = new Map) {
441
+ const results = [];
442
+ switch (filter.type) {
443
+ case "stx_transfer":
444
+ case "stx_mint":
445
+ case "stx_burn":
446
+ case "stx_lock": {
447
+ const eventType = `${filter.type}_event`;
448
+ for (const tx of transactions) {
449
+ const txEvents = eventsByTx.get(tx.tx_id) ?? [];
450
+ const matched = txEvents.filter((e) => e.type === eventType);
451
+ if (matched.length === 0)
452
+ continue;
453
+ const filtered = matched.filter((e) => {
454
+ const data = e.data;
455
+ if (!data)
456
+ return false;
457
+ if ("sender" in filter && filter.sender) {
458
+ if (!matchPattern(data.sender, filter.sender))
459
+ return false;
460
+ }
461
+ if ("recipient" in filter && filter.recipient) {
462
+ if (!matchPattern(data.recipient, filter.recipient))
463
+ return false;
464
+ }
465
+ if ("lockedAddress" in filter && filter.lockedAddress) {
466
+ if (!matchPattern(data.locked_address, filter.lockedAddress))
467
+ return false;
468
+ }
469
+ if ("minAmount" in filter && filter.minAmount !== undefined) {
470
+ const amount = BigInt(data.amount ?? data.locked_amount ?? "0");
471
+ if (amount < filter.minAmount)
472
+ return false;
473
+ }
474
+ if ("maxAmount" in filter && filter.maxAmount !== undefined) {
475
+ const amount = BigInt(data.amount ?? "0");
476
+ if (amount > filter.maxAmount)
477
+ return false;
478
+ }
479
+ return true;
480
+ });
481
+ if (filtered.length > 0) {
482
+ results.push({ tx, events: filtered });
483
+ }
484
+ }
485
+ break;
486
+ }
487
+ case "ft_transfer":
488
+ case "ft_mint":
489
+ case "ft_burn": {
490
+ const eventType = `${filter.type}_event`;
491
+ for (const tx of transactions) {
492
+ const txEvents = eventsByTx.get(tx.tx_id) ?? [];
493
+ const matched = txEvents.filter((e) => {
494
+ if (e.type !== eventType)
495
+ return false;
496
+ const data = e.data;
497
+ if (!data)
498
+ return false;
499
+ if (filter.assetIdentifier) {
500
+ const assetId = data.asset_identifier;
501
+ if (!assetId || !matchPattern(assetId, filter.assetIdentifier))
502
+ return false;
503
+ }
504
+ if (!traitAllows(filter, assetContract(data.asset_identifier), traitContracts))
505
+ return false;
506
+ if ("sender" in filter && filter.sender) {
507
+ if (!matchPattern(data.sender, filter.sender))
508
+ return false;
509
+ }
510
+ if ("recipient" in filter && filter.recipient) {
511
+ if (!matchPattern(data.recipient, filter.recipient))
512
+ return false;
513
+ }
514
+ if (filter.minAmount !== undefined) {
515
+ const amount = BigInt(data.amount ?? "0");
516
+ if (amount < filter.minAmount)
517
+ return false;
518
+ }
519
+ return true;
520
+ });
521
+ if (matched.length > 0) {
522
+ results.push({ tx, events: matched });
523
+ }
524
+ }
525
+ break;
526
+ }
527
+ case "nft_transfer":
528
+ case "nft_mint":
529
+ case "nft_burn": {
530
+ const eventType = `${filter.type}_event`;
531
+ for (const tx of transactions) {
532
+ const txEvents = eventsByTx.get(tx.tx_id) ?? [];
533
+ const matched = txEvents.filter((e) => {
534
+ if (e.type !== eventType)
535
+ return false;
536
+ const data = e.data;
537
+ if (!data)
538
+ return false;
539
+ if (filter.assetIdentifier) {
540
+ const assetId = data.asset_identifier;
541
+ if (!assetId || !matchPattern(assetId, filter.assetIdentifier))
542
+ return false;
543
+ }
544
+ if (!traitAllows(filter, assetContract(data.asset_identifier), traitContracts))
545
+ return false;
546
+ if ("sender" in filter && filter.sender) {
547
+ if (!matchPattern(data.sender, filter.sender))
548
+ return false;
549
+ }
550
+ if ("recipient" in filter && filter.recipient) {
551
+ if (!matchPattern(data.recipient, filter.recipient))
552
+ return false;
553
+ }
554
+ return true;
555
+ });
556
+ if (matched.length > 0) {
557
+ results.push({ tx, events: matched });
558
+ }
559
+ }
560
+ break;
904
561
  }
905
- for (const list of eventLists) {
906
- for (const e of list) {
907
- const bd = map.get(e.block_height);
908
- if (!bd)
562
+ case "contract_call": {
563
+ for (const tx of transactions) {
564
+ if (tx.type !== "contract_call")
909
565
  continue;
910
- if (VM_INDEX_EVENT_TYPES.has(e.event_type)) {
911
- bd.vmEvents ??= [];
912
- bd.vmEvents.push(reconstructEvent(e));
913
- } else {
914
- bd.events.push(reconstructEvent(e));
566
+ if (filter.contractId) {
567
+ if (!tx.contract_id || !matchAny(tx.contract_id, filter.contractId))
568
+ continue;
569
+ }
570
+ if (!factoryAllows(filter, tx.contract_id, factoryContracts))
571
+ continue;
572
+ if (filter.functionName) {
573
+ if (!tx.function_name || !matchPattern(tx.function_name, filter.functionName))
574
+ continue;
575
+ }
576
+ if (filter.caller) {
577
+ if (!matchPattern(tx.sender, filter.caller))
578
+ continue;
915
579
  }
580
+ if (!traitAllows(filter, tx.contract_id, traitContracts))
581
+ continue;
582
+ const txEvents = eventsByTx.get(tx.tx_id) ?? [];
583
+ results.push({ tx, events: txEvents });
916
584
  }
585
+ break;
917
586
  }
918
- for (const bd of map.values()) {
919
- bd.txs.sort((a, b) => a.tx_index - b.tx_index);
920
- bd.events.sort((a, b) => a.event_index - b.event_index);
921
- bd.vmEvents?.sort((a, b) => a.event_index - b.event_index);
922
- }
923
- return map;
924
- }
925
- }
926
-
927
- class FallbackBlockSource {
928
- primary;
929
- fallback;
930
- constructor(primary, fallback) {
931
- this.primary = primary;
932
- this.fallback = fallback;
933
- }
934
- async getTip() {
935
- try {
936
- return await this.primary.getTip();
937
- } catch (err) {
938
- logger.warn("block source primary getTip failed — using DB tap", {
939
- error: err instanceof Error ? err.message : String(err)
940
- });
941
- return this.fallback.getTip();
942
- }
943
- }
944
- async loadBlockRange(fromHeight, toHeight) {
945
- try {
946
- return await this.primary.loadBlockRange(fromHeight, toHeight);
947
- } catch (err) {
948
- logger.warn("block source primary loadBlockRange failed — using DB tap", {
949
- from: fromHeight,
950
- to: toHeight,
951
- error: err instanceof Error ? err.message : String(err)
952
- });
953
- return this.fallback.loadBlockRange(fromHeight, toHeight);
587
+ case "contract_deploy": {
588
+ for (const tx of transactions) {
589
+ if (tx.type !== "smart_contract")
590
+ continue;
591
+ if (filter.deployer) {
592
+ if (!matchPattern(tx.sender, filter.deployer))
593
+ continue;
594
+ }
595
+ if (filter.contractName) {
596
+ const name = tx.contract_id?.split(".")[1] ?? "";
597
+ if (!matchPattern(name, filter.contractName))
598
+ continue;
599
+ }
600
+ const txEvents = eventsByTx.get(tx.tx_id) ?? [];
601
+ results.push({ tx, events: txEvents });
602
+ }
603
+ break;
954
604
  }
955
- }
956
- async nextDataHeight(afterHeight, untilHeight) {
957
- if (!this.primary.nextDataHeight)
958
- return afterHeight + 1;
959
- try {
960
- return await this.primary.nextDataHeight(afterHeight, untilHeight);
961
- } catch {
962
- return afterHeight + 1;
605
+ case "nested_contract_call":
606
+ case "var_set":
607
+ case "map_set":
608
+ case "map_insert":
609
+ case "map_delete": {
610
+ for (const tx of transactions) {
611
+ const txEvents = vmEventsByTx.get(tx.tx_id) ?? [];
612
+ const matched = txEvents.filter((e) => {
613
+ if (e.type !== filter.type)
614
+ return false;
615
+ const data = e.data;
616
+ if (!data)
617
+ return false;
618
+ const contractId = printContractId(data);
619
+ if ("contractId" in filter && filter.contractId) {
620
+ if (!contractId || !matchAny(contractId, filter.contractId))
621
+ return false;
622
+ }
623
+ if (!traitAllows(filter, contractId, traitContracts))
624
+ return false;
625
+ if (!factoryAllows(filter, contractId, factoryContracts))
626
+ return false;
627
+ if (filter.type === "nested_contract_call") {
628
+ if (filter.functionName) {
629
+ if (typeof data.function_name !== "string" || !matchPattern(data.function_name, filter.functionName))
630
+ return false;
631
+ }
632
+ if (filter.caller) {
633
+ if (typeof data.caller !== "string" || !matchPattern(data.caller, filter.caller))
634
+ return false;
635
+ }
636
+ if (filter.sender) {
637
+ if (typeof data.sender !== "string" || !matchPattern(data.sender, filter.sender))
638
+ return false;
639
+ }
640
+ }
641
+ if (filter.type === "var_set" && filter.varName) {
642
+ if (typeof data.var_name !== "string" || !matchPattern(data.var_name, filter.varName))
643
+ return false;
644
+ }
645
+ if ((filter.type === "map_set" || filter.type === "map_insert" || filter.type === "map_delete") && filter.map) {
646
+ if (typeof data.map_name !== "string" || !matchPattern(data.map_name, filter.map))
647
+ return false;
648
+ }
649
+ return true;
650
+ });
651
+ if (matched.length > 0)
652
+ results.push({ tx, events: matched });
653
+ }
654
+ break;
963
655
  }
964
- }
965
- }
966
- var postgresBlockSource = new PostgresBlockSource;
967
- function buildHttpClient() {
968
- return createInternalIndexHttpClient();
969
- }
970
- function resolveBlockSource(subgraph) {
971
- if (process.env.SUBGRAPH_SOURCE === "observer-http") {
972
- const baseUrl = process.env.OBSERVER_HTTP_URL;
973
- if (!baseUrl) {
974
- throw new Error("SUBGRAPH_SOURCE=observer-http requires OBSERVER_HTTP_URL");
656
+ case "print_event": {
657
+ for (const tx of transactions) {
658
+ const txEvents = eventsByTx.get(tx.tx_id) ?? [];
659
+ const matched = txEvents.filter((e) => {
660
+ if (e.type !== "smart_contract_event" && e.type !== "contract_event")
661
+ return false;
662
+ const data = e.data;
663
+ if (!data)
664
+ return false;
665
+ if (data.topic !== "print")
666
+ return false;
667
+ const contractId = printContractId(data);
668
+ if (filter.contractId) {
669
+ if (!contractId || !matchAny(contractId, filter.contractId))
670
+ return false;
671
+ }
672
+ if (!traitAllows(filter, contractId, traitContracts))
673
+ return false;
674
+ if (!factoryAllows(filter, contractId, factoryContracts))
675
+ return false;
676
+ return true;
677
+ });
678
+ if (matched.length > 0) {
679
+ results.push({ tx, events: matched });
680
+ }
681
+ }
682
+ break;
975
683
  }
976
- return new ObserverHttpBlockSource({
977
- baseUrl,
978
- token: process.env.OBSERVER_HTTP_EXPORT_TOKEN || null
979
- });
980
- }
981
- if (process.env.SUBGRAPH_SOURCE === "streams-index" && subgraph && isStreamsIndexEligible(subgraph)) {
982
- return new FallbackBlockSource(new PublicApiBlockSource(buildHttpClient(), referencedIndexEventTypes(subgraph), canSparseScan(subgraph) ? sparseProbeTargets(subgraph) : undefined, needsTransactionData(subgraph)), postgresBlockSource);
983
- }
984
- if (process.env.SUBGRAPH_SOURCE === "streams-index" && subgraph) {
985
- logger.debug("Subgraph not streams-index eligible, using DB tap", {
986
- subgraph: subgraph.name
987
- });
988
684
  }
989
- return postgresBlockSource;
990
- }
991
-
992
- // src/runtime/trigger-evaluator.ts
993
- import { getSourceDb as getSourceDb3 } from "@secondlayer/shared/db";
994
- import { resolveTraitContractIds } from "@secondlayer/shared/db/queries/contracts";
995
-
996
- // src/runtime/decoder-bound.ts
997
- import { decodeStreamsCursor, isEmptyRangeCursor } from "@secondlayer/shared";
998
- import { getSourceDb as getSourceDb2 } from "@secondlayer/shared/db";
999
- function decoderNameForEventType(indexEventType) {
1000
- return `decode.${indexEventType}.v1`;
1001
- }
1002
- function decoderNamesForIndexEventTypes(eventTypes) {
1003
- return eventTypes.filter((t) => !VM_INDEX_EVENT_TYPES.has(t)).map(decoderNameForEventType);
1004
- }
1005
- function decoderNamesForSubgraph(subgraph) {
1006
- return decoderNamesForIndexEventTypes(referencedIndexEventTypes(subgraph));
1007
- }
1008
- function usesDecodedIndexPlane(subgraph) {
1009
- return process.env.SUBGRAPH_SOURCE === "streams-index" && isStreamsIndexEligible(subgraph);
685
+ return results;
1010
686
  }
1011
- function committedHeight(cursor) {
1012
- if (!cursor)
1013
- return null;
1014
- try {
1015
- const decoded = decodeStreamsCursor(cursor);
1016
- if (isEmptyRangeCursor(decoded))
1017
- return decoded.block_height;
1018
- return Math.max(0, decoded.block_height - 1);
1019
- } catch {
1020
- return null;
687
+ function matchSources(sources, transactions, events, traitContracts = new Map, factoryContracts = new Map, vmEvents = []) {
688
+ const eventsByTx = new Map;
689
+ for (const event of events) {
690
+ const list = eventsByTx.get(event.tx_id) ?? [];
691
+ list.push(event);
692
+ eventsByTx.set(event.tx_id, list);
1021
693
  }
1022
- }
1023
- async function decoderBoundTip(decoderNames, opts) {
1024
- if (decoderNames.length === 0)
1025
- return { kind: "unbounded" };
1026
- const sourceDb = opts?.sourceDb ?? getSourceDb2();
1027
- const rows = await sourceDb.selectFrom("decoder_checkpoints").select(["decoder_name", "last_cursor"]).where("decoder_name", "in", decoderNames).execute();
1028
- const byName = new Map(rows.map((r) => [r.decoder_name, r.last_cursor]));
1029
- const missing = [];
1030
- const heights = [];
1031
- for (const name of decoderNames) {
1032
- const height = committedHeight(byName.get(name));
1033
- if (height === null)
1034
- missing.push(name);
1035
- else
1036
- heights.push(height);
694
+ const vmEventsByTx = new Map;
695
+ for (const event of vmEvents) {
696
+ const list = vmEventsByTx.get(event.tx_id) ?? [];
697
+ list.push(event);
698
+ vmEventsByTx.set(event.tx_id, list);
1037
699
  }
1038
- if (missing.length > 0)
1039
- return { kind: "stall", missing };
1040
- return { kind: "height", height: Math.min(...heights) };
1041
- }
1042
- async function boundSourceTip(rawTip, decoderNames, opts) {
1043
- const bound = await decoderBoundTip(decoderNames, opts);
1044
- if (bound.kind === "stall")
1045
- return { ok: false, missing: bound.missing };
1046
- if (bound.kind === "unbounded") {
1047
- return { ok: true, tip: rawTip, floor: null };
700
+ const seen = new Set;
701
+ const results = [];
702
+ for (const [sourceName, filter] of Object.entries(sources)) {
703
+ const matches = matchFilter(filter, transactions, eventsByTx, traitContracts, factoryContracts, vmEventsByTx);
704
+ for (const match of matches) {
705
+ const dedupeKey = `${match.tx.tx_id}:${sourceName}`;
706
+ if (!seen.has(dedupeKey)) {
707
+ seen.add(dedupeKey);
708
+ results.push({ ...match, sourceName });
709
+ }
710
+ }
1048
711
  }
1049
- return {
1050
- ok: true,
1051
- tip: Math.min(rawTip, bound.height),
1052
- floor: bound.height
1053
- };
712
+ return results;
1054
713
  }
1055
714
 
1056
715
  // src/runtime/trigger-evaluator.ts
@@ -1473,9 +1132,9 @@ async function replayChainWebhook(db, sub, input, opts) {
1473
1132
  return { replayId, enqueuedCount: enqueued, scannedCount: scanned };
1474
1133
  }
1475
1134
  export {
1476
- replayWebhook,
1477
- replayChainWebhook
1135
+ replayChainWebhook,
1136
+ replayWebhook
1478
1137
  };
1479
1138
 
1480
- //# debugId=7D5A9CBD8E5E35A364756E2164756E21
1139
+ //# debugId=12B00680B3E69AE464756E2164756E21
1481
1140
  //# sourceMappingURL=replay.js.map