brakit 0.9.0 → 0.9.1

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.
@@ -15,7 +15,7 @@ var __export = (target, all) => {
15
15
  };
16
16
 
17
17
  // src/constants/config.ts
18
- var MAX_REQUEST_ENTRIES, DEFAULT_MAX_BODY_CAPTURE, DEFAULT_API_LIMIT, MAX_TELEMETRY_ENTRIES, MAX_TAB_NAME_LENGTH, MAX_INGEST_BYTES, TERMINAL_TRUNCATE_LENGTH, SENSITIVE_MASK_MIN_LENGTH, SENSITIVE_MASK_VISIBLE_CHARS, MAX_JSON_BODY_BYTES, ANALYSIS_DEBOUNCE_MS, ISSUE_ID_HASH_LENGTH, ISSUES_DATA_VERSION, SENSITIVE_MASK_PLACEHOLDER, PROJECT_HASH_LENGTH, SECRET_SCAN_ARRAY_LIMIT, PII_SCAN_ARRAY_LIMIT, MIN_SECRET_VALUE_LENGTH, FULL_RECORD_MIN_FIELDS, LIST_PII_MIN_ITEMS, MAX_API_LIMIT, MAX_OBJECT_SCAN_DEPTH, MAX_UNIQUE_ENDPOINTS, MAX_ACCUMULATOR_ENTRIES, ISSUE_PRUNE_TTL_MS, FLOW_GAP_MS, SLOW_REQUEST_THRESHOLD_MS, MIN_POLLING_SEQUENCE, ENDPOINT_TRUNCATE_LENGTH, N1_QUERY_THRESHOLD, ERROR_RATE_THRESHOLD_PCT, MIN_REQUESTS_FOR_INSIGHT, HIGH_QUERY_COUNT_PER_REQ, CROSS_ENDPOINT_MIN_ENDPOINTS, CROSS_ENDPOINT_PCT, CROSS_ENDPOINT_MIN_OCCURRENCES, REDUNDANT_QUERY_MIN_COUNT, LARGE_RESPONSE_BYTES, HIGH_ROW_COUNT, OVERFETCH_MIN_REQUESTS, OVERFETCH_MIN_FIELDS, OVERFETCH_MIN_INTERNAL_IDS, OVERFETCH_NULL_RATIO, REGRESSION_PCT_THRESHOLD, REGRESSION_MIN_INCREASE_MS, REGRESSION_MIN_REQUESTS, QUERY_COUNT_REGRESSION_RATIO, OVERFETCH_MANY_FIELDS, OVERFETCH_UNWRAP_MIN_SIZE, MAX_DUPLICATE_INSIGHTS, INSIGHT_WINDOW_PER_ENDPOINT, CLEAN_HITS_FOR_RESOLUTION, STALE_ISSUE_TTL_MS, STRICT_MODE_MAX_GAP_MS, BASELINE_MIN_SESSIONS, BASELINE_MIN_REQUESTS_PER_SESSION, BASELINE_PENDING_POINTS_MIN, METRICS_DIR, METRICS_FILE, PORT_FILE, ISSUES_FILE, METRICS_FLUSH_INTERVAL_MS, METRICS_MAX_SESSIONS, METRICS_MAX_DATA_POINTS, ISSUES_FLUSH_INTERVAL_MS, SSE_HEARTBEAT_INTERVAL_MS, NOISE_HOSTS, NOISE_PATH_PATTERNS, VALID_ISSUE_STATES, VALID_ISSUE_CATEGORIES, VALID_AI_FIX_STATUSES;
18
+ var MAX_REQUEST_ENTRIES, DEFAULT_MAX_BODY_CAPTURE, DEFAULT_API_LIMIT, MAX_TELEMETRY_ENTRIES, MAX_TAB_NAME_LENGTH, MAX_INGEST_BYTES, TERMINAL_TRUNCATE_LENGTH, SENSITIVE_MASK_MIN_LENGTH, SENSITIVE_MASK_VISIBLE_CHARS, MAX_JSON_BODY_BYTES, ANALYSIS_DEBOUNCE_MS, ISSUE_ID_HASH_LENGTH, ISSUES_DATA_VERSION, SENSITIVE_MASK_PLACEHOLDER, PROJECT_HASH_LENGTH, SECRET_SCAN_ARRAY_LIMIT, PII_SCAN_ARRAY_LIMIT, MIN_SECRET_VALUE_LENGTH, FULL_RECORD_MIN_FIELDS, LIST_PII_MIN_ITEMS, MAX_API_LIMIT, MAX_OBJECT_SCAN_DEPTH, MAX_UNIQUE_ENDPOINTS, MAX_ACCUMULATOR_ENTRIES, ISSUE_PRUNE_TTL_MS, FLOW_GAP_MS, SLOW_REQUEST_THRESHOLD_MS, MIN_POLLING_SEQUENCE, ENDPOINT_TRUNCATE_LENGTH, N1_QUERY_THRESHOLD, ERROR_RATE_THRESHOLD_PCT, MIN_REQUESTS_FOR_INSIGHT, HIGH_QUERY_COUNT_PER_REQ, CROSS_ENDPOINT_MIN_ENDPOINTS, CROSS_ENDPOINT_PCT, CROSS_ENDPOINT_MIN_OCCURRENCES, REDUNDANT_QUERY_MIN_COUNT, LARGE_RESPONSE_BYTES, HIGH_ROW_COUNT, OVERFETCH_MIN_REQUESTS, OVERFETCH_MIN_FIELDS, OVERFETCH_MIN_INTERNAL_IDS, OVERFETCH_NULL_RATIO, REGRESSION_PCT_THRESHOLD, REGRESSION_MIN_INCREASE_MS, REGRESSION_MIN_REQUESTS, QUERY_COUNT_REGRESSION_RATIO, OVERFETCH_MANY_FIELDS, OVERFETCH_UNWRAP_MIN_SIZE, MAX_DUPLICATE_INSIGHTS, INSIGHT_WINDOW_PER_ENDPOINT, CLEAN_HITS_FOR_RESOLUTION, STALE_ISSUE_TTL_MS, STRICT_MODE_MAX_GAP_MS, BASELINE_MIN_SESSIONS, BASELINE_MIN_REQUESTS_PER_SESSION, BASELINE_PENDING_POINTS_MIN, METRICS_DIR, METRICS_FILE, PORT_FILE, ISSUES_FILE, METRICS_FLUSH_INTERVAL_MS, METRICS_MAX_SESSIONS, METRICS_MAX_DATA_POINTS, ISSUES_FLUSH_INTERVAL_MS, SSE_HEARTBEAT_INTERVAL_MS, NOISE_HOSTS, NOISE_PATH_PATTERNS, VALID_ISSUE_STATES, VALID_ISSUE_CATEGORIES, VALID_AI_FIX_STATUSES, TELEMETRY_EVENT_SETUP_COMPLETED, TELEMETRY_EVENT_FIRST_REQUEST, TELEMETRY_EVENT_DASHBOARD_VIEWED, TELEMETRY_EVENT_SESSION, EXIT_REASON_CLEAN, EXIT_REASON_SIGINT, EXIT_REASON_SIGTERM, DETAIL_PREVIEW_LENGTH, KNOWN_DEPENDENCY_NAMES;
19
19
  var init_config = __esm({
20
20
  "src/constants/config.ts"() {
21
21
  "use strict";
@@ -90,6 +90,30 @@ var init_config = __esm({
90
90
  VALID_ISSUE_STATES = /* @__PURE__ */ new Set(["open", "fixing", "resolved", "stale", "regressed"]);
91
91
  VALID_ISSUE_CATEGORIES = /* @__PURE__ */ new Set(["security", "performance", "reliability"]);
92
92
  VALID_AI_FIX_STATUSES = /* @__PURE__ */ new Set(["fixed", "wont_fix"]);
93
+ TELEMETRY_EVENT_SETUP_COMPLETED = "setup_completed";
94
+ TELEMETRY_EVENT_FIRST_REQUEST = "first_request";
95
+ TELEMETRY_EVENT_DASHBOARD_VIEWED = "dashboard_viewed";
96
+ TELEMETRY_EVENT_SESSION = "session";
97
+ EXIT_REASON_CLEAN = "clean";
98
+ EXIT_REASON_SIGINT = "sigint";
99
+ EXIT_REASON_SIGTERM = "sigterm";
100
+ DETAIL_PREVIEW_LENGTH = 120;
101
+ KNOWN_DEPENDENCY_NAMES = [
102
+ "next",
103
+ "@remix-run/dev",
104
+ "nuxt",
105
+ "vite",
106
+ "astro",
107
+ "express",
108
+ "fastify",
109
+ "hono",
110
+ "koa",
111
+ "nest",
112
+ "prisma",
113
+ "drizzle-orm",
114
+ "typeorm",
115
+ "sequelize"
116
+ ];
93
117
  }
94
118
  });
95
119
 
@@ -422,6 +446,7 @@ var init_adapter_registry = __esm({
422
446
  constructor() {
423
447
  this.adapters = [];
424
448
  this.active = [];
449
+ this.failed = [];
425
450
  }
426
451
  register(adapter) {
427
452
  this.adapters.push(adapter);
@@ -434,6 +459,7 @@ var init_adapter_registry = __esm({
434
459
  this.active.push(adapter);
435
460
  }
436
461
  } catch {
462
+ this.failed.push(adapter.name);
437
463
  }
438
464
  }
439
465
  }
@@ -449,6 +475,9 @@ var init_adapter_registry = __esm({
449
475
  getActive() {
450
476
  return this.active;
451
477
  }
478
+ getFailed() {
479
+ return this.failed;
480
+ }
452
481
  };
453
482
  }
454
483
  });
@@ -2837,7 +2866,7 @@ var init_data_rules = __esm({
2837
2866
  title: "Stack Trace Leaked to Client",
2838
2867
  desc: `${ep} \u2014 response exposes internal stack trace`,
2839
2868
  hint: this.hint,
2840
- detail: firstLine ? `Stack trace: ${firstLine.slice(0, 120)}` : void 0,
2869
+ detail: firstLine ? `Stack trace: ${firstLine.slice(0, DETAIL_PREVIEW_LENGTH)}` : void 0,
2841
2870
  endpoint: ep,
2842
2871
  count: 1
2843
2872
  }
@@ -3253,7 +3282,7 @@ var init_query_rules = __esm({
3253
3282
  title: "N+1 Query Pattern",
3254
3283
  desc: `${endpoint} runs ${shapeGroup.count}x ${info.op} ${info.table} with different params in a single request`,
3255
3284
  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.",
3256
- detail: `${shapeGroup.count} queries with ${shapeGroup.distinctSql.size} distinct param variations. Example: ${[...shapeGroup.distinctSql][0]?.slice(0, 100) ?? info.op + " " + info.table}`
3285
+ detail: `${shapeGroup.count} queries with ${shapeGroup.distinctSql.size} distinct param variations. Example: ${[...shapeGroup.distinctSql][0]?.slice(0, DETAIL_PREVIEW_LENGTH) ?? info.op + " " + info.table}`
3257
3286
  });
3258
3287
  }
3259
3288
  }
