@secondlayer/shared 13.3.0 → 13.5.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 (42) hide show
  1. package/dist/src/archive/range-digest.d.ts +50 -0
  2. package/dist/src/archive/semantic-digest-builder.d.ts +50 -0
  3. package/dist/src/coverage/index.d.ts +50 -0
  4. package/dist/src/db/index.d.ts +54 -1
  5. package/dist/src/db/index.js +4 -1
  6. package/dist/src/db/index.js.map +3 -3
  7. package/dist/src/db/queries/chain-reorgs.d.ts +50 -0
  8. package/dist/src/db/queries/chain-reorgs.js +4 -1
  9. package/dist/src/db/queries/chain-reorgs.js.map +3 -3
  10. package/dist/src/db/queries/contracts.d.ts +50 -0
  11. package/dist/src/db/queries/instance.d.ts +50 -0
  12. package/dist/src/db/queries/integrity.d.ts +50 -0
  13. package/dist/src/db/queries/subgraph-gaps.d.ts +50 -0
  14. package/dist/src/db/queries/subgraph-operations.d.ts +50 -0
  15. package/dist/src/db/queries/subgraphs.d.ts +50 -0
  16. package/dist/src/db/queries/sync-scope.d.ts +50 -0
  17. package/dist/src/db/queries/webhooks.d.ts +50 -0
  18. package/dist/src/db/schema.d.ts +51 -1
  19. package/dist/src/index.d.ts +169 -7
  20. package/dist/src/index.js +239 -87
  21. package/dist/src/index.js.map +6 -4
  22. package/dist/src/insights/index.d.ts +1540 -0
  23. package/dist/src/insights/index.js +2284 -0
  24. package/dist/src/insights/index.js.map +29 -0
  25. package/dist/src/insights/thresholds.d.ts +56 -0
  26. package/dist/src/insights/thresholds.js +86 -0
  27. package/dist/src/insights/thresholds.js.map +10 -0
  28. package/dist/src/leader.d.ts +3 -1
  29. package/dist/src/leader.js +3 -1
  30. package/dist/src/leader.js.map +3 -3
  31. package/dist/src/node/local-client.d.ts +50 -0
  32. package/dist/src/queue/listener.js +4 -1
  33. package/dist/src/queue/listener.js.map +3 -3
  34. package/dist/src/schemas/index.d.ts +95 -7
  35. package/dist/src/schemas/index.js +180 -87
  36. package/dist/src/schemas/index.js.map +4 -3
  37. package/dist/src/schemas/insights.d.ts +89 -0
  38. package/dist/src/schemas/insights.js +98 -0
  39. package/dist/src/schemas/insights.js.map +10 -0
  40. package/migrations/0169_subgraph_health_history.ts +54 -0
  41. package/migrations/0170_insights.ts +63 -0
  42. package/package.json +13 -1
