@q32/core 0.1.7 → 0.1.9

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.
@@ -1,25 +1,120 @@
1
+ import type postgres from "postgres";
1
2
  import type { D1DatabaseLike } from "./d1.js";
2
- export type OpsSeverity = "debug" | "info" | "warn" | "error";
3
+ export type OpsEventStatus = "started" | "ok" | "warning" | "error" | "skipped";
4
+ export type OpsEventSeverity = "debug" | "info" | "warn" | "error";
3
5
  export type OpsEventInput = {
4
- eventType: string;
5
- severity?: OpsSeverity;
6
- source: string;
7
- fingerprint?: string | null;
6
+ eventId?: string;
7
+ eventName?: string;
8
+ eventType?: string;
9
+ workflow?: string;
10
+ source?: string;
11
+ status?: OpsEventStatus;
12
+ severity?: OpsEventSeverity;
13
+ runId?: string | null;
14
+ jobId?: string | null;
15
+ parentEventId?: string | null;
16
+ scopeId?: string | null;
17
+ sourceId?: string | null;
18
+ destinationId?: string | null;
19
+ actorId?: string | null;
20
+ orgId?: string | null;
21
+ customerId?: string | null;
22
+ provider?: string | null;
23
+ targetType?: string | null;
24
+ targetId?: string | null;
25
+ durationMs?: number | null;
26
+ statusCode?: number | null;
27
+ requestMethod?: string | null;
28
+ requestPath?: string | null;
29
+ requestUrl?: string | null;
30
+ message?: string | null;
31
+ error?: unknown;
32
+ errorMessage?: string | null;
8
33
  payload?: unknown;
34
+ metrics?: unknown;
35
+ metadata?: unknown;
36
+ occurredAt?: string | Date;
37
+ fingerprint?: string | null;
38
+ };
39
+ export type NormalizedOpsEvent = {
40
+ eventId: string;
41
+ eventName: string;
42
+ workflow: string;
43
+ status: OpsEventStatus;
44
+ severity: OpsEventSeverity;
45
+ runId: string | null;
46
+ jobId: string | null;
47
+ parentEventId: string | null;
48
+ scopeId: string | null;
49
+ sourceId: string | null;
50
+ destinationId: string | null;
51
+ actorId: string | null;
52
+ orgId: string | null;
53
+ customerId: string | null;
54
+ provider: string | null;
55
+ targetType: string | null;
56
+ targetId: string | null;
57
+ durationMs: number | null;
58
+ statusCode: number | null;
59
+ requestMethod: string | null;
60
+ requestPath: string | null;
61
+ requestUrl: string | null;
62
+ message: string | null;
63
+ errorName: string | null;
64
+ errorMessage: string | null;
65
+ errorStack: string | null;
66
+ payload: unknown;
67
+ metrics: unknown;
68
+ metadata: unknown;
69
+ occurredAt: string;
70
+ fingerprint: string | null;
71
+ };
72
+ export type OpsEventNormalizeOptions = {
73
+ idPrefix?: string;
74
+ now?: () => Date;
9
75
  };
76
+ export type OpsEventColumnValue = keyof NormalizedOpsEvent | "payloadJson" | "metricsJson" | "metadataJson" | "null";
77
+ export type OpsEventColumn = {
78
+ column: string;
79
+ value: OpsEventColumnValue | ((event: NormalizedOpsEvent) => unknown);
80
+ cast?: "jsonb" | "timestamptz";
81
+ };
82
+ export type OpsEventSqlInsert = {
83
+ text: string;
84
+ values: unknown[];
85
+ };
86
+ export type OpsEventSqlConfig = {
87
+ tableName: string;
88
+ columns: OpsEventColumn[];
89
+ conflictTarget?: string[];
90
+ updateColumns?: string[];
91
+ };
92
+ export declare function normalizeOpsEvent(input: OpsEventInput, options?: OpsEventNormalizeOptions): NormalizedOpsEvent;
93
+ export declare function buildOpsEventInsert(config: OpsEventSqlConfig, input: OpsEventInput | NormalizedOpsEvent, options?: OpsEventNormalizeOptions): OpsEventSqlInsert;
94
+ export declare function recordPostgresOpsEvent(sql: postgres.Sql, config: OpsEventSqlConfig, input: OpsEventInput | NormalizedOpsEvent, options?: OpsEventNormalizeOptions): Promise<NormalizedOpsEvent>;
95
+ export type D1OpsEventConfig = {
96
+ tableName?: string;
97
+ columns?: OpsEventColumn[];
98
+ };
99
+ export declare function recordD1OpsEvent(db: D1DatabaseLike, input: OpsEventInput | NormalizedOpsEvent, config?: D1OpsEventConfig): Promise<NormalizedOpsEvent>;
100
+ export declare function recordOpsEvent(db: D1DatabaseLike, input: OpsEventInput): Promise<void>;
10
101
  export type OpsEventRow = {
11
102
  id: number;
12
103
  event_type: string;
13
- severity: OpsSeverity;
104
+ severity: OpsEventSeverity;
14
105
  source: string;
15
106
  fingerprint: string | null;
16
107
  payload_json: string;
17
108
  created_at: string;
18
109
  };
