@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.
- package/dist/src/archive/range-digest.d.ts +50 -0
- package/dist/src/archive/semantic-digest-builder.d.ts +50 -0
- package/dist/src/coverage/index.d.ts +50 -0
- package/dist/src/db/index.d.ts +54 -1
- package/dist/src/db/index.js +4 -1
- package/dist/src/db/index.js.map +3 -3
- package/dist/src/db/queries/chain-reorgs.d.ts +50 -0
- package/dist/src/db/queries/chain-reorgs.js +4 -1
- package/dist/src/db/queries/chain-reorgs.js.map +3 -3
- package/dist/src/db/queries/contracts.d.ts +50 -0
- package/dist/src/db/queries/instance.d.ts +50 -0
- package/dist/src/db/queries/integrity.d.ts +50 -0
- package/dist/src/db/queries/subgraph-gaps.d.ts +50 -0
- package/dist/src/db/queries/subgraph-operations.d.ts +50 -0
- package/dist/src/db/queries/subgraphs.d.ts +50 -0
- package/dist/src/db/queries/sync-scope.d.ts +50 -0
- package/dist/src/db/queries/webhooks.d.ts +50 -0
- package/dist/src/db/schema.d.ts +51 -1
- package/dist/src/index.d.ts +169 -7
- package/dist/src/index.js +239 -87
- package/dist/src/index.js.map +6 -4
- package/dist/src/insights/index.d.ts +1540 -0
- package/dist/src/insights/index.js +2284 -0
- package/dist/src/insights/index.js.map +29 -0
- package/dist/src/insights/thresholds.d.ts +56 -0
- package/dist/src/insights/thresholds.js +86 -0
- package/dist/src/insights/thresholds.js.map +10 -0
- package/dist/src/leader.d.ts +3 -1
- package/dist/src/leader.js +3 -1
- package/dist/src/leader.js.map +3 -3
- package/dist/src/node/local-client.d.ts +50 -0
- package/dist/src/queue/listener.js +4 -1
- package/dist/src/queue/listener.js.map +3 -3
- package/dist/src/schemas/index.d.ts +95 -7
- package/dist/src/schemas/index.js +180 -87
- package/dist/src/schemas/index.js.map +4 -3
- package/dist/src/schemas/insights.d.ts +89 -0
- package/dist/src/schemas/insights.js +98 -0
- package/dist/src/schemas/insights.js.map +10 -0
- package/migrations/0169_subgraph_health_history.ts +54 -0
- package/migrations/0170_insights.ts +63 -0
- 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
|
+
"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"],
|