@secondlayer/sdk 12.0.0 → 12.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.
@@ -0,0 +1,42 @@
1
+ import { SubgraphDetail } from "@secondlayer/shared/schemas/subgraphs";
2
+ import { DeadRow, DeliveryRow, WebhookDetail } from "@secondlayer/shared/schemas/webhooks";
3
+ /**
4
+ * Webhook diagnosis, shared by `secondlayer webhooks doctor` and the
5
+ * dashboard's webhook detail page. Moved out of the CLI (unchanged behavior)
6
+ * so the web can build the same judgment without printing CLI-command text.
7
+ */
8
+ declare function isSuccessDelivery(row: DeliveryRow): boolean;
9
+ type DoctorIssueCode = "warning" | "paused" | "last_error" | "circuit" | "dead_letters" | "subgraph_gaps" | "subgraph_catching_up" | "no_deliveries";
10
+ interface DoctorIssue {
11
+ code: DoctorIssueCode;
12
+ detail?: string;
13
+ }
14
+ interface DoctorReport {
15
+ webhook: WebhookDetail;
16
+ deliverySummary: {
17
+ total: number;
18
+ successful: number;
19
+ failed: number;
20
+ last: DeliveryRow | null;
21
+ };
22
+ deadCount: number;
23
+ subgraph: {
24
+ name: string;
25
+ status: string;
26
+ syncStatus: string;
27
+ lastProcessedBlock: number;
28
+ chainTip: number;
29
+ gapCount: number;
30
+ integrity: string;
31
+ } | null;
32
+ /** CLI-facing next steps, one per issue, in the same order as `issues`. */
33
+ hints: string[];
34
+ issues: DoctorIssue[];
35
+ }
36
+ declare function buildDoctorReport(input: {
37
+ webhook: WebhookDetail;
38
+ deliveries: DeliveryRow[];
39
+ dead: DeadRow[];
40
+ subgraph?: SubgraphDetail | null;
41
+ }): DoctorReport;
42
+ export { DoctorIssue, DoctorIssueCode, DoctorReport, buildDoctorReport, isSuccessDelivery };
@@ -0,0 +1,74 @@
1
+ // src/webhooks/doctor.ts
2
+ function isSuccessDelivery(row) {
3
+ return row.statusCode !== null && row.statusCode >= 200 && row.statusCode < 300;
4
+ }
5
+ function buildDoctorReport(input) {
6
+ const successful = input.deliveries.filter(isSuccessDelivery).length;
7
+ const failed = input.deliveries.length - successful;
8
+ const subgraph = input.subgraph ? {
9
+ name: input.subgraph.name,
10
+ status: input.subgraph.status,
11
+ syncStatus: input.subgraph.sync.status,
12
+ lastProcessedBlock: input.subgraph.sync.lastProcessedBlock,
13
+ chainTip: input.subgraph.sync.chainTip,
14
+ gapCount: input.subgraph.sync.gaps.count,
15
+ integrity: input.subgraph.sync.integrity
16
+ } : null;
17
+ const hints = [];
18
+ const issues = [];
19
+ if (input.webhook.warning) {
20
+ hints.push(input.webhook.warning);
21
+ issues.push({ code: "warning", detail: input.webhook.warning });
22
+ }
23
+ if (input.webhook.status === "paused") {
24
+ hints.push(`Resume when the receiver is healthy: secondlayer webhooks resume ${input.webhook.id}`);
25
+ issues.push({ code: "paused" });
26
+ }
27
+ if (input.webhook.lastError) {
28
+ hints.push("Run secondlayer webhooks test to reproduce the receiver request.");
29
+ issues.push({ code: "last_error", detail: input.webhook.lastError });
30
+ }
31
+ if (input.webhook.circuitOpenedAt || input.webhook.circuitFailures > 0) {
32
+ hints.push("Circuit breaker has failures; inspect receiver logs and delivery status codes.");
33
+ issues.push({ code: "circuit" });
34
+ }
35
+ if (input.dead.length > 0) {
36
+ hints.push(`Dead-letter rows exist; inspect with secondlayer webhooks dead ${input.webhook.id} and requeue selected rows.`);
37
+ issues.push({ code: "dead_letters" });
38
+ }
39
+ if (subgraph?.gapCount && subgraph.gapCount > 0) {
40
+ hints.push(`Linked subgraph has gaps; run secondlayer subgraphs gaps ${input.webhook.subgraphName}.`);
41
+ issues.push({ code: "subgraph_gaps" });
42
+ }
43
+ if (subgraph?.syncStatus === "catching_up") {
44
+ hints.push("Linked subgraph is still catching up; new matching rows may arrive later.");
45
+ issues.push({ code: "subgraph_catching_up" });
46
+ }
47
+ if (input.deliveries.length === 0) {
48
+ hints.push("No deliveries yet; confirm the table is receiving inserted rows that match the filter.");
49
+ issues.push({ code: "no_deliveries" });
50
+ }
51
+ if (hints.length === 0) {
52
+ hints.push("No immediate action needed.");
53
+ }
54
+ return {
55
+ webhook: input.webhook,
56
+ deliverySummary: {
57
+ total: input.deliveries.length,
58
+ successful,
59
+ failed,
60
+ last: input.deliveries[0] ?? null
61
+ },
62
+ deadCount: input.dead.length,
63
+ subgraph,
64
+ hints,
65
+ issues
66
+ };
67
+ }
68
+ export {
69
+ buildDoctorReport,
70
+ isSuccessDelivery
71
+ };
72
+
73
+ //# debugId=2B12BAB34B7CE6B164756E2164756E21
74
+ //# sourceMappingURL=doctor.js.map
@@ -0,0 +1,10 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["src/webhooks/doctor.ts"],
4
+ "sourcesContent": [
5
+ "import type { SubgraphDetail } from \"@secondlayer/shared/schemas/subgraphs\";\nimport type {\n\tDeadRow,\n\tDeliveryRow,\n\tWebhookDetail,\n} from \"@secondlayer/shared/schemas/webhooks\";\n\n/**\n * Webhook diagnosis, shared by `secondlayer webhooks doctor` and the\n * dashboard's webhook detail page. Moved out of the CLI (unchanged behavior)\n * so the web can build the same judgment without printing CLI-command text.\n */\n\nexport function isSuccessDelivery(row: DeliveryRow): boolean {\n\treturn (\n\t\trow.statusCode !== null && row.statusCode >= 200 && row.statusCode < 300\n\t);\n}\n\nexport type DoctorIssueCode =\n\t| \"warning\"\n\t| \"paused\"\n\t| \"last_error\"\n\t| \"circuit\"\n\t| \"dead_letters\"\n\t| \"subgraph_gaps\"\n\t| \"subgraph_catching_up\"\n\t| \"no_deliveries\";\n\nexport interface DoctorIssue {\n\tcode: DoctorIssueCode;\n\tdetail?: string;\n}\n\nexport interface DoctorReport {\n\twebhook: WebhookDetail;\n\tdeliverySummary: {\n\t\ttotal: number;\n\t\tsuccessful: number;\n\t\tfailed: number;\n\t\tlast: DeliveryRow | null;\n\t};\n\tdeadCount: number;\n\tsubgraph: {\n\t\tname: string;\n\t\tstatus: string;\n\t\tsyncStatus: string;\n\t\tlastProcessedBlock: number;\n\t\tchainTip: number;\n\t\tgapCount: number;\n\t\tintegrity: string;\n\t} | null;\n\t/** CLI-facing next steps, one per issue, in the same order as `issues`. */\n\thints: string[];\n\tissues: DoctorIssue[];\n}\n\nexport function buildDoctorReport(input: {\n\twebhook: WebhookDetail;\n\tdeliveries: DeliveryRow[];\n\tdead: DeadRow[];\n\tsubgraph?: SubgraphDetail | null;\n}): DoctorReport {\n\tconst successful = input.deliveries.filter(isSuccessDelivery).length;\n\tconst failed = input.deliveries.length - successful;\n\tconst subgraph = input.subgraph\n\t\t? {\n\t\t\t\tname: input.subgraph.name,\n\t\t\t\tstatus: input.subgraph.status,\n\t\t\t\tsyncStatus: input.subgraph.sync.status,\n\t\t\t\tlastProcessedBlock: input.subgraph.sync.lastProcessedBlock,\n\t\t\t\tchainTip: input.subgraph.sync.chainTip,\n\t\t\t\tgapCount: input.subgraph.sync.gaps.count,\n\t\t\t\tintegrity: input.subgraph.sync.integrity,\n\t\t\t}\n\t\t: null;\n\n\tconst hints: string[] = [];\n\tconst issues: DoctorIssue[] = [];\n\tif (input.webhook.warning) {\n\t\thints.push(input.webhook.warning);\n\t\tissues.push({ code: \"warning\", detail: input.webhook.warning });\n\t}\n\tif (input.webhook.status === \"paused\") {\n\t\thints.push(\n\t\t\t`Resume when the receiver is healthy: secondlayer webhooks resume ${input.webhook.id}`,\n\t\t);\n\t\tissues.push({ code: \"paused\" });\n\t}\n\tif (input.webhook.lastError) {\n\t\thints.push(\n\t\t\t\"Run secondlayer webhooks test to reproduce the receiver request.\",\n\t\t);\n\t\tissues.push({ code: \"last_error\", detail: input.webhook.lastError });\n\t}\n\tif (input.webhook.circuitOpenedAt || input.webhook.circuitFailures > 0) {\n\t\thints.push(\n\t\t\t\"Circuit breaker has failures; inspect receiver logs and delivery status codes.\",\n\t\t);\n\t\tissues.push({ code: \"circuit\" });\n\t}\n\tif (input.dead.length > 0) {\n\t\thints.push(\n\t\t\t`Dead-letter rows exist; inspect with secondlayer webhooks dead ${input.webhook.id} and requeue selected rows.`,\n\t\t);\n\t\tissues.push({ code: \"dead_letters\" });\n\t}\n\tif (subgraph?.gapCount && subgraph.gapCount > 0) {\n\t\thints.push(\n\t\t\t`Linked subgraph has gaps; run secondlayer subgraphs gaps ${input.webhook.subgraphName}.`,\n\t\t);\n\t\tissues.push({ code: \"subgraph_gaps\" });\n\t}\n\tif (subgraph?.syncStatus === \"catching_up\") {\n\t\thints.push(\n\t\t\t\"Linked subgraph is still catching up; new matching rows may arrive later.\",\n\t\t);\n\t\tissues.push({ code: \"subgraph_catching_up\" });\n\t}\n\tif (input.deliveries.length === 0) {\n\t\thints.push(\n\t\t\t\"No deliveries yet; confirm the table is receiving inserted rows that match the filter.\",\n\t\t);\n\t\tissues.push({ code: \"no_deliveries\" });\n\t}\n\tif (hints.length === 0) {\n\t\thints.push(\"No immediate action needed.\");\n\t}\n\n\treturn {\n\t\twebhook: input.webhook,\n\t\tdeliverySummary: {\n\t\t\ttotal: input.deliveries.length,\n\t\t\tsuccessful,\n\t\t\tfailed,\n\t\t\tlast: input.deliveries[0] ?? null,\n\t\t},\n\t\tdeadCount: input.dead.length,\n\t\tsubgraph,\n\t\thints,\n\t\tissues,\n\t};\n}\n"
6
+ ],
7
+ "mappings": ";AAaO,SAAS,iBAAiB,CAAC,KAA2B;AAAA,EAC5D,OACC,IAAI,eAAe,QAAQ,IAAI,cAAc,OAAO,IAAI,aAAa;AAAA;AA0ChE,SAAS,iBAAiB,CAAC,OAKjB;AAAA,EAChB,MAAM,aAAa,MAAM,WAAW,OAAO,iBAAiB,EAAE;AAAA,EAC9D,MAAM,SAAS,MAAM,WAAW,SAAS;AAAA,EACzC,MAAM,WAAW,MAAM,WACpB;AAAA,IACA,MAAM,MAAM,SAAS;AAAA,IACrB,QAAQ,MAAM,SAAS;AAAA,IACvB,YAAY,MAAM,SAAS,KAAK;AAAA,IAChC,oBAAoB,MAAM,SAAS,KAAK;AAAA,IACxC,UAAU,MAAM,SAAS,KAAK;AAAA,IAC9B,UAAU,MAAM,SAAS,KAAK,KAAK;AAAA,IACnC,WAAW,MAAM,SAAS,KAAK;AAAA,EAChC,IACC;AAAA,EAEH,MAAM,QAAkB,CAAC;AAAA,EACzB,MAAM,SAAwB,CAAC;AAAA,EAC/B,IAAI,MAAM,QAAQ,SAAS;AAAA,IAC1B,MAAM,KAAK,MAAM,QAAQ,OAAO;AAAA,IAChC,OAAO,KAAK,EAAE,MAAM,WAAW,QAAQ,MAAM,QAAQ,QAAQ,CAAC;AAAA,EAC/D;AAAA,EACA,IAAI,MAAM,QAAQ,WAAW,UAAU;AAAA,IACtC,MAAM,KACL,oEAAoE,MAAM,QAAQ,IACnF;AAAA,IACA,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAAA,EAC/B;AAAA,EACA,IAAI,MAAM,QAAQ,WAAW;AAAA,IAC5B,MAAM,KACL,kEACD;AAAA,IACA,OAAO,KAAK,EAAE,MAAM,cAAc,QAAQ,MAAM,QAAQ,UAAU,CAAC;AAAA,EACpE;AAAA,EACA,IAAI,MAAM,QAAQ,mBAAmB,MAAM,QAAQ,kBAAkB,GAAG;AAAA,IACvE,MAAM,KACL,gFACD;AAAA,IACA,OAAO,KAAK,EAAE,MAAM,UAAU,CAAC;AAAA,EAChC;AAAA,EACA,IAAI,MAAM,KAAK,SAAS,GAAG;AAAA,IAC1B,MAAM,KACL,kEAAkE,MAAM,QAAQ,+BACjF;AAAA,IACA,OAAO,KAAK,EAAE,MAAM,eAAe,CAAC;AAAA,EACrC;AAAA,EACA,IAAI,UAAU,YAAY,SAAS,WAAW,GAAG;AAAA,IAChD,MAAM,KACL,4DAA4D,MAAM,QAAQ,eAC3E;AAAA,IACA,OAAO,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAAA,EACtC;AAAA,EACA,IAAI,UAAU,eAAe,eAAe;AAAA,IAC3C,MAAM,KACL,2EACD;AAAA,IACA,OAAO,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAAA,EAC7C;AAAA,EACA,IAAI,MAAM,WAAW,WAAW,GAAG;AAAA,IAClC,MAAM,KACL,wFACD;AAAA,IACA,OAAO,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAAA,EACtC;AAAA,EACA,IAAI,MAAM,WAAW,GAAG;AAAA,IACvB,MAAM,KAAK,6BAA6B;AAAA,EACzC;AAAA,EAEA,OAAO;AAAA,IACN,SAAS,MAAM;AAAA,IACf,iBAAiB;AAAA,MAChB,OAAO,MAAM,WAAW;AAAA,MACxB;AAAA,MACA;AAAA,MACA,MAAM,MAAM,WAAW,MAAM;AAAA,IAC9B;AAAA,IACA,WAAW,MAAM,KAAK;AAAA,IACtB;AAAA,IACA;AAAA,IACA;AAAA,EACD;AAAA;",
8
+ "debugId": "2B12BAB34B7CE6B164756E2164756E21",
9
+ "names": []
10
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@secondlayer/sdk",
3
- "version": "12.0.0",
3
+ "version": "12.2.0",
4
4
  "description": "Decoded Stacks data in your own database — stream rows into your schema, or query the tables you defined.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -48,6 +48,10 @@
48
48
  "./sinks/testing": {
49
49
  "types": "./dist/sinks/testing.d.ts",
50
50
  "import": "./dist/sinks/testing.js"
51
+ },
52
+ "./webhooks/doctor": {
53
+ "types": "./dist/webhooks/doctor.d.ts",
54
+ "import": "./dist/webhooks/doctor.js"
51
55
  }
52
56
  },
53
57
  "files": ["dist", "AGENTS.md"],
@@ -60,9 +64,9 @@
60
64
  "prepublishOnly": "bun run build"
61
65
  },
62
66
  "dependencies": {
63
- "@secondlayer/shared": "^11.10.1",
67
+ "@secondlayer/shared": "^11.11.0",
64
68
  "@secondlayer/stacks": "^6.1.0",
65
- "@secondlayer/subgraphs": "^6.0.0"
69
+ "@secondlayer/subgraphs": "^6.2.0"
66
70
  },
67
71
  "devDependencies": {
68
72
  "@openpkg-ts/cli": "^0.11.3",