@@ -3292,7 +3321,7 @@ var init_query_rules = __esm({
3292
3321
  title: "Redundant Query",
3293
3322
  desc: `${label} runs ${entry.count}x with identical params in ${endpoint}.`,
3294
3323
  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.",
3295
- detail: entry.first.sql ? `Query: ${entry.first.sql.slice(0, 120)}` : void 0
3324
+ detail: entry.first.sql ? `Query: ${entry.first.sql.slice(0, DETAIL_PREVIEW_LENGTH)}` : void 0
3296
3325
  });
3297
3326
  }
3298
3327
  }
@@ -3927,7 +3956,7 @@ var init_src = __esm({
3927
3956
  init_engine();
3928
3957
  init_insights2();
3929
3958
  init_insights();
3930
- VERSION = "0.9.0";
3959
+ VERSION = "0.9.1";
3931
3960
  }
3932
3961
  });
3933
3962
 
@@ -4817,7 +4846,7 @@ var init_page = __esm({
4817
4846
  });
4818
4847
 
4819
4848
  // src/telemetry/config.ts
4820
- import { homedir as homedir2 } from "os";
4849
+ import { homedir as homedir2, platform } from "os";
4821
4850
  import { join as join3 } from "path";
4822
4851
  import { existsSync as existsSync5, readFileSync as readFileSync4, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3 } from "fs";
