@presto1314w/vite-devtools-browser 0.2.2 → 0.3.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.
package/dist/cli.js CHANGED
@@ -143,6 +143,11 @@ export async function runCli(argv, io) {
143
143
  const res = await io.send("correlate-errors", { mapped, inlineSource, windowMs });
144
144
  exit(io, res, res.ok && res.data ? String(res.data) : "");
145
145
  }
146
+ if (cmd === "correlate" && arg === "renders") {
147
+ const windowMs = parseNumberFlag(args, "--window", 5000);
148
+ const res = await io.send("correlate-renders", { windowMs });
149
+ exit(io, res, res.ok && res.data ? String(res.data) : "");
150
+ }
146
151
  if (cmd === "diagnose" && arg === "hmr") {
147
152
  const mapped = args.includes("--mapped");
148
153
  const inlineSource = args.includes("--inline-source");
@@ -151,6 +156,11 @@ export async function runCli(argv, io) {
151
156
  const res = await io.send("diagnose-hmr", { mapped, inlineSource, windowMs, limit });
152
157
  exit(io, res, res.ok && res.data ? String(res.data) : "");
153
158
  }
159
+ if (cmd === "diagnose" && arg === "propagation") {
160
+ const windowMs = parseNumberFlag(args, "--window", 5000);
161
+ const res = await io.send("diagnose-propagation", { windowMs });
162
+ exit(io, res, res.ok && res.data ? String(res.data) : "");
163
+ }
154
164
  if (cmd === "logs") {
155
165
  const res = await io.send("logs");
156
166
  exit(io, res, res.ok && res.data ? String(res.data) : "");
@@ -185,63 +195,69 @@ export function exit(io, res, msg) {
185
195
  io.exit(0);
186
196
  }
187
197
  export function printUsage() {
188
- return `
189
- vite-browser - Programmatic access to Vue/React/Svelte DevTools and Vite dev server
190
-
191
- USAGE
192
- vite-browser <command> [options]
193
-
194
- BROWSER CONTROL
195
- open <url> [--cookies-json <file>] Launch browser and navigate
196
- close Close browser and daemon
197
- goto <url> Full-page navigation
198
- back Go back in history
199
- reload Reload current page
200
-
201
- FRAMEWORK DETECTION
202
- detect Detect framework (vue/react/svelte)
203
-
204
- VUE COMMANDS
205
- vue tree [id] Show Vue component tree or inspect component
206
- vue pinia [store] Show Pinia stores or inspect specific store
207
- vue router Show Vue Router information
208
-
209
- REACT COMMANDS
210
- react tree [id] Show React component tree or inspect component
211
-
212
- SVELTE COMMANDS
213
- svelte tree [id] Show Svelte component tree or inspect component
214
-
215
- VITE COMMANDS
216
- vite restart Restart Vite dev server
217
- vite hmr Show HMR summary
218
- vite hmr trace [--limit <n>] Show HMR timeline
219
- vite hmr clear Clear tracked HMR timeline
220
- vite runtime Show Vite runtime status
221
- vite module-graph [--filter <txt>] [--limit <n>]
222
- Show loaded Vite module resources
223
- vite module-graph trace [--filter <txt>] [--limit <n>]
224
- Show module additions/removals since baseline
225
- vite module-graph clear Clear module-graph baseline
226
- errors Show build/runtime errors
227
- errors --mapped Show errors with source-map mapping
228
- errors --mapped --inline-source Include mapped source snippets
229
- correlate errors [--window <ms>] Correlate current errors with recent HMR events
230
- correlate errors --mapped Correlate mapped errors with recent HMR events
231
- diagnose hmr [--window <ms>] Diagnose HMR failures from runtime, errors, and trace data
232
- diagnose hmr [--limit <n>] Control how many recent HMR trace entries are inspected
233
- logs Show dev server logs
234
-
235
- UTILITIES
236
- screenshot Save screenshot to temp file
237
- eval <script> Evaluate JavaScript in page context
238
- network [idx] List network requests or inspect one
239
-
240
- OPTIONS
241
- -h, --help Show this help message
198
+ return `
199
+ vite-browser - Programmatic access to Vue/React/Svelte DevTools and Vite dev server
200
+
201
+ USAGE
202
+ vite-browser <command> [options]
203
+
204
+ BROWSER CONTROL
205
+ open <url> [--cookies-json <file>] Launch browser and navigate
206
+ close Close browser and daemon
207
+ goto <url> Full-page navigation
208
+ back Go back in history
209
+ reload Reload current page
210
+
211
+ FRAMEWORK DETECTION
212
+ detect Detect framework (vue/react/svelte)
213
+
214
+ VUE COMMANDS
215
+ vue tree [id] Show Vue component tree or inspect component
216
+ vue pinia [store] Show Pinia stores or inspect specific store
217
+ vue router Show Vue Router information
218
+
219
+ REACT COMMANDS
220
+ react tree [id] Show React component tree or inspect component
221
+
222
+ SVELTE COMMANDS
223
+ svelte tree [id] Show Svelte component tree or inspect component
224
+
225
+ VITE COMMANDS
226
+ vite restart Restart Vite dev server
227
+ vite hmr Show HMR summary
228
+ vite hmr trace [--limit <n>] Show HMR timeline
229
+ vite hmr clear Clear tracked HMR timeline
230
+ vite runtime Show Vite runtime status
231
+ vite module-graph [--filter <txt>] [--limit <n>]
232
+ Show loaded Vite module resources
233
+ vite module-graph trace [--filter <txt>] [--limit <n>]
234
+ Show module additions/removals since baseline
235
+ vite module-graph clear Clear module-graph baseline
236
+ errors Show build/runtime errors
237
+ errors --mapped Show errors with source-map mapping
238
+ errors --mapped --inline-source Include mapped source snippets
239
+ correlate errors [--window <ms>] Correlate current errors with recent HMR events
240
+ correlate renders [--window <ms>] Summarize recent render/update propagation evidence
241
+ correlate errors --mapped Correlate mapped errors with recent HMR events
242
+ diagnose hmr [--window <ms>] Diagnose HMR failures from runtime, errors, and trace data
243
+ diagnose hmr [--limit <n>] Control how many recent HMR trace entries are inspected
244
+ diagnose propagation [--window <ms>]
245
+ Diagnose likely update -> render -> error propagation
246
+ logs Show dev server logs
247
+
248
+ UTILITIES
249
+ screenshot Save screenshot to temp file
250
+ eval <script> Evaluate JavaScript in page context
251
+ network [idx] List network requests or inspect one
252
+
253
+ OPTIONS
254
+ -h, --help Show this help message
242
255
  `;
243
256
  }
244
- if (process.argv[1] && import.meta.url.endsWith(process.argv[1].replaceAll("\\", "/"))) {
257
+ function isEntrypoint(argv1) {
258
+ return Boolean(argv1 && import.meta.url.endsWith(argv1.replaceAll("\\", "/")));
259
+ }
260
+ async function main() {
245
261
  await runCli(process.argv, {
246
262
  send,
247
263
  readFile: readFileSync,
@@ -250,3 +266,10 @@ if (process.argv[1] && import.meta.url.endsWith(process.argv[1].replaceAll("\\",
250
266
  exit: (code) => process.exit(code),
251
267
  });
252
268
  }
269
+ if (isEntrypoint(process.argv[1])) {
270
+ void main().catch((error) => {
271
+ const message = error instanceof Error ? error.message : String(error);
272
+ console.error(message);
273
+ process.exit(1);
274
+ });
275
+ }
@@ -1,3 +1,4 @@
1
+ import { type HmrVBEvent } from "./event-analysis.js";
1
2
  import type { VBEvent } from "./event-queue.js";
2
3
  export type CorrelationConfidence = "high" | "medium" | "low";
3
4
  export type ErrorCorrelation = {
@@ -6,7 +7,7 @@ export type ErrorCorrelation = {
6
7
  confidence: CorrelationConfidence;
7
8
  windowMs: number;
8
9
  matchingModules: string[];
9
- relatedEvents: VBEvent[];
10
+ relatedEvents: HmrVBEvent[];
10
11
  };
11
12
  export type RenderNetworkCorrelation = {
12
13
  summary: string;
package/dist/correlate.js CHANGED
@@ -1,21 +1,17 @@
1
- const MODULE_PATTERNS = [
2
- /\/src\/[^\s"'`):]+/g,
3
- /\/@fs\/[^\s"'`):]+/g,
4
- /[A-Za-z]:\\[^:\n]+/g,
5
- ];
1
+ import { extractModules, extractModulesFromHmrEvent, getHmrEvents, getNetworkEvents, getRenderEvents, uniqueStrings, } from "./event-analysis.js";
6
2
  export function correlateErrorWithHMR(errorText, events, windowMs = 5000) {
7
- const recentEvents = events.filter((event) => event.type === "hmr-update" || event.type === "hmr-error");
3
+ const recentEvents = getHmrEvents(events);
8
4
  if (recentEvents.length === 0)
9
5
  return null;
10
6
  const errorModules = extractModules(errorText);
11
7
  const matchedEvents = recentEvents.filter((event) => {
12
- const modules = extractModulesFromEvent(event);
8
+ const modules = extractModulesFromHmrEvent(event);
13
9
  if (errorModules.length === 0)
14
10
  return event.type === "hmr-error";
15
11
  return modules.some((module) => errorModules.includes(module));
16
12
  });
17
13
  const relatedEvents = matchedEvents.length > 0 ? matchedEvents : recentEvents;
18
- const matchingModules = unique(relatedEvents.flatMap((event) => extractModulesFromEvent(event)).filter((module) => errorModules.includes(module)));
14
+ const matchingModules = uniqueStrings(relatedEvents.flatMap((event) => extractModulesFromHmrEvent(event)).filter((module) => errorModules.includes(module)));
19
15
  const confidence = inferConfidence(errorModules, matchingModules, relatedEvents);
20
16
  const eventKind = relatedEvents.some((event) => event.type === "hmr-error") ? "HMR error" : "HMR update";
21
17
  const moduleText = matchingModules.length > 0
@@ -33,17 +29,17 @@ export function correlateErrorWithHMR(errorText, events, windowMs = 5000) {
33
29
  };
34
30
  }
35
31
  export function correlateRenderWithNetwork(events, requestThreshold = 3) {
36
- const renderEvents = events.filter((event) => event.type === "render");
37
- const networkEvents = events.filter((event) => event.type === "network");
32
+ const renderEvents = getRenderEvents(events);
33
+ const networkEvents = getNetworkEvents(events);
38
34
  if (renderEvents.length === 0 || networkEvents.length === 0)
39
35
  return null;
40
36
  const latestRender = renderEvents[renderEvents.length - 1];
41
37
  const start = latestRender.timestamp - 1000;
42
38
  const end = latestRender.timestamp + 1000;
43
39
  const overlappingNetwork = networkEvents.filter((event) => event.timestamp >= start && event.timestamp <= end);
44
- const urls = unique(overlappingNetwork
40
+ const urls = uniqueStrings(overlappingNetwork
45
41
  .map((event) => event.payload.url)
46
- .filter((url) => typeof url === "string"));
42
+ .filter((url) => typeof url === "string" && url.length > 0));
47
43
  if (overlappingNetwork.length < requestThreshold)
48
44
  return null;
49
45
  return {
@@ -64,14 +60,13 @@ export function formatErrorCorrelationReport(errorText, correlation) {
64
60
  return lines.join("\n");
65
61
  }
66
62
  function formatEventLine(event) {
67
- const payload = event.payload;
68
- const path = payload.path;
69
- const message = payload.message;
63
+ const path = event.payload.path;
64
+ const message = event.payload.message;
70
65
  if (typeof path === "string")
71
66
  return `- ${event.type}: ${path}`;
72
67
  if (typeof message === "string")
73
68
  return `- ${event.type}: ${message}`;
74
- return `- ${event.type}: ${JSON.stringify(payload)}`;
69
+ return `- ${event.type}: ${JSON.stringify(event.payload)}`;
75
70
  }
76
71
  function inferConfidence(errorModules, matchingModules, events) {
77
72
  if (matchingModules.length > 0)
@@ -82,29 +77,3 @@ function inferConfidence(errorModules, matchingModules, events) {
82
77
  return "low";
83
78
  return "medium";
84
79
  }
85
- function extractModulesFromEvent(event) {
86
- const payload = event.payload;
87
- const candidates = [];
88
- if (typeof payload.path === "string")
89
- candidates.push(payload.path);
90
- if (typeof payload.message === "string")
91
- candidates.push(payload.message);
92
- if (Array.isArray(payload.updates)) {
93
- for (const update of payload.updates) {
94
- if (update && typeof update === "object" && typeof update.path === "string") {
95
- candidates.push(update.path);
96
- }
97
- }
98
- }
99
- return unique(candidates.flatMap((candidate) => extractModules(candidate)));
100
- }
101
- function extractModules(text) {
102
- const matches = MODULE_PATTERNS.flatMap((pattern) => text.match(pattern) ?? []);
103
- return unique(matches.map(normalizeModulePath).filter(Boolean));
104
- }
105
- function normalizeModulePath(value) {
106
- return value.replace(/[),.:]+$/, "");
107
- }
108
- function unique(values) {
109
- return [...new Set(values)];
110
- }
package/dist/daemon.js CHANGED
@@ -4,7 +4,9 @@ import { fileURLToPath } from "node:url";
4
4
  import * as browser from "./browser.js";
5
5
  import { correlateErrorWithHMR, formatErrorCorrelationReport } from "./correlate.js";
6
6
  import { diagnoseHMR, formatDiagnosisReport } from "./diagnose.js";
7
+ import { diagnosePropagation, formatPropagationDiagnosisReport } from "./diagnose-propagation.js";
7
8
  import { socketDir, socketPath, pidFile } from "./paths.js";
9
+ import { correlateRenderPropagation, formatPropagationTraceReport } from "./trace.js";
8
10
  import { EventQueue } from "./event-queue.js";
9
11
  import * as networkLog from "./network.js";
10
12
  export function cleanError(err) {
@@ -111,6 +113,11 @@ export function createRunner(api = browser) {
111
113
  const data = formatErrorCorrelationReport(errorText, errorText === "no errors" ? null : correlateErrorWithHMR(errorText, events, cmd.windowMs ?? 5000));
112
114
  return { ok: true, data };
113
115
  }
116
+ if (cmd.action === "correlate-renders") {
117
+ const events = queue ? queue.window(cmd.windowMs ?? 5000) : [];
118
+ const data = formatPropagationTraceReport(correlateRenderPropagation(events));
119
+ return { ok: true, data };
120
+ }
114
121
  if (cmd.action === "diagnose-hmr") {
115
122
  const errorText = String(await api.errors(Boolean(cmd.mapped), Boolean(cmd.inlineSource)));
116
123
  const runtimeText = String(await api.viteRuntimeStatus());
@@ -120,6 +127,11 @@ export function createRunner(api = browser) {
120
127
  const data = formatDiagnosisReport(diagnoseHMR({ errorText, runtimeText, hmrTraceText, correlation }));
121
128
  return { ok: true, data };
122
129
  }
130
+ if (cmd.action === "diagnose-propagation") {
131
+ const events = queue ? queue.window(cmd.windowMs ?? 5000) : [];
132
+ const data = formatPropagationDiagnosisReport(diagnosePropagation(correlateRenderPropagation(events)));
133
+ return { ok: true, data };
134
+ }
123
135
  if (cmd.action === "logs") {
124
136
  const data = await api.logs();
125
137
  return { ok: true, data };
@@ -0,0 +1,10 @@
1
+ import type { PropagationTrace } from "./trace.js";
2
+ export type PropagationDiagnosis = {
3
+ status: "pass" | "warn" | "fail";
4
+ confidence: "high" | "medium" | "low";
5
+ summary: string;
6
+ detail: string;
7
+ suggestion: string;
8
+ };
9
+ export declare function diagnosePropagation(trace: PropagationTrace | null): PropagationDiagnosis;
10
+ export declare function formatPropagationDiagnosisReport(result: PropagationDiagnosis): string;
@@ -0,0 +1,58 @@
1
+ export function diagnosePropagation(trace) {
2
+ if (!trace) {
3
+ return {
4
+ status: "warn",
5
+ confidence: "low",
6
+ summary: "No propagation trace is available yet.",
7
+ detail: "The current event window does not contain render/update events, so propagation reasoning cannot start.",
8
+ suggestion: "Reproduce the issue once, then rerun `vite-browser correlate renders` or `vite-browser diagnose propagation`.",
9
+ };
10
+ }
11
+ if (trace.storeUpdates.length > 0 && trace.renderComponents.length > 0 && trace.errorMessages.length > 0) {
12
+ const changedKeys = trace.changedKeys.length > 0 ? ` Changed keys: ${trace.changedKeys.join(", ")}.` : "";
13
+ return {
14
+ status: "fail",
15
+ confidence: trace.confidence,
16
+ summary: "A plausible store -> render -> error propagation path was found.",
17
+ detail: `Start with store ${trace.storeUpdates[0]}, then inspect ${trace.renderComponents[0]} as the nearest affected component.${changedKeys}`,
18
+ suggestion: "Verify the recent store mutation first, then check whether the affected component or a dependent effect turns that state change into the visible failure.",
19
+ };
20
+ }
21
+ if (trace.sourceModules.length > 0 && trace.renderComponents.length > 0 && trace.errorMessages.length > 0) {
22
+ const storeHint = trace.storeHints.length > 0 ? ` Store hint: ${trace.storeHints[0]}.` : "";
23
+ return {
24
+ status: "fail",
25
+ confidence: trace.confidence,
26
+ summary: "A plausible update -> render -> error propagation path was found.",
27
+ detail: `Start with ${trace.sourceModules[0]}, then inspect ${trace.renderComponents[0]} as the nearest affected component.${storeHint}`,
28
+ suggestion: "Verify the updated source module first, then confirm whether the affected component consumes stale props, store state, or side effects.",
29
+ };
30
+ }
31
+ if (trace.renderComponents.length > 0 && trace.networkUrls.length > 1) {
32
+ return {
33
+ status: "warn",
34
+ confidence: trace.confidence,
35
+ summary: "Render activity overlaps with repeated network work.",
36
+ detail: `Observed render activity around ${trace.networkUrls.length} network request target(s).`,
37
+ suggestion: "Check whether rerenders are retriggering data fetches or invalidating derived state more often than expected.",
38
+ };
39
+ }
40
+ return {
41
+ status: "pass",
42
+ confidence: trace.confidence,
43
+ summary: "Propagation data is present but not yet conclusive.",
44
+ detail: "The current trace shows render activity, but not enough linked source/error evidence for a stronger diagnosis.",
45
+ suggestion: "Use the render correlation output to narrow the likely component path, then inspect source updates and runtime errors together.",
46
+ };
47
+ }
48
+ export function formatPropagationDiagnosisReport(result) {
49
+ return [
50
+ "# Propagation Diagnosis",
51
+ "",
52
+ `Status: ${result.status}`,
53
+ `Confidence: ${result.confidence}`,
54
+ result.summary,
55
+ result.detail,
56
+ `Suggestion: ${result.suggestion}`,
57
+ ].join("\n");
58
+ }
@@ -0,0 +1,32 @@
1
+ import type { ErrorEventPayload, HmrEventPayload, NetworkEventPayload, RenderEventPayload, StoreUpdatePayload, VBEvent } from "./event-queue.js";
2
+ export type HmrVBEvent = Extract<VBEvent, {
3
+ type: "hmr-update" | "hmr-error";
4
+ }>;
5
+ export type RenderVBEvent = Extract<VBEvent, {
6
+ type: "render";
7
+ }>;
8
+ export type StoreUpdateVBEvent = Extract<VBEvent, {
9
+ type: "store-update";
10
+ }>;
11
+ export type NetworkVBEvent = Extract<VBEvent, {
12
+ type: "network";
13
+ }>;
14
+ export type ErrorVBEvent = Extract<VBEvent, {
15
+ type: "error";
16
+ }>;
17
+ export declare function sortEventsChronologically(events: VBEvent[]): VBEvent[];
18
+ export declare function getHmrEvents(events: VBEvent[]): HmrVBEvent[];
19
+ export declare function getRenderEvents(events: VBEvent[]): RenderVBEvent[];
20
+ export declare function getStoreUpdateEvents(events: VBEvent[]): StoreUpdateVBEvent[];
21
+ export declare function getNetworkEvents(events: VBEvent[]): NetworkVBEvent[];
22
+ export declare function getErrorEvents(events: VBEvent[]): ErrorVBEvent[];
23
+ export declare function extractModules(text: string): string[];
24
+ export declare function extractModulesFromHmrPayload(payload: HmrEventPayload): string[];
25
+ export declare function extractModulesFromHmrEvent(event: HmrVBEvent): string[];
26
+ export declare function getRenderLabel(payload: RenderEventPayload): string;
27
+ export declare function getStoreName(payload: StoreUpdatePayload): string | null;
28
+ export declare function getChangedKeys(payload: StoreUpdatePayload): string[];
29
+ export declare function getStoreHints(payload: RenderEventPayload): string[];
30
+ export declare function getNetworkUrl(payload: NetworkEventPayload): string | null;
31
+ export declare function getErrorMessage(payload: ErrorEventPayload): string | null;
32
+ export declare function uniqueStrings(values: string[]): string[];
@@ -0,0 +1,75 @@
1
+ const MODULE_PATTERNS = [
2
+ /\/src\/[^\s"'`):]+/g,
3
+ /\/@fs\/[^\s"'`)]+/g,
4
+ /[A-Za-z]:\\[^:\n]+/g,
5
+ ];
6
+ export function sortEventsChronologically(events) {
7
+ return events.slice().sort((left, right) => left.timestamp - right.timestamp);
8
+ }
9
+ export function getHmrEvents(events) {
10
+ return events.filter((event) => event.type === "hmr-update" || event.type === "hmr-error");
11
+ }
12
+ export function getRenderEvents(events) {
13
+ return events.filter((event) => event.type === "render");
14
+ }
15
+ export function getStoreUpdateEvents(events) {
16
+ return events.filter((event) => event.type === "store-update");
17
+ }
18
+ export function getNetworkEvents(events) {
19
+ return events.filter((event) => event.type === "network");
20
+ }
21
+ export function getErrorEvents(events) {
22
+ return events.filter((event) => event.type === "error");
23
+ }
24
+ export function extractModules(text) {
25
+ const matches = MODULE_PATTERNS.flatMap((pattern) => text.match(pattern) ?? []);
26
+ return uniqueStrings(matches.map(normalizeModulePath).filter(Boolean));
27
+ }
28
+ export function extractModulesFromHmrPayload(payload) {
29
+ const candidates = [];
30
+ if (typeof payload.path === "string")
31
+ candidates.push(payload.path);
32
+ if (typeof payload.message === "string")
33
+ candidates.push(payload.message);
34
+ if (Array.isArray(payload.updates)) {
35
+ for (const update of payload.updates) {
36
+ if (typeof update?.path === "string") {
37
+ candidates.push(update.path);
38
+ }
39
+ }
40
+ }
41
+ return uniqueStrings(candidates.flatMap(extractModules));
42
+ }
43
+ export function extractModulesFromHmrEvent(event) {
44
+ return extractModulesFromHmrPayload(event.payload);
45
+ }
46
+ export function getRenderLabel(payload) {
47
+ if (payload.path.length > 0)
48
+ return payload.path;
49
+ if (payload.component.length > 0)
50
+ return payload.component;
51
+ return "anonymous-render";
52
+ }
53
+ export function getStoreName(payload) {
54
+ return payload.store.length > 0 ? payload.store : null;
55
+ }
56
+ export function getChangedKeys(payload) {
57
+ return payload.changedKeys.filter((value) => value.length > 0);
58
+ }
59
+ export function getStoreHints(payload) {
60
+ return payload.storeHints.filter((value) => value.length > 0);
61
+ }
62
+ export function getNetworkUrl(payload) {
63
+ return payload.url.length > 0 ? payload.url : null;
64
+ }
65
+ export function getErrorMessage(payload) {
66
+ return typeof payload.message === "string" && payload.message.length > 0 ? payload.message : null;
67
+ }
68
+ export function uniqueStrings(values) {
69
+ return [...new Set(values)];
70
+ }
71
+ function normalizeModulePath(value) {
72
+ return value
73
+ .replace(/:\d+:\d+$/, "")
74
+ .replace(/[),.:]+$/, "");
75
+ }
@@ -1,9 +1,74 @@
1
- export type VBEventType = 'hmr-update' | 'hmr-error' | 'module-change' | 'network' | 'error' | 'render';
2
- export interface VBEvent {
1
+ export type HmrEventPayload = {
2
+ type?: string;
3
+ path?: string;
4
+ message?: string;
5
+ updates?: Array<{
6
+ path?: string;
7
+ }>;
8
+ [key: string]: unknown;
9
+ };
10
+ export type StoreUpdatePayload = {
11
+ store: string;
12
+ mutationType: string;
13
+ events: number;
14
+ changedKeys: string[];
15
+ };
16
+ export type NetworkEventPayload = {
17
+ url: string;
18
+ method?: string;
19
+ status?: number;
20
+ ms?: number;
21
+ };
22
+ export type ErrorEventPayload = {
23
+ message?: string;
24
+ source?: string | null;
25
+ line?: number | null;
26
+ col?: number | null;
27
+ stack?: string;
28
+ };
29
+ export type RenderEventPayload = {
30
+ component: string;
31
+ path: string;
32
+ framework: "vue" | "react" | "svelte" | "unknown";
33
+ reason: string;
34
+ mutationCount: number;
35
+ storeHints: string[];
36
+ changedKeys: string[];
37
+ };
38
+ export type ModuleChangePayload = {
39
+ path?: string;
40
+ [key: string]: unknown;
41
+ };
42
+ export type VBEvent = {
3
43
  timestamp: number;
4
- type: VBEventType;
5
- payload: unknown;
6
- }
44
+ type: "hmr-update";
45
+ payload: HmrEventPayload;
46
+ } | {
47
+ timestamp: number;
48
+ type: "hmr-error";
49
+ payload: HmrEventPayload;
50
+ } | {
51
+ timestamp: number;
52
+ type: "module-change";
53
+ payload: ModuleChangePayload;
54
+ } | {
55
+ timestamp: number;
56
+ type: "store-update";
57
+ payload: StoreUpdatePayload;
58
+ } | {
59
+ timestamp: number;
60
+ type: "network";
61
+ payload: NetworkEventPayload;
62
+ } | {
63
+ timestamp: number;
64
+ type: "error";
65
+ payload: ErrorEventPayload;
66
+ } | {
67
+ timestamp: number;
68
+ type: "render";
69
+ payload: RenderEventPayload;
70
+ };
71
+ export type VBEventType = VBEvent["type"];
7
72
  export declare class EventQueue {
8
73
  private events;
9
74
  private readonly maxSize;
@@ -0,0 +1,15 @@
1
+ import type { VBEvent } from "./event-queue.js";
2
+ export type PropagationTrace = {
3
+ summary: string;
4
+ confidence: "high" | "medium" | "low";
5
+ sourceModules: string[];
6
+ storeUpdates: string[];
7
+ changedKeys: string[];
8
+ renderComponents: string[];
9
+ storeHints: string[];
10
+ networkUrls: string[];
11
+ errorMessages: string[];
12
+ events: VBEvent[];
13
+ };
14
+ export declare function correlateRenderPropagation(events: VBEvent[]): PropagationTrace | null;
15
+ export declare function formatPropagationTraceReport(trace: PropagationTrace | null): string;
package/dist/trace.js ADDED
@@ -0,0 +1,75 @@
1
+ import { extractModulesFromHmrEvent, getChangedKeys, getErrorEvents, getErrorMessage, getHmrEvents, getNetworkEvents, getNetworkUrl, getRenderEvents, getRenderLabel, getStoreHints, getStoreName, getStoreUpdateEvents, sortEventsChronologically, uniqueStrings, } from "./event-analysis.js";
2
+ export function correlateRenderPropagation(events) {
3
+ const recent = sortEventsChronologically(events);
4
+ const renderEvents = getRenderEvents(recent);
5
+ if (renderEvents.length === 0)
6
+ return null;
7
+ const hmrEvents = getHmrEvents(recent);
8
+ const storeEvents = getStoreUpdateEvents(recent);
9
+ const networkEvents = getNetworkEvents(recent);
10
+ const errorEvents = getErrorEvents(recent);
11
+ const sourceModules = uniqueStrings(hmrEvents.flatMap(extractModulesFromHmrEvent));
12
+ const storeUpdates = uniqueStrings(storeEvents.map((event) => getStoreName(event.payload)).filter((value) => value != null));
13
+ const changedKeys = uniqueStrings(storeEvents.flatMap((event) => getChangedKeys(event.payload)));
14
+ const renderComponents = uniqueStrings(renderEvents.map((event) => getRenderLabel(event.payload)).filter(Boolean));
15
+ const storeHints = uniqueStrings(renderEvents.flatMap((event) => getStoreHints(event.payload)));
16
+ const networkUrls = uniqueStrings(networkEvents.map((event) => getNetworkUrl(event.payload)).filter((value) => value != null));
17
+ const errorMessages = uniqueStrings(errorEvents.map((event) => getErrorMessage(event.payload)).filter((value) => value != null));
18
+ const confidence = inferConfidence(sourceModules, storeUpdates, renderComponents, errorMessages);
19
+ const eventCount = hmrEvents.length + storeEvents.length + renderEvents.length + networkEvents.length + errorEvents.length;
20
+ return {
21
+ summary: sourceModules.length > 0 || storeUpdates.length > 0
22
+ ? `Recent source/store updates likely propagated through ${renderComponents.length || 1} render step(s).`
23
+ : `Render activity observed${errorMessages.length > 0 ? " near the current failure" : ""}.`,
24
+ confidence,
25
+ sourceModules,
26
+ storeUpdates,
27
+ changedKeys,
28
+ renderComponents,
29
+ storeHints,
30
+ networkUrls,
31
+ errorMessages,
32
+ events: recent.slice(-Math.max(eventCount, 1)),
33
+ };
34
+ }
35
+ export function formatPropagationTraceReport(trace) {
36
+ const lines = ["# Render Correlation"];
37
+ if (!trace) {
38
+ lines.push("", "No render/update events available in the current event window.");
39
+ return lines.join("\n");
40
+ }
41
+ lines.push("", `Confidence: ${trace.confidence}`, trace.summary, "");
42
+ lines.push("## Source Updates");
43
+ lines.push(...formatList(trace.sourceModules));
44
+ if (trace.storeUpdates.length > 0) {
45
+ lines.push("", "## Store Updates", ...formatList(trace.storeUpdates));
46
+ }
47
+ if (trace.changedKeys.length > 0) {
48
+ lines.push("", "## Changed Keys", ...formatList(trace.changedKeys));
49
+ }
50
+ lines.push("");
51
+ lines.push("## Render Path");
52
+ lines.push(...formatList(trace.renderComponents));
53
+ if (trace.storeHints.length > 0) {
54
+ lines.push("", "## Store Hints", ...formatList(trace.storeHints));
55
+ }
56
+ if (trace.networkUrls.length > 0) {
57
+ lines.push("", "## Network Activity", ...formatList(trace.networkUrls));
58
+ }
59
+ if (trace.errorMessages.length > 0) {
60
+ lines.push("", "## Errors", ...formatList(trace.errorMessages));
61
+ }
62
+ return lines.join("\n");
63
+ }
64
+ function inferConfidence(sourceModules, storeUpdates, renderComponents, errorMessages) {
65
+ if ((sourceModules.length > 0 || storeUpdates.length > 0) && renderComponents.length > 0 && errorMessages.length > 0)
66
+ return "high";
67
+ if (renderComponents.length > 0 && (sourceModules.length > 0 || storeUpdates.length > 0 || errorMessages.length > 0))
68
+ return "medium";
69
+ return "low";
70
+ }
71
+ function formatList(values) {
72
+ if (values.length === 0)
73
+ return ["(none)"];
74
+ return values.map((value) => `- ${value}`);
75
+ }