@snapback/cli 1.1.14 → 1.1.15
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/README.md +70 -9
- package/dist/SkippedTestDetector-AXTMWWHC.js +5 -0
- package/dist/SkippedTestDetector-QLSQV7K7.js +5 -0
- package/dist/analysis-6WTBZJH3.js +6 -0
- package/dist/analysis-C472LUGW.js +2475 -0
- package/dist/auth-HFJRXXG2.js +1446 -0
- package/dist/auto-provision-organization-SF6XM7X4.js +161 -0
- package/dist/chunk-23G5VYA3.js +4259 -0
- package/dist/{chunk-QAKFE3NE.js → chunk-4YTE4JEW.js} +3 -4
- package/dist/chunk-5EOPYJ4Y.js +12 -0
- package/dist/{chunk-OJNDAPC2.js → chunk-5SQA44V7.js} +1086 -19
- package/dist/{chunk-BW7RALUZ.js → chunk-7ADPL4Q3.js} +11 -4
- package/dist/chunk-CBGOC6RV.js +293 -0
- package/dist/chunk-DNEADD2G.js +3499 -0
- package/dist/{chunk-Q5XZ3DCB.js → chunk-DPWFZNMY.js} +122 -15
- package/dist/chunk-GQ73B37K.js +314 -0
- package/dist/chunk-HR34NJP7.js +6133 -0
- package/dist/chunk-ICKSHS3A.js +2264 -0
- package/dist/{chunk-2TOJVUVJ.js → chunk-OI2HNNT6.js} +285 -33
- package/dist/chunk-PL4HF4M2.js +593 -0
- package/dist/chunk-WS36HDEU.js +3735 -0
- package/dist/chunk-XYU5FFE3.js +111 -0
- package/dist/chunk-ZBQDE6WJ.js +108 -0
- package/dist/client-WIO6W447.js +8 -0
- package/dist/dist-E7E2T3DQ.js +9 -0
- package/dist/dist-TEWNOZYS.js +5 -0
- package/dist/dist-YZBJAYEJ.js +12 -0
- package/dist/index.js +63852 -39806
- package/dist/local-service-adapter-3JHN6G4O.js +6 -0
- package/dist/pioneer-oauth-hook-V2JKEXM7.js +12 -0
- package/dist/{secure-credentials-A4QHHOE2.js → secure-credentials-UEPG7GWW.js} +3 -4
- package/dist/snapback-dir-MG7DTRMF.js +6 -0
- package/package.json +8 -41
- package/scripts/postinstall.mjs +2 -3
- package/dist/SkippedTestDetector-B3JZUE5G.js +0 -5
- package/dist/SkippedTestDetector-B3JZUE5G.js.map +0 -1
- package/dist/analysis-C6XVLBAL.js +0 -6
- package/dist/analysis-C6XVLBAL.js.map +0 -1
- package/dist/chunk-2TOJVUVJ.js.map +0 -1
- package/dist/chunk-5EQLSU5B.js +0 -385
- package/dist/chunk-5EQLSU5B.js.map +0 -1
- package/dist/chunk-6MR2TINI.js +0 -27
- package/dist/chunk-6MR2TINI.js.map +0 -1
- package/dist/chunk-A3TUM7U4.js +0 -13002
- package/dist/chunk-A3TUM7U4.js.map +0 -1
- package/dist/chunk-BW7RALUZ.js.map +0 -1
- package/dist/chunk-LEXNOXPV.js +0 -21621
- package/dist/chunk-LEXNOXPV.js.map +0 -1
- package/dist/chunk-OJNDAPC2.js.map +0 -1
- package/dist/chunk-Q5XZ3DCB.js.map +0 -1
- package/dist/chunk-QAKFE3NE.js.map +0 -1
- package/dist/chunk-QLCHTUT5.js +0 -1067
- package/dist/chunk-QLCHTUT5.js.map +0 -1
- package/dist/dist-D2SHOZMS.js +0 -8
- package/dist/dist-D2SHOZMS.js.map +0 -1
- package/dist/dist-L76VXYJ5.js +0 -5
- package/dist/dist-L76VXYJ5.js.map +0 -1
- package/dist/dist-RPM72FHJ.js +0 -5
- package/dist/dist-RPM72FHJ.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/learning-pruner-YSZSOOOC.js +0 -7
- package/dist/learning-pruner-YSZSOOOC.js.map +0 -1
- package/dist/secure-credentials-A4QHHOE2.js.map +0 -1
- package/dist/snapback-dir-6QUSO6Y3.js +0 -6
- package/dist/snapback-dir-6QUSO6Y3.js.map +0 -1
- package/dist/storage-H366UNAR.js +0 -6
- package/dist/storage-H366UNAR.js.map +0 -1
|
@@ -0,0 +1,2264 @@
|
|
|
1
|
+
#!/usr/bin/env node --no-warnings=ExperimentalWarning
|
|
2
|
+
import { logger } from './chunk-PL4HF4M2.js';
|
|
3
|
+
import { FEATURE_FLAGS, validateTelemetryEvent, FeatureManager } from './chunk-WS36HDEU.js';
|
|
4
|
+
import { __commonJS, __name, __export } from './chunk-7ADPL4Q3.js';
|
|
5
|
+
import { neonConfig, neon } from '@neondatabase/serverless';
|
|
6
|
+
import { readFileSync } from 'fs';
|
|
7
|
+
import { dirname, join } from 'path';
|
|
8
|
+
import { fileURLToPath } from 'url';
|
|
9
|
+
import { PostHog } from 'posthog-node';
|
|
10
|
+
import client from 'prom-client';
|
|
11
|
+
import { trace, context, SpanStatusCode, propagation, ROOT_CONTEXT } from '@opentelemetry/api';
|
|
12
|
+
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
|
|
13
|
+
import { PgInstrumentation } from '@opentelemetry/instrumentation-pg';
|
|
14
|
+
import { PinoInstrumentation } from '@opentelemetry/instrumentation-pino';
|
|
15
|
+
import { resourceFromAttributes } from '@opentelemetry/resources';
|
|
16
|
+
import { BatchSpanProcessor, ConsoleSpanExporter, TraceIdRatioBasedSampler } from '@opentelemetry/sdk-trace-base';
|
|
17
|
+
import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node';
|
|
18
|
+
import { ATTR_SERVICE_VERSION, ATTR_SERVICE_NAME } from '@opentelemetry/semantic-conventions';
|
|
19
|
+
|
|
20
|
+
process.env.SNAPBACK_CLI='true';
|
|
21
|
+
|
|
22
|
+
// ../../packages/infrastructure/package.json
|
|
23
|
+
var require_package = __commonJS({
|
|
24
|
+
"../../packages/infrastructure/package.json"(exports$1, module) {
|
|
25
|
+
module.exports = {
|
|
26
|
+
name: "@snapback/infrastructure",
|
|
27
|
+
version: "0.2.0",
|
|
28
|
+
license: "Apache-2.0",
|
|
29
|
+
author: "SnapBack Team",
|
|
30
|
+
repository: {
|
|
31
|
+
type: "git",
|
|
32
|
+
url: "https://github.com/snapback-dev.git",
|
|
33
|
+
directory: "packages/infrastructure"
|
|
34
|
+
},
|
|
35
|
+
files: [
|
|
36
|
+
"dist"
|
|
37
|
+
],
|
|
38
|
+
dependencies: {
|
|
39
|
+
"@neondatabase/serverless": "catalog:",
|
|
40
|
+
"@opentelemetry/api": "catalog:",
|
|
41
|
+
"@opentelemetry/exporter-trace-otlp-http": "catalog:",
|
|
42
|
+
"@opentelemetry/instrumentation-pg": "catalog:",
|
|
43
|
+
"@opentelemetry/instrumentation-pino": "catalog:",
|
|
44
|
+
"@opentelemetry/resources": "catalog:",
|
|
45
|
+
"@opentelemetry/sdk-trace-base": "catalog:",
|
|
46
|
+
"@opentelemetry/sdk-trace-node": "catalog:",
|
|
47
|
+
"@opentelemetry/semantic-conventions": "catalog:",
|
|
48
|
+
"@snapback/contracts": "workspace:*",
|
|
49
|
+
bullmq: "catalog:",
|
|
50
|
+
nanoid: "catalog:",
|
|
51
|
+
pino: "catalog:",
|
|
52
|
+
"posthog-node": "catalog:",
|
|
53
|
+
opossum: "catalog:",
|
|
54
|
+
"p-queue": "catalog:",
|
|
55
|
+
"p-retry": "catalog:",
|
|
56
|
+
"lru-cache": "catalog:",
|
|
57
|
+
chokidar: "catalog:",
|
|
58
|
+
"prom-client": "catalog:"
|
|
59
|
+
},
|
|
60
|
+
optionalDependencies: {
|
|
61
|
+
"@sentry/node": "catalog:",
|
|
62
|
+
"@sentry/profiling-node": "catalog:"
|
|
63
|
+
},
|
|
64
|
+
devDependencies: {
|
|
65
|
+
"@snapback/tsconfig": "workspace:*",
|
|
66
|
+
"@types/node": "catalog:",
|
|
67
|
+
"posthog-js": "catalog:",
|
|
68
|
+
tsup: "catalog:",
|
|
69
|
+
typescript: "catalog:",
|
|
70
|
+
vitest: "catalog:"
|
|
71
|
+
},
|
|
72
|
+
exports: {
|
|
73
|
+
".": {
|
|
74
|
+
types: "./dist/index.d.ts",
|
|
75
|
+
default: "./dist/index.js"
|
|
76
|
+
},
|
|
77
|
+
"./logging/logger": {
|
|
78
|
+
types: "./dist/logging/logger.d.ts",
|
|
79
|
+
default: "./dist/logging/logger.js"
|
|
80
|
+
},
|
|
81
|
+
"./metrics": {
|
|
82
|
+
types: "./dist/metrics/index.d.ts",
|
|
83
|
+
default: "./dist/metrics/index.js"
|
|
84
|
+
},
|
|
85
|
+
"./tracing": {
|
|
86
|
+
types: "./dist/tracing/index.d.ts",
|
|
87
|
+
default: "./dist/tracing/index.js"
|
|
88
|
+
},
|
|
89
|
+
"./health": {
|
|
90
|
+
types: "./dist/health/index.d.ts",
|
|
91
|
+
default: "./dist/health/index.js"
|
|
92
|
+
},
|
|
93
|
+
"./resiliency": {
|
|
94
|
+
types: "./dist/resiliency/index.d.ts",
|
|
95
|
+
default: "./dist/resiliency/index.js"
|
|
96
|
+
},
|
|
97
|
+
"./cache": {
|
|
98
|
+
types: "./dist/cache/index.d.ts",
|
|
99
|
+
default: "./dist/cache/index.js"
|
|
100
|
+
},
|
|
101
|
+
"./files": {
|
|
102
|
+
types: "./dist/files/watcher.d.ts",
|
|
103
|
+
default: "./dist/files/watcher.js"
|
|
104
|
+
},
|
|
105
|
+
"./sqlite": {
|
|
106
|
+
types: "./dist/sqlite/index.d.ts",
|
|
107
|
+
default: "./dist/sqlite/index.js"
|
|
108
|
+
},
|
|
109
|
+
"./neon": {
|
|
110
|
+
types: "./dist/neon/index.d.ts",
|
|
111
|
+
default: "./dist/neon/index.js"
|
|
112
|
+
},
|
|
113
|
+
"./prometheus": {
|
|
114
|
+
types: "./dist/prometheus/index.d.ts",
|
|
115
|
+
default: "./dist/prometheus/index.js"
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
publishConfig: {
|
|
119
|
+
exports: {
|
|
120
|
+
".": {
|
|
121
|
+
types: "./dist/logging/logger.d.ts",
|
|
122
|
+
default: "./dist/logging/logger.js"
|
|
123
|
+
},
|
|
124
|
+
"./logging/logger": {
|
|
125
|
+
types: "./dist/logging/logger.d.ts",
|
|
126
|
+
default: "./dist/logging/logger.js"
|
|
127
|
+
},
|
|
128
|
+
"./health": {
|
|
129
|
+
types: "./dist/health/index.d.ts",
|
|
130
|
+
default: "./dist/health/index.js"
|
|
131
|
+
},
|
|
132
|
+
"./tracing": {
|
|
133
|
+
types: "./dist/tracing/index.d.ts",
|
|
134
|
+
default: "./dist/tracing/index.js"
|
|
135
|
+
},
|
|
136
|
+
"./metrics": {
|
|
137
|
+
types: "./dist/metrics/index.d.ts",
|
|
138
|
+
default: "./dist/metrics/index.js"
|
|
139
|
+
},
|
|
140
|
+
"./resiliency": {
|
|
141
|
+
types: "./dist/resiliency/index.d.ts",
|
|
142
|
+
default: "./dist/resiliency/index.js"
|
|
143
|
+
},
|
|
144
|
+
"./cache": {
|
|
145
|
+
types: "./dist/cache/index.d.ts",
|
|
146
|
+
default: "./dist/cache/index.js"
|
|
147
|
+
},
|
|
148
|
+
"./files": {
|
|
149
|
+
types: "./dist/files/watcher.d.ts",
|
|
150
|
+
default: "./dist/files/watcher.js"
|
|
151
|
+
},
|
|
152
|
+
"./sqlite": {
|
|
153
|
+
types: "./dist/sqlite/index.d.ts",
|
|
154
|
+
default: "./dist/sqlite/index.js"
|
|
155
|
+
},
|
|
156
|
+
"./neon": {
|
|
157
|
+
types: "./dist/neon/index.d.ts",
|
|
158
|
+
default: "./dist/neon/index.js"
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
},
|
|
162
|
+
main: "dist/index.js",
|
|
163
|
+
private: true,
|
|
164
|
+
scripts: {
|
|
165
|
+
build: "tsup && tsc --build tsconfig.build.json --force && node ../../scripts/build-utils/add-js-extensions.mjs",
|
|
166
|
+
"build:docker": "tsup --no-dts && node ../../scripts/build-utils/add-js-extensions.mjs",
|
|
167
|
+
dev: "tsup --watch",
|
|
168
|
+
check: "biome check .",
|
|
169
|
+
format: "biome format --write .",
|
|
170
|
+
lint: "biome lint .",
|
|
171
|
+
"lint:fix": "biome lint --fix .",
|
|
172
|
+
postbuild: "test -f dist/index.d.ts && node scripts/patch-dts.cjs || echo 'Warning: Type declarations not fully generated'",
|
|
173
|
+
test: "vitest run",
|
|
174
|
+
"test:coverage": "vitest run --coverage",
|
|
175
|
+
"test:watch": "vitest",
|
|
176
|
+
"type-check": "tsc --noEmit"
|
|
177
|
+
},
|
|
178
|
+
type: "module",
|
|
179
|
+
types: "dist/index.d.ts"
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
// ../../packages/infrastructure/dist/environment/index.js
|
|
185
|
+
function isDevelopment() {
|
|
186
|
+
return getDeploymentEnv() === "development";
|
|
187
|
+
}
|
|
188
|
+
__name(isDevelopment, "isDevelopment");
|
|
189
|
+
function isProduction() {
|
|
190
|
+
return getDeploymentEnv() === "production";
|
|
191
|
+
}
|
|
192
|
+
__name(isProduction, "isProduction");
|
|
193
|
+
function getDeploymentEnv() {
|
|
194
|
+
const explicitEnv = process.env.DEPLOYMENT_ENV;
|
|
195
|
+
if (explicitEnv === "development" || explicitEnv === "staging" || explicitEnv === "production") {
|
|
196
|
+
return explicitEnv;
|
|
197
|
+
}
|
|
198
|
+
if (process.env.VERCEL || process.env.VERCEL_ENV || process.env.FLY_ALLOC_ID) {
|
|
199
|
+
return "production";
|
|
200
|
+
}
|
|
201
|
+
const authUrl = process.env.BETTER_AUTH_URL || process.env.APP_URL || "";
|
|
202
|
+
if (authUrl.includes("localhost") || authUrl.includes("127.0.0.1")) {
|
|
203
|
+
return "development";
|
|
204
|
+
}
|
|
205
|
+
if (typeof window !== "undefined") {
|
|
206
|
+
const hostname = window.location.hostname;
|
|
207
|
+
if (hostname === "localhost" || hostname === "127.0.0.1" || hostname.endsWith(".local") || hostname.endsWith(".test")) {
|
|
208
|
+
return "development";
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
if (process.env.CI) {
|
|
212
|
+
return "production";
|
|
213
|
+
}
|
|
214
|
+
return "production";
|
|
215
|
+
}
|
|
216
|
+
__name(getDeploymentEnv, "getDeploymentEnv");
|
|
217
|
+
function getAnalyticsEnv() {
|
|
218
|
+
return getDeploymentEnv() === "development" ? "dev" : "prod";
|
|
219
|
+
}
|
|
220
|
+
__name(getAnalyticsEnv, "getAnalyticsEnv");
|
|
221
|
+
function getEnvironmentInfo() {
|
|
222
|
+
const deployment = getDeploymentEnv();
|
|
223
|
+
return {
|
|
224
|
+
deployment,
|
|
225
|
+
analytics: deployment === "development" ? "dev" : "prod",
|
|
226
|
+
nodeEnv: process.env.NODE_ENV,
|
|
227
|
+
isCi: !!process.env.CI,
|
|
228
|
+
isVercel: !!(process.env.VERCEL || process.env.VERCEL_ENV),
|
|
229
|
+
isFly: !!process.env.FLY_ALLOC_ID,
|
|
230
|
+
isLocalhost: (process.env.BETTER_AUTH_URL?.includes("localhost") ?? false) || typeof window !== "undefined" && (window.location.hostname === "localhost" || window.location.hostname === "127.0.0.1")
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
__name(getEnvironmentInfo, "getEnvironmentInfo");
|
|
234
|
+
function detectSurface() {
|
|
235
|
+
if (process.env.VSCODE_EXTENSION || typeof process.env.VSCODE_PID !== "undefined") {
|
|
236
|
+
return "vscode";
|
|
237
|
+
}
|
|
238
|
+
if (process.env.SNAPBACK_CLI || process.argv[1]?.includes("cli")) {
|
|
239
|
+
return "cli";
|
|
240
|
+
}
|
|
241
|
+
if (process.env.MCP_SERVER || process.env.MCP_MODE) {
|
|
242
|
+
return "mcp";
|
|
243
|
+
}
|
|
244
|
+
if (process.env.DAEMON_MODE || process.env.LOCAL_SERVICE) {
|
|
245
|
+
return "daemon";
|
|
246
|
+
}
|
|
247
|
+
if (process.env.API_SERVER || typeof process.env.PORT !== "undefined") {
|
|
248
|
+
return "api";
|
|
249
|
+
}
|
|
250
|
+
if (typeof window !== "undefined") {
|
|
251
|
+
return "web";
|
|
252
|
+
}
|
|
253
|
+
return "api";
|
|
254
|
+
}
|
|
255
|
+
__name(detectSurface, "detectSurface");
|
|
256
|
+
function getAnalyticsSuperProperties(surface) {
|
|
257
|
+
return {
|
|
258
|
+
env: getAnalyticsEnv(),
|
|
259
|
+
surface: surface ?? detectSurface()
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
__name(getAnalyticsSuperProperties, "getAnalyticsSuperProperties");
|
|
263
|
+
|
|
264
|
+
// ../../packages/infrastructure/dist/graceful-shutdown/index.js
|
|
265
|
+
function createGracefulShutdown(options) {
|
|
266
|
+
const { logger: logger2, timeoutMs = 25e3, healthMarkerPath } = options;
|
|
267
|
+
const cleanupHandlers = [];
|
|
268
|
+
let isShuttingDown = false;
|
|
269
|
+
let isReady = false;
|
|
270
|
+
function updateReadiness(ready) {
|
|
271
|
+
isReady = ready;
|
|
272
|
+
if (healthMarkerPath) {
|
|
273
|
+
try {
|
|
274
|
+
if (ready) {
|
|
275
|
+
globalThis.Bun?.write?.(healthMarkerPath, process.pid.toString());
|
|
276
|
+
} else {
|
|
277
|
+
globalThis.Bun?.write?.(healthMarkerPath, "");
|
|
278
|
+
}
|
|
279
|
+
} catch (error) {
|
|
280
|
+
logger2.debug("Health marker file operation failed", {
|
|
281
|
+
error
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
__name(updateReadiness, "updateReadiness");
|
|
287
|
+
async function shutdown(signal) {
|
|
288
|
+
if (isShuttingDown) {
|
|
289
|
+
logger2.warn("Shutdown already in progress, ignoring signal", {
|
|
290
|
+
signal
|
|
291
|
+
});
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
isShuttingDown = true;
|
|
295
|
+
logger2.info("Shutdown initiated", {
|
|
296
|
+
signal,
|
|
297
|
+
pid: process.pid
|
|
298
|
+
});
|
|
299
|
+
updateReadiness(false);
|
|
300
|
+
logger2.info("Service marked as not ready, stopping new traffic");
|
|
301
|
+
const drainTimeout = Math.min(5e3, timeoutMs / 5);
|
|
302
|
+
logger2.info("Waiting for in-flight requests to complete", {
|
|
303
|
+
drainTimeoutMs: drainTimeout
|
|
304
|
+
});
|
|
305
|
+
await new Promise((resolve) => setTimeout(resolve, drainTimeout));
|
|
306
|
+
logger2.info("Running cleanup handlers", {
|
|
307
|
+
count: cleanupHandlers.length
|
|
308
|
+
});
|
|
309
|
+
const cleanupTimeout = timeoutMs - drainTimeout - 2e3;
|
|
310
|
+
const cleanupStart = Date.now();
|
|
311
|
+
for (let i = cleanupHandlers.length - 1; i >= 0; i--) {
|
|
312
|
+
const remainingTime = cleanupTimeout - (Date.now() - cleanupStart);
|
|
313
|
+
if (remainingTime <= 0) {
|
|
314
|
+
logger2.warn("Cleanup timeout reached, some handlers may not have completed");
|
|
315
|
+
break;
|
|
316
|
+
}
|
|
317
|
+
try {
|
|
318
|
+
const handler = cleanupHandlers[i];
|
|
319
|
+
const handlerTimeout = Math.min(remainingTime, 5e3);
|
|
320
|
+
await Promise.race([
|
|
321
|
+
handler(),
|
|
322
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error("Handler timeout")), handlerTimeout))
|
|
323
|
+
]);
|
|
324
|
+
} catch (error) {
|
|
325
|
+
logger2.error("Cleanup handler error", {
|
|
326
|
+
error,
|
|
327
|
+
handlerIndex: i
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
logger2.info("Shutdown complete", {
|
|
332
|
+
durationMs: Date.now() - cleanupStart + drainTimeout
|
|
333
|
+
});
|
|
334
|
+
process.exit(0);
|
|
335
|
+
}
|
|
336
|
+
__name(shutdown, "shutdown");
|
|
337
|
+
const forceExit = /* @__PURE__ */ __name(() => {
|
|
338
|
+
logger2.error("Forced exit due to timeout", {
|
|
339
|
+
timeoutMs
|
|
340
|
+
});
|
|
341
|
+
process.exit(1);
|
|
342
|
+
}, "forceExit");
|
|
343
|
+
return {
|
|
344
|
+
register(handler) {
|
|
345
|
+
cleanupHandlers.push(handler);
|
|
346
|
+
},
|
|
347
|
+
setReady() {
|
|
348
|
+
updateReadiness(true);
|
|
349
|
+
},
|
|
350
|
+
setNotReady() {
|
|
351
|
+
updateReadiness(false);
|
|
352
|
+
},
|
|
353
|
+
init() {
|
|
354
|
+
const handleShutdown = /* @__PURE__ */ __name((signal) => {
|
|
355
|
+
shutdown(signal).catch((error) => {
|
|
356
|
+
logger2.error("Shutdown error", {
|
|
357
|
+
error
|
|
358
|
+
});
|
|
359
|
+
process.exit(1);
|
|
360
|
+
});
|
|
361
|
+
setTimeout(forceExit, timeoutMs);
|
|
362
|
+
}, "handleShutdown");
|
|
363
|
+
process.on("SIGTERM", () => handleShutdown("SIGTERM"));
|
|
364
|
+
process.on("SIGINT", () => handleShutdown("SIGINT"));
|
|
365
|
+
process.on("uncaughtException", (error) => {
|
|
366
|
+
logger2.error("Uncaught exception, initiating shutdown", {
|
|
367
|
+
error
|
|
368
|
+
});
|
|
369
|
+
handleShutdown("uncaughtException");
|
|
370
|
+
});
|
|
371
|
+
process.on("unhandledRejection", (reason) => {
|
|
372
|
+
logger2.error("Unhandled rejection, initiating shutdown", {
|
|
373
|
+
reason
|
|
374
|
+
});
|
|
375
|
+
if (process.env.NODE_ENV !== "production") {
|
|
376
|
+
handleShutdown("unhandledRejection");
|
|
377
|
+
}
|
|
378
|
+
});
|
|
379
|
+
logger2.info("Graceful shutdown handler initialized", {
|
|
380
|
+
timeoutMs
|
|
381
|
+
});
|
|
382
|
+
},
|
|
383
|
+
isReady() {
|
|
384
|
+
return isReady;
|
|
385
|
+
},
|
|
386
|
+
isShuttingDown() {
|
|
387
|
+
return isShuttingDown;
|
|
388
|
+
}
|
|
389
|
+
};
|
|
390
|
+
}
|
|
391
|
+
__name(createGracefulShutdown, "createGracefulShutdown");
|
|
392
|
+
async function drainAndCloseServer(server, logger2) {
|
|
393
|
+
logger2.info("Closing server...");
|
|
394
|
+
const closePromise = server.close();
|
|
395
|
+
if (server.closeIdleConnections) {
|
|
396
|
+
logger2.info("Closing idle connections...");
|
|
397
|
+
server.closeIdleConnections();
|
|
398
|
+
}
|
|
399
|
+
const timeout = 1e4;
|
|
400
|
+
await Promise.race([
|
|
401
|
+
closePromise,
|
|
402
|
+
new Promise((resolve) => setTimeout(resolve, timeout))
|
|
403
|
+
]);
|
|
404
|
+
if (server.closeAllConnections) {
|
|
405
|
+
logger2.info("Force closing remaining connections...");
|
|
406
|
+
server.closeAllConnections();
|
|
407
|
+
}
|
|
408
|
+
logger2.info("Server closed");
|
|
409
|
+
}
|
|
410
|
+
__name(drainAndCloseServer, "drainAndCloseServer");
|
|
411
|
+
async function preStopDelay(ms = 5e3) {
|
|
412
|
+
await new Promise((resolve) => setTimeout(resolve, ms));
|
|
413
|
+
}
|
|
414
|
+
__name(preStopDelay, "preStopDelay");
|
|
415
|
+
|
|
416
|
+
// ../../packages/infrastructure/dist/health/index.js
|
|
417
|
+
function createHealthCheck(options) {
|
|
418
|
+
return async () => {
|
|
419
|
+
const checks = {};
|
|
420
|
+
checks.system = {
|
|
421
|
+
status: "healthy",
|
|
422
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
423
|
+
latency: 0
|
|
424
|
+
};
|
|
425
|
+
const memoryUsage = process.memoryUsage();
|
|
426
|
+
checks.memory = {
|
|
427
|
+
status: memoryUsage.heapUsed < 0.8 * memoryUsage.heapTotal ? "healthy" : "degraded",
|
|
428
|
+
message: `Memory usage: ${Math.round(memoryUsage.heapUsed / 1024 / 1024 * 100) / 100} MB`,
|
|
429
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
430
|
+
};
|
|
431
|
+
const uptime = process.uptime();
|
|
432
|
+
if (options.dependencies) {
|
|
433
|
+
for (const dep of options.dependencies) {
|
|
434
|
+
try {
|
|
435
|
+
const depStartTime = Date.now();
|
|
436
|
+
const result = await dep.check();
|
|
437
|
+
const latency = Date.now() - depStartTime;
|
|
438
|
+
checks[dep.name] = {
|
|
439
|
+
status: result.status,
|
|
440
|
+
message: result.message,
|
|
441
|
+
latency,
|
|
442
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
443
|
+
};
|
|
444
|
+
} catch (error) {
|
|
445
|
+
checks[dep.name] = {
|
|
446
|
+
status: "unhealthy",
|
|
447
|
+
message: error instanceof Error ? error.message : "Unknown error",
|
|
448
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
449
|
+
};
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
let overallStatus = "healthy";
|
|
454
|
+
for (const check of Object.values(checks)) {
|
|
455
|
+
if (check.status === "unhealthy") {
|
|
456
|
+
overallStatus = "unhealthy";
|
|
457
|
+
break;
|
|
458
|
+
}
|
|
459
|
+
if (check.status === "degraded" && overallStatus === "healthy") {
|
|
460
|
+
overallStatus = "degraded";
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
const response = {
|
|
464
|
+
status: overallStatus,
|
|
465
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
466
|
+
service: options.service,
|
|
467
|
+
checks,
|
|
468
|
+
uptime,
|
|
469
|
+
memoryUsage: {
|
|
470
|
+
heapUsed: memoryUsage.heapUsed,
|
|
471
|
+
heapTotal: memoryUsage.heapTotal,
|
|
472
|
+
rss: memoryUsage.rss
|
|
473
|
+
}
|
|
474
|
+
};
|
|
475
|
+
if (options.version) {
|
|
476
|
+
response.version = options.version;
|
|
477
|
+
}
|
|
478
|
+
return response;
|
|
479
|
+
};
|
|
480
|
+
}
|
|
481
|
+
__name(createHealthCheck, "createHealthCheck");
|
|
482
|
+
async function checkDatabaseConnection(_connectionString) {
|
|
483
|
+
try {
|
|
484
|
+
const startTime = Date.now();
|
|
485
|
+
await new Promise((resolve) => setTimeout(resolve, Math.random() * 50));
|
|
486
|
+
const latency = Date.now() - startTime;
|
|
487
|
+
if (latency > 1e3) {
|
|
488
|
+
return {
|
|
489
|
+
status: "degraded",
|
|
490
|
+
message: `Database connection is slow (${latency}ms)`
|
|
491
|
+
};
|
|
492
|
+
}
|
|
493
|
+
return {
|
|
494
|
+
status: "healthy",
|
|
495
|
+
message: `Database connection successful (${latency}ms)`
|
|
496
|
+
};
|
|
497
|
+
} catch (error) {
|
|
498
|
+
return {
|
|
499
|
+
status: "unhealthy",
|
|
500
|
+
message: error instanceof Error ? error.message : "Database connection failed"
|
|
501
|
+
};
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
__name(checkDatabaseConnection, "checkDatabaseConnection");
|
|
505
|
+
async function checkRedisConnection(_redisUrl) {
|
|
506
|
+
try {
|
|
507
|
+
const startTime = Date.now();
|
|
508
|
+
await new Promise((resolve) => setTimeout(resolve, Math.random() * 30));
|
|
509
|
+
const latency = Date.now() - startTime;
|
|
510
|
+
if (latency > 200) {
|
|
511
|
+
return {
|
|
512
|
+
status: "degraded",
|
|
513
|
+
message: `Redis connection is slow (${latency}ms)`
|
|
514
|
+
};
|
|
515
|
+
}
|
|
516
|
+
return {
|
|
517
|
+
status: "healthy",
|
|
518
|
+
message: `Redis connection successful (${latency}ms)`
|
|
519
|
+
};
|
|
520
|
+
} catch (error) {
|
|
521
|
+
return {
|
|
522
|
+
status: "unhealthy",
|
|
523
|
+
message: error instanceof Error ? error.message : "Redis connection failed"
|
|
524
|
+
};
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
__name(checkRedisConnection, "checkRedisConnection");
|
|
528
|
+
async function checkHttpService(_url) {
|
|
529
|
+
try {
|
|
530
|
+
const startTime = Date.now();
|
|
531
|
+
await new Promise((resolve) => setTimeout(resolve, Math.random() * 100));
|
|
532
|
+
const latency = Date.now() - startTime;
|
|
533
|
+
if (latency > 1e3) {
|
|
534
|
+
return {
|
|
535
|
+
status: "degraded",
|
|
536
|
+
message: `HTTP service is slow (${latency}ms)`
|
|
537
|
+
};
|
|
538
|
+
}
|
|
539
|
+
return {
|
|
540
|
+
status: "healthy",
|
|
541
|
+
message: `HTTP service check successful (${latency}ms)`
|
|
542
|
+
};
|
|
543
|
+
} catch (error) {
|
|
544
|
+
return {
|
|
545
|
+
status: "unhealthy",
|
|
546
|
+
message: error instanceof Error ? error.message : "HTTP service check failed"
|
|
547
|
+
};
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
__name(checkHttpService, "checkHttpService");
|
|
551
|
+
|
|
552
|
+
// ../../packages/infrastructure/dist/metrics/core/events.js
|
|
553
|
+
var AnalyticsEvents = {
|
|
554
|
+
// ===== Authentication Events (6) =====
|
|
555
|
+
AUTH_SIGNUP_COMPLETED: "auth_signup_completed",
|
|
556
|
+
AUTH_LOGIN_COMPLETED: "auth_login_completed",
|
|
557
|
+
AUTH_LOGOUT_COMPLETED: "auth_logout_completed",
|
|
558
|
+
AUTH_EMAIL_VERIFIED: "auth_email_verified",
|
|
559
|
+
AUTH_PASSWORD_RESET_REQUESTED: "auth_password_reset_requested",
|
|
560
|
+
AUTH_PASSWORD_RESET_COMPLETED: "auth_password_reset_completed",
|
|
561
|
+
// ===== Snapshot Events (20) =====
|
|
562
|
+
SNAPSHOT_CREATED: "snapshot_created",
|
|
563
|
+
SNAPSHOT_RESTORED: "snapshot_restored",
|
|
564
|
+
SNAPSHOT_DELETED: "snapshot_deleted",
|
|
565
|
+
SNAPSHOT_SEARCHED: "snapshot_searched",
|
|
566
|
+
SNAPSHOT_LIMIT_HIT: "snapshot_limit_hit",
|
|
567
|
+
SNAPSHOT_AUTO_CREATED: "snapshot_auto_created",
|
|
568
|
+
SNAPSHOT_SHARED: "snapshot_shared",
|
|
569
|
+
SNAPSHOT_EXPORTED: "snapshot_exported",
|
|
570
|
+
SNAPSHOT_VIEWED: "snapshot_viewed",
|
|
571
|
+
SNAPSHOT_DIFF_VIEWED: "snapshot_diff_viewed",
|
|
572
|
+
// Removed duplicate snapshot_checkpoint_* events to maintain consistent terminology
|
|
573
|
+
// ===== Billing/Monetization Events (12) =====
|
|
574
|
+
BILLING_UPGRADE_PROMPT_SHOWN: "billing_upgrade_prompt_shown",
|
|
575
|
+
BILLING_UPGRADE_PROMPT_CLICKED: "billing_upgrade_prompt_clicked",
|
|
576
|
+
BILLING_PRICING_VIEWED: "billing_pricing_viewed",
|
|
577
|
+
BILLING_CHECKOUT_STARTED: "billing_checkout_started",
|
|
578
|
+
BILLING_CHECKOUT_COMPLETED: "billing_checkout_completed",
|
|
579
|
+
BILLING_CHECKOUT_ABANDONED: "billing_checkout_abandoned",
|
|
580
|
+
BILLING_SUBSCRIPTION_UPGRADED: "billing_subscription_upgraded",
|
|
581
|
+
BILLING_SUBSCRIPTION_DOWNGRADED: "billing_subscription_downgraded",
|
|
582
|
+
BILLING_SUBSCRIPTION_CANCELLED: "billing_subscription_cancelled",
|
|
583
|
+
BILLING_PAYMENT_FAILED: "billing_payment_failed",
|
|
584
|
+
BILLING_COUPON_APPLIED: "billing_coupon_applied",
|
|
585
|
+
BILLING_INVOICE_VIEWED: "billing_invoice_viewed",
|
|
586
|
+
// ===== Extension Events (8) =====
|
|
587
|
+
EXTENSION_INSTALLED: "extension_installed",
|
|
588
|
+
EXTENSION_ACTIVATED: "extension_activated",
|
|
589
|
+
EXTENSION_COMMAND_USED: "extension_command_used",
|
|
590
|
+
EXTENSION_SETTINGS_CHANGED: "extension_settings_changed",
|
|
591
|
+
EXTENSION_ERROR_OCCURRED: "extension_error_occurred",
|
|
592
|
+
EXTENSION_UPDATED: "extension_updated",
|
|
593
|
+
EXTENSION_UNINSTALLED: "extension_uninstalled",
|
|
594
|
+
EXTENSION_FEEDBACK_SUBMITTED: "extension_feedback_submitted",
|
|
595
|
+
// ===== Dashboard Events (8) =====
|
|
596
|
+
DASHBOARD_VIEWED: "dashboard_viewed",
|
|
597
|
+
DASHBOARD_API_KEY_CREATED: "dashboard_api_key_created",
|
|
598
|
+
DASHBOARD_API_KEY_REVOKED: "dashboard_api_key_revoked",
|
|
599
|
+
DASHBOARD_USAGE_CHART_VIEWED: "dashboard_usage_chart_viewed",
|
|
600
|
+
DASHBOARD_SETTINGS_UPDATED: "dashboard_settings_updated",
|
|
601
|
+
DASHBOARD_SEARCH_PERFORMED: "dashboard_search_performed",
|
|
602
|
+
DASHBOARD_EXPORT_TRIGGERED: "dashboard_export_triggered",
|
|
603
|
+
DASHBOARD_HELP_ACCESSED: "dashboard_help_accessed",
|
|
604
|
+
// ===== Team Collaboration Events (6) =====
|
|
605
|
+
TEAM_CREATED: "team_created",
|
|
606
|
+
TEAM_MEMBER_INVITED: "team_member_invited",
|
|
607
|
+
TEAM_MEMBER_JOINED: "team_member_joined",
|
|
608
|
+
TEAM_SNAPSHOT_SHARED: "team_snapshot_shared",
|
|
609
|
+
TEAM_SETTINGS_CHANGED: "team_settings_changed",
|
|
610
|
+
TEAM_MEMBER_REMOVED: "team_member_removed",
|
|
611
|
+
// ===== AI Features Events (5) =====
|
|
612
|
+
AI_SUGGESTION_SHOWN: "ai_suggestion_shown",
|
|
613
|
+
AI_SUGGESTION_ACCEPTED: "ai_suggestion_accepted",
|
|
614
|
+
AI_SUGGESTION_REJECTED: "ai_suggestion_rejected",
|
|
615
|
+
AI_RISK_DETECTED: "ai_risk_detected",
|
|
616
|
+
AI_RISK_PREVENTED: "ai_risk_prevented",
|
|
617
|
+
// ===== API Usage Events (5) =====
|
|
618
|
+
API_CALL_MADE: "api_call_made",
|
|
619
|
+
API_RATE_LIMIT_HIT: "api_rate_limit_hit",
|
|
620
|
+
API_ERROR_OCCURRED: "api_error_occurred",
|
|
621
|
+
API_KEY_ROTATED: "api_key_rotated",
|
|
622
|
+
API_WEBHOOK_CONFIGURED: "api_webhook_configured",
|
|
623
|
+
// ===== Intelligence Layer: Prediction & Learning (6) =====
|
|
624
|
+
PREDICTION_MADE: "prediction_made",
|
|
625
|
+
PREDICTION_OUTCOME_RECORDED: "prediction_outcome_recorded",
|
|
626
|
+
TRUST_SCORE_UPDATED: "trust_score_updated",
|
|
627
|
+
PATTERN_DETECTED: "pattern_detected",
|
|
628
|
+
PATTERN_CONFIRMED: "pattern_confirmed",
|
|
629
|
+
MODEL_CALIBRATION_TRIGGERED: "model_calibration_triggered",
|
|
630
|
+
// ===== Intelligence Layer: Cross-Repo Intelligence (4) =====
|
|
631
|
+
WORKSPACE_CONNECTED: "workspace_connected",
|
|
632
|
+
CROSS_REPO_PATTERN_DETECTED: "cross_repo_pattern_detected",
|
|
633
|
+
REPO_PERSONALITY_UPDATED: "repo_personality_updated",
|
|
634
|
+
GLOBAL_INSIGHT_APPLIED: "global_insight_applied",
|
|
635
|
+
// ===== Intelligence Layer: GitHub Integration (5) =====
|
|
636
|
+
GITHUB_REPO_CONNECTED: "github_repo_connected",
|
|
637
|
+
GITHUB_PR_ANALYZED: "github_pr_analyzed",
|
|
638
|
+
GITHUB_COMMIT_SCANNED: "github_commit_scanned",
|
|
639
|
+
GITHUB_AI_CONTRIBUTION_DETECTED: "github_ai_contribution_detected",
|
|
640
|
+
GITHUB_CHECK_POSTED: "github_check_posted",
|
|
641
|
+
// ===== Intelligence Layer: MCP Tools (3) =====
|
|
642
|
+
MCP_TOOL_CALLED: "mcp_tool_called",
|
|
643
|
+
MCP_CONTEXT_PROVIDED: "mcp_context_provided",
|
|
644
|
+
MCP_AGENT_SELF_CHECK: "mcp_agent_self_check",
|
|
645
|
+
// ===== Intelligence Layer: Community & Engagement (6) =====
|
|
646
|
+
DISASTER_STORY_SHARED: "disaster_story_shared",
|
|
647
|
+
FEEDBACK_SUBMITTED: "feedback_submitted",
|
|
648
|
+
COMMUNITY_ACTION_COMPLETED: "community_action_completed",
|
|
649
|
+
BETA_ELIGIBILITY_CALCULATED: "beta_eligibility_calculated",
|
|
650
|
+
REFERRAL_LINK_GENERATED: "referral_link_generated",
|
|
651
|
+
REFERRAL_CONVERTED: "referral_converted",
|
|
652
|
+
// ===== Activation Funnel Events (2) =====
|
|
653
|
+
AUTH_COMPLETED: "auth_completed",
|
|
654
|
+
FIRST_SNAPSHOT_CREATED: "first_snapshot_created"
|
|
655
|
+
};
|
|
656
|
+
|
|
657
|
+
// ../../packages/infrastructure/dist/neon/index.js
|
|
658
|
+
var neon_exports = {};
|
|
659
|
+
__export(neon_exports, {
|
|
660
|
+
NeonDocClient: () => NeonDocClient,
|
|
661
|
+
runMigration: () => runMigration,
|
|
662
|
+
verifySchema: () => verifySchema
|
|
663
|
+
});
|
|
664
|
+
var NeonDocClient = class {
|
|
665
|
+
static {
|
|
666
|
+
__name(this, "NeonDocClient");
|
|
667
|
+
}
|
|
668
|
+
sql;
|
|
669
|
+
constructor(config) {
|
|
670
|
+
if (config.enableCache !== false) {
|
|
671
|
+
neonConfig.fetchConnectionCache = true;
|
|
672
|
+
}
|
|
673
|
+
this.sql = neon(config.connectionString);
|
|
674
|
+
}
|
|
675
|
+
/**
|
|
676
|
+
* Execute hybrid search against doc_embeddings
|
|
677
|
+
*
|
|
678
|
+
* @param queryEmbedding - 768-dim vector from text-embedding-3-small
|
|
679
|
+
* @param options - Search filters and limits
|
|
680
|
+
* @returns Ranked search results with similarity scores
|
|
681
|
+
*/
|
|
682
|
+
async hybridSearch(queryEmbedding, options = {}) {
|
|
683
|
+
if (queryEmbedding.length !== 768) {
|
|
684
|
+
throw new Error(`Expected 768-dim embedding, got ${queryEmbedding.length}`);
|
|
685
|
+
}
|
|
686
|
+
const { library = null, version = null, limit = 5, similarityThreshold = 0.75 } = options;
|
|
687
|
+
const embeddingStr = `[${queryEmbedding.join(",")}]`;
|
|
688
|
+
const results = await this.sql`
|
|
689
|
+
SELECT * FROM hybrid_search(
|
|
690
|
+
${embeddingStr}::vector(768),
|
|
691
|
+
${library},
|
|
692
|
+
${version},
|
|
693
|
+
${limit},
|
|
694
|
+
${similarityThreshold}
|
|
695
|
+
)
|
|
696
|
+
`;
|
|
697
|
+
return results;
|
|
698
|
+
}
|
|
699
|
+
/**
|
|
700
|
+
* Insert a single doc embedding
|
|
701
|
+
*
|
|
702
|
+
* @param doc - Document embedding record
|
|
703
|
+
* @returns Inserted record ID
|
|
704
|
+
*/
|
|
705
|
+
async insertDoc(doc) {
|
|
706
|
+
if (doc.embedding.length !== 768) {
|
|
707
|
+
throw new Error(`Expected 768-dim embedding, got ${doc.embedding.length}`);
|
|
708
|
+
}
|
|
709
|
+
const embeddingStr = `[${doc.embedding.join(",")}]`;
|
|
710
|
+
const result = await this.sql`
|
|
711
|
+
INSERT INTO doc_embeddings (library, version, doc_type, chunk_text, embedding, metadata, source_url)
|
|
712
|
+
VALUES (
|
|
713
|
+
${doc.library},
|
|
714
|
+
${doc.version},
|
|
715
|
+
${doc.doc_type},
|
|
716
|
+
${doc.chunk_text},
|
|
717
|
+
${embeddingStr}::vector(768),
|
|
718
|
+
${JSON.stringify(doc.metadata)},
|
|
719
|
+
${doc.source_url}
|
|
720
|
+
)
|
|
721
|
+
ON CONFLICT (library, version, chunk_text)
|
|
722
|
+
DO UPDATE SET
|
|
723
|
+
embedding = EXCLUDED.embedding,
|
|
724
|
+
metadata = EXCLUDED.metadata,
|
|
725
|
+
indexed_at = NOW()
|
|
726
|
+
RETURNING id
|
|
727
|
+
`;
|
|
728
|
+
return result[0]?.id ?? "";
|
|
729
|
+
}
|
|
730
|
+
/**
|
|
731
|
+
* Batch insert doc embeddings (optimized for bulk ingestion)
|
|
732
|
+
*
|
|
733
|
+
* @param docs - Array of document embeddings
|
|
734
|
+
* @returns Count of inserted/updated records
|
|
735
|
+
*/
|
|
736
|
+
async batchInsertDocs(docs) {
|
|
737
|
+
if (docs.length === 0) {
|
|
738
|
+
return 0;
|
|
739
|
+
}
|
|
740
|
+
for (const doc of docs) {
|
|
741
|
+
if (doc.embedding.length !== 768) {
|
|
742
|
+
throw new Error(`Expected 768-dim embedding, got ${doc.embedding.length}`);
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
let count = 0;
|
|
746
|
+
for (const doc of docs) {
|
|
747
|
+
await this.insertDoc(doc);
|
|
748
|
+
count++;
|
|
749
|
+
}
|
|
750
|
+
return count;
|
|
751
|
+
}
|
|
752
|
+
/**
|
|
753
|
+
* Get embedding stats (total chunks, by library, etc.)
|
|
754
|
+
*/
|
|
755
|
+
async getStats() {
|
|
756
|
+
const totalResult = await this.sql`SELECT COUNT(*) as count FROM doc_embeddings`;
|
|
757
|
+
const total = Number(totalResult[0]?.count ?? 0);
|
|
758
|
+
const byLibraryResult = await this.sql`
|
|
759
|
+
SELECT library, COUNT(*) as count
|
|
760
|
+
FROM doc_embeddings
|
|
761
|
+
GROUP BY library
|
|
762
|
+
`;
|
|
763
|
+
const byDocTypeResult = await this.sql`
|
|
764
|
+
SELECT doc_type, COUNT(*) as count
|
|
765
|
+
FROM doc_embeddings
|
|
766
|
+
GROUP BY doc_type
|
|
767
|
+
`;
|
|
768
|
+
return {
|
|
769
|
+
total,
|
|
770
|
+
byLibrary: Object.fromEntries(byLibraryResult.map((r) => [
|
|
771
|
+
r.library,
|
|
772
|
+
Number(r.count)
|
|
773
|
+
])),
|
|
774
|
+
byDocType: Object.fromEntries(byDocTypeResult.map((r) => [
|
|
775
|
+
r.doc_type,
|
|
776
|
+
Number(r.count)
|
|
777
|
+
]))
|
|
778
|
+
};
|
|
779
|
+
}
|
|
780
|
+
};
|
|
781
|
+
var __filename$1 = fileURLToPath(import.meta.url);
|
|
782
|
+
var __dirname$1 = dirname(__filename$1);
|
|
783
|
+
async function runMigration(connectionString) {
|
|
784
|
+
try {
|
|
785
|
+
const sql = neon(connectionString);
|
|
786
|
+
const schemaPath = join(__dirname$1, "schema.sql");
|
|
787
|
+
const schemaSql = readFileSync(schemaPath, "utf-8");
|
|
788
|
+
await sql(schemaSql);
|
|
789
|
+
return {
|
|
790
|
+
success: true
|
|
791
|
+
};
|
|
792
|
+
} catch (error) {
|
|
793
|
+
return {
|
|
794
|
+
success: false,
|
|
795
|
+
error: error instanceof Error ? error.message : String(error)
|
|
796
|
+
};
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
__name(runMigration, "runMigration");
|
|
800
|
+
async function verifySchema(connectionString) {
|
|
801
|
+
try {
|
|
802
|
+
const sql = neon(connectionString);
|
|
803
|
+
const tableCheck = await sql`
|
|
804
|
+
SELECT EXISTS (
|
|
805
|
+
SELECT FROM information_schema.tables
|
|
806
|
+
WHERE table_name = 'doc_embeddings'
|
|
807
|
+
) as exists
|
|
808
|
+
`;
|
|
809
|
+
const tableExists = tableCheck[0]?.exists === true;
|
|
810
|
+
const indexCheck = await sql`
|
|
811
|
+
SELECT EXISTS (
|
|
812
|
+
SELECT FROM pg_indexes
|
|
813
|
+
WHERE indexname = 'doc_embeddings_hnsw_idx'
|
|
814
|
+
) as exists
|
|
815
|
+
`;
|
|
816
|
+
const hnswIndexExists = indexCheck[0]?.exists === true;
|
|
817
|
+
const functionCheck = await sql`
|
|
818
|
+
SELECT EXISTS (
|
|
819
|
+
SELECT FROM pg_proc
|
|
820
|
+
WHERE proname = 'hybrid_search'
|
|
821
|
+
) as exists
|
|
822
|
+
`;
|
|
823
|
+
const hybridSearchFunctionExists = functionCheck[0]?.exists === true;
|
|
824
|
+
const valid = tableExists && hnswIndexExists && hybridSearchFunctionExists;
|
|
825
|
+
return {
|
|
826
|
+
valid,
|
|
827
|
+
checks: {
|
|
828
|
+
tableExists,
|
|
829
|
+
hnswIndexExists,
|
|
830
|
+
hybridSearchFunctionExists
|
|
831
|
+
}
|
|
832
|
+
};
|
|
833
|
+
} catch (error) {
|
|
834
|
+
return {
|
|
835
|
+
valid: false,
|
|
836
|
+
checks: {
|
|
837
|
+
tableExists: false,
|
|
838
|
+
hnswIndexExists: false,
|
|
839
|
+
hybridSearchFunctionExists: false
|
|
840
|
+
},
|
|
841
|
+
error: error instanceof Error ? error.message : String(error)
|
|
842
|
+
};
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
__name(verifySchema, "verifySchema");
|
|
846
|
+
async function createAlert(config) {
|
|
847
|
+
try {
|
|
848
|
+
logger.info({
|
|
849
|
+
alert: config
|
|
850
|
+
}, "PostHog Alert Configuration (Manual Setup Required)");
|
|
851
|
+
return `alert_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`;
|
|
852
|
+
} catch (error) {
|
|
853
|
+
logger.error({
|
|
854
|
+
error,
|
|
855
|
+
config
|
|
856
|
+
}, "Failed to create PostHog alert");
|
|
857
|
+
throw new Error("Failed to create PostHog alert");
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
__name(createAlert, "createAlert");
|
|
861
|
+
async function getAlerts() {
|
|
862
|
+
try {
|
|
863
|
+
return [];
|
|
864
|
+
} catch (error) {
|
|
865
|
+
logger.error({
|
|
866
|
+
error
|
|
867
|
+
}, "Failed to fetch PostHog alerts");
|
|
868
|
+
throw new Error("Failed to fetch PostHog alerts");
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
__name(getAlerts, "getAlerts");
|
|
872
|
+
async function toggleAlert(alertId, enabled) {
|
|
873
|
+
try {
|
|
874
|
+
logger.info({
|
|
875
|
+
alertId,
|
|
876
|
+
enabled
|
|
877
|
+
}, "Toggling PostHog alert");
|
|
878
|
+
return true;
|
|
879
|
+
} catch (error) {
|
|
880
|
+
logger.error({
|
|
881
|
+
error,
|
|
882
|
+
alertId,
|
|
883
|
+
enabled
|
|
884
|
+
}, "Failed to toggle PostHog alert");
|
|
885
|
+
throw new Error("Failed to toggle PostHog alert");
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
__name(toggleAlert, "toggleAlert");
|
|
889
|
+
async function deleteAlert(alertId) {
|
|
890
|
+
try {
|
|
891
|
+
logger.info({
|
|
892
|
+
alertId
|
|
893
|
+
}, "Deleting PostHog alert");
|
|
894
|
+
return true;
|
|
895
|
+
} catch (error) {
|
|
896
|
+
logger.error({
|
|
897
|
+
error,
|
|
898
|
+
alertId
|
|
899
|
+
}, "Failed to delete PostHog alert");
|
|
900
|
+
throw new Error("Failed to delete PostHog alert");
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
__name(deleteAlert, "deleteAlert");
|
|
904
|
+
var KEY_METRIC_ALERTS = [
|
|
905
|
+
{
|
|
906
|
+
name: "TTFV p75 Alert",
|
|
907
|
+
insightId: "ttfv_insight",
|
|
908
|
+
series: "ttfv_p75",
|
|
909
|
+
type: "value",
|
|
910
|
+
threshold: 7,
|
|
911
|
+
thresholdType: "absolute",
|
|
912
|
+
frequency: "daily",
|
|
913
|
+
recipients: [
|
|
914
|
+
"engineering-team@snapback.ai"
|
|
915
|
+
]
|
|
916
|
+
},
|
|
917
|
+
{
|
|
918
|
+
name: "Onboarding Completion Rate Alert",
|
|
919
|
+
insightId: "onboarding_insight",
|
|
920
|
+
series: "completion_rate",
|
|
921
|
+
type: "value",
|
|
922
|
+
threshold: 60,
|
|
923
|
+
thresholdType: "absolute",
|
|
924
|
+
frequency: "daily",
|
|
925
|
+
recipients: [
|
|
926
|
+
"product-team@snapback.ai"
|
|
927
|
+
]
|
|
928
|
+
},
|
|
929
|
+
{
|
|
930
|
+
name: "Crash-free Sessions Alert",
|
|
931
|
+
insightId: "crash_insight",
|
|
932
|
+
series: "crash_free_rate",
|
|
933
|
+
type: "value",
|
|
934
|
+
threshold: 95,
|
|
935
|
+
thresholdType: "absolute",
|
|
936
|
+
frequency: "daily",
|
|
937
|
+
recipients: [
|
|
938
|
+
"engineering-team@snapback.ai"
|
|
939
|
+
]
|
|
940
|
+
},
|
|
941
|
+
{
|
|
942
|
+
name: "Replay Budget Alert",
|
|
943
|
+
insightId: "replay_insight",
|
|
944
|
+
series: "replay_budget",
|
|
945
|
+
type: "value",
|
|
946
|
+
threshold: 80,
|
|
947
|
+
thresholdType: "percentage",
|
|
948
|
+
frequency: "weekly",
|
|
949
|
+
recipients: [
|
|
950
|
+
"analytics-team@snapback.ai"
|
|
951
|
+
]
|
|
952
|
+
},
|
|
953
|
+
{
|
|
954
|
+
name: "D7 Retention Alert",
|
|
955
|
+
insightId: "retention_insight",
|
|
956
|
+
series: "d7_retention",
|
|
957
|
+
type: "decrease",
|
|
958
|
+
threshold: 5,
|
|
959
|
+
thresholdType: "percentage",
|
|
960
|
+
frequency: "weekly",
|
|
961
|
+
recipients: [
|
|
962
|
+
"growth-team@snapback.ai"
|
|
963
|
+
]
|
|
964
|
+
}
|
|
965
|
+
];
|
|
966
|
+
var posthogClient = null;
|
|
967
|
+
var posthogApiKey = null;
|
|
968
|
+
var posthogHost = null;
|
|
969
|
+
function getPostHog() {
|
|
970
|
+
if (!posthogClient) {
|
|
971
|
+
const posthogKey = process.env.POSTHOG_PERSONAL_API_KEY;
|
|
972
|
+
if (!posthogKey) {
|
|
973
|
+
throw new Error("PostHog personal API key not configured");
|
|
974
|
+
}
|
|
975
|
+
const host = process.env.POSTHOG_HOST || "https://app.posthog.com";
|
|
976
|
+
posthogClient = new PostHog(posthogKey, {
|
|
977
|
+
host
|
|
978
|
+
});
|
|
979
|
+
posthogApiKey = posthogKey;
|
|
980
|
+
posthogHost = host;
|
|
981
|
+
}
|
|
982
|
+
return posthogClient;
|
|
983
|
+
}
|
|
984
|
+
__name(getPostHog, "getPostHog");
|
|
985
|
+
function getPostHogConfig() {
|
|
986
|
+
if (!posthogApiKey || !posthogHost) {
|
|
987
|
+
getPostHog();
|
|
988
|
+
}
|
|
989
|
+
if (!posthogApiKey || !posthogHost) {
|
|
990
|
+
throw new Error("PostHog configuration not initialized");
|
|
991
|
+
}
|
|
992
|
+
return {
|
|
993
|
+
apiKey: posthogApiKey,
|
|
994
|
+
host: posthogHost
|
|
995
|
+
};
|
|
996
|
+
}
|
|
997
|
+
__name(getPostHogConfig, "getPostHogConfig");
|
|
998
|
+
async function createCohort(config) {
|
|
999
|
+
try {
|
|
1000
|
+
getPostHog();
|
|
1001
|
+
const phConfig = getPostHogConfig();
|
|
1002
|
+
const response = await fetch(`${phConfig.host}/api/projects/@current/cohorts/`, {
|
|
1003
|
+
method: "POST",
|
|
1004
|
+
headers: {
|
|
1005
|
+
Authorization: `Bearer ${phConfig.apiKey}`,
|
|
1006
|
+
"Content-Type": "application/json"
|
|
1007
|
+
},
|
|
1008
|
+
body: JSON.stringify({
|
|
1009
|
+
name: config.name,
|
|
1010
|
+
description: config.description,
|
|
1011
|
+
filters: config.filters,
|
|
1012
|
+
is_static: config.is_static
|
|
1013
|
+
})
|
|
1014
|
+
});
|
|
1015
|
+
if (!response.ok) {
|
|
1016
|
+
throw new Error(`Failed to create cohort: ${response.statusText}`);
|
|
1017
|
+
}
|
|
1018
|
+
const cohort = await response.json();
|
|
1019
|
+
logger.info({
|
|
1020
|
+
cohort
|
|
1021
|
+
}, "Created PostHog cohort");
|
|
1022
|
+
return cohort;
|
|
1023
|
+
} catch (error) {
|
|
1024
|
+
logger.error({
|
|
1025
|
+
error,
|
|
1026
|
+
config
|
|
1027
|
+
}, "Failed to create PostHog cohort");
|
|
1028
|
+
throw new Error(`Failed to create PostHog cohort: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
1031
|
+
__name(createCohort, "createCohort");
|
|
1032
|
+
async function getCohorts() {
|
|
1033
|
+
try {
|
|
1034
|
+
getPostHog();
|
|
1035
|
+
const phConfig = getPostHogConfig();
|
|
1036
|
+
const response = await fetch(`${phConfig.host}/api/projects/@current/cohorts/`, {
|
|
1037
|
+
method: "GET",
|
|
1038
|
+
headers: {
|
|
1039
|
+
Authorization: `Bearer ${phConfig.apiKey}`,
|
|
1040
|
+
"Content-Type": "application/json"
|
|
1041
|
+
}
|
|
1042
|
+
});
|
|
1043
|
+
if (!response.ok) {
|
|
1044
|
+
throw new Error(`Failed to fetch cohorts: ${response.statusText}`);
|
|
1045
|
+
}
|
|
1046
|
+
const data = await response.json();
|
|
1047
|
+
return data.results || [];
|
|
1048
|
+
} catch (error) {
|
|
1049
|
+
logger.error({
|
|
1050
|
+
error
|
|
1051
|
+
}, "Failed to fetch PostHog cohorts");
|
|
1052
|
+
throw new Error(`Failed to fetch PostHog cohorts: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
1053
|
+
}
|
|
1054
|
+
}
|
|
1055
|
+
__name(getCohorts, "getCohorts");
|
|
1056
|
+
async function getCohort(cohortId) {
|
|
1057
|
+
try {
|
|
1058
|
+
getPostHog();
|
|
1059
|
+
const phConfig = getPostHogConfig();
|
|
1060
|
+
const response = await fetch(`${phConfig.host}/api/projects/@current/cohorts/${cohortId}/`, {
|
|
1061
|
+
method: "GET",
|
|
1062
|
+
headers: {
|
|
1063
|
+
Authorization: `Bearer ${phConfig.apiKey}`,
|
|
1064
|
+
"Content-Type": "application/json"
|
|
1065
|
+
}
|
|
1066
|
+
});
|
|
1067
|
+
if (!response.ok) {
|
|
1068
|
+
throw new Error(`Failed to fetch cohort: ${response.statusText}`);
|
|
1069
|
+
}
|
|
1070
|
+
const cohort = await response.json();
|
|
1071
|
+
return cohort;
|
|
1072
|
+
} catch (error) {
|
|
1073
|
+
logger.error({
|
|
1074
|
+
error,
|
|
1075
|
+
cohortId
|
|
1076
|
+
}, "Failed to fetch PostHog cohort");
|
|
1077
|
+
throw new Error(`Failed to fetch PostHog cohort: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
__name(getCohort, "getCohort");
|
|
1081
|
+
async function updateCohort(cohortId, config) {
|
|
1082
|
+
try {
|
|
1083
|
+
getPostHog();
|
|
1084
|
+
const phConfig = getPostHogConfig();
|
|
1085
|
+
const response = await fetch(`${phConfig.host}/api/projects/@current/cohorts/${cohortId}/`, {
|
|
1086
|
+
method: "PATCH",
|
|
1087
|
+
headers: {
|
|
1088
|
+
Authorization: `Bearer ${phConfig.apiKey}`,
|
|
1089
|
+
"Content-Type": "application/json"
|
|
1090
|
+
},
|
|
1091
|
+
body: JSON.stringify(config)
|
|
1092
|
+
});
|
|
1093
|
+
if (!response.ok) {
|
|
1094
|
+
throw new Error(`Failed to update cohort: ${response.statusText}`);
|
|
1095
|
+
}
|
|
1096
|
+
const cohort = await response.json();
|
|
1097
|
+
logger.info({
|
|
1098
|
+
cohort
|
|
1099
|
+
}, "Updated PostHog cohort");
|
|
1100
|
+
return cohort;
|
|
1101
|
+
} catch (error) {
|
|
1102
|
+
logger.error({
|
|
1103
|
+
error,
|
|
1104
|
+
cohortId,
|
|
1105
|
+
config
|
|
1106
|
+
}, "Failed to update PostHog cohort");
|
|
1107
|
+
throw new Error(`Failed to update PostHog cohort: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
1110
|
+
__name(updateCohort, "updateCohort");
|
|
1111
|
+
async function deleteCohort(cohortId) {
|
|
1112
|
+
try {
|
|
1113
|
+
getPostHog();
|
|
1114
|
+
const phConfig = getPostHogConfig();
|
|
1115
|
+
const response = await fetch(`${phConfig.host}/api/projects/@current/cohorts/${cohortId}/`, {
|
|
1116
|
+
method: "DELETE",
|
|
1117
|
+
headers: {
|
|
1118
|
+
Authorization: `Bearer ${phConfig.apiKey}`,
|
|
1119
|
+
"Content-Type": "application/json"
|
|
1120
|
+
}
|
|
1121
|
+
});
|
|
1122
|
+
if (!response.ok) {
|
|
1123
|
+
throw new Error(`Failed to delete cohort: ${response.statusText}`);
|
|
1124
|
+
}
|
|
1125
|
+
logger.info({
|
|
1126
|
+
cohortId
|
|
1127
|
+
}, "Deleted PostHog cohort");
|
|
1128
|
+
} catch (error) {
|
|
1129
|
+
logger.error({
|
|
1130
|
+
error,
|
|
1131
|
+
cohortId
|
|
1132
|
+
}, "Failed to delete PostHog cohort");
|
|
1133
|
+
throw new Error(`Failed to delete PostHog cohort: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
1134
|
+
}
|
|
1135
|
+
}
|
|
1136
|
+
__name(deleteCohort, "deleteCohort");
|
|
1137
|
+
async function getCohortMembers(cohortId) {
|
|
1138
|
+
try {
|
|
1139
|
+
getPostHog();
|
|
1140
|
+
const phConfig = getPostHogConfig();
|
|
1141
|
+
const response = await fetch(`${phConfig.host}/api/projects/@current/cohorts/${cohortId}/persons/`, {
|
|
1142
|
+
method: "GET",
|
|
1143
|
+
headers: {
|
|
1144
|
+
Authorization: `Bearer ${phConfig.apiKey}`,
|
|
1145
|
+
"Content-Type": "application/json"
|
|
1146
|
+
}
|
|
1147
|
+
});
|
|
1148
|
+
if (!response.ok) {
|
|
1149
|
+
throw new Error(`Failed to fetch cohort members: ${response.statusText}`);
|
|
1150
|
+
}
|
|
1151
|
+
const data = await response.json();
|
|
1152
|
+
return data.results || [];
|
|
1153
|
+
} catch (error) {
|
|
1154
|
+
logger.error({
|
|
1155
|
+
error,
|
|
1156
|
+
cohortId
|
|
1157
|
+
}, "Failed to fetch PostHog cohort members");
|
|
1158
|
+
throw new Error(`Failed to fetch PostHog cohort members: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
1159
|
+
}
|
|
1160
|
+
}
|
|
1161
|
+
__name(getCohortMembers, "getCohortMembers");
|
|
1162
|
+
var RETENTION_COHORTS = [
|
|
1163
|
+
{
|
|
1164
|
+
name: "D7 Retention",
|
|
1165
|
+
description: "Users who return within 7 days of their first activity",
|
|
1166
|
+
filters: {
|
|
1167
|
+
properties: [
|
|
1168
|
+
{
|
|
1169
|
+
key: "first_seen",
|
|
1170
|
+
value: "7 days",
|
|
1171
|
+
operator: "within",
|
|
1172
|
+
type: "event"
|
|
1173
|
+
}
|
|
1174
|
+
]
|
|
1175
|
+
}
|
|
1176
|
+
},
|
|
1177
|
+
{
|
|
1178
|
+
name: "D30 Retention",
|
|
1179
|
+
description: "Users who return within 30 days of their first activity",
|
|
1180
|
+
filters: {
|
|
1181
|
+
properties: [
|
|
1182
|
+
{
|
|
1183
|
+
key: "first_seen",
|
|
1184
|
+
value: "30 days",
|
|
1185
|
+
operator: "within",
|
|
1186
|
+
type: "event"
|
|
1187
|
+
}
|
|
1188
|
+
]
|
|
1189
|
+
}
|
|
1190
|
+
},
|
|
1191
|
+
{
|
|
1192
|
+
name: "Onboarding Completion Cohort",
|
|
1193
|
+
description: "Users who completed the onboarding process",
|
|
1194
|
+
filters: {
|
|
1195
|
+
properties: [
|
|
1196
|
+
{
|
|
1197
|
+
key: "onboarding_completed",
|
|
1198
|
+
value: true,
|
|
1199
|
+
operator: "exact",
|
|
1200
|
+
type: "event"
|
|
1201
|
+
}
|
|
1202
|
+
]
|
|
1203
|
+
}
|
|
1204
|
+
},
|
|
1205
|
+
{
|
|
1206
|
+
name: "High Engagement Users",
|
|
1207
|
+
description: "Users with high engagement (5+ sessions in 7 days)",
|
|
1208
|
+
filters: {
|
|
1209
|
+
properties: [
|
|
1210
|
+
{
|
|
1211
|
+
key: "session_count",
|
|
1212
|
+
value: 5,
|
|
1213
|
+
operator: "gt",
|
|
1214
|
+
type: "event"
|
|
1215
|
+
},
|
|
1216
|
+
{
|
|
1217
|
+
key: "activity_period",
|
|
1218
|
+
value: "7 days",
|
|
1219
|
+
operator: "within",
|
|
1220
|
+
type: "event"
|
|
1221
|
+
}
|
|
1222
|
+
]
|
|
1223
|
+
}
|
|
1224
|
+
}
|
|
1225
|
+
];
|
|
1226
|
+
var CORRELATION_COHORTS = [
|
|
1227
|
+
{
|
|
1228
|
+
name: "Feature Power Users",
|
|
1229
|
+
description: "Users who use advanced features regularly",
|
|
1230
|
+
filters: {
|
|
1231
|
+
properties: [
|
|
1232
|
+
{
|
|
1233
|
+
key: "advanced_feature_usage",
|
|
1234
|
+
value: true,
|
|
1235
|
+
operator: "exact",
|
|
1236
|
+
type: "event"
|
|
1237
|
+
}
|
|
1238
|
+
]
|
|
1239
|
+
}
|
|
1240
|
+
},
|
|
1241
|
+
{
|
|
1242
|
+
name: "At-Risk Churn",
|
|
1243
|
+
description: "Users showing signs of disengagement",
|
|
1244
|
+
filters: {
|
|
1245
|
+
properties: [
|
|
1246
|
+
{
|
|
1247
|
+
key: "days_since_last_activity",
|
|
1248
|
+
value: 14,
|
|
1249
|
+
operator: "gt",
|
|
1250
|
+
type: "event"
|
|
1251
|
+
}
|
|
1252
|
+
]
|
|
1253
|
+
}
|
|
1254
|
+
},
|
|
1255
|
+
{
|
|
1256
|
+
name: "Free to Paid Converters",
|
|
1257
|
+
description: "Users who upgraded from free to paid plan",
|
|
1258
|
+
filters: {
|
|
1259
|
+
properties: [
|
|
1260
|
+
{
|
|
1261
|
+
key: "plan_upgrade",
|
|
1262
|
+
value: "free_to_paid",
|
|
1263
|
+
operator: "exact",
|
|
1264
|
+
type: "event"
|
|
1265
|
+
}
|
|
1266
|
+
]
|
|
1267
|
+
}
|
|
1268
|
+
}
|
|
1269
|
+
];
|
|
1270
|
+
var posthogClient2 = null;
|
|
1271
|
+
function getPostHog2() {
|
|
1272
|
+
if (!posthogClient2) {
|
|
1273
|
+
const posthogKey = process.env.POSTHOG_PERSONAL_API_KEY;
|
|
1274
|
+
if (!posthogKey) {
|
|
1275
|
+
throw new Error("PostHog personal API key not configured");
|
|
1276
|
+
}
|
|
1277
|
+
const posthogHost2 = process.env.POSTHOG_HOST || "https://app.posthog.com";
|
|
1278
|
+
posthogClient2 = new PostHog(posthogKey, {
|
|
1279
|
+
host: posthogHost2
|
|
1280
|
+
});
|
|
1281
|
+
}
|
|
1282
|
+
return posthogClient2;
|
|
1283
|
+
}
|
|
1284
|
+
__name(getPostHog2, "getPostHog");
|
|
1285
|
+
async function performCorrelationAnalysis(config) {
|
|
1286
|
+
try {
|
|
1287
|
+
const _posthog = getPostHog2();
|
|
1288
|
+
logger.info({
|
|
1289
|
+
config
|
|
1290
|
+
}, "Performing correlation analysis");
|
|
1291
|
+
const results = config.propertyNames.map((property, _index) => ({
|
|
1292
|
+
property,
|
|
1293
|
+
correlation: Math.random() * 2 - 1,
|
|
1294
|
+
count: Math.floor(Math.random() * 1e3) + 100,
|
|
1295
|
+
relativeFrequency: Math.random()
|
|
1296
|
+
}));
|
|
1297
|
+
results.sort((a, b) => Math.abs(b.correlation) - Math.abs(a.correlation));
|
|
1298
|
+
const analysis = {
|
|
1299
|
+
id: `correlation_${Date.now()}`,
|
|
1300
|
+
name: config.name,
|
|
1301
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1302
|
+
results: results.slice(0, 10)
|
|
1303
|
+
};
|
|
1304
|
+
logger.info({
|
|
1305
|
+
analysis
|
|
1306
|
+
}, "Correlation analysis completed");
|
|
1307
|
+
return analysis;
|
|
1308
|
+
} catch (error) {
|
|
1309
|
+
logger.error({
|
|
1310
|
+
error,
|
|
1311
|
+
config
|
|
1312
|
+
}, "Failed to perform correlation analysis");
|
|
1313
|
+
throw new Error(`Failed to perform correlation analysis: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
1314
|
+
}
|
|
1315
|
+
}
|
|
1316
|
+
__name(performCorrelationAnalysis, "performCorrelationAnalysis");
|
|
1317
|
+
async function getCorrelationAnalysis(analysisId) {
|
|
1318
|
+
try {
|
|
1319
|
+
throw new Error("Correlation analysis persistence not implemented");
|
|
1320
|
+
} catch (error) {
|
|
1321
|
+
logger.error({
|
|
1322
|
+
error,
|
|
1323
|
+
analysisId
|
|
1324
|
+
}, "Failed to fetch correlation analysis");
|
|
1325
|
+
throw new Error(`Failed to fetch correlation analysis: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
1326
|
+
}
|
|
1327
|
+
}
|
|
1328
|
+
__name(getCorrelationAnalysis, "getCorrelationAnalysis");
|
|
1329
|
+
var CORRELATION_ANALYSES = [
|
|
1330
|
+
{
|
|
1331
|
+
name: "Onboarding Completion Factors",
|
|
1332
|
+
eventName: "onboarding_completed",
|
|
1333
|
+
propertyNames: [
|
|
1334
|
+
"signup_source",
|
|
1335
|
+
"device_type",
|
|
1336
|
+
"browser",
|
|
1337
|
+
"utm_campaign",
|
|
1338
|
+
"time_to_complete",
|
|
1339
|
+
"steps_completed",
|
|
1340
|
+
"help_articles_viewed"
|
|
1341
|
+
]
|
|
1342
|
+
},
|
|
1343
|
+
{
|
|
1344
|
+
name: "Feature Adoption Correlations",
|
|
1345
|
+
eventName: "feature_used",
|
|
1346
|
+
propertyNames: [
|
|
1347
|
+
"user_plan",
|
|
1348
|
+
"account_age_days",
|
|
1349
|
+
"session_frequency",
|
|
1350
|
+
"support_tickets",
|
|
1351
|
+
"documentation_views",
|
|
1352
|
+
"community_posts"
|
|
1353
|
+
]
|
|
1354
|
+
},
|
|
1355
|
+
{
|
|
1356
|
+
name: "Churn Risk Indicators",
|
|
1357
|
+
eventName: "account_deactivated",
|
|
1358
|
+
propertyNames: [
|
|
1359
|
+
"days_since_last_activity",
|
|
1360
|
+
"feature_usage_count",
|
|
1361
|
+
"support_ticket_count",
|
|
1362
|
+
"billing_issues",
|
|
1363
|
+
"plan_downgrade",
|
|
1364
|
+
"session_duration_avg"
|
|
1365
|
+
]
|
|
1366
|
+
},
|
|
1367
|
+
{
|
|
1368
|
+
name: "High Value User Characteristics",
|
|
1369
|
+
eventName: "plan_upgraded",
|
|
1370
|
+
propertyNames: [
|
|
1371
|
+
"initial_plan",
|
|
1372
|
+
"signup_source",
|
|
1373
|
+
"feature_discovery_rate",
|
|
1374
|
+
"engagement_score",
|
|
1375
|
+
"referral_count",
|
|
1376
|
+
"content_creation"
|
|
1377
|
+
]
|
|
1378
|
+
}
|
|
1379
|
+
];
|
|
1380
|
+
|
|
1381
|
+
// ../../packages/infrastructure/dist/prometheus/index.js
|
|
1382
|
+
var prometheus_exports = {};
|
|
1383
|
+
__export(prometheus_exports, {
|
|
1384
|
+
client: () => client,
|
|
1385
|
+
dbConnections: () => dbConnections,
|
|
1386
|
+
dbQueryDurationSeconds: () => dbQueryDurationSeconds,
|
|
1387
|
+
getContentType: () => getContentType,
|
|
1388
|
+
getMetrics: () => getMetrics,
|
|
1389
|
+
healthCheckDurationSeconds: () => healthCheckDurationSeconds,
|
|
1390
|
+
healthCheckFailuresTotal: () => healthCheckFailuresTotal,
|
|
1391
|
+
healthCheckLastSuccessTimestamp: () => healthCheckLastSuccessTimestamp,
|
|
1392
|
+
healthCheckStatus: () => healthCheckStatus,
|
|
1393
|
+
httpRequestDurationSeconds: () => httpRequestDurationSeconds,
|
|
1394
|
+
httpRequestsTotal: () => httpRequestsTotal,
|
|
1395
|
+
mcpActiveSessions: () => mcpActiveSessions,
|
|
1396
|
+
mcpProxyErrorsTotal: () => mcpProxyErrorsTotal,
|
|
1397
|
+
readinessProbeLastSuccessTimestamp: () => readinessProbeLastSuccessTimestamp,
|
|
1398
|
+
readinessProbeStatus: () => readinessProbeStatus,
|
|
1399
|
+
recordHealthCheck: () => recordHealthCheck,
|
|
1400
|
+
recordReadinessProbe: () => recordReadinessProbe,
|
|
1401
|
+
recordStartupComplete: () => recordStartupComplete,
|
|
1402
|
+
registry: () => registry,
|
|
1403
|
+
sseConnectionsTotal: () => sseConnectionsTotal,
|
|
1404
|
+
startupProbeDurationSeconds: () => startupProbeDurationSeconds,
|
|
1405
|
+
startupProbeFailuresTotal: () => startupProbeFailuresTotal
|
|
1406
|
+
});
|
|
1407
|
+
var registry = new client.Registry();
|
|
1408
|
+
var defaultMetrics = client.collectDefaultMetrics;
|
|
1409
|
+
defaultMetrics({
|
|
1410
|
+
register: registry
|
|
1411
|
+
});
|
|
1412
|
+
var healthCheckStatus = new client.Gauge({
|
|
1413
|
+
name: "health_check_status",
|
|
1414
|
+
help: "Current health check status (1=healthy, 0=unhealthy)",
|
|
1415
|
+
labelNames: [
|
|
1416
|
+
"service",
|
|
1417
|
+
"check"
|
|
1418
|
+
],
|
|
1419
|
+
registers: [
|
|
1420
|
+
registry
|
|
1421
|
+
]
|
|
1422
|
+
});
|
|
1423
|
+
var healthCheckFailuresTotal = new client.Counter({
|
|
1424
|
+
name: "health_check_failures_total",
|
|
1425
|
+
help: "Total number of health check failures",
|
|
1426
|
+
labelNames: [
|
|
1427
|
+
"service",
|
|
1428
|
+
"check",
|
|
1429
|
+
"reason"
|
|
1430
|
+
],
|
|
1431
|
+
registers: [
|
|
1432
|
+
registry
|
|
1433
|
+
]
|
|
1434
|
+
});
|
|
1435
|
+
var healthCheckDurationSeconds = new client.Histogram({
|
|
1436
|
+
name: "health_check_duration_seconds",
|
|
1437
|
+
help: "Duration of health check execution in seconds",
|
|
1438
|
+
labelNames: [
|
|
1439
|
+
"service",
|
|
1440
|
+
"check"
|
|
1441
|
+
],
|
|
1442
|
+
buckets: [
|
|
1443
|
+
1e-3,
|
|
1444
|
+
5e-3,
|
|
1445
|
+
0.01,
|
|
1446
|
+
0.025,
|
|
1447
|
+
0.05,
|
|
1448
|
+
0.1,
|
|
1449
|
+
0.25,
|
|
1450
|
+
0.5,
|
|
1451
|
+
1,
|
|
1452
|
+
2.5,
|
|
1453
|
+
5,
|
|
1454
|
+
10
|
|
1455
|
+
],
|
|
1456
|
+
registers: [
|
|
1457
|
+
registry
|
|
1458
|
+
]
|
|
1459
|
+
});
|
|
1460
|
+
var healthCheckLastSuccessTimestamp = new client.Gauge({
|
|
1461
|
+
name: "health_check_last_success_timestamp_seconds",
|
|
1462
|
+
help: "Unix timestamp of the last successful health check",
|
|
1463
|
+
labelNames: [
|
|
1464
|
+
"service",
|
|
1465
|
+
"check"
|
|
1466
|
+
],
|
|
1467
|
+
registers: [
|
|
1468
|
+
registry
|
|
1469
|
+
]
|
|
1470
|
+
});
|
|
1471
|
+
var startupProbeFailuresTotal = new client.Counter({
|
|
1472
|
+
name: "startup_probe_failures_total",
|
|
1473
|
+
help: "Total number of startup probe failures",
|
|
1474
|
+
labelNames: [
|
|
1475
|
+
"service"
|
|
1476
|
+
],
|
|
1477
|
+
registers: [
|
|
1478
|
+
registry
|
|
1479
|
+
]
|
|
1480
|
+
});
|
|
1481
|
+
var startupProbeDurationSeconds = new client.Gauge({
|
|
1482
|
+
name: "startup_probe_duration_seconds",
|
|
1483
|
+
help: "Time taken for service startup in seconds",
|
|
1484
|
+
labelNames: [
|
|
1485
|
+
"service"
|
|
1486
|
+
],
|
|
1487
|
+
registers: [
|
|
1488
|
+
registry
|
|
1489
|
+
]
|
|
1490
|
+
});
|
|
1491
|
+
var readinessProbeStatus = new client.Gauge({
|
|
1492
|
+
name: "readiness_probe_status",
|
|
1493
|
+
help: "Current readiness probe status (1=ready, 0=not_ready)",
|
|
1494
|
+
labelNames: [
|
|
1495
|
+
"service"
|
|
1496
|
+
],
|
|
1497
|
+
registers: [
|
|
1498
|
+
registry
|
|
1499
|
+
]
|
|
1500
|
+
});
|
|
1501
|
+
var readinessProbeLastSuccessTimestamp = new client.Gauge({
|
|
1502
|
+
name: "readiness_probe_last_success_timestamp_seconds",
|
|
1503
|
+
help: "Unix timestamp of the last successful readiness probe",
|
|
1504
|
+
labelNames: [
|
|
1505
|
+
"service"
|
|
1506
|
+
],
|
|
1507
|
+
registers: [
|
|
1508
|
+
registry
|
|
1509
|
+
]
|
|
1510
|
+
});
|
|
1511
|
+
var httpRequestDurationSeconds = new client.Histogram({
|
|
1512
|
+
name: "http_request_duration_seconds",
|
|
1513
|
+
help: "Duration of HTTP requests in seconds",
|
|
1514
|
+
labelNames: [
|
|
1515
|
+
"method",
|
|
1516
|
+
"path",
|
|
1517
|
+
"status"
|
|
1518
|
+
],
|
|
1519
|
+
buckets: [
|
|
1520
|
+
1e-3,
|
|
1521
|
+
5e-3,
|
|
1522
|
+
0.01,
|
|
1523
|
+
0.025,
|
|
1524
|
+
0.05,
|
|
1525
|
+
0.1,
|
|
1526
|
+
0.25,
|
|
1527
|
+
0.5,
|
|
1528
|
+
1,
|
|
1529
|
+
2.5,
|
|
1530
|
+
5
|
|
1531
|
+
],
|
|
1532
|
+
registers: [
|
|
1533
|
+
registry
|
|
1534
|
+
]
|
|
1535
|
+
});
|
|
1536
|
+
var httpRequestsTotal = new client.Counter({
|
|
1537
|
+
name: "http_requests_total",
|
|
1538
|
+
help: "Total number of HTTP requests",
|
|
1539
|
+
labelNames: [
|
|
1540
|
+
"method",
|
|
1541
|
+
"path",
|
|
1542
|
+
"status"
|
|
1543
|
+
],
|
|
1544
|
+
registers: [
|
|
1545
|
+
registry
|
|
1546
|
+
]
|
|
1547
|
+
});
|
|
1548
|
+
var dbConnections = new client.Gauge({
|
|
1549
|
+
name: "db_connections",
|
|
1550
|
+
help: "Current number of database connections",
|
|
1551
|
+
labelNames: [
|
|
1552
|
+
"pool",
|
|
1553
|
+
"state"
|
|
1554
|
+
],
|
|
1555
|
+
registers: [
|
|
1556
|
+
registry
|
|
1557
|
+
]
|
|
1558
|
+
});
|
|
1559
|
+
var dbQueryDurationSeconds = new client.Histogram({
|
|
1560
|
+
name: "db_query_duration_seconds",
|
|
1561
|
+
help: "Duration of database queries in seconds",
|
|
1562
|
+
labelNames: [
|
|
1563
|
+
"operation",
|
|
1564
|
+
"table"
|
|
1565
|
+
],
|
|
1566
|
+
buckets: [
|
|
1567
|
+
1e-3,
|
|
1568
|
+
5e-3,
|
|
1569
|
+
0.01,
|
|
1570
|
+
0.025,
|
|
1571
|
+
0.05,
|
|
1572
|
+
0.1,
|
|
1573
|
+
0.25,
|
|
1574
|
+
0.5,
|
|
1575
|
+
1
|
|
1576
|
+
],
|
|
1577
|
+
registers: [
|
|
1578
|
+
registry
|
|
1579
|
+
]
|
|
1580
|
+
});
|
|
1581
|
+
var mcpActiveSessions = new client.Gauge({
|
|
1582
|
+
name: "mcp_active_sessions",
|
|
1583
|
+
help: "Current number of active MCP sessions",
|
|
1584
|
+
registers: [
|
|
1585
|
+
registry
|
|
1586
|
+
]
|
|
1587
|
+
});
|
|
1588
|
+
var mcpProxyErrorsTotal = new client.Counter({
|
|
1589
|
+
name: "mcp_proxy_errors_total",
|
|
1590
|
+
help: "Total number of MCP proxy errors",
|
|
1591
|
+
labelNames: [
|
|
1592
|
+
"endpoint",
|
|
1593
|
+
"error_type"
|
|
1594
|
+
],
|
|
1595
|
+
registers: [
|
|
1596
|
+
registry
|
|
1597
|
+
]
|
|
1598
|
+
});
|
|
1599
|
+
var sseConnectionsTotal = new client.Gauge({
|
|
1600
|
+
name: "sse_connections_total",
|
|
1601
|
+
help: "Current number of SSE connections",
|
|
1602
|
+
labelNames: [
|
|
1603
|
+
"service"
|
|
1604
|
+
],
|
|
1605
|
+
registers: [
|
|
1606
|
+
registry
|
|
1607
|
+
]
|
|
1608
|
+
});
|
|
1609
|
+
function recordHealthCheck(service, check, status, durationMs) {
|
|
1610
|
+
const isHealthy = status === "healthy" ? 1 : 0;
|
|
1611
|
+
healthCheckStatus.set({
|
|
1612
|
+
service,
|
|
1613
|
+
check
|
|
1614
|
+
}, isHealthy);
|
|
1615
|
+
healthCheckDurationSeconds.observe({
|
|
1616
|
+
service,
|
|
1617
|
+
check
|
|
1618
|
+
}, durationMs / 1e3);
|
|
1619
|
+
if (status === "healthy") {
|
|
1620
|
+
healthCheckLastSuccessTimestamp.set({
|
|
1621
|
+
service,
|
|
1622
|
+
check
|
|
1623
|
+
}, Date.now() / 1e3);
|
|
1624
|
+
}
|
|
1625
|
+
if (status === "unhealthy") {
|
|
1626
|
+
healthCheckFailuresTotal.inc({
|
|
1627
|
+
service,
|
|
1628
|
+
check,
|
|
1629
|
+
reason: "check_failed"
|
|
1630
|
+
});
|
|
1631
|
+
}
|
|
1632
|
+
}
|
|
1633
|
+
__name(recordHealthCheck, "recordHealthCheck");
|
|
1634
|
+
function recordReadinessProbe(service, isReady) {
|
|
1635
|
+
readinessProbeStatus.set({
|
|
1636
|
+
service
|
|
1637
|
+
}, isReady ? 1 : 0);
|
|
1638
|
+
if (isReady) {
|
|
1639
|
+
readinessProbeLastSuccessTimestamp.set({
|
|
1640
|
+
service
|
|
1641
|
+
}, Date.now() / 1e3);
|
|
1642
|
+
}
|
|
1643
|
+
}
|
|
1644
|
+
__name(recordReadinessProbe, "recordReadinessProbe");
|
|
1645
|
+
function recordStartupComplete(service, durationSeconds) {
|
|
1646
|
+
startupProbeDurationSeconds.set({
|
|
1647
|
+
service
|
|
1648
|
+
}, durationSeconds);
|
|
1649
|
+
}
|
|
1650
|
+
__name(recordStartupComplete, "recordStartupComplete");
|
|
1651
|
+
async function getMetrics() {
|
|
1652
|
+
return registry.metrics();
|
|
1653
|
+
}
|
|
1654
|
+
__name(getMetrics, "getMetrics");
|
|
1655
|
+
function getContentType() {
|
|
1656
|
+
return registry.contentType;
|
|
1657
|
+
}
|
|
1658
|
+
__name(getContentType, "getContentType");
|
|
1659
|
+
|
|
1660
|
+
// ../../packages/infrastructure/dist/sentry/index.js
|
|
1661
|
+
var Sentry = null;
|
|
1662
|
+
var ProfilingIntegration = null;
|
|
1663
|
+
async function loadSentry() {
|
|
1664
|
+
if (Sentry) {
|
|
1665
|
+
return {
|
|
1666
|
+
Sentry,
|
|
1667
|
+
ProfilingIntegration
|
|
1668
|
+
};
|
|
1669
|
+
}
|
|
1670
|
+
if (process.env.DISABLE_SENTRY === "true") {
|
|
1671
|
+
return null;
|
|
1672
|
+
}
|
|
1673
|
+
try {
|
|
1674
|
+
Sentry = await import('@sentry/node');
|
|
1675
|
+
ProfilingIntegration = await import('@sentry/profiling-node');
|
|
1676
|
+
return {
|
|
1677
|
+
Sentry,
|
|
1678
|
+
ProfilingIntegration
|
|
1679
|
+
};
|
|
1680
|
+
} catch (error) {
|
|
1681
|
+
console.warn("\u26A0\uFE0F Failed to load Sentry:", error instanceof Error ? error.message : String(error));
|
|
1682
|
+
return null;
|
|
1683
|
+
}
|
|
1684
|
+
}
|
|
1685
|
+
__name(loadSentry, "loadSentry");
|
|
1686
|
+
async function initSentry(options) {
|
|
1687
|
+
if (process.env.DISABLE_SENTRY === "true" || options?.enabled === false) {
|
|
1688
|
+
console.log("\u2139\uFE0F Sentry is disabled");
|
|
1689
|
+
return;
|
|
1690
|
+
}
|
|
1691
|
+
const dsn = options?.dsn || process.env.SENTRY_DSN;
|
|
1692
|
+
if (!dsn) {
|
|
1693
|
+
console.warn("\u26A0\uFE0F SENTRY_DSN not configured. Error tracking disabled.");
|
|
1694
|
+
return;
|
|
1695
|
+
}
|
|
1696
|
+
const modules = await loadSentry();
|
|
1697
|
+
if (!modules) {
|
|
1698
|
+
return;
|
|
1699
|
+
}
|
|
1700
|
+
const { Sentry: SentryModule, ProfilingIntegration: ProfilingIntegration2 } = modules;
|
|
1701
|
+
SentryModule.init({
|
|
1702
|
+
dsn,
|
|
1703
|
+
environment: options?.environment || process.env.NODE_ENV || "development",
|
|
1704
|
+
release: options?.release || process.env.GIT_SHA || process.env.RELEASE || void 0,
|
|
1705
|
+
tracesSampleRate: options?.tracesSampleRate ?? (process.env.NODE_ENV === "production" ? 0.1 : 1),
|
|
1706
|
+
profilesSampleRate: options?.profilesSampleRate ?? 0.1,
|
|
1707
|
+
debug: options?.debug || process.env.DEBUG_SENTRY === "true",
|
|
1708
|
+
integrations: [
|
|
1709
|
+
new SentryModule.HttpIntegration({
|
|
1710
|
+
tracing: true,
|
|
1711
|
+
request: true
|
|
1712
|
+
}),
|
|
1713
|
+
...ProfilingIntegration2 ? [
|
|
1714
|
+
ProfilingIntegration2.nodeProfilingIntegration()
|
|
1715
|
+
] : []
|
|
1716
|
+
],
|
|
1717
|
+
// Filter out sensitive data
|
|
1718
|
+
beforeSend: /* @__PURE__ */ __name((event) => {
|
|
1719
|
+
if (event?.exception?.values?.[0]?.value?.includes?.("404") || event?.request?.url?.includes?.("/favicon.ico")) {
|
|
1720
|
+
return null;
|
|
1721
|
+
}
|
|
1722
|
+
if (event?.request?.headers) {
|
|
1723
|
+
delete event.request.headers.authorization;
|
|
1724
|
+
delete event.request.headers.cookie;
|
|
1725
|
+
}
|
|
1726
|
+
return event;
|
|
1727
|
+
}, "beforeSend")
|
|
1728
|
+
});
|
|
1729
|
+
console.log("\u2705 Sentry initialized for error tracking");
|
|
1730
|
+
}
|
|
1731
|
+
__name(initSentry, "initSentry");
|
|
1732
|
+
function createSentryMiddleware() {
|
|
1733
|
+
if (!Sentry) {
|
|
1734
|
+
return {
|
|
1735
|
+
requestHandler: /* @__PURE__ */ __name((_c, next) => next(), "requestHandler"),
|
|
1736
|
+
errorHandler: /* @__PURE__ */ __name((_c, next) => next(), "errorHandler")
|
|
1737
|
+
};
|
|
1738
|
+
}
|
|
1739
|
+
return {
|
|
1740
|
+
requestHandler: Sentry.Handlers?.requestHandler?.() || ((_c, next) => next()),
|
|
1741
|
+
errorHandler: Sentry.Handlers?.errorHandler?.() || ((_c, next) => next())
|
|
1742
|
+
};
|
|
1743
|
+
}
|
|
1744
|
+
__name(createSentryMiddleware, "createSentryMiddleware");
|
|
1745
|
+
function captureError(error, context2) {
|
|
1746
|
+
if (process.env.DISABLE_SENTRY === "true" || !Sentry) {
|
|
1747
|
+
return;
|
|
1748
|
+
}
|
|
1749
|
+
Sentry.withScope((scope) => {
|
|
1750
|
+
if (context2) {
|
|
1751
|
+
if (context2.userId) {
|
|
1752
|
+
scope.setUser({
|
|
1753
|
+
id: context2.userId
|
|
1754
|
+
});
|
|
1755
|
+
}
|
|
1756
|
+
if (context2.organizationId) {
|
|
1757
|
+
scope.setTag("organization_id", context2.organizationId);
|
|
1758
|
+
}
|
|
1759
|
+
if (context2.tags) {
|
|
1760
|
+
Object.entries(context2.tags).forEach(([key, value]) => {
|
|
1761
|
+
scope.setTag(key, value);
|
|
1762
|
+
});
|
|
1763
|
+
}
|
|
1764
|
+
if (context2.extra) {
|
|
1765
|
+
scope.setContext("extra", context2.extra);
|
|
1766
|
+
}
|
|
1767
|
+
}
|
|
1768
|
+
if (Sentry) {
|
|
1769
|
+
Sentry.captureException(typeof error === "string" ? new Error(error) : error);
|
|
1770
|
+
}
|
|
1771
|
+
});
|
|
1772
|
+
}
|
|
1773
|
+
__name(captureError, "captureError");
|
|
1774
|
+
function captureMessage(message, level = "info", context2) {
|
|
1775
|
+
if (process.env.DISABLE_SENTRY === "true" || !Sentry) {
|
|
1776
|
+
return;
|
|
1777
|
+
}
|
|
1778
|
+
Sentry.captureMessage(message, level);
|
|
1779
|
+
if (context2) {
|
|
1780
|
+
Sentry.withScope((scope) => {
|
|
1781
|
+
if (context2.userId) {
|
|
1782
|
+
scope.setUser({
|
|
1783
|
+
id: context2.userId
|
|
1784
|
+
});
|
|
1785
|
+
}
|
|
1786
|
+
if (context2.tags) {
|
|
1787
|
+
Object.entries(context2.tags).forEach(([key, value]) => {
|
|
1788
|
+
scope.setTag(key, value);
|
|
1789
|
+
});
|
|
1790
|
+
}
|
|
1791
|
+
if (context2.extra) {
|
|
1792
|
+
scope.setContext("extra", context2.extra);
|
|
1793
|
+
}
|
|
1794
|
+
});
|
|
1795
|
+
}
|
|
1796
|
+
}
|
|
1797
|
+
__name(captureMessage, "captureMessage");
|
|
1798
|
+
function setSentryUser(userId, userInfo) {
|
|
1799
|
+
if (process.env.DISABLE_SENTRY === "true" || !Sentry) {
|
|
1800
|
+
return;
|
|
1801
|
+
}
|
|
1802
|
+
Sentry.setUser({
|
|
1803
|
+
id: userId,
|
|
1804
|
+
email: userInfo?.email,
|
|
1805
|
+
username: userInfo?.username,
|
|
1806
|
+
organization_id: userInfo?.organizationId
|
|
1807
|
+
});
|
|
1808
|
+
}
|
|
1809
|
+
__name(setSentryUser, "setSentryUser");
|
|
1810
|
+
function clearSentryUser() {
|
|
1811
|
+
if (process.env.DISABLE_SENTRY === "true" || !Sentry) {
|
|
1812
|
+
return;
|
|
1813
|
+
}
|
|
1814
|
+
Sentry.setUser(null);
|
|
1815
|
+
}
|
|
1816
|
+
__name(clearSentryUser, "clearSentryUser");
|
|
1817
|
+
function addSentryBreadcrumb(message, data, level = "info") {
|
|
1818
|
+
if (process.env.DISABLE_SENTRY === "true" || !Sentry) {
|
|
1819
|
+
return;
|
|
1820
|
+
}
|
|
1821
|
+
Sentry.addBreadcrumb({
|
|
1822
|
+
message,
|
|
1823
|
+
level,
|
|
1824
|
+
data,
|
|
1825
|
+
timestamp: Date.now() / 1e3
|
|
1826
|
+
});
|
|
1827
|
+
}
|
|
1828
|
+
__name(addSentryBreadcrumb, "addSentryBreadcrumb");
|
|
1829
|
+
function startSentryTransaction(name, op) {
|
|
1830
|
+
if (process.env.DISABLE_SENTRY === "true") {
|
|
1831
|
+
return null;
|
|
1832
|
+
}
|
|
1833
|
+
return Sentry.startTransaction?.({
|
|
1834
|
+
name,
|
|
1835
|
+
op: op || "operation"
|
|
1836
|
+
}) || null;
|
|
1837
|
+
}
|
|
1838
|
+
__name(startSentryTransaction, "startSentryTransaction");
|
|
1839
|
+
async function flushSentry(timeout = 2e3) {
|
|
1840
|
+
if (process.env.DISABLE_SENTRY === "true" || !Sentry) {
|
|
1841
|
+
return true;
|
|
1842
|
+
}
|
|
1843
|
+
return await Sentry.close(timeout);
|
|
1844
|
+
}
|
|
1845
|
+
__name(flushSentry, "flushSentry");
|
|
1846
|
+
|
|
1847
|
+
// ../../packages/infrastructure/dist/tracing/error-budget.js
|
|
1848
|
+
var ERROR_BUDGET = 0.01;
|
|
1849
|
+
var ALERT_THRESHOLD = 5e-3;
|
|
1850
|
+
var errorMetrics = {
|
|
1851
|
+
totalRequests: 0,
|
|
1852
|
+
errorCount: 0,
|
|
1853
|
+
lastAlertTime: 0
|
|
1854
|
+
};
|
|
1855
|
+
function recordSuccess() {
|
|
1856
|
+
errorMetrics.totalRequests++;
|
|
1857
|
+
}
|
|
1858
|
+
__name(recordSuccess, "recordSuccess");
|
|
1859
|
+
function recordError() {
|
|
1860
|
+
errorMetrics.totalRequests++;
|
|
1861
|
+
errorMetrics.errorCount++;
|
|
1862
|
+
}
|
|
1863
|
+
__name(recordError, "recordError");
|
|
1864
|
+
function getErrorRate() {
|
|
1865
|
+
if (errorMetrics.totalRequests === 0) {
|
|
1866
|
+
return 0;
|
|
1867
|
+
}
|
|
1868
|
+
return errorMetrics.errorCount / errorMetrics.totalRequests;
|
|
1869
|
+
}
|
|
1870
|
+
__name(getErrorRate, "getErrorRate");
|
|
1871
|
+
async function checkErrorBudget() {
|
|
1872
|
+
const errorRate = getErrorRate();
|
|
1873
|
+
if (errorRate > ALERT_THRESHOLD && Date.now() - errorMetrics.lastAlertTime > 6e4) {
|
|
1874
|
+
logger.warn({
|
|
1875
|
+
errorRate: `${(errorRate * 100).toFixed(2)}%`,
|
|
1876
|
+
threshold: `${(ALERT_THRESHOLD * 100).toFixed(2)}%`,
|
|
1877
|
+
errorCount: errorMetrics.errorCount,
|
|
1878
|
+
totalRequests: errorMetrics.totalRequests
|
|
1879
|
+
}, "Error rate approaching budget threshold");
|
|
1880
|
+
errorMetrics.lastAlertTime = Date.now();
|
|
1881
|
+
}
|
|
1882
|
+
if (errorRate > ERROR_BUDGET) {
|
|
1883
|
+
logger.error({
|
|
1884
|
+
errorRate: `${(errorRate * 100).toFixed(2)}%`,
|
|
1885
|
+
budget: `${(ERROR_BUDGET * 100).toFixed(2)}%`,
|
|
1886
|
+
errorCount: errorMetrics.errorCount,
|
|
1887
|
+
totalRequests: errorMetrics.totalRequests,
|
|
1888
|
+
recommendation: "Investigate root cause immediately and consider rolling back"
|
|
1889
|
+
}, "\u{1F6A8} Error budget exceeded!");
|
|
1890
|
+
await sendAlert({
|
|
1891
|
+
channel: "#alerts",
|
|
1892
|
+
message: `\u{1F6A8} Error budget exceeded! Current error rate: ${(errorRate * 100).toFixed(2)}% (Budget: ${(ERROR_BUDGET * 100).toFixed(2)}%)`
|
|
1893
|
+
});
|
|
1894
|
+
}
|
|
1895
|
+
}
|
|
1896
|
+
__name(checkErrorBudget, "checkErrorBudget");
|
|
1897
|
+
async function sendAlert(alert) {
|
|
1898
|
+
logger.info(alert, "Alert sent");
|
|
1899
|
+
}
|
|
1900
|
+
__name(sendAlert, "sendAlert");
|
|
1901
|
+
function resetMetrics() {
|
|
1902
|
+
errorMetrics.totalRequests = 0;
|
|
1903
|
+
errorMetrics.errorCount = 0;
|
|
1904
|
+
errorMetrics.lastAlertTime = 0;
|
|
1905
|
+
}
|
|
1906
|
+
__name(resetMetrics, "resetMetrics");
|
|
1907
|
+
function getMetrics2() {
|
|
1908
|
+
return {
|
|
1909
|
+
...errorMetrics,
|
|
1910
|
+
errorRate: getErrorRate()
|
|
1911
|
+
};
|
|
1912
|
+
}
|
|
1913
|
+
__name(getMetrics2, "getMetrics");
|
|
1914
|
+
var OTelSpanAdapter = class OTelSpanAdapter2 {
|
|
1915
|
+
static {
|
|
1916
|
+
__name(this, "OTelSpanAdapter");
|
|
1917
|
+
}
|
|
1918
|
+
otelSpan;
|
|
1919
|
+
constructor(otelSpan) {
|
|
1920
|
+
this.otelSpan = otelSpan;
|
|
1921
|
+
}
|
|
1922
|
+
setAttribute(key, value) {
|
|
1923
|
+
this.otelSpan.setAttribute(key, value);
|
|
1924
|
+
}
|
|
1925
|
+
setAttributes(attributes) {
|
|
1926
|
+
this.otelSpan.setAttributes(attributes);
|
|
1927
|
+
}
|
|
1928
|
+
addEvent(name, attributes) {
|
|
1929
|
+
this.otelSpan.addEvent(name, attributes);
|
|
1930
|
+
}
|
|
1931
|
+
setStatus(status) {
|
|
1932
|
+
this.otelSpan.setStatus({
|
|
1933
|
+
code: status.code,
|
|
1934
|
+
message: status.message
|
|
1935
|
+
});
|
|
1936
|
+
}
|
|
1937
|
+
recordException(error) {
|
|
1938
|
+
this.otelSpan.recordException(error);
|
|
1939
|
+
}
|
|
1940
|
+
end() {
|
|
1941
|
+
this.otelSpan.end();
|
|
1942
|
+
}
|
|
1943
|
+
isRecording() {
|
|
1944
|
+
return this.otelSpan.isRecording();
|
|
1945
|
+
}
|
|
1946
|
+
};
|
|
1947
|
+
var OTelInstrumentationProvider = class {
|
|
1948
|
+
static {
|
|
1949
|
+
__name(this, "OTelInstrumentationProvider");
|
|
1950
|
+
}
|
|
1951
|
+
tracer;
|
|
1952
|
+
provider;
|
|
1953
|
+
constructor(config) {
|
|
1954
|
+
const resource = resourceFromAttributes({
|
|
1955
|
+
[ATTR_SERVICE_NAME]: config.serviceName,
|
|
1956
|
+
[ATTR_SERVICE_VERSION]: config.serviceVersion || "unknown",
|
|
1957
|
+
"deployment.environment": config.environment || "development",
|
|
1958
|
+
"service.instance.id": process.pid.toString(),
|
|
1959
|
+
"host.name": process.env.HOSTNAME || "unknown",
|
|
1960
|
+
"process.pid": process.pid
|
|
1961
|
+
});
|
|
1962
|
+
const spanProcessors = [];
|
|
1963
|
+
if (config.collectorUrl) {
|
|
1964
|
+
const otlpExporter = new OTLPTraceExporter({
|
|
1965
|
+
url: config.collectorUrl
|
|
1966
|
+
});
|
|
1967
|
+
spanProcessors.push(new BatchSpanProcessor(otlpExporter));
|
|
1968
|
+
}
|
|
1969
|
+
if (config.enableConsole) {
|
|
1970
|
+
const consoleExporter = new ConsoleSpanExporter();
|
|
1971
|
+
spanProcessors.push(new BatchSpanProcessor(consoleExporter));
|
|
1972
|
+
}
|
|
1973
|
+
const samplingRate = config.sampleRate ?? 1;
|
|
1974
|
+
const sampler = new TraceIdRatioBasedSampler(samplingRate);
|
|
1975
|
+
this.provider = new NodeTracerProvider({
|
|
1976
|
+
resource,
|
|
1977
|
+
sampler,
|
|
1978
|
+
spanProcessors
|
|
1979
|
+
});
|
|
1980
|
+
this.provider.register();
|
|
1981
|
+
const pinoInstrumentation = new PinoInstrumentation({
|
|
1982
|
+
// Keep default log keys: trace_id, span_id, trace_flags
|
|
1983
|
+
// These match standard OTel semantic conventions
|
|
1984
|
+
disableLogSending: true,
|
|
1985
|
+
disableLogCorrelation: false
|
|
1986
|
+
});
|
|
1987
|
+
const pgInstrumentation = new PgInstrumentation({
|
|
1988
|
+
// Add database query attributes to spans
|
|
1989
|
+
requireParentSpan: false,
|
|
1990
|
+
enhancedDatabaseReporting: true
|
|
1991
|
+
});
|
|
1992
|
+
try {
|
|
1993
|
+
pinoInstrumentation.enable();
|
|
1994
|
+
pgInstrumentation.enable();
|
|
1995
|
+
} catch {
|
|
1996
|
+
}
|
|
1997
|
+
this.tracer = trace.getTracer(config.serviceName, config.serviceVersion);
|
|
1998
|
+
}
|
|
1999
|
+
startSpan(name, options) {
|
|
2000
|
+
const otelSpan = this.tracer.startSpan(name, {
|
|
2001
|
+
kind: options?.kind,
|
|
2002
|
+
attributes: options?.attributes,
|
|
2003
|
+
startTime: options?.startTime
|
|
2004
|
+
});
|
|
2005
|
+
return new OTelSpanAdapter(otelSpan);
|
|
2006
|
+
}
|
|
2007
|
+
async withSpan(name, fn, options) {
|
|
2008
|
+
const parentCtx = options?.parent ? options.parent : context.active();
|
|
2009
|
+
return await this.tracer.startActiveSpan(name, {
|
|
2010
|
+
kind: options?.kind,
|
|
2011
|
+
attributes: options?.attributes,
|
|
2012
|
+
startTime: options?.startTime
|
|
2013
|
+
}, parentCtx, async (otelSpan) => {
|
|
2014
|
+
const span = new OTelSpanAdapter(otelSpan);
|
|
2015
|
+
try {
|
|
2016
|
+
const result = await fn(span);
|
|
2017
|
+
otelSpan.setStatus({
|
|
2018
|
+
code: SpanStatusCode.OK
|
|
2019
|
+
});
|
|
2020
|
+
return result;
|
|
2021
|
+
} catch (error) {
|
|
2022
|
+
otelSpan.recordException(error);
|
|
2023
|
+
otelSpan.setStatus({
|
|
2024
|
+
code: SpanStatusCode.ERROR,
|
|
2025
|
+
message: error instanceof Error ? error.message : String(error)
|
|
2026
|
+
});
|
|
2027
|
+
throw error;
|
|
2028
|
+
} finally {
|
|
2029
|
+
otelSpan.end();
|
|
2030
|
+
}
|
|
2031
|
+
});
|
|
2032
|
+
}
|
|
2033
|
+
injectContext(carrier) {
|
|
2034
|
+
propagation.inject(context.active(), carrier);
|
|
2035
|
+
}
|
|
2036
|
+
extractContext(carrier) {
|
|
2037
|
+
const extractedContext = propagation.extract(ROOT_CONTEXT, carrier);
|
|
2038
|
+
const span = trace.getSpan(extractedContext);
|
|
2039
|
+
if (span?.spanContext().traceId) {
|
|
2040
|
+
return extractedContext;
|
|
2041
|
+
}
|
|
2042
|
+
return null;
|
|
2043
|
+
}
|
|
2044
|
+
recordMetric(_name, _value, _attributes) {
|
|
2045
|
+
}
|
|
2046
|
+
recordEvent(_name, _attributes) {
|
|
2047
|
+
}
|
|
2048
|
+
async shutdown() {
|
|
2049
|
+
await this.provider.shutdown();
|
|
2050
|
+
}
|
|
2051
|
+
};
|
|
2052
|
+
|
|
2053
|
+
// ../../packages/infrastructure/dist/tracing/telemetry-client.js
|
|
2054
|
+
var TelemetryClient = class {
|
|
2055
|
+
static {
|
|
2056
|
+
__name(this, "TelemetryClient");
|
|
2057
|
+
}
|
|
2058
|
+
environment;
|
|
2059
|
+
flags = /* @__PURE__ */ new Map();
|
|
2060
|
+
eventQueue = [];
|
|
2061
|
+
flushInterval = 5e3;
|
|
2062
|
+
maxQueueSize = 100;
|
|
2063
|
+
rateLimitWindow = 6e4;
|
|
2064
|
+
eventCounts = /* @__PURE__ */ new Map();
|
|
2065
|
+
lastRateLimitReset = Date.now();
|
|
2066
|
+
proxyUrl;
|
|
2067
|
+
offlineMode = false;
|
|
2068
|
+
constructor(_apiKey, proxyHost, environment) {
|
|
2069
|
+
this.environment = environment;
|
|
2070
|
+
this.proxyUrl = `${proxyHost}/api/telemetry/events`;
|
|
2071
|
+
this.generateAnonymousId();
|
|
2072
|
+
setInterval(() => this.flush(), this.flushInterval);
|
|
2073
|
+
}
|
|
2074
|
+
async initialize() {
|
|
2075
|
+
console.warn("Feature flags not supported in proxy mode");
|
|
2076
|
+
}
|
|
2077
|
+
/**
|
|
2078
|
+
* Set offline mode
|
|
2079
|
+
* @param enabled Whether offline mode is enabled
|
|
2080
|
+
*/
|
|
2081
|
+
setOfflineMode(enabled) {
|
|
2082
|
+
this.offlineMode = enabled;
|
|
2083
|
+
}
|
|
2084
|
+
/**
|
|
2085
|
+
* Check if offline mode is enabled
|
|
2086
|
+
* @returns Whether offline mode is enabled
|
|
2087
|
+
*/
|
|
2088
|
+
isOfflineMode() {
|
|
2089
|
+
return this.offlineMode;
|
|
2090
|
+
}
|
|
2091
|
+
isEnabled(flag) {
|
|
2092
|
+
const value = this.flags.get(flag) ?? FEATURE_FLAGS[flag];
|
|
2093
|
+
return Boolean(value);
|
|
2094
|
+
}
|
|
2095
|
+
async reloadFlags() {
|
|
2096
|
+
console.warn("Feature flags not supported in proxy mode");
|
|
2097
|
+
}
|
|
2098
|
+
/**
|
|
2099
|
+
* Track a telemetry event with strict typing and validation
|
|
2100
|
+
* @param event The event name (must be from the whitelist)
|
|
2101
|
+
* @param properties The event properties (validated at runtime)
|
|
2102
|
+
*/
|
|
2103
|
+
trackEvent(event) {
|
|
2104
|
+
if (!validateTelemetryEvent(event)) {
|
|
2105
|
+
console.warn("Invalid telemetry event, skipping:", event);
|
|
2106
|
+
return;
|
|
2107
|
+
}
|
|
2108
|
+
if (this.offlineMode) {
|
|
2109
|
+
return;
|
|
2110
|
+
}
|
|
2111
|
+
const featureManager = FeatureManager.getInstance();
|
|
2112
|
+
if (!featureManager.isEnabled("telemetry.detailed_events")) {
|
|
2113
|
+
if (![
|
|
2114
|
+
"checkpoint.created",
|
|
2115
|
+
"risk.high",
|
|
2116
|
+
"error"
|
|
2117
|
+
].includes(event.event)) {
|
|
2118
|
+
return;
|
|
2119
|
+
}
|
|
2120
|
+
}
|
|
2121
|
+
const samplingRate = featureManager.getValue("telemetry.sampling_rate") ?? 1;
|
|
2122
|
+
if (Math.random() > samplingRate) {
|
|
2123
|
+
return;
|
|
2124
|
+
}
|
|
2125
|
+
if (this.isRateLimited(event.event)) {
|
|
2126
|
+
return;
|
|
2127
|
+
}
|
|
2128
|
+
this.eventQueue.push({
|
|
2129
|
+
event: event.event,
|
|
2130
|
+
properties: {
|
|
2131
|
+
...this.sanitizeProperties(event.properties || {}),
|
|
2132
|
+
environment: this.environment,
|
|
2133
|
+
timestamp: event.timestamp
|
|
2134
|
+
},
|
|
2135
|
+
timestamp: event.timestamp
|
|
2136
|
+
});
|
|
2137
|
+
if (this.eventQueue.length >= this.maxQueueSize) {
|
|
2138
|
+
this.flush();
|
|
2139
|
+
}
|
|
2140
|
+
}
|
|
2141
|
+
/**
|
|
2142
|
+
* Track a telemetry event with string-based event name (legacy compatibility)
|
|
2143
|
+
* @param event The event name
|
|
2144
|
+
* @param properties The event properties
|
|
2145
|
+
*/
|
|
2146
|
+
track(event, properties) {
|
|
2147
|
+
const typedEvent = {
|
|
2148
|
+
event,
|
|
2149
|
+
properties,
|
|
2150
|
+
timestamp: Date.now()
|
|
2151
|
+
};
|
|
2152
|
+
this.trackEvent(typedEvent);
|
|
2153
|
+
}
|
|
2154
|
+
isRateLimited(event) {
|
|
2155
|
+
const now = Date.now();
|
|
2156
|
+
if (now - this.lastRateLimitReset > this.rateLimitWindow) {
|
|
2157
|
+
this.eventCounts.clear();
|
|
2158
|
+
this.lastRateLimitReset = now;
|
|
2159
|
+
}
|
|
2160
|
+
const count = this.eventCounts.get(event) || 0;
|
|
2161
|
+
const maxEventsPerWindow = 10;
|
|
2162
|
+
if (count >= maxEventsPerWindow) {
|
|
2163
|
+
return true;
|
|
2164
|
+
}
|
|
2165
|
+
this.eventCounts.set(event, count + 1);
|
|
2166
|
+
return false;
|
|
2167
|
+
}
|
|
2168
|
+
/**
|
|
2169
|
+
* Sanitize properties to remove PII before sending
|
|
2170
|
+
*/
|
|
2171
|
+
sanitizeProperties(properties) {
|
|
2172
|
+
const sanitized = {};
|
|
2173
|
+
if (!properties) {
|
|
2174
|
+
return sanitized;
|
|
2175
|
+
}
|
|
2176
|
+
const allowedProps = [
|
|
2177
|
+
"version",
|
|
2178
|
+
"platform",
|
|
2179
|
+
"duration",
|
|
2180
|
+
"success",
|
|
2181
|
+
"filesCount",
|
|
2182
|
+
"method",
|
|
2183
|
+
"trigger",
|
|
2184
|
+
"feature",
|
|
2185
|
+
"viewId",
|
|
2186
|
+
"command"
|
|
2187
|
+
];
|
|
2188
|
+
for (const key of allowedProps) {
|
|
2189
|
+
if (key in properties) {
|
|
2190
|
+
sanitized[key] = properties[key];
|
|
2191
|
+
}
|
|
2192
|
+
}
|
|
2193
|
+
return sanitized;
|
|
2194
|
+
}
|
|
2195
|
+
generateAnonymousId() {
|
|
2196
|
+
return `${this.environment}_${Math.random().toString(36).substr(2, 9)}`;
|
|
2197
|
+
}
|
|
2198
|
+
/**
|
|
2199
|
+
* Get current package version
|
|
2200
|
+
* @returns The current version string
|
|
2201
|
+
*/
|
|
2202
|
+
getVersion() {
|
|
2203
|
+
try {
|
|
2204
|
+
const packageJson = require_package();
|
|
2205
|
+
return packageJson.version || "unknown";
|
|
2206
|
+
} catch (_error) {
|
|
2207
|
+
return process.env.SNAPBACK_VERSION || "1.0.0";
|
|
2208
|
+
}
|
|
2209
|
+
}
|
|
2210
|
+
/**
|
|
2211
|
+
* Custom transport layer - routes all events through proxy
|
|
2212
|
+
*/
|
|
2213
|
+
async customTransport(batch) {
|
|
2214
|
+
if (this.offlineMode) {
|
|
2215
|
+
return;
|
|
2216
|
+
}
|
|
2217
|
+
try {
|
|
2218
|
+
const response = await fetch(this.proxyUrl, {
|
|
2219
|
+
method: "POST",
|
|
2220
|
+
headers: {
|
|
2221
|
+
"Content-Type": "application/json",
|
|
2222
|
+
"X-SnapBack-Platform": this.environment,
|
|
2223
|
+
"X-SnapBack-Version": this.getVersion()
|
|
2224
|
+
},
|
|
2225
|
+
body: JSON.stringify({
|
|
2226
|
+
events: batch.map((event) => ({
|
|
2227
|
+
event: event.event,
|
|
2228
|
+
properties: this.sanitizeProperties(event.properties || {}),
|
|
2229
|
+
timestamp: event.timestamp
|
|
2230
|
+
}))
|
|
2231
|
+
})
|
|
2232
|
+
});
|
|
2233
|
+
if (!response.ok) {
|
|
2234
|
+
const error = await response.text();
|
|
2235
|
+
console.warn("Telemetry proxy rejected events", {
|
|
2236
|
+
status: response.status,
|
|
2237
|
+
error
|
|
2238
|
+
});
|
|
2239
|
+
}
|
|
2240
|
+
} catch (error) {
|
|
2241
|
+
console.error("Failed to send telemetry through proxy", error);
|
|
2242
|
+
}
|
|
2243
|
+
}
|
|
2244
|
+
async flush() {
|
|
2245
|
+
if (this.offlineMode) {
|
|
2246
|
+
return;
|
|
2247
|
+
}
|
|
2248
|
+
if (this.eventQueue.length === 0) {
|
|
2249
|
+
return;
|
|
2250
|
+
}
|
|
2251
|
+
const eventsToFlush = [
|
|
2252
|
+
...this.eventQueue
|
|
2253
|
+
];
|
|
2254
|
+
this.eventQueue = [];
|
|
2255
|
+
try {
|
|
2256
|
+
await this.customTransport(eventsToFlush);
|
|
2257
|
+
} catch (error) {
|
|
2258
|
+
console.warn("Failed to send telemetry events:", error);
|
|
2259
|
+
this.eventQueue.unshift(...eventsToFlush);
|
|
2260
|
+
}
|
|
2261
|
+
}
|
|
2262
|
+
};
|
|
2263
|
+
|
|
2264
|
+
export { AnalyticsEvents, CORRELATION_ANALYSES, CORRELATION_COHORTS, KEY_METRIC_ALERTS, OTelInstrumentationProvider, RETENTION_COHORTS, TelemetryClient, addSentryBreadcrumb, captureError, captureMessage, checkDatabaseConnection, checkErrorBudget, checkHttpService, checkRedisConnection, clearSentryUser, createAlert, createCohort, createGracefulShutdown, createHealthCheck, createSentryMiddleware, deleteAlert, deleteCohort, detectSurface, drainAndCloseServer, flushSentry, getAlerts, getAnalyticsEnv, getAnalyticsSuperProperties, getCohort, getCohortMembers, getCohorts, getCorrelationAnalysis, getDeploymentEnv, getEnvironmentInfo, getErrorRate, getMetrics2 as getMetrics, initSentry, isDevelopment, isProduction, neon_exports, performCorrelationAnalysis, preStopDelay, prometheus_exports, recordError, recordSuccess, resetMetrics, setSentryUser, startSentryTransaction, toggleAlert, updateCohort };
|