@secondlayer/subgraphs 3.20.0 → 3.21.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.
@@ -758,6 +758,87 @@ import {
758
758
  deserializeCV as deserializeCV2,
759
759
  toCamelCase
760
760
  } from "@secondlayer/stacks/clarity";
761
+
762
+ // src/runtime/print-validate.ts
763
+ var COLUMN_TYPES = new Set([
764
+ "text",
765
+ "uint",
766
+ "int",
767
+ "principal",
768
+ "boolean",
769
+ "timestamp",
770
+ "jsonb"
771
+ ]);
772
+ function isColumnType(field) {
773
+ return typeof field === "string" && COLUMN_TYPES.has(field);
774
+ }
775
+ function matchesField(field, value) {
776
+ if (isColumnType(field)) {
777
+ switch (field) {
778
+ case "uint":
779
+ case "int":
780
+ return typeof value === "bigint" || typeof value === "number";
781
+ case "boolean":
782
+ return typeof value === "boolean";
783
+ case "text":
784
+ case "principal":
785
+ case "timestamp":
786
+ return typeof value === "string" || typeof value === "number" || typeof value === "bigint";
787
+ case "jsonb":
788
+ return value !== undefined;
789
+ }
790
+ }
791
+ if (typeof field !== "object" || field === null)
792
+ return false;
793
+ if ("optional" in field) {
794
+ return value === undefined || value === null || matchesField(field.type, value);
795
+ }
796
+ if ("tuple" in field) {
797
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
798
+ return false;
799
+ }
800
+ return matchesTopic(field.tuple, value).ok;
801
+ }
802
+ if ("list" in field) {
803
+ return Array.isArray(value) && value.every((item) => matchesField(field.list, item));
804
+ }
805
+ return false;
806
+ }
807
+ function matchesTopic(declared, data) {
808
+ for (const [name, field] of Object.entries(declared)) {
809
+ const present = name in data;
810
+ const optional = typeof field === "object" && field !== null && "optional" in field;
811
+ if (!present) {
812
+ if (optional)
813
+ continue;
814
+ return { ok: false, reason: `missing declared field "${name}"` };
815
+ }
816
+ if (!matchesField(field, data[name])) {
817
+ return {
818
+ ok: false,
819
+ reason: `field "${name}" does not match its declared type (got ${describe(data[name])})`
820
+ };
821
+ }
822
+ }
823
+ return { ok: true };
824
+ }
825
+ function describe(value) {
826
+ if (value === null)
827
+ return "null";
828
+ if (Array.isArray(value))
829
+ return "array";
830
+ return typeof value;
831
+ }
832
+ function validatePrintPayload(prints, topic, data) {
833
+ if (!prints)
834
+ return { ok: true };
835
+ const declared = prints[topic];
836
+ if (!declared)
837
+ return { ok: true };
838
+ return matchesTopic(declared, data);
839
+ }
840
+
841
+ // src/runtime/runner.ts
761
842
  var DEFAULT_ERROR_THRESHOLD = 50;
762
843
  function camelCase(str) {
763
844
  return str.replace(/-([a-z0-9])/g, (_, c) => c.toUpperCase());
@@ -1002,6 +1083,7 @@ function buildEventPayload(filter, tx, event) {
1002
1083
  async function runHandlers(subgraph, matched, ctx, opts) {
1003
1084
  let processed = 0;
1004
1085
  let errors = 0;
1086
+ let skipped = 0;
1005
1087
  const threshold = opts?.errorThreshold ?? DEFAULT_ERROR_THRESHOLD;
1006
1088
  const filterLookup = new Map;
1007
1089
  if (!Array.isArray(subgraph.sources)) {
@@ -1026,7 +1108,7 @@ async function runHandlers(subgraph, matched, ctx, opts) {
1026
1108
  errors,
1027
1109
  threshold
1028
1110
  });
1029
- return { processed, errors };
1111
+ return { processed, errors, ...skipped > 0 ? { skipped } : {} };
1030
1112
  }
1031
1113
  const handler = subgraph.handlers[sourceName] ?? subgraph.handlers["*"] ?? null;
1032
1114
  if (!handler) {
@@ -1082,6 +1164,21 @@ async function runHandlers(subgraph, matched, ctx, opts) {
1082
1164
  if (event !== null && filter?.type === "print_event" && filter.topic && payload.topic !== filter.topic) {
1083
1165
  continue;
1084
1166
  }
1167
+ if (event !== null && filter?.type === "print_event" && filter.prints) {
1168
+ const printPayload = payload;
1169
+ const verdict = validatePrintPayload(filter.prints, printPayload.topic ?? "", printPayload.data ?? {});
1170
+ if (!verdict.ok) {
1171
+ skipped++;
1172
+ logger2.warn("Print payload does not match declared prints schema", {
1173
+ subgraph: subgraph.name,
1174
+ sourceName,
1175
+ topic: printPayload.topic,
1176
+ txId: tx.tx_id,
1177
+ reason: verdict.reason
1178
+ });
1179
+ continue;
1180
+ }
1181
+ }
1085
1182
  await handler(payload, ctx);
1086
1183
  processed++;
1087
1184
  } catch (err) {
@@ -1096,7 +1193,7 @@ async function runHandlers(subgraph, matched, ctx, opts) {
1096
1193
  });
1097
1194
  }
1098
1195
  }
1099
- return { processed, errors };
1196
+ return { processed, errors, ...skipped > 0 ? { skipped } : {} };
1100
1197
  }
1101
1198
 
1102
1199
  // src/runtime/source-matcher.ts
@@ -3671,5 +3768,5 @@ export {
3671
3768
  startSubgraphOperationRunner
3672
3769
  };
3673
3770
 
3674
- //# debugId=61F67650680699B664756E2164756E21
3771
+ //# debugId=0DCB2AA2FDAD1E6864756E2164756E21
3675
3772
  //# sourceMappingURL=processor.js.map