19
- export declare function recordOpsEvent(db: D1DatabaseLike, input: OpsEventInput): Promise<void>;
20
110
  export declare function listRecentOpsEvents(db: D1DatabaseLike, options?: {
21
111
  limit?: number;
22
112
  eventType?: string | null;
23
113
  }): Promise<OpsEventRow[]>;
114
+ export declare const DEFAULT_D1_OPS_EVENT_COLUMNS: OpsEventColumn[];
115
+ export declare const GRAPHILIZE_GRAPH_EVENTS_COLUMNS: OpsEventColumn[];
116
+ export declare const DIRT_SIGNAL_OPS_EVENTS_COLUMNS: OpsEventColumn[];
117
+ export declare const RELIN_OPERATIONAL_EVENTS_COLUMNS: OpsEventColumn[];
118
+ export declare const ADGIRO_OPS_EVENTS_COLUMNS: OpsEventColumn[];
24
119
  export declare const D1_OPS_EVENTS_SCHEMA = "\nCREATE TABLE IF NOT EXISTS ops_events (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n event_type TEXT NOT NULL,\n severity TEXT NOT NULL DEFAULT 'info',\n source TEXT NOT NULL,\n fingerprint TEXT,\n payload_json TEXT NOT NULL DEFAULT '{}',\n created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP\n);\nCREATE INDEX IF NOT EXISTS ops_events_created_at_idx ON ops_events(created_at DESC);\nCREATE INDEX IF NOT EXISTS ops_events_type_created_idx ON ops_events(event_type, created_at DESC);\nCREATE INDEX IF NOT EXISTS ops_events_severity_created_idx ON ops_events(severity, created_at DESC);\n";
25
120
  //# sourceMappingURL=ops-events.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ops-events.d.ts","sourceRoot":"","sources":["../src/ops-events.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAG9C,MAAM,MAAM,WAAW,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;AAE9D,MAAM,MAAM,aAAa,GAAG;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,WAAW,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,WAAW,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,wBAAsB,cAAc,CAAC,EAAE,EAAE,cAAc,EAAE,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAQ5F;AAED,wBAAsB,mBAAmB,CACvC,EAAE,EAAE,cAAc,EAClB,OAAO,GAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAO,GAC1D,OAAO,CAAC,WAAW,EAAE,CAAC,CAuBxB;AAED,eAAO,MAAM,oBAAoB,olBAahC,CAAC"}
1
+ {"version":3,"file":"ops-events.d.ts","sourceRoot":"","sources":["../src/ops-events.ts"],"names":[],"mappings":"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"}
@@ -1,10 +1,79 @@
1
- import { stringifyJsonColumn } from "./d1.js";
1
+ import { createId } from "./ids.js";
2
+ export function normalizeOpsEvent(input, options = {}) {
3
+ const errorInfo = normalizeError(input.error);
4
+ const eventName = requiredName(input.eventName ?? input.eventType, "eventName");
5
+ return {
6
+ eventId: input.eventId ?? createId(options.idPrefix ?? "opsevt"),
7
+ eventName,
8
+ workflow: requiredName(input.workflow ?? input.source ?? eventName, "workflow"),
9
+ status: input.status ?? statusFromSeverity(input.severity),
10
+ severity: input.severity ?? severityFromStatus(input.status),
11
+ runId: input.runId ?? null,
12
+ jobId: input.jobId ?? null,
13
+ parentEventId: input.parentEventId ?? null,
14
+ scopeId: input.scopeId ?? null,
15
+ sourceId: input.sourceId ?? null,
16
+ destinationId: input.destinationId ?? null,
17
+ actorId: input.actorId ?? null,
18
+ orgId: input.orgId ?? null,
19
+ customerId: input.customerId ?? null,
20
+ provider: input.provider ?? null,
21
+ targetType: input.targetType ?? null,
22
+ targetId: input.targetId ?? null,
23
+ durationMs: input.durationMs ?? null,
24
+ statusCode: input.statusCode ?? null,
25
+ requestMethod: input.requestMethod ?? null,
26
+ requestPath: input.requestPath ?? null,
27
+ requestUrl: input.requestUrl ?? null,
28
+ message: input.message ?? errorInfo.message,
29
+ errorName: errorInfo.name,
30
+ errorMessage: input.errorMessage ?? errorInfo.message,
31
+ errorStack: errorInfo.stack,
32
+ payload: input.payload ?? {},
33
+ metrics: input.metrics ?? {},
34
+ metadata: input.metadata ?? {},
35
+ occurredAt: normalizeOccurredAt(input.occurredAt, options.now),
36
+ fingerprint: input.fingerprint ?? null,
37
+ };
38
+ }
39
+ export function buildOpsEventInsert(config, input, options = {}) {
40
+ const event = isNormalizedOpsEvent(input) ? input : normalizeOpsEvent(input, options);
41
+ const columns = config.columns.map((column) => quoteIdentifier(column.column)).join(", ");
42
+ const values = config.columns.map((column) => valueForColumn(event, column));
43
+ const placeholders = config.columns
44
+ .map((column, index) => `$${index + 1}${column.cast ? `::${column.cast}` : ""}`)
45
+ .join(", ");
46
+ let text = `INSERT INTO ${quoteIdentifier(config.tableName)} (${columns}) VALUES (${placeholders})`;
47
+ if (config.conflictTarget?.length) {
48
+ const target = config.conflictTarget.map(quoteIdentifier).join(", ");
49
+ const updateColumns = config.updateColumns ?? config.columns.map((column) => column.column).filter((column) => !config.conflictTarget?.includes(column));
50
+ if (updateColumns.length === 0) {
51
+ text += ` ON CONFLICT (${target}) DO NOTHING`;
52
+ }
53
+ else {
54
+ text += ` ON CONFLICT (${target}) DO UPDATE SET ${updateColumns
55
+ .map((column) => `${quoteIdentifier(column)} = excluded.${quoteIdentifier(column)}`)
56
+ .join(", ")}`;
57
+ }
58
+ }
59
+ return { text, values };
60
+ }
61
+ export async function recordPostgresOpsEvent(sql, config, input, options = {}) {
62
+ const event = isNormalizedOpsEvent(input) ? input : normalizeOpsEvent(input, options);
63
+ const statement = buildOpsEventInsert(config, event, options);
64
+ await sql.unsafe(statement.text, statement.values);
65
+ return event;
66
+ }
67
+ export async function recordD1OpsEvent(db, input, config = {}) {
68
+ const event = isNormalizedOpsEvent(input) ? input : normalizeOpsEvent(input);
69
+ const columns = config.columns ?? DEFAULT_D1_OPS_EVENT_COLUMNS;
70
+ const tableName = config.tableName ?? "ops_events";
71
+ const sql = `INSERT INTO ${quoteIdentifier(tableName)} (${columns.map((column) => quoteIdentifier(column.column)).join(", ")}) VALUES (${columns.map(() => "?").join(", ")})`;
72
+ await db.prepare(sql).bind(...columns.map((column) => valueForColumn(event, column))).run();
73
+ return event;
74
+ }
2
75
  export async function recordOpsEvent(db, input) {
3
- await db
4
- .prepare(`INSERT INTO ops_events (event_type, severity, source, fingerprint, payload_json)
5
- VALUES (?, ?, ?, ?, ?)`)
6
- .bind(input.eventType, input.severity ?? "info", input.source, input.fingerprint ?? null, stringifyJsonColumn(input.payload ?? {}))
7
- .run();
76
+ await recordD1OpsEvent(db, input);
8
77
  }
