brakit 0.9.0 → 0.9.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api.d.ts +2 -0
- package/dist/api.js +10 -4
- package/dist/bin/brakit.js +180 -26
- package/dist/dashboard-client.global.js +30 -30
- package/dist/dashboard.html +30 -30
- package/dist/mcp/server.js +1 -1
- package/dist/runtime/index.js +178 -67
- package/package.json +1 -1
package/dist/api.d.ts
CHANGED
|
@@ -348,10 +348,12 @@ declare function detectProject(rootDir: string): Promise<DetectedProject>;
|
|
|
348
348
|
declare class AdapterRegistry {
|
|
349
349
|
private adapters;
|
|
350
350
|
private active;
|
|
351
|
+
private failed;
|
|
351
352
|
register(adapter: BrakitAdapter): void;
|
|
352
353
|
patchAll(emit: (event: TelemetryEvent) => void): void;
|
|
353
354
|
unpatchAll(): void;
|
|
354
355
|
getActive(): readonly BrakitAdapter[];
|
|
356
|
+
getFailed(): readonly string[];
|
|
355
357
|
}
|
|
356
358
|
|
|
357
359
|
interface AnalysisUpdate {
|
package/dist/api.js
CHANGED
|
@@ -54,6 +54,7 @@ var BASELINE_MIN_SESSIONS = 2;
|
|
|
54
54
|
var BASELINE_MIN_REQUESTS_PER_SESSION = 3;
|
|
55
55
|
var ISSUES_FILE = "issues.json";
|
|
56
56
|
var ISSUES_FLUSH_INTERVAL_MS = 1e4;
|
|
57
|
+
var DETAIL_PREVIEW_LENGTH = 120;
|
|
57
58
|
|
|
58
59
|
// src/utils/log.ts
|
|
59
60
|
var PREFIX = "[brakit]";
|
|
@@ -429,6 +430,7 @@ var AdapterRegistry = class {
|
|
|
429
430
|
constructor() {
|
|
430
431
|
this.adapters = [];
|
|
431
432
|
this.active = [];
|
|
433
|
+
this.failed = [];
|
|
432
434
|
}
|
|
433
435
|
register(adapter) {
|
|
434
436
|
this.adapters.push(adapter);
|
|
@@ -441,6 +443,7 @@ var AdapterRegistry = class {
|
|
|
441
443
|
this.active.push(adapter);
|
|
442
444
|
}
|
|
443
445
|
} catch {
|
|
446
|
+
this.failed.push(adapter.name);
|
|
444
447
|
}
|
|
445
448
|
}
|
|
446
449
|
}
|
|
@@ -456,6 +459,9 @@ var AdapterRegistry = class {
|
|
|
456
459
|
getActive() {
|
|
457
460
|
return this.active;
|
|
458
461
|
}
|
|
462
|
+
getFailed() {
|
|
463
|
+
return this.failed;
|
|
464
|
+
}
|
|
459
465
|
};
|
|
460
466
|
|
|
461
467
|
// src/utils/response.ts
|
|
@@ -774,7 +780,7 @@ var stackTraceLeakRule = {
|
|
|
774
780
|
title: "Stack Trace Leaked to Client",
|
|
775
781
|
desc: `${ep} \u2014 response exposes internal stack trace`,
|
|
776
782
|
hint: this.hint,
|
|
777
|
-
detail: firstLine ? `Stack trace: ${firstLine.slice(0,
|
|
783
|
+
detail: firstLine ? `Stack trace: ${firstLine.slice(0, DETAIL_PREVIEW_LENGTH)}` : void 0,
|
|
778
784
|
endpoint: ep,
|
|
779
785
|
count: 1
|
|
780
786
|
}
|
|
@@ -1664,7 +1670,7 @@ var n1Rule = {
|
|
|
1664
1670
|
title: "N+1 Query Pattern",
|
|
1665
1671
|
desc: `${endpoint} runs ${shapeGroup.count}x ${info.op} ${info.table} with different params in a single request`,
|
|
1666
1672
|
hint: "This typically happens when fetching related data in a loop. Use a batch query, JOIN, or include/eager-load to fetch all records at once.",
|
|
1667
|
-
detail: `${shapeGroup.count} queries with ${shapeGroup.distinctSql.size} distinct param variations. Example: ${[...shapeGroup.distinctSql][0]?.slice(0,
|
|
1673
|
+
detail: `${shapeGroup.count} queries with ${shapeGroup.distinctSql.size} distinct param variations. Example: ${[...shapeGroup.distinctSql][0]?.slice(0, DETAIL_PREVIEW_LENGTH) ?? info.op + " " + info.table}`
|
|
1668
1674
|
});
|
|
1669
1675
|
}
|
|
1670
1676
|
}
|
|
@@ -1703,7 +1709,7 @@ var redundantQueryRule = {
|
|
|
1703
1709
|
title: "Redundant Query",
|
|
1704
1710
|
desc: `${label} runs ${entry.count}x with identical params in ${endpoint}.`,
|
|
1705
1711
|
hint: "The exact same query with identical parameters runs multiple times in one request. Cache the first result or lift the query to a shared function.",
|
|
1706
|
-
detail: entry.first.sql ? `Query: ${entry.first.sql.slice(0,
|
|
1712
|
+
detail: entry.first.sql ? `Query: ${entry.first.sql.slice(0, DETAIL_PREVIEW_LENGTH)}` : void 0
|
|
1707
1713
|
});
|
|
1708
1714
|
}
|
|
1709
1715
|
}
|
|
@@ -2235,7 +2241,7 @@ var AnalysisEngine = class {
|
|
|
2235
2241
|
};
|
|
2236
2242
|
|
|
2237
2243
|
// src/index.ts
|
|
2238
|
-
var VERSION = "0.9.
|
|
2244
|
+
var VERSION = "0.9.2";
|
|
2239
2245
|
export {
|
|
2240
2246
|
AdapterRegistry,
|
|
2241
2247
|
AnalysisEngine,
|
package/dist/bin/brakit.js
CHANGED
|
@@ -10,7 +10,7 @@ var __export = (target, all) => {
|
|
|
10
10
|
};
|
|
11
11
|
|
|
12
12
|
// src/constants/config.ts
|
|
13
|
-
var PROJECT_HASH_LENGTH, SECRET_SCAN_ARRAY_LIMIT, PII_SCAN_ARRAY_LIMIT, MIN_SECRET_VALUE_LENGTH, FULL_RECORD_MIN_FIELDS, LIST_PII_MIN_ITEMS, MAX_OBJECT_SCAN_DEPTH, ISSUE_PRUNE_TTL_MS, OVERFETCH_UNWRAP_MIN_SIZE, STALE_ISSUE_TTL_MS, METRICS_DIR, PORT_FILE, VALID_ISSUE_STATES, VALID_AI_FIX_STATUSES, VALID_SECURITY_SEVERITIES;
|
|
13
|
+
var PROJECT_HASH_LENGTH, SECRET_SCAN_ARRAY_LIMIT, PII_SCAN_ARRAY_LIMIT, MIN_SECRET_VALUE_LENGTH, FULL_RECORD_MIN_FIELDS, LIST_PII_MIN_ITEMS, MAX_OBJECT_SCAN_DEPTH, ISSUE_PRUNE_TTL_MS, OVERFETCH_UNWRAP_MIN_SIZE, STALE_ISSUE_TTL_MS, METRICS_DIR, PORT_FILE, VALID_ISSUE_STATES, VALID_AI_FIX_STATUSES, VALID_SECURITY_SEVERITIES, TELEMETRY_EVENT_CLI_INVOKED, TELEMETRY_EVENT_CLI_UNINSTALL, DETAIL_PREVIEW_LENGTH;
|
|
14
14
|
var init_config = __esm({
|
|
15
15
|
"src/constants/config.ts"() {
|
|
16
16
|
"use strict";
|
|
@@ -29,6 +29,9 @@ var init_config = __esm({
|
|
|
29
29
|
VALID_ISSUE_STATES = /* @__PURE__ */ new Set(["open", "fixing", "resolved", "stale", "regressed"]);
|
|
30
30
|
VALID_AI_FIX_STATUSES = /* @__PURE__ */ new Set(["fixed", "wont_fix"]);
|
|
31
31
|
VALID_SECURITY_SEVERITIES = /* @__PURE__ */ new Set(["critical", "warning"]);
|
|
32
|
+
TELEMETRY_EVENT_CLI_INVOKED = "cli_invoked";
|
|
33
|
+
TELEMETRY_EVENT_CLI_UNINSTALL = "cli_uninstall";
|
|
34
|
+
DETAIL_PREVIEW_LENGTH = 120;
|
|
32
35
|
}
|
|
33
36
|
});
|
|
34
37
|
|
|
@@ -70,7 +73,7 @@ var init_type_guards = __esm({
|
|
|
70
73
|
});
|
|
71
74
|
|
|
72
75
|
// src/constants/labels.ts
|
|
73
|
-
var DASHBOARD_PREFIX, DASHBOARD_API_REQUESTS, DASHBOARD_API_EVENTS, DASHBOARD_API_FLOWS, DASHBOARD_API_CLEAR, DASHBOARD_API_LOGS, DASHBOARD_API_FETCHES, DASHBOARD_API_ERRORS, DASHBOARD_API_QUERIES, DASHBOARD_API_INGEST, DASHBOARD_API_METRICS, DASHBOARD_API_ACTIVITY, DASHBOARD_API_METRICS_LIVE, DASHBOARD_API_INSIGHTS, DASHBOARD_API_SECURITY, DASHBOARD_API_TAB, DASHBOARD_API_FINDINGS, DASHBOARD_API_FINDINGS_REPORT, VALID_TABS_TUPLE, VALID_TABS;
|
|
76
|
+
var DASHBOARD_PREFIX, DASHBOARD_API_REQUESTS, DASHBOARD_API_EVENTS, DASHBOARD_API_FLOWS, DASHBOARD_API_CLEAR, DASHBOARD_API_LOGS, DASHBOARD_API_FETCHES, DASHBOARD_API_ERRORS, DASHBOARD_API_QUERIES, DASHBOARD_API_INGEST, DASHBOARD_API_METRICS, DASHBOARD_API_ACTIVITY, DASHBOARD_API_METRICS_LIVE, DASHBOARD_API_INSIGHTS, DASHBOARD_API_SECURITY, DASHBOARD_API_TAB, DASHBOARD_API_FINDINGS, DASHBOARD_API_FINDINGS_REPORT, VALID_TABS_TUPLE, VALID_TABS, POSTHOG_HOST, POSTHOG_CAPTURE_PATH, POSTHOG_REQUEST_TIMEOUT_MS;
|
|
74
77
|
var init_labels = __esm({
|
|
75
78
|
"src/constants/labels.ts"() {
|
|
76
79
|
"use strict";
|
|
@@ -104,11 +107,14 @@ var init_labels = __esm({
|
|
|
104
107
|
"security"
|
|
105
108
|
];
|
|
106
109
|
VALID_TABS = new Set(VALID_TABS_TUPLE);
|
|
110
|
+
POSTHOG_HOST = "https://us.i.posthog.com";
|
|
111
|
+
POSTHOG_CAPTURE_PATH = "/i/v0/e/";
|
|
112
|
+
POSTHOG_REQUEST_TIMEOUT_MS = 3e3;
|
|
107
113
|
}
|
|
108
114
|
});
|
|
109
115
|
|
|
110
116
|
// src/constants/features.ts
|
|
111
|
-
var SUPPORTED_SOURCE_EXTENSIONS, BUILD_CACHE_DIRS, FALLBACK_SCAN_DIRS, MCP_SERVER_NAME, INITIAL_DISCOVERY_TIMEOUT_MS, LAZY_DISCOVERY_TIMEOUT_MS, CLIENT_FETCH_TIMEOUT_MS, HEALTH_CHECK_TIMEOUT_MS, DISCOVERY_POLL_INTERVAL_MS, MAX_DISCOVERY_DEPTH, MAX_TIMELINE_EVENTS, MAX_RESOLVED_DISPLAY, ENRICHMENT_SEVERITY_FILTER, MCP_SERVER_VERSION, RECOVERY_WINDOW_MS, PORT_MIN, PORT_MAX;
|
|
117
|
+
var SUPPORTED_SOURCE_EXTENSIONS, BUILD_CACHE_DIRS, FALLBACK_SCAN_DIRS, MCP_SERVER_NAME, INITIAL_DISCOVERY_TIMEOUT_MS, LAZY_DISCOVERY_TIMEOUT_MS, CLIENT_FETCH_TIMEOUT_MS, HEALTH_CHECK_TIMEOUT_MS, DISCOVERY_POLL_INTERVAL_MS, MAX_DISCOVERY_DEPTH, MAX_TIMELINE_EVENTS, MAX_RESOLVED_DISPLAY, ENRICHMENT_SEVERITY_FILTER, MCP_SERVER_VERSION, RECOVERY_WINDOW_MS, PORT_MIN, PORT_MAX, DIR_MODE_OWNER_ONLY, FILE_MODE_OWNER_ONLY;
|
|
112
118
|
var init_features = __esm({
|
|
113
119
|
"src/constants/features.ts"() {
|
|
114
120
|
"use strict";
|
|
@@ -132,10 +138,12 @@ var init_features = __esm({
|
|
|
132
138
|
MAX_TIMELINE_EVENTS = 20;
|
|
133
139
|
MAX_RESOLVED_DISPLAY = 5;
|
|
134
140
|
ENRICHMENT_SEVERITY_FILTER = ["critical", "warning"];
|
|
135
|
-
MCP_SERVER_VERSION = "0.9.
|
|
141
|
+
MCP_SERVER_VERSION = "0.9.2";
|
|
136
142
|
RECOVERY_WINDOW_MS = 5 * 60 * 1e3;
|
|
137
143
|
PORT_MIN = 1;
|
|
138
144
|
PORT_MAX = 65535;
|
|
145
|
+
DIR_MODE_OWNER_ONLY = 448;
|
|
146
|
+
FILE_MODE_OWNER_ONLY = 384;
|
|
139
147
|
}
|
|
140
148
|
});
|
|
141
149
|
|
|
@@ -307,8 +315,8 @@ async function searchForPort(startDir) {
|
|
|
307
315
|
}
|
|
308
316
|
return null;
|
|
309
317
|
}
|
|
310
|
-
async function discoverBrakitPort(
|
|
311
|
-
const port = await searchForPort(
|
|
318
|
+
async function discoverBrakitPort(cwd2) {
|
|
319
|
+
const port = await searchForPort(cwd2 ?? process.cwd());
|
|
312
320
|
if (!port) {
|
|
313
321
|
throw new Error(
|
|
314
322
|
"Brakit is not running. Start your app with brakit enabled first."
|
|
@@ -316,11 +324,11 @@ async function discoverBrakitPort(cwd) {
|
|
|
316
324
|
}
|
|
317
325
|
return { port, baseUrl: `http://localhost:${port}` };
|
|
318
326
|
}
|
|
319
|
-
async function waitForBrakit(
|
|
327
|
+
async function waitForBrakit(cwd2, timeoutMs = 1e4, pollMs = DISCOVERY_POLL_INTERVAL_MS) {
|
|
320
328
|
const deadline = Date.now() + timeoutMs;
|
|
321
329
|
while (Date.now() < deadline) {
|
|
322
330
|
try {
|
|
323
|
-
const result = await discoverBrakitPort(
|
|
331
|
+
const result = await discoverBrakitPort(cwd2);
|
|
324
332
|
const res = await fetch(`${result.baseUrl}${DASHBOARD_API_REQUESTS}?limit=1`);
|
|
325
333
|
if (res.ok) return result;
|
|
326
334
|
} catch {
|
|
@@ -1047,13 +1055,15 @@ var init_server = __esm({
|
|
|
1047
1055
|
|
|
1048
1056
|
// bin/brakit.ts
|
|
1049
1057
|
import { runMain } from "citty";
|
|
1058
|
+
import { existsSync as existsSync7 } from "fs";
|
|
1059
|
+
import { resolve as resolve6 } from "path";
|
|
1050
1060
|
|
|
1051
1061
|
// src/cli/commands/install.ts
|
|
1052
1062
|
import { defineCommand } from "citty";
|
|
1053
1063
|
import { resolve as resolve3, join as join3, dirname } from "path";
|
|
1054
1064
|
import { readFile as readFile4, writeFile as writeFile3 } from "fs/promises";
|
|
1055
1065
|
import { execSync } from "child_process";
|
|
1056
|
-
import { existsSync as existsSync5 } from "fs";
|
|
1066
|
+
import { existsSync as existsSync5, readFileSync as readFileSync3 } from "fs";
|
|
1057
1067
|
import pc from "picocolors";
|
|
1058
1068
|
|
|
1059
1069
|
// src/store/issue-store.ts
|
|
@@ -1563,7 +1573,7 @@ var stackTraceLeakRule = {
|
|
|
1563
1573
|
title: "Stack Trace Leaked to Client",
|
|
1564
1574
|
desc: `${ep} \u2014 response exposes internal stack trace`,
|
|
1565
1575
|
hint: this.hint,
|
|
1566
|
-
detail: firstLine ? `Stack trace: ${firstLine.slice(0,
|
|
1576
|
+
detail: firstLine ? `Stack trace: ${firstLine.slice(0, DETAIL_PREVIEW_LENGTH)}` : void 0,
|
|
1567
1577
|
endpoint: ep,
|
|
1568
1578
|
count: 1
|
|
1569
1579
|
}
|
|
@@ -1802,7 +1812,7 @@ init_constants();
|
|
|
1802
1812
|
init_endpoint();
|
|
1803
1813
|
|
|
1804
1814
|
// src/index.ts
|
|
1805
|
-
var VERSION = "0.9.
|
|
1815
|
+
var VERSION = "0.9.2";
|
|
1806
1816
|
|
|
1807
1817
|
// src/cli/commands/install.ts
|
|
1808
1818
|
init_constants();
|
|
@@ -1905,6 +1915,14 @@ var install_default = defineCommand({
|
|
|
1905
1915
|
}
|
|
1906
1916
|
process.exit(1);
|
|
1907
1917
|
}
|
|
1918
|
+
const firstNode = nodeProjects[0];
|
|
1919
|
+
if (isFrontendOnly(firstNode.dir)) {
|
|
1920
|
+
console.log(pc.yellow(" \u26A0 This looks like a frontend-only app (React, Vue, etc.)."));
|
|
1921
|
+
console.log(pc.dim(" Brakit instruments your backend server, not the browser."));
|
|
1922
|
+
console.log(pc.dim(" Install brakit in your backend project instead (Express, Fastify, Next.js, etc.)."));
|
|
1923
|
+
console.log();
|
|
1924
|
+
process.exit(1);
|
|
1925
|
+
}
|
|
1908
1926
|
for (const p of nodeProjects) {
|
|
1909
1927
|
const node = p.node;
|
|
1910
1928
|
const suffix = p.relDir === "." ? "" : ` in ${p.relDir}`;
|
|
@@ -1941,9 +1959,8 @@ var install_default = defineCommand({
|
|
|
1941
1959
|
}
|
|
1942
1960
|
}
|
|
1943
1961
|
console.log();
|
|
1944
|
-
const port = nodeProjects[0].node?.defaultPort ?? 3e3;
|
|
1945
1962
|
console.log(pc.dim(" Start your app and visit:"));
|
|
1946
|
-
console.log(pc.bold(
|
|
1963
|
+
console.log(pc.bold(" http://localhost:<your-port>/__brakit"));
|
|
1947
1964
|
if (pythonProjects.length > 0) {
|
|
1948
1965
|
const pyLabel = pythonProjects.map((p) => p.relDir).join(", ");
|
|
1949
1966
|
console.log();
|
|
@@ -2014,8 +2031,8 @@ async function setupNuxt(rootDir) {
|
|
|
2014
2031
|
}
|
|
2015
2032
|
const content = BRAKIT_TEMPLATES.nuxt + "\n";
|
|
2016
2033
|
const dir = join3(rootDir, "server/plugins");
|
|
2017
|
-
const { mkdirSync:
|
|
2018
|
-
|
|
2034
|
+
const { mkdirSync: mkdirSync4 } = await import("fs");
|
|
2035
|
+
mkdirSync4(dir, { recursive: true });
|
|
2019
2036
|
await writeFile3(absPath, content);
|
|
2020
2037
|
return { action: "created", file: relPath, content };
|
|
2021
2038
|
}
|
|
@@ -2095,6 +2112,20 @@ function findGitRoot(startDir) {
|
|
|
2095
2112
|
dir = parent;
|
|
2096
2113
|
}
|
|
2097
2114
|
}
|
|
2115
|
+
var FRONTEND_ONLY_DEPS = ["react", "vue", "svelte", "@angular/core", "solid-js", "preact"];
|
|
2116
|
+
var SERVER_DEPS = ["express", "fastify", "hono", "koa", "nest", "next", "@remix-run/dev", "nuxt", "astro"];
|
|
2117
|
+
function isFrontendOnly(rootDir) {
|
|
2118
|
+
try {
|
|
2119
|
+
const raw = readFileSync3(join3(rootDir, "package.json"), "utf-8");
|
|
2120
|
+
const pkg = JSON.parse(raw);
|
|
2121
|
+
const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
2122
|
+
const hasFrontend = FRONTEND_ONLY_DEPS.some((d) => d in allDeps);
|
|
2123
|
+
const hasServer = SERVER_DEPS.some((d) => d in allDeps);
|
|
2124
|
+
return hasFrontend && !hasServer;
|
|
2125
|
+
} catch {
|
|
2126
|
+
return false;
|
|
2127
|
+
}
|
|
2128
|
+
}
|
|
2098
2129
|
function printManualInstructions(framework) {
|
|
2099
2130
|
console.log(pc.yellow(" \u26A0 Could not auto-detect entry file."));
|
|
2100
2131
|
console.log();
|
|
@@ -2114,13 +2145,118 @@ function printManualInstructions(framework) {
|
|
|
2114
2145
|
|
|
2115
2146
|
// src/cli/commands/uninstall.ts
|
|
2116
2147
|
import { defineCommand as defineCommand2 } from "citty";
|
|
2117
|
-
import { resolve as resolve4, join as
|
|
2148
|
+
import { resolve as resolve4, join as join5, relative as relative2 } from "path";
|
|
2118
2149
|
import { readFile as readFile5, writeFile as writeFile4, unlink, rm, readdir as readdir2 } from "fs/promises";
|
|
2119
2150
|
import { execSync as execSync2 } from "child_process";
|
|
2120
2151
|
import pc2 from "picocolors";
|
|
2121
2152
|
init_constants();
|
|
2122
2153
|
init_log();
|
|
2123
2154
|
init_type_guards();
|
|
2155
|
+
|
|
2156
|
+
// src/telemetry/index.ts
|
|
2157
|
+
import { platform as platform2, release, arch } from "os";
|
|
2158
|
+
import { spawn } from "child_process";
|
|
2159
|
+
|
|
2160
|
+
// src/telemetry/config.ts
|
|
2161
|
+
init_features();
|
|
2162
|
+
import { homedir as homedir2, platform } from "os";
|
|
2163
|
+
import { join as join4 } from "path";
|
|
2164
|
+
import { existsSync as existsSync6, readFileSync as readFileSync4, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3 } from "fs";
|
|
2165
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
2166
|
+
var IS_WINDOWS = platform() === "win32";
|
|
2167
|
+
var CONFIG_DIR = join4(homedir2(), ".brakit");
|
|
2168
|
+
var CONFIG_PATH = join4(CONFIG_DIR, "config.json");
|
|
2169
|
+
function readConfig() {
|
|
2170
|
+
try {
|
|
2171
|
+
if (!existsSync6(CONFIG_PATH)) return null;
|
|
2172
|
+
return JSON.parse(readFileSync4(CONFIG_PATH, "utf-8"));
|
|
2173
|
+
} catch {
|
|
2174
|
+
return null;
|
|
2175
|
+
}
|
|
2176
|
+
}
|
|
2177
|
+
function writeConfig(config) {
|
|
2178
|
+
try {
|
|
2179
|
+
if (!existsSync6(CONFIG_DIR))
|
|
2180
|
+
mkdirSync3(CONFIG_DIR, { recursive: true, ...IS_WINDOWS ? {} : { mode: DIR_MODE_OWNER_ONLY } });
|
|
2181
|
+
writeFileSync3(
|
|
2182
|
+
CONFIG_PATH,
|
|
2183
|
+
JSON.stringify(config, null, 2) + "\n",
|
|
2184
|
+
IS_WINDOWS ? {} : { mode: FILE_MODE_OWNER_ONLY }
|
|
2185
|
+
);
|
|
2186
|
+
} catch (err) {
|
|
2187
|
+
if (process.env.BRAKIT_DEBUG) {
|
|
2188
|
+
process.stderr.write(`[brakit] config write failed: ${err?.message ?? err}
|
|
2189
|
+
`);
|
|
2190
|
+
}
|
|
2191
|
+
}
|
|
2192
|
+
}
|
|
2193
|
+
function getOrCreateConfig() {
|
|
2194
|
+
const existing = readConfig();
|
|
2195
|
+
if (existing && typeof existing.telemetry === "boolean" && existing.anonymousId) {
|
|
2196
|
+
return existing;
|
|
2197
|
+
}
|
|
2198
|
+
const config = { telemetry: true, anonymousId: randomUUID2() };
|
|
2199
|
+
writeConfig(config);
|
|
2200
|
+
return config;
|
|
2201
|
+
}
|
|
2202
|
+
var cachedEnabled = null;
|
|
2203
|
+
function isTelemetryEnabled() {
|
|
2204
|
+
if (cachedEnabled !== null) return cachedEnabled;
|
|
2205
|
+
const env = process.env.BRAKIT_TELEMETRY;
|
|
2206
|
+
if (env !== void 0) {
|
|
2207
|
+
cachedEnabled = env !== "false" && env !== "0" && env !== "off";
|
|
2208
|
+
return cachedEnabled;
|
|
2209
|
+
}
|
|
2210
|
+
cachedEnabled = readConfig()?.telemetry ?? true;
|
|
2211
|
+
return cachedEnabled;
|
|
2212
|
+
}
|
|
2213
|
+
|
|
2214
|
+
// src/telemetry/index.ts
|
|
2215
|
+
init_labels();
|
|
2216
|
+
init_config();
|
|
2217
|
+
var POSTHOG_KEY = "phc_E9TwydCGnSfPLIUhNxChpeg32TSowjk31KiPhnLPP0x";
|
|
2218
|
+
function commonProperties() {
|
|
2219
|
+
return {
|
|
2220
|
+
brakit_version: VERSION,
|
|
2221
|
+
node_version: process.version,
|
|
2222
|
+
os: `${platform2()}-${release()}`,
|
|
2223
|
+
arch: arch(),
|
|
2224
|
+
$lib: "brakit",
|
|
2225
|
+
$process_person_profile: false,
|
|
2226
|
+
$geoip_disable: true
|
|
2227
|
+
};
|
|
2228
|
+
}
|
|
2229
|
+
function sendToPosthog(event, properties) {
|
|
2230
|
+
if (!isTelemetryEnabled()) return;
|
|
2231
|
+
const config = getOrCreateConfig();
|
|
2232
|
+
const payload = {
|
|
2233
|
+
api_key: POSTHOG_KEY,
|
|
2234
|
+
event,
|
|
2235
|
+
distinct_id: config.anonymousId,
|
|
2236
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2237
|
+
properties: { ...commonProperties(), ...properties }
|
|
2238
|
+
};
|
|
2239
|
+
try {
|
|
2240
|
+
const body = JSON.stringify(payload);
|
|
2241
|
+
const url = `${POSTHOG_HOST}${POSTHOG_CAPTURE_PATH}`;
|
|
2242
|
+
const child = spawn(
|
|
2243
|
+
process.execPath,
|
|
2244
|
+
[
|
|
2245
|
+
"-e",
|
|
2246
|
+
`fetch(${JSON.stringify(url)},{method:"POST",headers:{"content-type":"application/json"},body:${JSON.stringify(body)},signal:AbortSignal.timeout(${POSTHOG_REQUEST_TIMEOUT_MS})}).catch(()=>{})`
|
|
2247
|
+
],
|
|
2248
|
+
{ detached: true, stdio: "ignore" }
|
|
2249
|
+
);
|
|
2250
|
+
child.unref();
|
|
2251
|
+
} catch {
|
|
2252
|
+
}
|
|
2253
|
+
}
|
|
2254
|
+
function trackEvent(event, properties) {
|
|
2255
|
+
sendToPosthog(event, { sdk: "node", ...properties });
|
|
2256
|
+
}
|
|
2257
|
+
|
|
2258
|
+
// src/cli/commands/uninstall.ts
|
|
2259
|
+
init_config();
|
|
2124
2260
|
var PREPENDED_FILES = [
|
|
2125
2261
|
"app/entry.server.tsx",
|
|
2126
2262
|
"app/entry.server.ts",
|
|
@@ -2155,16 +2291,20 @@ var uninstall_default = defineCommand2({
|
|
|
2155
2291
|
console.log();
|
|
2156
2292
|
console.log(pc2.bold(" \u25C6 brakit uninstall"));
|
|
2157
2293
|
console.log();
|
|
2294
|
+
let anyInstrumentationRemoved = false;
|
|
2295
|
+
let anyPackageRemoved = false;
|
|
2158
2296
|
for (const project of projects) {
|
|
2159
2297
|
const suffix = projects.length > 1 ? ` in ${relative2(rootDir, project.dir) || "."}` : "";
|
|
2160
2298
|
const removed = await removeInstrumentation(project.dir);
|
|
2161
2299
|
if (removed) {
|
|
2300
|
+
anyInstrumentationRemoved = true;
|
|
2162
2301
|
console.log(pc2.green(` \u2713 ${removed}${suffix}`));
|
|
2163
2302
|
} else {
|
|
2164
2303
|
console.log(pc2.dim(` No brakit instrumentation files found${suffix}.`));
|
|
2165
2304
|
}
|
|
2166
2305
|
const uninstalled = await uninstallPackage(project.dir, project.pm);
|
|
2167
2306
|
if (uninstalled === true) {
|
|
2307
|
+
anyPackageRemoved = true;
|
|
2168
2308
|
console.log(pc2.green(` \u2713 Removed brakit from devDependencies${suffix}`));
|
|
2169
2309
|
} else if (uninstalled === "failed") {
|
|
2170
2310
|
}
|
|
@@ -2185,6 +2325,12 @@ var uninstall_default = defineCommand2({
|
|
|
2185
2325
|
if (cacheCleared) {
|
|
2186
2326
|
console.log(pc2.green(" \u2713 Cleared build cache"));
|
|
2187
2327
|
}
|
|
2328
|
+
trackEvent(TELEMETRY_EVENT_CLI_UNINSTALL, {
|
|
2329
|
+
instrumentation_removed: anyInstrumentationRemoved,
|
|
2330
|
+
package_removed: anyPackageRemoved,
|
|
2331
|
+
mcp_removed: mcpRemoved,
|
|
2332
|
+
data_removed: dataRemoved
|
|
2333
|
+
});
|
|
2188
2334
|
console.log();
|
|
2189
2335
|
}
|
|
2190
2336
|
});
|
|
@@ -2195,7 +2341,7 @@ async function removeInstrumentation(projectDir) {
|
|
|
2195
2341
|
}
|
|
2196
2342
|
const candidates = [...PREPENDED_FILES];
|
|
2197
2343
|
try {
|
|
2198
|
-
const pkgRaw = await readFile5(
|
|
2344
|
+
const pkgRaw = await readFile5(join5(projectDir, "package.json"), "utf-8");
|
|
2199
2345
|
const pkg = JSON.parse(pkgRaw);
|
|
2200
2346
|
if (pkg.main) candidates.unshift(pkg.main);
|
|
2201
2347
|
} catch (err) {
|
|
@@ -2210,7 +2356,7 @@ async function removeInstrumentation(projectDir) {
|
|
|
2210
2356
|
return null;
|
|
2211
2357
|
}
|
|
2212
2358
|
async function tryRemoveBrakitFromFile(projectDir, relPath) {
|
|
2213
|
-
const absPath =
|
|
2359
|
+
const absPath = join5(projectDir, relPath);
|
|
2214
2360
|
if (!await fileExists(absPath)) return null;
|
|
2215
2361
|
const content = await readFile5(absPath, "utf-8");
|
|
2216
2362
|
if (!content.includes("brakit")) return null;
|
|
@@ -2227,7 +2373,7 @@ async function tryRemoveBrakitFromFile(projectDir, relPath) {
|
|
|
2227
2373
|
return null;
|
|
2228
2374
|
}
|
|
2229
2375
|
async function tryRemoveImportLine(projectDir, relPath) {
|
|
2230
|
-
const absPath =
|
|
2376
|
+
const absPath = join5(projectDir, relPath);
|
|
2231
2377
|
if (!await fileExists(absPath)) return null;
|
|
2232
2378
|
const content = await readFile5(absPath, "utf-8");
|
|
2233
2379
|
if (!content.includes(IMPORT_LINE)) return null;
|
|
@@ -2238,7 +2384,7 @@ async function tryRemoveImportLine(projectDir, relPath) {
|
|
|
2238
2384
|
async function fallbackSearchAndRemove(projectDir) {
|
|
2239
2385
|
const dirsToScan = FALLBACK_SCAN_DIRS;
|
|
2240
2386
|
for (const dir of dirsToScan) {
|
|
2241
|
-
const absDir =
|
|
2387
|
+
const absDir = join5(projectDir, dir);
|
|
2242
2388
|
if (!await fileExists(absDir)) continue;
|
|
2243
2389
|
let entries;
|
|
2244
2390
|
try {
|
|
@@ -2251,7 +2397,7 @@ async function fallbackSearchAndRemove(projectDir) {
|
|
|
2251
2397
|
const ext = entry.slice(entry.lastIndexOf("."));
|
|
2252
2398
|
if (!SUPPORTED_SOURCE_EXTENSIONS.has(ext)) continue;
|
|
2253
2399
|
const relPath = dir === "." ? entry : `${dir}/${entry}`;
|
|
2254
|
-
const absPath =
|
|
2400
|
+
const absPath = join5(projectDir, relPath);
|
|
2255
2401
|
try {
|
|
2256
2402
|
const content = await readFile5(absPath, "utf-8");
|
|
2257
2403
|
if (!containsBrakitImport(content)) continue;
|
|
@@ -2274,7 +2420,7 @@ async function fallbackSearchAndRemove(projectDir) {
|
|
|
2274
2420
|
return null;
|
|
2275
2421
|
}
|
|
2276
2422
|
async function removeMcpConfig(rootDir) {
|
|
2277
|
-
const mcpPath =
|
|
2423
|
+
const mcpPath = join5(rootDir, ".mcp.json");
|
|
2278
2424
|
if (!await fileExists(mcpPath)) return false;
|
|
2279
2425
|
try {
|
|
2280
2426
|
const raw = await readFile5(mcpPath, "utf-8");
|
|
@@ -2294,7 +2440,7 @@ async function removeMcpConfig(rootDir) {
|
|
|
2294
2440
|
}
|
|
2295
2441
|
async function uninstallPackage(rootDir, pm) {
|
|
2296
2442
|
try {
|
|
2297
|
-
const pkgRaw = await readFile5(
|
|
2443
|
+
const pkgRaw = await readFile5(join5(rootDir, "package.json"), "utf-8");
|
|
2298
2444
|
const pkg = JSON.parse(pkgRaw);
|
|
2299
2445
|
if (!pkg.devDependencies?.brakit && !pkg.dependencies?.brakit) return false;
|
|
2300
2446
|
} catch (err) {
|
|
@@ -2318,7 +2464,7 @@ async function uninstallPackage(rootDir, pm) {
|
|
|
2318
2464
|
}
|
|
2319
2465
|
async function removeBrakitData(rootDir) {
|
|
2320
2466
|
let removed = false;
|
|
2321
|
-
const projectDir =
|
|
2467
|
+
const projectDir = join5(rootDir, METRICS_DIR);
|
|
2322
2468
|
if (await fileExists(projectDir)) {
|
|
2323
2469
|
try {
|
|
2324
2470
|
await rm(projectDir, { recursive: true, force: true });
|
|
@@ -2339,7 +2485,7 @@ async function removeBrakitData(rootDir) {
|
|
|
2339
2485
|
return removed;
|
|
2340
2486
|
}
|
|
2341
2487
|
async function cleanGitignore(rootDir) {
|
|
2342
|
-
const gitignorePath =
|
|
2488
|
+
const gitignorePath = join5(rootDir, ".gitignore");
|
|
2343
2489
|
if (!await fileExists(gitignorePath)) return false;
|
|
2344
2490
|
try {
|
|
2345
2491
|
const content = await readFile5(gitignorePath, "utf-8");
|
|
@@ -2356,7 +2502,7 @@ async function cleanGitignore(rootDir) {
|
|
|
2356
2502
|
async function clearBuildCaches(rootDir) {
|
|
2357
2503
|
let cleared = false;
|
|
2358
2504
|
for (const dir of BUILD_CACHE_DIRS) {
|
|
2359
|
-
const absDir =
|
|
2505
|
+
const absDir = join5(rootDir, dir);
|
|
2360
2506
|
if (!await fileExists(absDir)) continue;
|
|
2361
2507
|
try {
|
|
2362
2508
|
await rm(absDir, { recursive: true, force: true });
|
|
@@ -2369,7 +2515,15 @@ async function clearBuildCaches(rootDir) {
|
|
|
2369
2515
|
}
|
|
2370
2516
|
|
|
2371
2517
|
// bin/brakit.ts
|
|
2518
|
+
init_config();
|
|
2372
2519
|
var sub = process.argv[2];
|
|
2520
|
+
var command = sub === "uninstall" ? "uninstall" : sub === "mcp" ? "mcp" : "install";
|
|
2521
|
+
var cwd = process.cwd();
|
|
2522
|
+
trackEvent(TELEMETRY_EVENT_CLI_INVOKED, {
|
|
2523
|
+
command,
|
|
2524
|
+
has_package_json: existsSync7(resolve6(cwd, "package.json")),
|
|
2525
|
+
cwd_has_node_modules: existsSync7(resolve6(cwd, "node_modules"))
|
|
2526
|
+
});
|
|
2373
2527
|
if (sub === "uninstall") {
|
|
2374
2528
|
process.argv.splice(2, 1);
|
|
2375
2529
|
runMain(uninstall_default);
|