4823
4852
  import { randomUUID as randomUUID5 } from "crypto";
@@ -4832,11 +4861,17 @@ function readConfig() {
4832
4861
  function writeConfig(config) {
4833
4862
  try {
4834
4863
  if (!existsSync5(CONFIG_DIR))
4835
- mkdirSync3(CONFIG_DIR, { recursive: true, mode: DIR_MODE_OWNER_ONLY });
4836
- writeFileSync3(CONFIG_PATH, JSON.stringify(config, null, 2) + "\n", {
4837
- mode: FILE_MODE_OWNER_ONLY
4838
- });
4839
- } catch {
4864
+ mkdirSync3(CONFIG_DIR, { recursive: true, ...IS_WINDOWS ? {} : { mode: DIR_MODE_OWNER_ONLY } });
4865
+ writeFileSync3(
4866
+ CONFIG_PATH,
4867
+ JSON.stringify(config, null, 2) + "\n",
4868
+ IS_WINDOWS ? {} : { mode: FILE_MODE_OWNER_ONLY }
4869
+ );
4870
+ } catch (err) {
4871
+ if (process.env.BRAKIT_DEBUG) {
4872
+ process.stderr.write(`[brakit] config write failed: ${err?.message ?? err}
4873
+ `);
4874
+ }
4840
4875
  }
4841
4876
  }
4842
4877
  function getOrCreateConfig() {
@@ -4858,11 +4893,12 @@ function isTelemetryEnabled() {
4858
4893
  cachedEnabled = readConfig()?.telemetry ?? true;
4859
4894
  return cachedEnabled;
4860
4895
  }
4861
- var CONFIG_DIR, CONFIG_PATH, cachedEnabled;
4896
+ var IS_WINDOWS, CONFIG_DIR, CONFIG_PATH, cachedEnabled;
4862
4897
  var init_config2 = __esm({
4863
4898
  "src/telemetry/config.ts"() {
4864
4899
  "use strict";
4865
4900
  init_features();
4901
+ IS_WINDOWS = platform() === "win32";
4866
4902
  CONFIG_DIR = join3(homedir2(), ".brakit");
4867
4903
  CONFIG_PATH = join3(CONFIG_DIR, "config.json");
4868
4904
  cachedEnabled = null;
@@ -4870,9 +4906,49 @@ var init_config2 = __esm({
4870
4906
  });
4871
4907
 
4872
4908
  // src/telemetry/index.ts
4873
- import { platform, release, arch } from "os";
4909
+ import { platform as platform2, release, arch } from "os";
4874
4910
  import { spawn } from "child_process";
4911
+ function commonProperties() {
4912
+ return {
4913
+ brakit_version: VERSION,
4914
+ node_version: process.version,
4915
+ os: `${platform2()}-${release()}`,
4916
+ arch: arch(),
4917
+ $lib: "brakit",
4918
+ $process_person_profile: false,
4919
+ $geoip_disable: true
4920
+ };
4921
+ }
4922
+ function sendToPosthog(event, properties) {
4923
+ if (!isTelemetryEnabled()) return;
4924
+ const config = getOrCreateConfig();
4925
+ const payload = {
4926
+ api_key: POSTHOG_KEY,
4927
+ event,
4928
+ distinct_id: config.anonymousId,
4929
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
4930
+ properties: { ...commonProperties(), ...properties }
4931
+ };
4932
+ try {
4933
+ const body = JSON.stringify(payload);
4934
+ const url = `${POSTHOG_HOST}${POSTHOG_CAPTURE_PATH}`;
4935
+ const child = spawn(
4936
+ process.execPath,
4937
+ [
4938
+ "-e",
4939
+ `fetch(${JSON.stringify(url)},{method:"POST",headers:{"content-type":"application/json"},body:${JSON.stringify(body)},signal:AbortSignal.timeout(${POSTHOG_REQUEST_TIMEOUT_MS})}).catch(()=>{})`
4940
+ ],
4941
+ { detached: true, stdio: "ignore" }
4942
+ );
4943
+ child.unref();
4944
+ } catch {
4945
+ }
4946
+ }
4947
+ function trackEvent(event, properties) {
4948
+ sendToPosthog(event, { sdk: "node", ...properties });
4949
+ }
4875
4950
  function initSession(framework, packageManager, isCustomCommand, adapters) {
4951
+ getOrCreateConfig();
4876
4952
  session.startTime = Date.now();
4877
4953
  session.framework = framework;
4878
4954
  session.packageManager = packageManager;
@@ -4892,7 +4968,25 @@ function recordTabViewed(tab) {
4892
4968
  session.tabsViewed.add(tab);
4893
4969
  }
4894
4970
  function recordDashboardOpened() {
4971
+ if (session.dashboardOpened) return;
4895
4972
  session.dashboardOpened = true;
4973
+ session.dashboardOpenedAt = Date.now();
4974
+ trackEvent(TELEMETRY_EVENT_DASHBOARD_VIEWED, {
4975
+ time_to_dashboard_ms: session.startTime > 0 ? Date.now() - session.startTime : null,
4976
+ request_count_at_open: session.requestCount
4977
+ });
4978
+ }
4979
+ function recordSetupCompleted(info) {
4980
+ session.frameworkCandidates = info.frameworkCandidates;
4981
+ session.adaptersFailed = info.adaptersFailed;
4982
+ session.setupDurationMs = info.setupDurationMs;
4983
+ session.setupSucceeded = true;
4984
+ }
4985
+ function recordFirstRequest() {
4986
+ if (!session.firstRequestAt) session.firstRequestAt = Date.now();
4987
+ }
4988
+ function recordExitReason(reason) {
4989
+ if (session.exitReason === "unknown") session.exitReason = reason;
4896
4990
  }
4897
4991
  function speedBucket(ms) {
4898
4992
  if (ms === 0) return "none";
@@ -4906,7 +5000,6 @@ function speedBucket(ms) {
4906
5000
  function trackSession(services) {
4907
5001
  if (!isTelemetryEnabled()) return;
4908
5002
  const isFirstSession = readConfig() === null;
4909
- const config = getOrCreateConfig();
4910
5003
  const metricsStore = services.metricsStore;
4911
5004
  const analysisEngine = services.analysisEngine;
4912
5005
  const live = metricsStore.getLiveEndpoints();
@@ -4920,55 +5013,39 @@ function trackSession(services) {
4920
5013
  totalDuration += ep.summary.p95Ms * ep.summary.totalRequests;
4921
5014
  if (ep.summary.p95Ms > slowestP95) slowestP95 = ep.summary.p95Ms;
4922
5015
  }
4923
- const payload = {
4924
- api_key: POSTHOG_KEY,
4925
- event: "session",
4926
- distinct_id: config.anonymousId,
4927
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
4928
- properties: {
4929
- brakit_version: VERSION,
4930
- node_version: process.version,
4931
- os: `${platform()}-${release()}`,
4932
- arch: arch(),
4933
- framework: session.framework,
4934
- package_manager: session.packageManager,
4935
- is_custom_command: session.isCustomCommand,
4936
- first_session: isFirstSession,
4937
- adapters_detected: session.adapters,
4938
- request_count: session.requestCount,
4939
- error_count: services.errorStore.getAll().length,
4940
- query_count: services.queryStore.getAll().length,
4941
- fetch_count: services.fetchStore.getAll().length,
4942
- insight_count: insights.length,
4943
- finding_count: findings.length,
4944
- insight_types: [...session.insightTypes],
4945
- rules_triggered: [...session.rulesTriggered],
4946
- endpoint_count: live.length,
4947
- avg_duration_ms: totalRequests > 0 ? Math.round(totalDuration / totalRequests) : 0,
4948
- slowest_endpoint_bucket: speedBucket(slowestP95),
4949
- tabs_viewed: [...session.tabsViewed],
4950
- dashboard_opened: session.dashboardOpened,
4951
- explain_used: session.explainUsed,
4952
- session_duration_s: Math.round((Date.now() - session.startTime) / 1e3),
4953
- $lib: "brakit",
4954
- $process_person_profile: false,
4955
- $geoip_disable: true
4956
- }
4957
- };
4958
- try {
4959
- const body = JSON.stringify(payload);
4960
- const url = `${POSTHOG_HOST}${POSTHOG_CAPTURE_PATH}`;
4961
- const child = spawn(
4962
- process.execPath,
4963
- [
4964
- "-e",
4965
- `fetch(${JSON.stringify(url)},{method:"POST",headers:{"content-type":"application/json"},body:${JSON.stringify(body)},signal:AbortSignal.timeout(${POSTHOG_REQUEST_TIMEOUT_MS})}).catch(()=>{})`
4966
- ],
4967
- { detached: true, stdio: "ignore" }
4968
- );
4969
- child.unref();
4970
- } catch {
4971
- }
5016
+ const now = Date.now();
5017
+ sendToPosthog(TELEMETRY_EVENT_SESSION, {
5018
+ sdk: "node",
5019
+ framework: session.framework,
5020
+ package_manager: session.packageManager,
5021
+ is_custom_command: session.isCustomCommand,
5022
+ first_session: isFirstSession,
5023
+ adapters_detected: session.adapters,
5024
+ request_count: session.requestCount,
5025
+ error_count: services.errorStore.getAll().length,
5026
+ query_count: services.queryStore.getAll().length,
5027
+ fetch_count: services.fetchStore.getAll().length,
5028
+ insight_count: insights.length,
5029
+ finding_count: findings.length,
5030
+ insight_types: [...session.insightTypes],
5031
+ rules_triggered: [...session.rulesTriggered],
5032
+ endpoint_count: live.length,
5033
+ avg_duration_ms: totalRequests > 0 ? Math.round(totalDuration / totalRequests) : 0,
5034
+ slowest_endpoint_bucket: speedBucket(slowestP95),
5035
+ tabs_viewed: [...session.tabsViewed],
5036
+ dashboard_opened: session.dashboardOpened,
5037
+ explain_used: session.explainUsed,
5038
+ session_duration_s: Math.round((now - session.startTime) / 1e3),
5039
+ // Enhanced fields
5040
+ setup_succeeded: session.setupSucceeded,
5041
+ setup_duration_ms: session.setupDurationMs,
5042
+ framework_detection_candidates: session.frameworkCandidates,
5043
+ adapters_failed: session.adaptersFailed,
5044
+ time_to_first_request_ms: session.firstRequestAt ? session.firstRequestAt - session.startTime : null,
5045
+ time_to_dashboard_ms: session.dashboardOpenedAt ? session.dashboardOpenedAt - session.startTime : null,
5046
+ exit_reason: session.exitReason
5047
+ });
5048
+ getOrCreateConfig();
4972
5049
  }
4973
5050
  var POSTHOG_KEY, session;
4974
5051
  var init_telemetry = __esm({
@@ -4977,6 +5054,7 @@ var init_telemetry = __esm({
4977
5054
  init_src();
4978
5055
  init_config2();
4979
5056
  init_labels();
5057
+ init_config();
4980
5058
  init_config2();
4981
5059
  POSTHOG_KEY = "phc_E9TwydCGnSfPLIUhNxChpeg32TSowjk31KiPhnLPP0x";
4982
5060
  session = {
@@ -4990,7 +5068,14 @@ var init_telemetry = __esm({
4990
5068
  rulesTriggered: /* @__PURE__ */ new Set(),
4991
5069
  tabsViewed: /* @__PURE__ */ new Set(),
4992
5070
  dashboardOpened: false,
4993
- explainUsed: false
5071
+ explainUsed: false,
5072
+ frameworkCandidates: [],
5073
+ adaptersFailed: [],
5074
+ setupDurationMs: 0,
5075
+ setupSucceeded: false,
5076
+ firstRequestAt: 0,
5077
+ dashboardOpenedAt: 0,
5078
+ exitReason: "unknown"
4994
5079
  };
4995
5080
  }
4996
5081
  });
@@ -5992,18 +6077,21 @@ function installHooks(bus) {
5992
6077
  adapterRegistry.patchAll(telemetryEmit);
5993
6078
  const cwd = process.cwd();
5994
6079
  let framework = "unknown";
6080
+ let frameworkCandidates = [];
5995
6081
  try {
5996
6082
  const pkg = JSON.parse(
5997
- // readFileSync is acceptable here — runs once at startup
5998
6083
  __require("fs").readFileSync(resolve5(cwd, "package.json"), "utf-8")
5999
6084
  );
6000
6085
  const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
6001
6086
  framework = detectFrameworkFromDeps(allDeps);
6087
+ frameworkCandidates = KNOWN_DEPENDENCY_NAMES.filter((dep) => dep in allDeps);
6002
6088
  } catch {
6003
6089
  }
6004
6090
  return {
6005
6091
  framework,
6006
- adapterNames: adapterRegistry.getActive().map((a) => a.name)
6092
+ adapterNames: adapterRegistry.getActive().map((a) => a.name),
6093
+ adaptersFailed: [...adapterRegistry.getFailed()],
6094
+ frameworkCandidates
6007
6095
  };
6008
6096
  }
6009
6097
  function startAnalysis(bus, stores, dataDir, services) {
@@ -6058,7 +6146,14 @@ function registerLifecycle(allServices, stores, services, cwd) {
6058
6146
  }
6059
6147
  };
6060
6148
  health.setTeardown(runTeardown);
6149
+ process.on("SIGINT", () => {
6150
+ recordExitReason(EXIT_REASON_SIGINT);
6151
+ });
6152
+ process.on("SIGTERM", () => {
6153
+ recordExitReason(EXIT_REASON_SIGTERM);
6154
+ });
6061
6155
  process.on("beforeExit", () => {
6156
+ recordExitReason(EXIT_REASON_CLEAN);
6062
6157
  sendTelemetry();
6063
6158
  });
6064
6159
  process.on("exit", () => {
@@ -6066,6 +6161,7 @@ function registerLifecycle(allServices, stores, services, cwd) {
6066
6161
  });
6067
6162
  }
6068
6163
  async function doSetup() {
6164
+ const setupStart = Date.now();
6069
6165
  brakitDebug(`[setup] doSetup called at ${(/* @__PURE__ */ new Date()).toISOString()}`);
6070
6166
  const bus = new EventBus();
6071
6167
  const cwd = process.cwd();
@@ -6074,8 +6170,18 @@ async function doSetup() {
6074
6170
  bus,
6075
6171
  ...stores
6076
6172
  };
6077
- const { framework, adapterNames } = installHooks(bus);
6173
+ const { framework, adapterNames, adaptersFailed, frameworkCandidates } = installHooks(bus);
6078
6174
  initSession(framework, detectPackageManagerSync(cwd), false, adapterNames);
6175
+ const setupDurationMs = Date.now() - setupStart;
6176
+ recordSetupCompleted({ frameworkCandidates, adaptersFailed, setupDurationMs });
6177
+ trackEvent(TELEMETRY_EVENT_SETUP_COMPLETED, {
6178
+ framework,
6179
+ framework_detection_candidates: frameworkCandidates,
6180
+ adapters_detected: adapterNames,
6181
+ adapters_failed: adaptersFailed,
6182
+ hooks_installed: ["fetch", "console", "error"],
6183
+ setup_duration_ms: setupDurationMs
6184
+ });
6079
6185
  const dataDir = getProjectDataDir(cwd);
6080
6186
  const analysisServices = startAnalysis(bus, stores, dataDir, services);
6081
6187
  const config = {
@@ -6092,6 +6198,11 @@ async function doSetup() {
6092
6198
  onFirstRequest(port) {
6093
6199
  setBrakitPort(port);
6094
6200
  brakitDebug(`[setup] onFirstRequest fired, port=${port}`);
6201
+ recordFirstRequest();
6202
+ trackEvent(TELEMETRY_EVENT_FIRST_REQUEST, {
6203
+ port,
6204
+ time_to_first_request_ms: Date.now() - setupStart
6205
+ });
6095
6206
  void (async () => {
6096
6207
  try {
6097
6208
  const dir = resolve5(cwd, METRICS_DIR);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "brakit",
3
- "version": "0.9.0",
3
+ "version": "0.9.1",
4
4
  "description": "See what your API is really doing. Security scanning, N+1 detection, duplicate calls, DB queries — one command, zero config.",
5
5
  "type": "module",
6
6
  "bin": {