9
78
  export async function listRecentOpsEvents(db, options = {}) {
10
79
  const limit = Math.max(1, Math.min(200, Math.floor(options.limit ?? 25)));
@@ -26,6 +95,70 @@ export async function listRecentOpsEvents(db, options = {}) {
26
95
  const rows = await statement.all();
27
96
  return rows.results ?? [];
28
97
  }
98
+ export const DEFAULT_D1_OPS_EVENT_COLUMNS = [
99
+ { column: "event_type", value: "eventName" },
100
+ { column: "severity", value: "severity" },
101
+ { column: "source", value: "workflow" },
102
+ { column: "fingerprint", value: "fingerprint" },
103
+ { column: "payload_json", value: "payloadJson" },
104
+ ];
105
+ export const GRAPHILIZE_GRAPH_EVENTS_COLUMNS = [
106
+ { column: "event_id", value: "eventId" },
107
+ { column: "graph_id", value: "scopeId" },
108
+ { column: "event_name", value: "eventName" },
109
+ { column: "workflow", value: "workflow" },
110
+ { column: "target_type", value: "targetType" },
111
+ { column: "target_id", value: "targetId" },
112
+ { column: "status", value: "status" },
113
+ { column: "message", value: "message" },
114
+ { column: "payload_json", value: "payloadJson", cast: "jsonb" },
115
+ { column: "metrics_json", value: "metricsJson", cast: "jsonb" },
116
+ { column: "metadata_json", value: "metadataJson", cast: "jsonb" },
117
+ { column: "occurred_at", value: "occurredAt", cast: "timestamptz" },
118
+ ];
119
+ export const DIRT_SIGNAL_OPS_EVENTS_COLUMNS = [
120
+ { column: "event_id", value: "eventId" },
121
+ { column: "run_id", value: "runId" },
122
+ { column: "event_name", value: "eventName" },
123
+ { column: "workflow", value: "workflow" },
124
+ { column: "target_type", value: "targetType" },
125
+ { column: "target_id", value: "targetId" },
126
+ { column: "status", value: "status" },
127
+ { column: "message", value: "message" },
128
+ { column: "error_message", value: "errorMessage" },
129
+ { column: "metrics_json", value: "metricsJson", cast: "jsonb" },
130
+ { column: "metadata_json", value: "metadataJson", cast: "jsonb" },
131
+ { column: "occurred_at", value: "occurredAt", cast: "timestamptz" },
132
+ ];
133
+ export const RELIN_OPERATIONAL_EVENTS_COLUMNS = [
134
+ { column: "operational_event_id", value: "eventId" },
135
+ { column: "name", value: "eventName" },
136
+ { column: "status", value: "status" },
137
+ { column: "customer_id", value: "customerId" },
138
+ { column: "source_id", value: "sourceId" },
139
+ { column: "event_id", value: "targetId" },
140
+ { column: "destination_id", value: "destinationId" },
141
+ { column: "duration_ms", value: "durationMs" },
142
+ { column: "metadata_json", value: "metadataJson" },
143
+ ];
144
+ export const ADGIRO_OPS_EVENTS_COLUMNS = [
145
+ { column: "ops_event_id", value: "eventId" },
146
+ { column: "level", value: "severity" },
147
+ { column: "event_type", value: "eventName" },
148
+ { column: "request_method", value: "requestMethod" },
149
+ { column: "request_path", value: "requestPath" },
150
+ { column: "request_url", value: "requestUrl" },
151
+ { column: "status_code", value: "statusCode" },
152
+ { column: "user_id", value: "actorId" },
153
+ { column: "org_id", value: "orgId" },
154
+ { column: "provider", value: "provider" },
155
+ { column: "message", value: (event) => event.message ?? event.eventName },
156
+ { column: "error_name", value: "errorName" },
157
+ { column: "error_stack", value: "errorStack" },
158
+ { column: "metadata_json", value: "metadataJson" },
159
+ { column: "job_id", value: "jobId" },
160
+ { column: "parent_ops_event_id", value: "parentEventId" },
161
+ ];
29
162
  export const D1_OPS_EVENTS_SCHEMA = `
30
163
  CREATE TABLE IF NOT EXISTS ops_events (
31
164
  id INTEGER PRIMARY KEY AUTOINCREMENT,
@@ -40,4 +173,74 @@ CREATE INDEX IF NOT EXISTS ops_events_created_at_idx ON ops_events(created_at DE
40
173
  CREATE INDEX IF NOT EXISTS ops_events_type_created_idx ON ops_events(event_type, created_at DESC);
41
174
  CREATE INDEX IF NOT EXISTS ops_events_severity_created_idx ON ops_events(severity, created_at DESC);
42
175
  `;
176
+ function valueForColumn(event, column) {
177
+ if (typeof column.value === "function")
178
+ return column.value(event);
179
+ if (column.value === "payloadJson")
180
+ return JSON.stringify(event.payload ?? {});
181
+ if (column.value === "metricsJson")
182
+ return JSON.stringify(event.metrics ?? {});
183
+ if (column.value === "metadataJson")
184
+ return JSON.stringify(event.metadata ?? {});
185
+ if (column.value === "null")
186
+ return null;
187
+ return event[column.value];
188
+ }
189
+ function normalizeError(error) {
190
+ if (!error)
191
+ return { name: null, message: null, stack: null };
192
+ if (error instanceof Error) {
193
+ return {
194
+ name: error.name,
195
+ message: error.message,
196
+ stack: error.stack ?? error.message,
197
+ };
198
+ }
199
+ return {
200
+ name: typeof error,
201
+ message: String(error),
202
+ stack: String(error),
203
+ };
204
+ }
205
+ function statusFromSeverity(severity) {
206
+ if (severity === "error")
207
+ return "error";
208
+ if (severity === "warn")
209
+ return "warning";
210
+ return "ok";
211
+ }
212
+ function severityFromStatus(status) {
213
+ if (status === "error")
214
+ return "error";
215
+ if (status === "warning" || status === "skipped")
216
+ return "warn";
217
+ return "info";
218
+ }
219
+ function normalizeOccurredAt(value, now) {
220
+ if (value instanceof Date)
221
+ return value.toISOString();
222
+ if (typeof value === "string" && value.trim())
223
+ return value;
224
+ return (now?.() ?? new Date()).toISOString();
225
+ }
226
+ function requiredName(value, field) {
227
+ const trimmed = value?.trim();
228
+ if (!trimmed)
229
+ throw new Error(`Ops event ${field} is required.`);
230
+ return trimmed;
231
+ }
232
+ function quoteIdentifier(value) {
233
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(value)) {
234
+ throw new Error(`Unsafe SQL identifier: ${value}`);
235
+ }
236
+ return `"${value}"`;
237
+ }
238
+ function isNormalizedOpsEvent(value) {
239
+ return ("eventId" in value &&
240
+ "eventName" in value &&
241
+ "workflow" in value &&
242
+ "severity" in value &&
243
+ "message" in value &&
244
+ "occurredAt" in value);
245
+ }
43
246
  //# sourceMappingURL=ops-events.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ops-events.js","sourceRoot":"","sources":["../src/ops-events.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAsB9C,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,EAAkB,EAAE,KAAoB;IAC3E,MAAM,EAAE;SACL,OAAO,CACN;8BACwB,CACzB;SACA,IAAI,CAAC,KAAK,CAAC,SAAS,EAAE,KAAK,CAAC,QAAQ,IAAI,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,WAAW,IAAI,IAAI,EAAE,mBAAmB,CAAC,KAAK,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC;SAClI,GAAG,EAAE,CAAC;AACX,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,EAAkB,EAClB,UAAyD,EAAE;IAE3D,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IAC1E,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,EAAE,IAAI,EAAE,CAAC;IAC5C,MAAM,SAAS,GAAG,SAAS;QACzB,CAAC,CAAC,EAAE;aACC,OAAO,CACN;;;;mBAIS,CACV;aACA,IAAI,CAAC,SAAS,EAAE,KAAK,CAAC;QAC3B,CAAC,CAAC,EAAE;aACC,OAAO,CACN;;;mBAGS,CACV;aACA,IAAI,CAAC,KAAK,CAAC,CAAC;IACnB,MAAM,IAAI,GAAG,MAAM,SAAS,CAAC,GAAG,EAAe,CAAC;IAChD,OAAO,IAAI,CAAC,OAAO,IAAI,EAAE,CAAC;AAC5B,CAAC;AAED,MAAM,CAAC,MAAM,oBAAoB,GAAG;;;;;;;;;;;;;CAanC,CAAC"}