@@ -0,0 +1,89 @@
1
+ import { z } from "zod";
2
+ /**
3
+ * The insight contract. The tenant (`/api/insights`) and platform
4
+ * (`/api/accounts/insights`) routes serve `Insight` verbatim. The web app keeps
5
+ * a field-for-field copy in `apps/web/src/lib/insights.ts`; change both
6
+ * together.
7
+ *
8
+ * The types are written out and the schemas annotated with them, rather than
9
+ * inferred: the bundled `.d.ts` loses inferred zod types (they come out as
10
+ * `unknown`), the same reason `schemas/webhooks.ts` annotates its schemas.
11
+ */
12
+ declare const INSIGHT_SEVERITIES: readonly ["losing", "will_fail", "costs", "recovered"];
13
+ type InsightSeverity = (typeof INSIGHT_SEVERITIES)[number];
14
+ type InsightResourceKind = "webhook" | "subgraph" | "account";
15
+ type InsightEvidence = {
16
+ kind: "series"
17
+ title: string
18
+ series: {
19
+ name: string
20
+ role: "a" | "context" | "mark"
21
+ points: number[]
22
+ }[]
23
+ labels: [number, string][]
24
+ unit?: string
25
+ } | {
26
+ kind: "table"
27
+ title: string
28
+ columns: string[]
29
+ rows: (string | number)[][]
30
+ markRow?: number
31
+ } | {
32
+ kind: "shapes"
33
+ title: string
34
+ shapes: {
35
+ code: string
36
+ body: string
37
+ count: number
38
+ isNew: boolean
39
+ }[]
40
+ } | {
41
+ kind: "timeline"
42
+ title: string
43
+ steps: {
44
+ what: string
45
+ at: string
46
+ note?: string
47
+ failed: boolean
48
+ }[]
49
+ };
50
+ interface Insight {
51
+ id: string;
52
+ rule: string;
53
+ severity: InsightSeverity;
54
+ impact: number;
55
+ resource: {
56
+ kind: InsightResourceKind
57
+ id: string
58
+ name: string
59
+ };
60
+ line: string;
61
+ meta?: string;
62
+ lead: string;
63
+ body: string;
64
+ evidence: InsightEvidence[];
65
+ handoff: {
66
+ prompt: string
67
+ commands: string[]
68
+ };
69
+ how: {
70
+ detector: string
71
+ facts: [string, string][]
72
+ label?: {
73
+ cause: string
74
+ confidence: number
75
+ }
76
+ };
77
+ createdAt: string;
78
+ resolvedAt?: string | null;
79
+ }
80
+ interface InsightListResponse {
81
+ data: Insight[];
82
+ }
83
+ declare const InsightSeveritySchema: z.ZodType<InsightSeverity>;
84
+ declare const InsightResourceKindSchema: z.ZodType<InsightResourceKind>;
85
+ declare const InsightEvidenceSchema: z.ZodType<InsightEvidence>;
86
+ declare const InsightSchema: z.ZodType<Insight>;
87
+ declare const InsightListResponseSchema: z.ZodType<InsightListResponse>;
88
+ declare const SEVERITY_ORDER: Record<InsightSeverity, number>;
89
+ export { INSIGHT_SEVERITIES, Insight, InsightEvidence, InsightEvidenceSchema, InsightListResponse, InsightListResponseSchema, InsightResourceKind, InsightResourceKindSchema, InsightSchema, InsightSeverity, InsightSeveritySchema, SEVERITY_ORDER };
@@ -0,0 +1,98 @@
1
+ // src/schemas/insights.ts
2
+ import { z } from "zod";
3
+ var INSIGHT_SEVERITIES = [
4
+ "losing",
5
+ "will_fail",
6
+ "costs",
7
+ "recovered"
8
+ ];
9
+ var InsightSeveritySchema = z.enum(INSIGHT_SEVERITIES);
10
+ var InsightResourceKindSchema = z.enum(["webhook", "subgraph", "account"]);
11
+ var InsightEvidenceSchema = z.discriminatedUnion("kind", [
12
+ z.object({
13
+ kind: z.literal("series"),
14
+ title: z.string(),
15
+ series: z.array(z.object({
16
+ name: z.string(),
17
+ role: z.enum(["a", "context", "mark"]),
18
+ points: z.array(z.number())
19
+ })),
20
+ labels: z.array(z.tuple([z.number(), z.string()])),
21
+ unit: z.string().optional()
22
+ }),
23
+ z.object({
24
+ kind: z.literal("table"),
25
+ title: z.string(),
26
+ columns: z.array(z.string()),
27
+ rows: z.array(z.array(z.union([z.string(), z.number()]))),
28
+ markRow: z.number().optional()
29
+ }),
30
+ z.object({
31
+ kind: z.literal("shapes"),
32
+ title: z.string(),
33
+ shapes: z.array(z.object({
34
+ code: z.string(),
35
+ body: z.string(),
36
+ count: z.number(),
37
+ isNew: z.boolean()
38
+ }))
39
+ }),
40
+ z.object({
41
+ kind: z.literal("timeline"),
42
+ title: z.string(),
43
+ steps: z.array(z.object({
44
+ what: z.string(),
45
+ at: z.string(),
46
+ note: z.string().optional(),
47
+ failed: z.boolean()
48
+ }))
49
+ })
50
+ ]);
51
+ var InsightSchema = z.object({
52
+ id: z.string(),
53
+ rule: z.string(),
54
+ severity: InsightSeveritySchema,
55
+ impact: z.number(),
56
+ resource: z.object({
57
+ kind: InsightResourceKindSchema,
58
+ id: z.string(),
59
+ name: z.string()
60
+ }),
61
+ line: z.string(),
62
+ meta: z.string().optional(),
63
+ lead: z.string(),
64
+ body: z.string(),
65
+ evidence: z.array(InsightEvidenceSchema),
66
+ handoff: z.object({
67
+ prompt: z.string(),
68
+ commands: z.array(z.string())
69
+ }),
70
+ how: z.object({
71
+ detector: z.string(),
72
+ facts: z.array(z.tuple([z.string(), z.string()])),
73
+ label: z.object({ cause: z.string(), confidence: z.number() }).optional()
74
+ }),
75
+ createdAt: z.string(),
76
+ resolvedAt: z.string().nullable().optional()
77
+ });
78
+ var InsightListResponseSchema = z.object({
79
+ data: z.array(InsightSchema)
80
+ });
81
+ var SEVERITY_ORDER = {
82
+ losing: 0,
83
+ will_fail: 1,
84
+ costs: 2,
85
+ recovered: 3
86
+ };
87
+ export {
88
+ INSIGHT_SEVERITIES,
89
+ InsightEvidenceSchema,
90
+ InsightListResponseSchema,
91
+ InsightResourceKindSchema,
92
+ InsightSchema,
93
+ InsightSeveritySchema,
94
+ SEVERITY_ORDER
95
+ };
96
+
97
+ //# debugId=3EFEA80342CD257B64756E2164756E21
98
+ //# sourceMappingURL=insights.js.map
@@ -0,0 +1,10 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/schemas/insights.ts"],
4
+ "sourcesContent": [
5
+ "import { z } from \"zod\";\n\n/**\n * The insight contract. The tenant (`/api/insights`) and platform\n * (`/api/accounts/insights`) routes serve `Insight` verbatim. The web app keeps\n * a field-for-field copy in `apps/web/src/lib/insights.ts`; change both\n * together.\n *\n * The types are written out and the schemas annotated with them, rather than\n * inferred: the bundled `.d.ts` loses inferred zod types (they come out as\n * `unknown`), the same reason `schemas/webhooks.ts` annotates its schemas.\n */\n\nexport const INSIGHT_SEVERITIES = [\n\t\"losing\",\n\t\"will_fail\",\n\t\"costs\",\n\t\"recovered\",\n] as const;\n\nexport type InsightSeverity = (typeof INSIGHT_SEVERITIES)[number];\n\nexport type InsightResourceKind = \"webhook\" | \"subgraph\" | \"account\";\n\nexport type InsightEvidence =\n\t| {\n\t\t\tkind: \"series\";\n\t\t\ttitle: string;\n\t\t\tseries: {\n\t\t\t\tname: string;\n\t\t\t\trole: \"a\" | \"context\" | \"mark\";\n\t\t\t\tpoints: number[];\n\t\t\t}[];\n\t\t\tlabels: [number, string][];\n\t\t\tunit?: string;\n\t }\n\t| {\n\t\t\tkind: \"table\";\n\t\t\ttitle: string;\n\t\t\tcolumns: string[];\n\t\t\trows: (string | number)[][];\n\t\t\tmarkRow?: number;\n\t }\n\t| {\n\t\t\tkind: \"shapes\";\n\t\t\ttitle: string;\n\t\t\tshapes: { code: string; body: string; count: number; isNew: boolean }[];\n\t }\n\t| {\n\t\t\tkind: \"timeline\";\n\t\t\ttitle: string;\n\t\t\tsteps: { what: string; at: string; note?: string; failed: boolean }[];\n\t };\n\nexport interface Insight {\n\tid: string;\n\trule: string;\n\tseverity: InsightSeverity;\n\timpact: number;\n\tresource: {\n\t\tkind: InsightResourceKind;\n\t\tid: string;\n\t\tname: string;\n\t};\n\tline: string;\n\tmeta?: string;\n\tlead: string;\n\tbody: string;\n\tevidence: InsightEvidence[];\n\thandoff: { prompt: string; commands: string[] };\n\thow: {\n\t\tdetector: string;\n\t\tfacts: [string, string][];\n\t\tlabel?: { cause: string; confidence: number };\n\t};\n\tcreatedAt: string;\n\tresolvedAt?: string | null;\n}\n\nexport interface InsightListResponse {\n\tdata: Insight[];\n}\n\nexport const InsightSeveritySchema: z.ZodType<InsightSeverity> =\n\tz.enum(INSIGHT_SEVERITIES);\n\nexport const InsightResourceKindSchema: z.ZodType<InsightResourceKind> = z.enum(\n\t[\"webhook\", \"subgraph\", \"account\"],\n);\n\nexport const InsightEvidenceSchema: z.ZodType<InsightEvidence> =\n\tz.discriminatedUnion(\"kind\", [\n\t\tz.object({\n\t\t\tkind: z.literal(\"series\"),\n\t\t\ttitle: z.string(),\n\t\t\tseries: z.array(\n\t\t\t\tz.object({\n\t\t\t\t\tname: z.string(),\n\t\t\t\t\trole: z.enum([\"a\", \"context\", \"mark\"]),\n\t\t\t\t\tpoints: z.array(z.number()),\n\t\t\t\t}),\n\t\t\t),\n\t\t\tlabels: z.array(z.tuple([z.number(), z.string()])),\n\t\t\tunit: z.string().optional(),\n\t\t}),\n\t\tz.object({\n\t\t\tkind: z.literal(\"table\"),\n\t\t\ttitle: z.string(),\n\t\t\tcolumns: z.array(z.string()),\n\t\t\trows: z.array(z.array(z.union([z.string(), z.number()]))),\n\t\t\tmarkRow: z.number().optional(),\n\t\t}),\n\t\tz.object({\n\t\t\tkind: z.literal(\"shapes\"),\n\t\t\ttitle: z.string(),\n\t\t\tshapes: z.array(\n\t\t\t\tz.object({\n\t\t\t\t\tcode: z.string(),\n\t\t\t\t\tbody: z.string(),\n\t\t\t\t\tcount: z.number(),\n\t\t\t\t\tisNew: z.boolean(),\n\t\t\t\t}),\n\t\t\t),\n\t\t}),\n\t\tz.object({\n\t\t\tkind: z.literal(\"timeline\"),\n\t\t\ttitle: z.string(),\n\t\t\tsteps: z.array(\n\t\t\t\tz.object({\n\t\t\t\t\twhat: z.string(),\n\t\t\t\t\tat: z.string(),\n\t\t\t\t\tnote: z.string().optional(),\n\t\t\t\t\tfailed: z.boolean(),\n\t\t\t\t}),\n\t\t\t),\n\t\t}),\n\t]);\n\nexport const InsightSchema: z.ZodType<Insight> = z.object({\n\tid: z.string(),\n\trule: z.string(),\n\tseverity: InsightSeveritySchema,\n\timpact: z.number(),\n\tresource: z.object({\n\t\tkind: InsightResourceKindSchema,\n\t\tid: z.string(),\n\t\tname: z.string(),\n\t}),\n\tline: z.string(),\n\tmeta: z.string().optional(),\n\tlead: z.string(),\n\tbody: z.string(),\n\tevidence: z.array(InsightEvidenceSchema),\n\thandoff: z.object({\n\t\tprompt: z.string(),\n\t\tcommands: z.array(z.string()),\n\t}),\n\thow: z.object({\n\t\tdetector: z.string(),\n\t\tfacts: z.array(z.tuple([z.string(), z.string()])),\n\t\tlabel: z.object({ cause: z.string(), confidence: z.number() }).optional(),\n\t}),\n\tcreatedAt: z.string(),\n\tresolvedAt: z.string().nullable().optional(),\n});\n\nexport const InsightListResponseSchema: z.ZodType<InsightListResponse> =\n\tz.object({\n\t\tdata: z.array(InsightSchema),\n\t});\n\nexport const SEVERITY_ORDER: Record<InsightSeverity, number> = {\n\tlosing: 0,\n\twill_fail: 1,\n\tcosts: 2,\n\trecovered: 3,\n};\n"
6
+ ],
7
+ "mappings": "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",
8
+ "debugId": "3EFEA80342CD257B64756E2164756E21",
9
+ "names": []
10
+ }
@@ -0,0 +1,54 @@
1
+ import { type Kysely, sql } from "kysely";
2
+ import { onControlPlane } from "../src/db/migration-role.ts";
3
+
4
+ /**
5
+ * Health history: lag/rows/storage columns on the (previously unwritten)
6
+ * snapshot table, plus fingerprint-grouped handler errors (cap 50/subgraph,
7
+ * enforced at write time).
8
+ */
9
+ export async function up(db: Kysely<unknown>): Promise<void> {
10
+ await sql`SET lock_timeout = '30s'`.execute(db);
11
+ await onControlPlane(async () => {
12
+ await sql`
13
+ ALTER TABLE subgraph_health_snapshots
14
+ ADD COLUMN IF NOT EXISTS chain_tip bigint,
15
+ ADD COLUMN IF NOT EXISTS rows_written_total bigint,
16
+ ADD COLUMN IF NOT EXISTS storage_bytes bigint
17
+ `.execute(db);
18
+
19
+ await sql`
20
+ CREATE TABLE IF NOT EXISTS subgraph_error_groups (
21
+ id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
22
+ subgraph_name text NOT NULL,
23
+ fingerprint text NOT NULL,
24
+ source_name text NOT NULL,
25
+ event_type text,
26
+ message text NOT NULL,
27
+ frame text,
28
+ count bigint NOT NULL DEFAULT 1,
29
+ sample_tx_id text,
30
+ sample_block_height bigint,
31
+ first_seen timestamptz NOT NULL DEFAULT now(),
32
+ last_seen timestamptz NOT NULL DEFAULT now(),
33
+ UNIQUE (subgraph_name, fingerprint)
34
+ )
35
+ `.execute(db);
36
+
37
+ await sql`
38
+ CREATE INDEX IF NOT EXISTS subgraph_error_groups_name_seen_idx
39
+ ON subgraph_error_groups (subgraph_name, last_seen DESC)
40
+ `.execute(db);
41
+ });
42
+ }
43
+
44
+ export async function down(db: Kysely<unknown>): Promise<void> {
45
+ await onControlPlane(async () => {
46
+ await sql`DROP TABLE IF EXISTS subgraph_error_groups`.execute(db);
47
+ await sql`
48
+ ALTER TABLE subgraph_health_snapshots
49
+ DROP COLUMN IF EXISTS chain_tip,
50
+ DROP COLUMN IF EXISTS rows_written_total,
51
+ DROP COLUMN IF EXISTS storage_bytes
52
+ `.execute(db);
53
+ });
54
+ }
@@ -0,0 +1,63 @@
1
+ import { type Kysely, sql } from "kysely";
2
+ import { onControlPlane } from "../src/db/migration-role.ts";
3
+
4
+ /**
5
+ * Ranked dashboard insights (one open row per resource + rule) and the
6
+ * per-resource "expected" mutes. Created on every control-plane database:
7
+ * the platform DB (account rules) and each tenant / self-host DB (webhook and
8
+ * subgraph rules). `account_id` is NULL on tenant and self-host rows (there is
9
+ * no meaningful account id there) so it carries no foreign key.
10
+ */
11
+ export async function up(db: Kysely<unknown>): Promise<void> {
12
+ await onControlPlane(async () => {
13
+ await sql`
14
+ CREATE TABLE insights (
15
+ id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
16
+ account_id uuid,
17
+ resource_kind text NOT NULL,
18
+ resource_id text NOT NULL,
19
+ rule text NOT NULL,
20
+ severity text NOT NULL,
21
+ impact double precision NOT NULL DEFAULT 0,
22
+ payload jsonb NOT NULL,
23
+ first_seen_at timestamptz NOT NULL DEFAULT now(),
24
+ last_seen_at timestamptz NOT NULL DEFAULT now(),
25
+ clear_streak integer NOT NULL DEFAULT 0,
26
+ resolved_at timestamptz,
27
+ dismissed_at timestamptz
28
+ )
29
+ `.execute(db);
30
+
31
+ await sql`
32
+ CREATE UNIQUE INDEX insights_open_key ON insights
33
+ (coalesce(account_id, '00000000-0000-0000-0000-000000000000'::uuid), resource_kind, resource_id, rule)
34
+ WHERE resolved_at IS NULL
35
+ `.execute(db);
36
+
37
+ await sql`
38
+ CREATE INDEX insights_account_recent ON insights (account_id, resolved_at)
39
+ `.execute(db);
40
+
41
+ await sql`
42
+ CREATE TABLE insight_mutes (
43
+ account_id uuid,
44
+ resource_kind text NOT NULL,
45
+ resource_id text NOT NULL,
46
+ rule text NOT NULL,
47
+ created_at timestamptz NOT NULL DEFAULT now()
48
+ )
49
+ `.execute(db);
50
+
51
+ await sql`
52
+ CREATE UNIQUE INDEX insight_mutes_key ON insight_mutes
53
+ (coalesce(account_id, '00000000-0000-0000-0000-000000000000'::uuid), resource_kind, resource_id, rule)
54
+ `.execute(db);
55
+ });
56
+ }
57
+
58
+ export async function down(db: Kysely<unknown>): Promise<void> {
59
+ await onControlPlane(async () => {
60
+ await sql`DROP TABLE IF EXISTS insight_mutes`.execute(db);
61
+ await sql`DROP TABLE IF EXISTS insights`.execute(db);
62
+ });
63
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@secondlayer/shared",
3
- "version": "13.3.0",
3
+ "version": "13.5.0",
4
4
  "description": "Internal building blocks shared across Secondlayer packages.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -240,6 +240,18 @@
240
240
  "./classify": {
241
241
  "types": "./dist/src/classify/index.d.ts",
242
242
  "import": "./dist/src/classify/index.js"
243
+ },
244
+ "./insights": {
245
+ "types": "./dist/src/insights/index.d.ts",
246
+ "import": "./dist/src/insights/index.js"
247
+ },
248
+ "./insights/thresholds": {
249
+ "types": "./dist/src/insights/thresholds.d.ts",
250
+ "import": "./dist/src/insights/thresholds.js"
251
+ },
252
+ "./schemas/insights": {
253
+ "types": "./dist/src/schemas/insights.d.ts",
254
+ "import": "./dist/src/schemas/insights.js"
243
255
  }
244
256
  },
245
257
  "files": ["dist", "migrations"],