@secondlayer/subgraphs 3.6.1 → 3.7.1

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.
@@ -1149,19 +1149,21 @@ function sourceFilters(subgraph) {
1149
1149
  const sources = subgraph.sources;
1150
1150
  return Array.isArray(sources) ? sources : Object.values(sources);
1151
1151
  }
1152
- function referencedIndexEventTypes(subgraph) {
1153
- const filters = sourceFilters(subgraph);
1154
- if (filters.some((f) => TX_SOURCE_TYPES.has(f.type))) {
1152
+ function indexEventTypesForFilterTypes(filterTypes) {
1153
+ if (filterTypes.some((t) => TX_SOURCE_TYPES.has(t))) {
1155
1154
  return ALL_INDEX_EVENT_TYPES;
1156
1155
  }
1157
1156
  const types = new Set;
1158
- for (const f of filters) {
1159
- const t = EVENT_FILTER_TO_INDEX_TYPE[f.type];
1160
- if (t)
1161
- types.add(t);
1157
+ for (const t of filterTypes) {
1158
+ const indexType = EVENT_FILTER_TO_INDEX_TYPE[t];
1159
+ if (indexType)
1160
+ types.add(indexType);
1162
1161
  }
1163
1162
  return [...types];
1164
1163
  }
1164
+ function referencedIndexEventTypes(subgraph) {
1165
+ return indexEventTypesForFilterTypes(sourceFilters(subgraph).map((f) => f.type));
1166
+ }
1165
1167
  function isStreamsIndexEligible(subgraph) {
1166
1168
  if (Array.isArray(subgraph.sources))
1167
1169
  return false;
@@ -1423,6 +1425,8 @@ class SubscriptionMatcher {
1423
1425
  for (const sub of subs) {
1424
1426
  if (sub.status !== "active")
1425
1427
  continue;
1428
+ if (sub.kind !== "subgraph" || !sub.subgraph_name || !sub.table_name)
1429
+ continue;
1426
1430
  this.byId.set(sub.id, sub);
1427
1431
  const k = key(sub.subgraph_name, sub.table_name);
1428
1432
  const arr = this.byKey.get(k);
@@ -2492,8 +2496,8 @@ import { randomUUID } from "node:crypto";
2492
2496
  import { hostname } from "node:os";
2493
2497
  import { resolve } from "node:path";
2494
2498
  import { pathToFileURL } from "node:url";
2495
- import { getErrorMessage as getErrorMessage5 } from "@secondlayer/shared";
2496
- import { getTargetDb as getTargetDb6 } from "@secondlayer/shared/db";
2499
+ import { getErrorMessage as getErrorMessage6 } from "@secondlayer/shared";
2500
+ import { getTargetDb as getTargetDb8 } from "@secondlayer/shared/db";
2497
2501
  import {
2498
2502
  cancelSubgraphOperation,
2499
2503
  claimSubgraphOperation,
@@ -2511,20 +2515,71 @@ import {
2511
2515
  pgSchemaName as pgSchemaName2,
2512
2516
  updateSubgraphStatus as updateSubgraphStatus3
2513
2517
  } from "@secondlayer/shared/db/queries/subgraphs";
2514
- import { logger as logger12 } from "@secondlayer/shared/logger";
2518
+ import { logger as logger14 } from "@secondlayer/shared/logger";
2515
2519
  import { listen as listen2 } from "@secondlayer/shared/queue/listener";
2516
2520
 
2521
+ // src/runtime/chain-reorg.ts
2522
+ import { getTargetDb as getTargetDb5 } from "@secondlayer/shared/db";
2523
+ import { logger as logger9 } from "@secondlayer/shared/logger";
2524
+ var MAX_ORPHANED_PER_SUB = 500;
2525
+ async function handleChainReorg(forkHeight, db = getTargetDb5()) {
2526
+ await db.deleteFrom("subscription_outbox").where("kind", "=", "chain").where("block_height", ">=", forkHeight).where("status", "=", "pending").where("event_type", "like", "chain.%.apply").execute();
2527
+ const delivered = await db.selectFrom("subscription_outbox").select(["subscription_id", "tx_id", "payload"]).where("kind", "=", "chain").where("block_height", ">=", forkHeight).where("status", "=", "delivered").where("event_type", "like", "chain.%.apply").orderBy("block_height").orderBy("id").execute();
2528
+ const bySub = new Map;
2529
+ for (const row of delivered) {
2530
+ const list = bySub.get(row.subscription_id) ?? [];
2531
+ const payload = row.payload;
2532
+ list.push({ tx_id: row.tx_id, event: payload?.event ?? null });
2533
+ bySub.set(row.subscription_id, list);
2534
+ }
2535
+ if (bySub.size > 0) {
2536
+ const rows = [];
2537
+ for (const [subscriptionId, entries] of bySub) {
2538
+ const truncated = entries.length > MAX_ORPHANED_PER_SUB;
2539
+ const payload = {
2540
+ action: "rollback",
2541
+ fork_point_height: forkHeight,
2542
+ orphaned: truncated ? entries.slice(0, MAX_ORPHANED_PER_SUB) : entries,
2543
+ truncated
2544
+ };
2545
+ rows.push({
2546
+ subscription_id: subscriptionId,
2547
+ kind: "chain",
2548
+ subgraph_name: null,
2549
+ table_name: null,
2550
+ block_height: forkHeight,
2551
+ tx_id: null,
2552
+ row_pk: { fork_point_height: forkHeight },
2553
+ event_type: "chain.reorg.rollback",
2554
+ payload,
2555
+ dedup_key: `chainreorg:${subscriptionId}:${forkHeight}`
2556
+ });
2557
+ }
2558
+ await db.insertInto("subscription_outbox").values(rows).onConflict((oc) => oc.columns(["subscription_id", "dedup_key"]).doNothing()).execute();
2559
+ logger9.info("Chain reorg — emitted rollbacks", {
2560
+ forkPointHeight: forkHeight,
2561
+ subscriptions: bySub.size
2562
+ });
2563
+ }
2564
+ await db.transaction().execute(async (trx) => {
2565
+ const cur = await trx.selectFrom("trigger_evaluator_state").select("last_processed_block").where("id", "=", true).forUpdate().executeTakeFirst();
2566
+ if (cur && Number(cur.last_processed_block) >= forkHeight) {
2567
+ await trx.updateTable("trigger_evaluator_state").set({ last_processed_block: forkHeight - 1, updated_at: new Date }).where("id", "=", true).execute();
2568
+ }
2569
+ });
2570
+ }
2571
+
2517
2572
  // src/runtime/emitter.ts
2518
2573
  import {
2519
- getTargetDb as getTargetDb5
2574
+ getTargetDb as getTargetDb6
2520
2575
  } from "@secondlayer/shared/db";
2521
2576
  import { getSubscriptionSigningSecret } from "@secondlayer/shared/db/queries/subscriptions";
2522
- import { logger as logger10 } from "@secondlayer/shared/logger";
2577
+ import { logger as logger11 } from "@secondlayer/shared/logger";
2523
2578
  import { listen } from "@secondlayer/shared/queue/listener";
2524
2579
  import { sql as sql4 } from "kysely";
2525
2580
 
2526
2581
  // src/runtime/formats/index.ts
2527
- import { logger as logger9 } from "@secondlayer/shared/logger";
2582
+ import { logger as logger10 } from "@secondlayer/shared/logger";
2528
2583
 
2529
2584
  // src/runtime/formats/cloudevents.ts
2530
2585
  function buildCloudEvents(outboxRow, _sub) {
@@ -2671,7 +2726,7 @@ function buildForFormat(outboxRow, sub, signingSecret) {
2671
2726
  case "standard-webhooks":
2672
2727
  return buildStandardWebhooks(outboxRow, signingSecret);
2673
2728
  default:
2674
- logger9.warn("Unknown subscription format, falling back to standard-webhooks", {
2729
+ logger10.warn("Unknown subscription format, falling back to standard-webhooks", {
2675
2730
  format: sub.format,
2676
2731
  subscriptionId: sub.id
2677
2732
  });
@@ -2754,7 +2809,7 @@ async function dispatchOne(db, outboxRow, sub) {
2754
2809
  let responseHeaders = {};
2755
2810
  if (isPrivateEgress(sub.url) && !allowPrivateEgress()) {
2756
2811
  error = "refused private egress (set SECONDLAYER_ALLOW_PRIVATE_EGRESS=true to allow)";
2757
- logger10.warn("[emitter] refused private egress", {
2812
+ logger11.warn("[emitter] refused private egress", {
2758
2813
  subscription: sub.name,
2759
2814
  url: sub.url
2760
2815
  });
@@ -2850,7 +2905,7 @@ async function settleFailed(db, outboxRow, sub, errText) {
2850
2905
  circuit_opened_at: new Date,
2851
2906
  updated_at: new Date
2852
2907
  }).where("id", "=", sub.id).execute();
2853
- logger10.warn("Subscription circuit tripped — paused after consecutive failures", {
2908
+ logger11.warn("Subscription circuit tripped — paused after consecutive failures", {
2854
2909
  subscription: sub.name,
2855
2910
  failures: newFailures
2856
2911
  });
@@ -2938,7 +2993,7 @@ async function drainForSub(db, state, sub, rows) {
2938
2993
  await settleFailed(db, row, sub, err);
2939
2994
  }
2940
2995
  } catch (err) {
2941
- logger10.error("Emitter dispatch crashed", {
2996
+ logger11.error("Emitter dispatch crashed", {
2942
2997
  outboxId: row.id,
2943
2998
  error: err instanceof Error ? err.message : String(err)
2944
2999
  });
@@ -2967,7 +3022,7 @@ async function runRetention(db) {
2967
3022
  }
2968
3023
  async function startEmitter(opts) {
2969
3024
  const emitterId = `emitter-${Math.random().toString(36).slice(2, 10)}`;
2970
- const db = getTargetDb5();
3025
+ const db = getTargetDb6();
2971
3026
  const state = {
2972
3027
  running: true,
2973
3028
  inFlightBySub: new Map,
@@ -2975,7 +3030,7 @@ async function startEmitter(opts) {
2975
3030
  };
2976
3031
  const pollIntervalMs = opts?.pollIntervalMs ?? 120000;
2977
3032
  const retentionIntervalMs = opts?.retentionIntervalMs ?? 60 * 60000;
2978
- logger10.info("[emitter] started", { id: emitterId });
3033
+ logger11.info("[emitter] started", { id: emitterId });
2979
3034
  const MATCHER_BOOT_ATTEMPTS = 5;
2980
3035
  let lastErr = null;
2981
3036
  for (let i = 0;i < MATCHER_BOOT_ATTEMPTS; i++) {
@@ -2986,7 +3041,7 @@ async function startEmitter(opts) {
2986
3041
  } catch (err) {
2987
3042
  lastErr = err;
2988
3043
  const delayMs = 500 * 2 ** i;
2989
- logger10.warn("[emitter] matcher refresh failed, retrying", {
3044
+ logger11.warn("[emitter] matcher refresh failed, retrying", {
2990
3045
  attempt: i + 1,
2991
3046
  delayMs,
2992
3047
  error: err instanceof Error ? err.message : String(err)
@@ -3000,21 +3055,21 @@ async function startEmitter(opts) {
3000
3055
  const stopNew = await listen("subscriptions:new_outbox", () => {
3001
3056
  if (!state.running)
3002
3057
  return;
3003
- claimAndDrain(db, state, emitterId).catch((err) => logger10.error("[emitter] claim failed", {
3058
+ claimAndDrain(db, state, emitterId).catch((err) => logger11.error("[emitter] claim failed", {
3004
3059
  error: err instanceof Error ? err.message : String(err)
3005
3060
  }));
3006
3061
  });
3007
3062
  const stopChanged = await listen("subscriptions:changed", () => {
3008
3063
  if (!state.running)
3009
3064
  return;
3010
- refreshMatcher(db).catch((err) => logger10.error("[emitter] matcher refresh failed", {
3065
+ refreshMatcher(db).catch((err) => logger11.error("[emitter] matcher refresh failed", {
3011
3066
  error: err instanceof Error ? err.message : String(err)
3012
3067
  }));
3013
3068
  });
3014
3069
  const poll = setInterval(() => {
3015
3070
  if (!state.running)
3016
3071
  return;
3017
- claimAndDrain(db, state, emitterId).catch((err) => logger10.error("[emitter] poll claim failed", {
3072
+ claimAndDrain(db, state, emitterId).catch((err) => logger11.error("[emitter] poll claim failed", {
3018
3073
  error: err instanceof Error ? err.message : String(err)
3019
3074
  }));
3020
3075
  }, pollIntervalMs);
@@ -3022,7 +3077,7 @@ async function startEmitter(opts) {
3022
3077
  const retention = setInterval(() => {
3023
3078
  if (!state.running)
3024
3079
  return;
3025
- runRetention(db).catch((err) => logger10.error("[emitter] retention failed", {
3080
+ runRetention(db).catch((err) => logger11.error("[emitter] retention failed", {
3026
3081
  error: err instanceof Error ? err.message : String(err)
3027
3082
  }));
3028
3083
  }, retentionIntervalMs);
@@ -3032,28 +3087,30 @@ async function startEmitter(opts) {
3032
3087
  clearInterval(retention);
3033
3088
  await stopNew();
3034
3089
  await stopChanged();
3035
- logger10.info("[emitter] stopped", { id: emitterId });
3090
+ logger11.info("[emitter] stopped", { id: emitterId });
3036
3091
  };
3037
3092
  }
3038
3093
 
3039
3094
  // src/runtime/streams-reorg-poll.ts
3040
3095
  import { getErrorMessage as getErrorMessage4 } from "@secondlayer/shared";
3041
3096
  import { IndexHttpClient as IndexHttpClient2 } from "@secondlayer/shared/index-http";
3042
- import { logger as logger11 } from "@secondlayer/shared/logger";
3097
+ import { logger as logger12 } from "@secondlayer/shared/logger";
3043
3098
  var POLL_MS = Number(process.env.SUBGRAPH_REORG_POLL_MS) || 15000;
3044
3099
  var STARTUP_MARGIN_MS = 60 * 60 * 1000;
3045
- async function pollReorgsOnce(http, cursor, handleReorg, loadDef) {
3100
+ async function pollReorgsOnce(http, cursor, handleReorg, loadDef, handleChainReorg2) {
3046
3101
  const { reorgs, next_since } = await http.listReorgs(cursor);
3047
3102
  const sorted = [...reorgs].sort((a, b) => a.fork_point_height - b.fork_point_height);
3048
3103
  for (const r of sorted) {
3049
- logger11.info("Streams reorg — rewinding subgraphs", {
3104
+ logger12.info("Streams reorg — rewinding subgraphs", {
3050
3105
  forkPointHeight: r.fork_point_height
3051
3106
  });
3052
3107
  await handleReorg(r.fork_point_height, loadDef);
3108
+ if (handleChainReorg2)
3109
+ await handleChainReorg2(r.fork_point_height);
3053
3110
  }
3054
3111
  return next_since ?? cursor;
3055
3112
  }
3056
- function startStreamsReorgPoll(handleReorg, loadDef) {
3113
+ function startStreamsReorgPoll(handleReorg, loadDef, handleChainReorg2) {
3057
3114
  const baseUrl = process.env.SUBGRAPH_INDEX_API_URL ?? process.env.STREAMS_API_URL ?? "http://api:3800";
3058
3115
  const http = new IndexHttpClient2({
3059
3116
  indexBaseUrl: baseUrl,
@@ -3067,9 +3124,9 @@ function startStreamsReorgPoll(handleReorg, loadDef) {
3067
3124
  if (!running)
3068
3125
  return;
3069
3126
  try {
3070
- since = await pollReorgsOnce(http, since, handleReorg, loadDef);
3127
+ since = await pollReorgsOnce(http, since, handleReorg, loadDef, handleChainReorg2);
3071
3128
  } catch (err) {
3072
- logger11.error("Streams reorg poll failed", {
3129
+ logger12.error("Streams reorg poll failed", {
3073
3130
  error: getErrorMessage4(err)
3074
3131
  });
3075
3132
  }
@@ -3077,7 +3134,222 @@ function startStreamsReorgPoll(handleReorg, loadDef) {
3077
3134
  timer = setTimeout(tick, POLL_MS);
3078
3135
  };
3079
3136
  timer = setTimeout(tick, POLL_MS);
3080
- logger11.info("Streams reorg poll started", { pollMs: POLL_MS });
3137
+ logger12.info("Streams reorg poll started", { pollMs: POLL_MS });
3138
+ return () => {
3139
+ running = false;
3140
+ if (timer)
3141
+ clearTimeout(timer);
3142
+ };
3143
+ }
3144
+
3145
+ // src/runtime/trigger-evaluator-loop.ts
3146
+ import { getErrorMessage as getErrorMessage5 } from "@secondlayer/shared";
3147
+ import { getTargetDb as getTargetDb7 } from "@secondlayer/shared/db";
3148
+ import { listActiveChainSubscriptions } from "@secondlayer/shared/db/queries/subscriptions";
3149
+ import { logger as logger13 } from "@secondlayer/shared/logger";
3150
+
3151
+ // src/runtime/trigger-evaluator.ts
3152
+ import { resolveTraitContractIds as resolveTraitContractIds2 } from "@secondlayer/shared/db/queries/contracts";
3153
+ var TX_LEVEL_TRIGGER_TYPES = new Set(["contract_call", "contract_deploy"]);
3154
+ function sourceKey(subscriptionId, triggerIndex) {
3155
+ return `${subscriptionId}#${triggerIndex}`;
3156
+ }
3157
+ function toAmount(v) {
3158
+ return v === undefined ? undefined : BigInt(v);
3159
+ }
3160
+ function chainTriggerToFilter(trigger) {
3161
+ const t = trigger;
3162
+ const filter = { ...trigger };
3163
+ const minAmount = toAmount(t.minAmount);
3164
+ const maxAmount = toAmount(t.maxAmount);
3165
+ if (minAmount !== undefined)
3166
+ filter.minAmount = minAmount;
3167
+ if (maxAmount !== undefined)
3168
+ filter.maxAmount = maxAmount;
3169
+ return filter;
3170
+ }
3171
+ function triggersOf(sub) {
3172
+ return sub.triggers ?? [];
3173
+ }
3174
+ function buildSourcesMap(chainSubs) {
3175
+ const sources = {};
3176
+ const keyMeta = new Map;
3177
+ for (const sub of chainSubs) {
3178
+ triggersOf(sub).forEach((trigger, triggerIndex) => {
3179
+ const key2 = sourceKey(sub.id, triggerIndex);
3180
+ sources[key2] = chainTriggerToFilter(trigger);
3181
+ keyMeta.set(key2, {
3182
+ subscriptionId: sub.id,
3183
+ triggerIndex,
3184
+ triggerType: trigger.type
3185
+ });
3186
+ });
3187
+ }
3188
+ return { sources, keyMeta };
3189
+ }
3190
+ function referencedEventTypes(chainSubs) {
3191
+ const filterTypes = new Set;
3192
+ for (const sub of chainSubs) {
3193
+ for (const trigger of triggersOf(sub))
3194
+ filterTypes.add(trigger.type);
3195
+ }
3196
+ return indexEventTypesForFilterTypes([...filterTypes]);
3197
+ }
3198
+ function referencedTraits(chainSubs) {
3199
+ const traits = new Set;
3200
+ for (const sub of chainSubs) {
3201
+ for (const trigger of triggersOf(sub)) {
3202
+ const trait = trigger.trait;
3203
+ if (trait)
3204
+ traits.add(trait);
3205
+ }
3206
+ }
3207
+ return [...traits];
3208
+ }
3209
+ async function buildTraitContracts(db, chainSubs, asOfBlock) {
3210
+ const resolved = new Map;
3211
+ for (const trait of referencedTraits(chainSubs)) {
3212
+ const ids = await resolveTraitContractIds2(db, trait, asOfBlock);
3213
+ resolved.set(trait, new Set(ids));
3214
+ }
3215
+ return resolved;
3216
+ }
3217
+ function evaluateBlock(block, sources, traitContracts) {
3218
+ return matchSources(sources, block.txs, block.events, traitContracts);
3219
+ }
3220
+ function chainDedupKey(subscriptionId, txId, eventIndex, blockHash) {
3221
+ return `chain:${subscriptionId}:${txId}:${eventIndex}:${blockHash}`;
3222
+ }
3223
+ function applyRow(meta, blockHeight, blockHash, txId, eventIndex, event) {
3224
+ const payload = {
3225
+ action: "apply",
3226
+ block_hash: blockHash,
3227
+ block_height: blockHeight,
3228
+ tx_id: txId,
3229
+ canonical: true,
3230
+ trigger: meta.triggerType,
3231
+ event
3232
+ };
3233
+ return {
3234
+ subscription_id: meta.subscriptionId,
3235
+ kind: "chain",
3236
+ subgraph_name: null,
3237
+ table_name: null,
3238
+ block_height: blockHeight,
3239
+ tx_id: txId,
3240
+ row_pk: { tx_id: txId, event_index: eventIndex },
3241
+ event_type: `chain.${meta.triggerType}.apply`,
3242
+ payload,
3243
+ dedup_key: chainDedupKey(meta.subscriptionId, txId, eventIndex, blockHash)
3244
+ };
3245
+ }
3246
+ async function emitChainOutbox(db, matches, keyMeta, blockHeight, blockHash) {
3247
+ const rows = [];
3248
+ for (const match of matches) {
3249
+ const meta = keyMeta.get(match.sourceName);
3250
+ if (!meta)
3251
+ continue;
3252
+ const txId = match.tx.tx_id;
3253
+ if (TX_LEVEL_TRIGGER_TYPES.has(meta.triggerType)) {
3254
+ rows.push(applyRow(meta, blockHeight, blockHash, txId, -1, {
3255
+ tx_id: txId,
3256
+ type: match.tx.type,
3257
+ sender: match.tx.sender,
3258
+ status: match.tx.status,
3259
+ contract_id: match.tx.contract_id ?? null,
3260
+ function_name: match.tx.function_name ?? null,
3261
+ function_args: match.tx.function_args ?? null,
3262
+ result_hex: match.tx.raw_result ?? null
3263
+ }));
3264
+ } else {
3265
+ for (const event of match.events) {
3266
+ rows.push(applyRow(meta, blockHeight, blockHash, txId, event.event_index, {
3267
+ tx_id: txId,
3268
+ type: event.type,
3269
+ event_index: event.event_index,
3270
+ data: event.data
3271
+ }));
3272
+ }
3273
+ }
3274
+ }
3275
+ if (rows.length === 0)
3276
+ return 0;
3277
+ await db.insertInto("subscription_outbox").values(rows).onConflict((oc) => oc.columns(["subscription_id", "dedup_key"]).doNothing()).execute();
3278
+ return rows.length;
3279
+ }
3280
+
3281
+ // src/runtime/trigger-evaluator-loop.ts
3282
+ var POLL_MS2 = Number(process.env.TRIGGER_EVALUATOR_POLL_MS) || 5000;
3283
+ var BATCH = Number(process.env.TRIGGER_EVALUATOR_BATCH) || 200;
3284
+ var MAX_BLOCKS_PER_TICK = Number(process.env.TRIGGER_EVALUATOR_MAX_BLOCKS) || 2000;
3285
+ async function readCursor(db) {
3286
+ const row = await db.selectFrom("trigger_evaluator_state").select("last_processed_block").where("id", "=", true).executeTakeFirst();
3287
+ return row ? Number(row.last_processed_block) : 0;
3288
+ }
3289
+ async function advanceCursor(db, to) {
3290
+ await db.transaction().execute(async (trx) => {
3291
+ const cur = await trx.selectFrom("trigger_evaluator_state").select("last_processed_block").where("id", "=", true).forUpdate().executeTakeFirst();
3292
+ if (cur && Number(cur.last_processed_block) < to) {
3293
+ await trx.updateTable("trigger_evaluator_state").set({ last_processed_block: to, updated_at: new Date }).where("id", "=", true).execute();
3294
+ }
3295
+ });
3296
+ }
3297
+ async function runEvaluatorOnce(db = getTargetDb7()) {
3298
+ const chainSubs = await listActiveChainSubscriptions(db);
3299
+ const source = new PublicApiBlockSource(buildHttpClient(), referencedEventTypes(chainSubs));
3300
+ const tip = await source.getTip();
3301
+ if (tip <= 0)
3302
+ return 0;
3303
+ const cursor = await readCursor(db);
3304
+ if (cursor === 0 || chainSubs.length === 0) {
3305
+ await advanceCursor(db, tip);
3306
+ return 0;
3307
+ }
3308
+ if (cursor >= tip)
3309
+ return 0;
3310
+ const { sources, keyMeta } = buildSourcesMap(chainSubs);
3311
+ const target = Math.min(tip, cursor + MAX_BLOCKS_PER_TICK);
3312
+ let emitted = 0;
3313
+ for (let from = cursor + 1;from <= target; from = from + BATCH) {
3314
+ const to = Math.min(from + BATCH - 1, target);
3315
+ const blocks = await source.loadBlockRange(from, to);
3316
+ const traitContracts = await buildTraitContracts(db, chainSubs, to);
3317
+ for (let h = from;h <= to; h++) {
3318
+ const bd = blocks.get(h);
3319
+ if (!bd)
3320
+ continue;
3321
+ const matches = evaluateBlock(bd, sources, traitContracts);
3322
+ if (matches.length === 0)
3323
+ continue;
3324
+ emitted += await emitChainOutbox(db, matches, keyMeta, h, bd.block.hash);
3325
+ }
3326
+ await advanceCursor(db, to);
3327
+ }
3328
+ return emitted;
3329
+ }
3330
+ function startTriggerEvaluator() {
3331
+ let running = true;
3332
+ let timer;
3333
+ const tick = async () => {
3334
+ if (!running)
3335
+ return;
3336
+ try {
3337
+ const emitted = await runEvaluatorOnce();
3338
+ if (emitted > 0) {
3339
+ logger13.info("Trigger evaluator emitted chain deliveries", {
3340
+ count: emitted
3341
+ });
3342
+ }
3343
+ } catch (err) {
3344
+ logger13.error("Trigger evaluator tick failed", {
3345
+ error: getErrorMessage5(err)
3346
+ });
3347
+ }
3348
+ if (running)
3349
+ timer = setTimeout(tick, POLL_MS2);
3350
+ };
3351
+ timer = setTimeout(tick, POLL_MS2);
3352
+ logger13.info("Trigger evaluator started", { pollMs: POLL_MS2 });
3081
3353
  return () => {
3082
3354
  running = false;
3083
3355
  if (timer)
@@ -3104,11 +3376,11 @@ async function catchUpAll(subgraphs, db, concurrency) {
3104
3376
  const def = await loadSubgraphDefinition(sg);
3105
3377
  await catchUpSubgraph(def, sg.name);
3106
3378
  } catch (err) {
3107
- const msg = getErrorMessage5(err);
3379
+ const msg = getErrorMessage6(err);
3108
3380
  if (isHandlerNotFoundError(err)) {
3109
3381
  await updateSubgraphStatus3(db, sg.name, "error");
3110
3382
  }
3111
- logger12.error("Subgraph catch-up failed", {
3383
+ logger14.error("Subgraph catch-up failed", {
3112
3384
  subgraph: sg.name,
3113
3385
  error: msg
3114
3386
  });
@@ -3121,10 +3393,10 @@ function handlerImportUrl(handlerPath, cacheBust = Date.now()) {
3121
3393
  return `${pathToFileURL(resolve(handlerPath)).href}?t=${cacheBust}`;
3122
3394
  }
3123
3395
  function sourceListenerUrl() {
3124
- return process.env.SOURCE_DATABASE_URL ?? process.env.DATABASE_URL;
3396
+ return process.env.SOURCE_DATABASE_URL || process.env.DATABASE_URL;
3125
3397
  }
3126
3398
  function targetListenerUrl() {
3127
- return process.env.TARGET_DATABASE_URL ?? process.env.DATABASE_URL;
3399
+ return process.env.TARGET_DATABASE_URL || process.env.DATABASE_URL;
3128
3400
  }
3129
3401
  function isHandlerNotFoundError(err) {
3130
3402
  if (!(err instanceof Error))
@@ -3154,7 +3426,7 @@ async function loadSubgraphDefinition(sg) {
3154
3426
  definitionCache.set(sg.name, def);
3155
3427
  if (prevVersion && prevVersion !== sg.version) {
3156
3428
  invalidateSubgraphRoute(sg.name);
3157
- logger12.info("Subgraph handler reloaded", {
3429
+ logger14.info("Subgraph handler reloaded", {
3158
3430
  subgraph: sg.name,
3159
3431
  from: prevVersion,
3160
3432
  to: sg.version
@@ -3173,7 +3445,7 @@ function cleanupCaches(active) {
3173
3445
  }
3174
3446
  }
3175
3447
  async function synthesizeLegacyReindexOperations() {
3176
- const db = getTargetDb6();
3448
+ const db = getTargetDb8();
3177
3449
  const stale = (await listSubgraphs2(db)).filter((sg) => sg.status === "reindexing");
3178
3450
  for (const sg of stale) {
3179
3451
  const active = await findActiveSubgraphOperation(db, sg.id);
@@ -3188,7 +3460,7 @@ async function synthesizeLegacyReindexOperations() {
3188
3460
  fromBlock: sg.reindex_from_block == null ? undefined : Number(sg.reindex_from_block),
3189
3461
  toBlock: sg.reindex_to_block == null ? undefined : Number(sg.reindex_to_block)
3190
3462
  });
3191
- logger12.info("Queued legacy reindex resume operation", {
3463
+ logger14.info("Queued legacy reindex resume operation", {
3192
3464
  subgraph: sg.name
3193
3465
  });
3194
3466
  } catch (err) {
@@ -3202,7 +3474,7 @@ async function runSubgraphOperation(operation, signal) {
3202
3474
  if (operation.cancel_requested) {
3203
3475
  return 0;
3204
3476
  }
3205
- const db = getTargetDb6();
3477
+ const db = getTargetDb8();
3206
3478
  const subgraph = await db.selectFrom("subgraphs").selectAll().where("id", "=", operation.subgraph_id).executeTakeFirst();
3207
3479
  if (!subgraph)
3208
3480
  throw new Error(`Subgraph not found: ${operation.subgraph_id}`);
@@ -3238,13 +3510,13 @@ async function runSubgraphOperation(operation, signal) {
3238
3510
  }
3239
3511
  async function startSubgraphOperationRunner(opts) {
3240
3512
  const concurrency = opts?.concurrency ?? DEFAULT_OPERATION_CONCURRENCY;
3241
- const db = getTargetDb6();
3513
+ const db = getTargetDb8();
3242
3514
  const lockedBy = `${hostname()}:${process.pid}:${randomUUID()}`;
3243
3515
  const active = new Map;
3244
3516
  const activeRuns = new Map;
3245
3517
  let running = true;
3246
3518
  let draining = false;
3247
- logger12.info("Starting subgraph operation runner", { concurrency, lockedBy });
3519
+ logger14.info("Starting subgraph operation runner", { concurrency, lockedBy });
3248
3520
  const startOperation = (operation) => {
3249
3521
  const controller = new AbortController;
3250
3522
  active.set(operation.id, controller);
@@ -3252,9 +3524,9 @@ async function startSubgraphOperationRunner(opts) {
3252
3524
  if (!running)
3253
3525
  return;
3254
3526
  heartbeatSubgraphOperation(db, operation.id, lockedBy).catch((err) => {
3255
- logger12.warn("Subgraph operation heartbeat failed", {
3527
+ logger14.warn("Subgraph operation heartbeat failed", {
3256
3528
  operationId: operation.id,
3257
- error: getErrorMessage5(err)
3529
+ error: getErrorMessage6(err)
3258
3530
  });
3259
3531
  });
3260
3532
  }, HEARTBEAT_INTERVAL_MS);
@@ -3264,9 +3536,9 @@ async function startSubgraphOperationRunner(opts) {
3264
3536
  controller.abort("user-cancelled");
3265
3537
  }
3266
3538
  }).catch((err) => {
3267
- logger12.warn("Subgraph operation cancel poll failed", {
3539
+ logger14.warn("Subgraph operation cancel poll failed", {
3268
3540
  operationId: operation.id,
3269
- error: getErrorMessage5(err)
3541
+ error: getErrorMessage6(err)
3270
3542
  });
3271
3543
  });
3272
3544
  }, CANCEL_POLL_INTERVAL_MS);
@@ -3281,14 +3553,14 @@ async function startSubgraphOperationRunner(opts) {
3281
3553
  const reason = String(controller.signal.reason ?? "");
3282
3554
  if (controller.signal.aborted && reason === "user-cancelled") {
3283
3555
  await cancelSubgraphOperation(db, operation.id, lockedBy, processed);
3284
- logger12.info("Subgraph operation cancelled", {
3556
+ logger14.info("Subgraph operation cancelled", {
3285
3557
  operationId: operation.id,
3286
3558
  subgraph: operation.subgraph_name
3287
3559
  });
3288
3560
  return;
3289
3561
  }
3290
3562
  if (controller.signal.aborted) {
3291
- logger12.info("Subgraph operation interrupted", {
3563
+ logger14.info("Subgraph operation interrupted", {
3292
3564
  operationId: operation.id,
3293
3565
  subgraph: operation.subgraph_name,
3294
3566
  reason
@@ -3296,7 +3568,7 @@ async function startSubgraphOperationRunner(opts) {
3296
3568
  return;
3297
3569
  }
3298
3570
  await completeSubgraphOperation(db, operation.id, lockedBy, processed);
3299
- logger12.info("Subgraph operation completed", {
3571
+ logger14.info("Subgraph operation completed", {
3300
3572
  operationId: operation.id,
3301
3573
  subgraph: operation.subgraph_name,
3302
3574
  processed
@@ -3304,7 +3576,7 @@ async function startSubgraphOperationRunner(opts) {
3304
3576
  } catch (err) {
3305
3577
  const reason = String(controller.signal.reason ?? "");
3306
3578
  if (controller.signal.aborted && reason === "shutdown") {
3307
- logger12.info("Subgraph operation interrupted by shutdown", {
3579
+ logger14.info("Subgraph operation interrupted by shutdown", {
3308
3580
  operationId: operation.id,
3309
3581
  subgraph: operation.subgraph_name
3310
3582
  });
@@ -3314,11 +3586,11 @@ async function startSubgraphOperationRunner(opts) {
3314
3586
  await cancelSubgraphOperation(db, operation.id, lockedBy, processed);
3315
3587
  return;
3316
3588
  }
3317
- await failSubgraphOperation(db, operation.id, lockedBy, getErrorMessage5(err), processed);
3318
- logger12.error("Subgraph operation failed", {
3589
+ await failSubgraphOperation(db, operation.id, lockedBy, getErrorMessage6(err), processed);
3590
+ logger14.error("Subgraph operation failed", {
3319
3591
  operationId: operation.id,
3320
3592
  subgraph: operation.subgraph_name,
3321
- error: getErrorMessage5(err)
3593
+ error: getErrorMessage6(err)
3322
3594
  });
3323
3595
  } finally {
3324
3596
  clearInterval(heartbeat);
@@ -3362,23 +3634,23 @@ async function startSubgraphOperationRunner(opts) {
3362
3634
  controller.abort("shutdown");
3363
3635
  }
3364
3636
  await Promise.allSettled(activeRuns.values());
3365
- logger12.info("Subgraph operation runner stopped");
3637
+ logger14.info("Subgraph operation runner stopped");
3366
3638
  };
3367
3639
  }
3368
3640
  async function startSubgraphProcessor(opts) {
3369
3641
  const concurrency = opts?.concurrency ?? DEFAULT_CONCURRENCY;
3370
3642
  let running = true;
3371
- logger12.info("Starting subgraph processor", { concurrency });
3643
+ logger14.info("Starting subgraph processor", { concurrency });
3372
3644
  const stopOperations = await startSubgraphOperationRunner({
3373
3645
  concurrency: Number.parseInt(process.env.SUBGRAPH_OPERATION_CONCURRENCY ?? String(DEFAULT_OPERATION_CONCURRENCY))
3374
3646
  });
3375
- const targetDb = getTargetDb6();
3647
+ const targetDb = getTargetDb8();
3376
3648
  const activeSubgraphs = (await listSubgraphs2(targetDb)).filter((v) => v.status === "active");
3377
3649
  await catchUpAll(activeSubgraphs, targetDb, concurrency);
3378
3650
  const stopListening = await listen2(CHANNEL_NEW_BLOCK, async () => {
3379
3651
  if (!running)
3380
3652
  return;
3381
- const db = getTargetDb6();
3653
+ const db = getTargetDb8();
3382
3654
  const subgraphs = (await listSubgraphs2(db)).filter((v) => v.status === "active");
3383
3655
  cleanupCaches(subgraphs);
3384
3656
  await catchUpAll(subgraphs, db, concurrency);
@@ -3393,31 +3665,33 @@ async function startSubgraphProcessor(opts) {
3393
3665
  await handleSubgraphReorg(blockHeight, loadSubgraphDefinition);
3394
3666
  }
3395
3667
  } catch (err) {
3396
- logger12.error("Subgraph reorg handling failed", {
3397
- error: getErrorMessage5(err)
3668
+ logger14.error("Subgraph reorg handling failed", {
3669
+ error: getErrorMessage6(err)
3398
3670
  });
3399
3671
  }
3400
3672
  }, { connectionString: sourceListenerUrl() });
3401
3673
  const pollInterval = setInterval(async () => {
3402
3674
  if (!running)
3403
3675
  return;
3404
- const db = getTargetDb6();
3676
+ const db = getTargetDb8();
3405
3677
  const subgraphs = (await listSubgraphs2(db)).filter((v) => v.status === "active");
3406
3678
  cleanupCaches(subgraphs);
3407
3679
  await catchUpAll(subgraphs, db, concurrency);
3408
3680
  }, POLL_INTERVAL_MS);
3409
- const stopStreamsReorgPoll = process.env.SUBGRAPH_SOURCE === "streams-index" ? startStreamsReorgPoll(handleSubgraphReorg, loadSubgraphDefinition) : undefined;
3681
+ const stopStreamsReorgPoll = process.env.SUBGRAPH_SOURCE === "streams-index" ? startStreamsReorgPoll(handleSubgraphReorg, loadSubgraphDefinition, (forkHeight) => handleChainReorg(forkHeight)) : undefined;
3682
+ const stopTriggerEvaluator = process.env.SUBGRAPH_SOURCE === "streams-index" ? startTriggerEvaluator() : undefined;
3410
3683
  const stopEmitter = await startEmitter();
3411
- logger12.info("Subgraph processor ready");
3684
+ logger14.info("Subgraph processor ready");
3412
3685
  return async () => {
3413
3686
  running = false;
3414
3687
  clearInterval(pollInterval);
3415
3688
  await stopListening();
3416
3689
  await stopReorgListening();
3417
3690
  stopStreamsReorgPoll?.();
3691
+ stopTriggerEvaluator?.();
3418
3692
  await stopOperations();
3419
3693
  await stopEmitter();
3420
- logger12.info("Subgraph processor stopped");
3694
+ logger14.info("Subgraph processor stopped");
3421
3695
  };
3422
3696
  }
3423
3697
  export {
@@ -3425,5 +3699,5 @@ export {
3425
3699
  startSubgraphOperationRunner
3426
3700
  };
3427
3701
 
3428
- //# debugId=882679D1617697E464756E2164756E21
3702
+ //# debugId=81F8DFE131D3131664756E2164756E21
3429
3703
  //# sourceMappingURL=processor.js.map