1
+ {"version":3,"file":"ops-events.js","sourceRoot":"","sources":["../src/ops-events.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@q32/core",
3
- "version": "0.1.7",
3
+ "version": "0.1.9",
4
4
  "description": "Shared TypeScript primitives for Q32 Cloudflare Worker projects.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -124,20 +124,28 @@
124
124
  "README.md",
125
125
  "LICENSE"
126
126
  ],
127
+ "peerDependencies": {
128
+ "kysely": ">=0.28 <0.30",
129
+ "kysely-postgres-js": "^3.0.0",
130
+ "postgres": "^3.4.0"
131
+ },
127
132
  "devDependencies": {
128
133
  "@cloudflare/workers-types": "^4.20260606.0",
129
134
  "@vitest/coverage-v8": "^4.1.8",
135
+ "kysely": "^0.29.2",
136
+ "kysely-postgres-js": "^3.0.0",
137
+ "postgres": "^3.4.9",
130
138
  "typescript": "^5.9.3",
131
139
  "vitest": "^4.0.15"
132
140
  },
141
+ "publishConfig": {
142
+ "access": "public"
143
+ },
133
144
  "dependencies": {
134
145
  "kysely": "^0.29.2",
135
146
  "kysely-postgres-js": "^3.0.0",
136
147
  "postgres": "^3.4.9"
137
148
  },
138
- "publishConfig": {
139
- "access": "public"
140
- },
141
149
  "scripts": {
142
150
  "build": "tsc -p tsconfig.json",
143
151
  "test": "vitest run",
package/src/ops-events.ts CHANGED
@@ -1,36 +1,222 @@
1
+ import type postgres from "postgres";
1
2
  import type { D1DatabaseLike } from "./d1.js";
2
- import { stringifyJsonColumn } from "./d1.js";
3
+ import { createId } from "./ids.js";
3
4
 
4
- export type OpsSeverity = "debug" | "info" | "warn" | "error";
5
+ export type OpsEventStatus = "started" | "ok" | "warning" | "error" | "skipped";
6
+ export type OpsEventSeverity = "debug" | "info" | "warn" | "error";
5
7
 
6
8
  export type OpsEventInput = {
7
- eventType: string;
8
- severity?: OpsSeverity;
9
- source: string;
10
- fingerprint?: string | null;
9
+ eventId?: string;
10
+ eventName?: string;
11
+ eventType?: string;
12
+ workflow?: string;
13
+ source?: string;
14
+ status?: OpsEventStatus;
15
+ severity?: OpsEventSeverity;
16
+ runId?: string | null;
17
+ jobId?: string | null;
18
+ parentEventId?: string | null;
19
+ scopeId?: string | null;
20
+ sourceId?: string | null;
21
+ destinationId?: string | null;
22
+ actorId?: string | null;
23
+ orgId?: string | null;
24
+ customerId?: string | null;
25
+ provider?: string | null;
26
+ targetType?: string | null;
27
+ targetId?: string | null;
28
+ durationMs?: number | null;
29
+ statusCode?: number | null;
30
+ requestMethod?: string | null;
31
+ requestPath?: string | null;
32
+ requestUrl?: string | null;
33
+ message?: string | null;
34
+ error?: unknown;
35
+ errorMessage?: string | null;
11
36
  payload?: unknown;
37
+ metrics?: unknown;
38
+ metadata?: unknown;
39
+ occurredAt?: string | Date;
40
+ fingerprint?: string | null;
41
+ };
42
+
43
+ export type NormalizedOpsEvent = {
44
+ eventId: string;
45
+ eventName: string;
46
+ workflow: string;
47
+ status: OpsEventStatus;
48
+ severity: OpsEventSeverity;
49
+ runId: string | null;
50
+ jobId: string | null;
51
+ parentEventId: string | null;
52
+ scopeId: string | null;
53
+ sourceId: string | null;
54
+ destinationId: string | null;
55
+ actorId: string | null;
56
+ orgId: string | null;
57
+ customerId: string | null;
58
+ provider: string | null;
59
+ targetType: string | null;
60
+ targetId: string | null;
61
+ durationMs: number | null;
62
+ statusCode: number | null;
63
+ requestMethod: string | null;
64
+ requestPath: string | null;
65
+ requestUrl: string | null;
66
+ message: string | null;
67
+ errorName: string | null;
68
+ errorMessage: string | null;
69
+ errorStack: string | null;
70
+ payload: unknown;
71
+ metrics: unknown;
72
+ metadata: unknown;
73
+ occurredAt: string;
74
+ fingerprint: string | null;
75
+ };
76
+
77
+ export type OpsEventNormalizeOptions = {
78
+ idPrefix?: string;
79
+ now?: () => Date;
80
+ };
81
+
82
+ export type OpsEventColumnValue =
83
+ | keyof NormalizedOpsEvent
84
+ | "payloadJson"
85
+ | "metricsJson"
86
+ | "metadataJson"
87
+ | "null";
88
+
89
+ export type OpsEventColumn = {
90
+ column: string;
91
+ value: OpsEventColumnValue | ((event: NormalizedOpsEvent) => unknown);
92
+ cast?: "jsonb" | "timestamptz";
93
+ };
94
+
95
+ export type OpsEventSqlInsert = {
96
+ text: string;
97
+ values: unknown[];
98
+ };
99
+
100
+ export type OpsEventSqlConfig = {
101
+ tableName: string;
102
+ columns: OpsEventColumn[];
103
+ conflictTarget?: string[];
104
+ updateColumns?: string[];
105
+ };
106
+
107
+ export function normalizeOpsEvent(
108
+ input: OpsEventInput,
109
+ options: OpsEventNormalizeOptions = {},
110
+ ): NormalizedOpsEvent {
111
+ const errorInfo = normalizeError(input.error);
112
+ const eventName = requiredName(input.eventName ?? input.eventType, "eventName");
113
+ return {
114
+ eventId: input.eventId ?? createId(options.idPrefix ?? "opsevt"),
115
+ eventName,
116
+ workflow: requiredName(input.workflow ?? input.source ?? eventName, "workflow"),
117
+ status: input.status ?? statusFromSeverity(input.severity),
118
+ severity: input.severity ?? severityFromStatus(input.status),
119
+ runId: input.runId ?? null,
120
+ jobId: input.jobId ?? null,
121
+ parentEventId: input.parentEventId ?? null,
122
+ scopeId: input.scopeId ?? null,
123
+ sourceId: input.sourceId ?? null,
124
+ destinationId: input.destinationId ?? null,
125
+ actorId: input.actorId ?? null,
126
+ orgId: input.orgId ?? null,
127
+ customerId: input.customerId ?? null,
128
+ provider: input.provider ?? null,
129
+ targetType: input.targetType ?? null,
130
+ targetId: input.targetId ?? null,
131
+ durationMs: input.durationMs ?? null,
132
+ statusCode: input.statusCode ?? null,
133
+ requestMethod: input.requestMethod ?? null,
134
+ requestPath: input.requestPath ?? null,
135
+ requestUrl: input.requestUrl ?? null,
136
+ message: input.message ?? errorInfo.message,
137
+ errorName: errorInfo.name,
138
+ errorMessage: input.errorMessage ?? errorInfo.message,
139
+ errorStack: errorInfo.stack,
140
+ payload: input.payload ?? {},
141
+ metrics: input.metrics ?? {},
142
+ metadata: input.metadata ?? {},
143
+ occurredAt: normalizeOccurredAt(input.occurredAt, options.now),
144
+ fingerprint: input.fingerprint ?? null,
145
+ };
146
+ }
147
+
148
+ export function buildOpsEventInsert(
149
+ config: OpsEventSqlConfig,
150
+ input: OpsEventInput | NormalizedOpsEvent,
151
+ options: OpsEventNormalizeOptions = {},
152
+ ): OpsEventSqlInsert {
153
+ const event = isNormalizedOpsEvent(input) ? input : normalizeOpsEvent(input, options);
154
+ const columns = config.columns.map((column) => quoteIdentifier(column.column)).join(", ");
155
+ const values = config.columns.map((column) => valueForColumn(event, column));
156
+ const placeholders = config.columns
157
+ .map((column, index) => `$${index + 1}${column.cast ? `::${column.cast}` : ""}`)
158
+ .join(", ");
159
+ let text = `INSERT INTO ${quoteIdentifier(config.tableName)} (${columns}) VALUES (${placeholders})`;
160
+
161
+ if (config.conflictTarget?.length) {
162
+ const target = config.conflictTarget.map(quoteIdentifier).join(", ");
163
+ const updateColumns = config.updateColumns ?? config.columns.map((column) => column.column).filter((column) => !config.conflictTarget?.includes(column));
164
+ if (updateColumns.length === 0) {
165
+ text += ` ON CONFLICT (${target}) DO NOTHING`;
166
+ } else {
167
+ text += ` ON CONFLICT (${target}) DO UPDATE SET ${updateColumns
168
+ .map((column) => `${quoteIdentifier(column)} = excluded.${quoteIdentifier(column)}`)
169
+ .join(", ")}`;
170
+ }
171
+ }
172
+
173
+ return { text, values };
174
+ }
175
+
176
+ export async function recordPostgresOpsEvent(
177
+ sql: postgres.Sql,
178
+ config: OpsEventSqlConfig,
179
+ input: OpsEventInput | NormalizedOpsEvent,
180
+ options: OpsEventNormalizeOptions = {},
181
+ ): Promise<NormalizedOpsEvent> {
182
+ const event = isNormalizedOpsEvent(input) ? input : normalizeOpsEvent(input, options);
183
+ const statement = buildOpsEventInsert(config, event, options);
184
+ await sql.unsafe(statement.text, statement.values as never[]);
185
+ return event;
186
+ }
187
+
188
+ export type D1OpsEventConfig = {
189
+ tableName?: string;
190
+ columns?: OpsEventColumn[];
12
191
  };
13
192
 
193
+ export async function recordD1OpsEvent(
194
+ db: D1DatabaseLike,
195
+ input: OpsEventInput | NormalizedOpsEvent,
196
+ config: D1OpsEventConfig = {},
197
+ ): Promise<NormalizedOpsEvent> {
198
+ const event = isNormalizedOpsEvent(input) ? input : normalizeOpsEvent(input);
199
+ const columns = config.columns ?? DEFAULT_D1_OPS_EVENT_COLUMNS;
200
+ const tableName = config.tableName ?? "ops_events";
201
+ const sql = `INSERT INTO ${quoteIdentifier(tableName)} (${columns.map((column) => quoteIdentifier(column.column)).join(", ")}) VALUES (${columns.map(() => "?").join(", ")})`;
202
+ await db.prepare(sql).bind(...(columns.map((column) => valueForColumn(event, column)) as never[])).run();
203
+ return event;
204
+ }
205
+
206
+ export async function recordOpsEvent(db: D1DatabaseLike, input: OpsEventInput): Promise<void> {
207
+ await recordD1OpsEvent(db, input);
208
+ }
209
+
14
210
  export type OpsEventRow = {
15
211
  id: number;
16
212
  event_type: string;
17
- severity: OpsSeverity;
213
+ severity: OpsEventSeverity;
18
214
  source: string;
19
215
  fingerprint: string | null;
20
216
  payload_json: string;
21
217
  created_at: string;
22
218
  };
23
219
 
24
- export async function recordOpsEvent(db: D1DatabaseLike, input: OpsEventInput): Promise<void> {
25
- await db
26
- .prepare(
27
- `INSERT INTO ops_events (event_type, severity, source, fingerprint, payload_json)
28
- VALUES (?, ?, ?, ?, ?)`,
29
- )
30
- .bind(input.eventType, input.severity ?? "info", input.source, input.fingerprint ?? null, stringifyJsonColumn(input.payload ?? {}))
31
- .run();
32
- }
33
-
34
220
  export async function listRecentOpsEvents(
35
221
  db: D1DatabaseLike,
36
222
  options: { limit?: number; eventType?: string | null } = {},
@@ -59,6 +245,75 @@ export async function listRecentOpsEvents(
59
245
  return rows.results ?? [];
60
246
  }
61
247
 
248
+ export const DEFAULT_D1_OPS_EVENT_COLUMNS: OpsEventColumn[] = [
249
+ { column: "event_type", value: "eventName" },
250
+ { column: "severity", value: "severity" },
251
+ { column: "source", value: "workflow" },
252
+ { column: "fingerprint", value: "fingerprint" },
253
+ { column: "payload_json", value: "payloadJson" },
254
+ ];
255
+
256
+ export const GRAPHILIZE_GRAPH_EVENTS_COLUMNS: OpsEventColumn[] = [
257
+ { column: "event_id", value: "eventId" },
258
+ { column: "graph_id", value: "scopeId" },
259
+ { column: "event_name", value: "eventName" },
260
+ { column: "workflow", value: "workflow" },
261
+ { column: "target_type", value: "targetType" },
262
+ { column: "target_id", value: "targetId" },
263
+ { column: "status", value: "status" },
264
+ { column: "message", value: "message" },
265
+ { column: "payload_json", value: "payloadJson", cast: "jsonb" },
266
+ { column: "metrics_json", value: "metricsJson", cast: "jsonb" },
267
+ { column: "metadata_json", value: "metadataJson", cast: "jsonb" },
268
+ { column: "occurred_at", value: "occurredAt", cast: "timestamptz" },
269
+ ];
270
+
271
+ export const DIRT_SIGNAL_OPS_EVENTS_COLUMNS: OpsEventColumn[] = [
272
+ { column: "event_id", value: "eventId" },
273
+ { column: "run_id", value: "runId" },
274
+ { column: "event_name", value: "eventName" },
275
+ { column: "workflow", value: "workflow" },
276
+ { column: "target_type", value: "targetType" },
277
+ { column: "target_id", value: "targetId" },
278
+ { column: "status", value: "status" },
279
+ { column: "message", value: "message" },
280
+ { column: "error_message", value: "errorMessage" },
281
+ { column: "metrics_json", value: "metricsJson", cast: "jsonb" },
282
+ { column: "metadata_json", value: "metadataJson", cast: "jsonb" },
283
+ { column: "occurred_at", value: "occurredAt", cast: "timestamptz" },
284
+ ];
285
+
286
+ export const RELIN_OPERATIONAL_EVENTS_COLUMNS: OpsEventColumn[] = [
287
+ { column: "operational_event_id", value: "eventId" },
288
+ { column: "name", value: "eventName" },
289
+ { column: "status", value: "status" },
290
+ { column: "customer_id", value: "customerId" },
291
+ { column: "source_id", value: "sourceId" },
292
+ { column: "event_id", value: "targetId" },
293
+ { column: "destination_id", value: "destinationId" },
294
+ { column: "duration_ms", value: "durationMs" },
295
+ { column: "metadata_json", value: "metadataJson" },
296
+ ];
297
+
298
+ export const ADGIRO_OPS_EVENTS_COLUMNS: OpsEventColumn[] = [
299
+ { column: "ops_event_id", value: "eventId" },
300
+ { column: "level", value: "severity" },
301
+ { column: "event_type", value: "eventName" },
302
+ { column: "request_method", value: "requestMethod" },
303
+ { column: "request_path", value: "requestPath" },
304
+ { column: "request_url", value: "requestUrl" },
305
+ { column: "status_code", value: "statusCode" },
306
+ { column: "user_id", value: "actorId" },
307
+ { column: "org_id", value: "orgId" },
308
+ { column: "provider", value: "provider" },
309
+ { column: "message", value: (event) => event.message ?? event.eventName },
310
+ { column: "error_name", value: "errorName" },
311
+ { column: "error_stack", value: "errorStack" },
312
+ { column: "metadata_json", value: "metadataJson" },
313
+ { column: "job_id", value: "jobId" },
314
+ { column: "parent_ops_event_id", value: "parentEventId" },
315
+ ];
316
+
62
317
  export const D1_OPS_EVENTS_SCHEMA = `
63
318
  CREATE TABLE IF NOT EXISTS ops_events (
64
319
  id INTEGER PRIMARY KEY AUTOINCREMENT,
@@ -73,3 +328,70 @@ CREATE INDEX IF NOT EXISTS ops_events_created_at_idx ON ops_events(created_at DE
73
328
  CREATE INDEX IF NOT EXISTS ops_events_type_created_idx ON ops_events(event_type, created_at DESC);
74
329
  CREATE INDEX IF NOT EXISTS ops_events_severity_created_idx ON ops_events(severity, created_at DESC);
75
330
  `;
331
+
332
+ function valueForColumn(event: NormalizedOpsEvent, column: OpsEventColumn): unknown {
333
+ if (typeof column.value === "function") return column.value(event);
334
+ if (column.value === "payloadJson") return JSON.stringify(event.payload ?? {});
335
+ if (column.value === "metricsJson") return JSON.stringify(event.metrics ?? {});
336
+ if (column.value === "metadataJson") return JSON.stringify(event.metadata ?? {});
337
+ if (column.value === "null") return null;
338
+ return event[column.value];
339
+ }
340
+
341
+ function normalizeError(error: unknown): { name: string | null; message: string | null; stack: string | null } {
342
+ if (!error) return { name: null, message: null, stack: null };
343
+ if (error instanceof Error) {
344
+ return {
345
+ name: error.name,
346
+ message: error.message,
347
+ stack: error.stack ?? error.message,
348
+ };
349
+ }
350
+ return {
351
+ name: typeof error,
352
+ message: String(error),
353
+ stack: String(error),
354
+ };
355
+ }
356
+
357
+ function statusFromSeverity(severity: OpsEventSeverity | undefined): OpsEventStatus {
358
+ if (severity === "error") return "error";
359
+ if (severity === "warn") return "warning";
360
+ return "ok";
361
+ }
362
+
363
+ function severityFromStatus(status: OpsEventStatus | undefined): OpsEventSeverity {
364
+ if (status === "error") return "error";
365
+ if (status === "warning" || status === "skipped") return "warn";
366
+ return "info";
367
+ }
368
+
369
+ function normalizeOccurredAt(value: string | Date | undefined, now: (() => Date) | undefined): string {
370
+ if (value instanceof Date) return value.toISOString();
371
+ if (typeof value === "string" && value.trim()) return value;
372
+ return (now?.() ?? new Date()).toISOString();
373
+ }
374
+
375
+ function requiredName(value: string | undefined, field: string): string {
376
+ const trimmed = value?.trim();
377
+ if (!trimmed) throw new Error(`Ops event ${field} is required.`);
378
+ return trimmed;
379
+ }
380
+
381
+ function quoteIdentifier(value: string): string {
382
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(value)) {
383
+ throw new Error(`Unsafe SQL identifier: ${value}`);
384
+ }
385
+ return `"${value}"`;
386
+ }
387
+
388
+ function isNormalizedOpsEvent(value: OpsEventInput | NormalizedOpsEvent): value is NormalizedOpsEvent {
389
+ return (
390
+ "eventId" in value &&
391
+ "eventName" in value &&
392
+ "workflow" in value &&
393
+ "severity" in value &&
394
+ "message" in value &&
395
+ "occurredAt" in value
396
+ );
397
+ }