agent-dbg 0.1.9 → 0.2.0
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/.claude/skills/agent-dbg/SKILL.md +44 -14
- package/.claude/skills/agent-dbg/references/commands.md +4 -0
- package/README.md +65 -29
- package/dist/main.js +362 -98
- package/package.json +3 -2
- package/src/cdp/client.ts +67 -6
- package/src/cdp/jsc-client.ts +58 -0
- package/src/cdp/jsc-protocol.d.ts +2807 -0
- package/src/daemon/adapters/bun-adapter.ts +190 -0
- package/src/daemon/adapters/index.ts +13 -0
- package/src/daemon/adapters/node-adapter.ts +121 -0
- package/src/daemon/runtime-adapter.ts +50 -0
- package/src/daemon/session-blackbox.ts +2 -6
- package/src/daemon/session-breakpoints.ts +64 -53
- package/src/daemon/session-inspection.ts +2 -2
- package/src/daemon/session-state.ts +1 -1
- package/src/daemon/session.ts +46 -44
- package/src/formatter/source.ts +6 -2
- package/tests/fixtures/bun-simple.js +12 -0
- package/tests/integration/bun-launch.test.ts +135 -0
- package/tests/unit/cdp-client.test.ts +134 -10
- package/tests/unit/jsc-client.test.ts +165 -0
- package/demo/DEMO.md +0 -71
- package/demo/order-processor.js +0 -35
- package/tests/fixtures/dap/hello +0 -0
- package/tests/fixtures/dap/hello.dSYM/Contents/Info.plist +0 -20
- package/tests/fixtures/dap/hello.dSYM/Contents/Resources/DWARF/hello +0 -0
- package/tests/fixtures/dap/hello.dSYM/Contents/Resources/Relocations/aarch64/hello.yml +0 -5
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import type Protocol from "devtools-protocol/types/protocol.js";
|
|
2
|
+
import type { CdpClient } from "../../cdp/client.ts";
|
|
3
|
+
import { JscClient } from "../../cdp/jsc-client.ts";
|
|
4
|
+
import type { RuntimeAdapter } from "../runtime-adapter.ts";
|
|
5
|
+
import type { DebugSession, ScriptInfo } from "../session.ts";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* BunAdapter handles WebKit Inspector Protocol differences from CDP.
|
|
9
|
+
*
|
|
10
|
+
* Key divergences from Node.js (CDP):
|
|
11
|
+
* - Breakpoints use Debugger.setBreakpoint (by scriptId) instead of setBreakpointByUrl
|
|
12
|
+
* - Conditions use `options: { condition }` instead of top-level `condition`
|
|
13
|
+
* - getPossibleBreakpoints → getBreakpointLocations (different API shape)
|
|
14
|
+
* - setBlackboxPatterns → setShouldBlackboxURL (per-URL instead of batch)
|
|
15
|
+
* - Inspector.enable must be called before other domains
|
|
16
|
+
* - Inspector.initialized starts JS execution (replaces runIfWaitingForDebugger)
|
|
17
|
+
*/
|
|
18
|
+
export class BunAdapter implements RuntimeAdapter {
|
|
19
|
+
readonly name = "bun" as const;
|
|
20
|
+
readonly internalUrlPrefix = "bun:";
|
|
21
|
+
private jsc: JscClient | null = null;
|
|
22
|
+
|
|
23
|
+
async preEnable(cdp: CdpClient): Promise<void> {
|
|
24
|
+
this.jsc = new JscClient(cdp);
|
|
25
|
+
await this.jsc.send("Inspector.enable");
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Pause at the entry script under Bun's --inspect-brk.
|
|
30
|
+
*
|
|
31
|
+
* Bun evaluates dependencies (node:, bun:) before the entry script.
|
|
32
|
+
* We use setPauseForInternalScripts(false) to skip those, then a single
|
|
33
|
+
* urlRegex breakpoint at line 1 to catch the entry script. Line 1
|
|
34
|
+
* breakpoints auto-resolve to the nearest breakable location in JSC,
|
|
35
|
+
* unlike line 0 which silently fails.
|
|
36
|
+
*/
|
|
37
|
+
async waitForBrkPause(session: DebugSession): Promise<void> {
|
|
38
|
+
if (!this.jsc) return;
|
|
39
|
+
|
|
40
|
+
await this.jsc.send("Debugger.setBreakpointsActive", { active: true });
|
|
41
|
+
await this.jsc.send("Debugger.setPauseForInternalScripts", { shouldPause: false });
|
|
42
|
+
|
|
43
|
+
const entryScript = this.resolveEntryScript(session);
|
|
44
|
+
const tempBpId = await this.setEntryBreakpoint(entryScript);
|
|
45
|
+
try {
|
|
46
|
+
const waiter = session.createPauseWaiter(5_000);
|
|
47
|
+
await this.jsc.send("Inspector.initialized");
|
|
48
|
+
await waiter;
|
|
49
|
+
} finally {
|
|
50
|
+
if (tempBpId && this.jsc.connected) {
|
|
51
|
+
try {
|
|
52
|
+
await this.jsc.send("Debugger.removeBreakpoint", { breakpointId: tempBpId });
|
|
53
|
+
} catch {
|
|
54
|
+
// Already removed or disconnected
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async setBreakpointByLocation(
|
|
61
|
+
_cdp: CdpClient,
|
|
62
|
+
params: {
|
|
63
|
+
file: string;
|
|
64
|
+
line: number;
|
|
65
|
+
column?: number;
|
|
66
|
+
condition?: string;
|
|
67
|
+
url?: string;
|
|
68
|
+
urlRegex?: string;
|
|
69
|
+
scriptId?: string;
|
|
70
|
+
scripts: Map<string, ScriptInfo>;
|
|
71
|
+
},
|
|
72
|
+
): Promise<{
|
|
73
|
+
breakpointId: string;
|
|
74
|
+
location?: { scriptId: string; lineNumber: number; columnNumber?: number };
|
|
75
|
+
url?: string;
|
|
76
|
+
}> {
|
|
77
|
+
const jsc = this.ensureJsc();
|
|
78
|
+
const scriptId = params.scriptId ?? this.findScriptId(params.scripts, params.url);
|
|
79
|
+
if (!scriptId) {
|
|
80
|
+
throw new Error(`Cannot find script for "${params.file}" — ensure the script is loaded`);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const r = await jsc.send("Debugger.setBreakpoint", {
|
|
84
|
+
location: {
|
|
85
|
+
scriptId,
|
|
86
|
+
lineNumber: params.line - 1,
|
|
87
|
+
columnNumber: params.column,
|
|
88
|
+
},
|
|
89
|
+
options: params.condition ? { condition: params.condition } : undefined,
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
return {
|
|
93
|
+
breakpointId: r.breakpointId,
|
|
94
|
+
location: r.actualLocation,
|
|
95
|
+
url: params.url,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
async getBreakableLocations(
|
|
100
|
+
_cdp: CdpClient,
|
|
101
|
+
scriptId: string,
|
|
102
|
+
startLine: number,
|
|
103
|
+
endLine: number,
|
|
104
|
+
): Promise<Array<{ line: number; column: number }>> {
|
|
105
|
+
const jsc = this.ensureJsc();
|
|
106
|
+
const r = await jsc.send("Debugger.getBreakpointLocations", {
|
|
107
|
+
start: { scriptId, lineNumber: startLine - 1 },
|
|
108
|
+
end: { scriptId, lineNumber: endLine },
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
return (r.locations ?? []).map((loc) => ({
|
|
112
|
+
line: loc.lineNumber + 1,
|
|
113
|
+
column: (loc.columnNumber ?? 0) + 1,
|
|
114
|
+
}));
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
async getProperties(
|
|
118
|
+
cdp: CdpClient,
|
|
119
|
+
params: Protocol.Runtime.GetPropertiesRequest,
|
|
120
|
+
): Promise<Protocol.Runtime.GetPropertiesResponse> {
|
|
121
|
+
const raw = (await cdp.sendRaw(
|
|
122
|
+
"Runtime.getProperties",
|
|
123
|
+
params as unknown as Record<string, unknown>,
|
|
124
|
+
)) as Record<string, unknown>;
|
|
125
|
+
// WebKit returns {properties: [...]} instead of {result: [...]}
|
|
126
|
+
if ("properties" in raw && !("result" in raw)) {
|
|
127
|
+
raw.result = raw.properties;
|
|
128
|
+
delete raw.properties;
|
|
129
|
+
}
|
|
130
|
+
return raw as unknown as Protocol.Runtime.GetPropertiesResponse;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
async setBlackboxPatterns(_cdp: CdpClient, patterns: string[]): Promise<void> {
|
|
134
|
+
const jsc = this.ensureJsc();
|
|
135
|
+
for (const pattern of patterns) {
|
|
136
|
+
await jsc.send("Debugger.setShouldBlackboxURL", {
|
|
137
|
+
url: pattern,
|
|
138
|
+
caseSensitive: false,
|
|
139
|
+
shouldBlackbox: true,
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// -- Private helpers --------------------------------------------------
|
|
145
|
+
|
|
146
|
+
private ensureJsc(): JscClient {
|
|
147
|
+
if (!this.jsc) throw new Error("JscClient not initialized — call preEnable first");
|
|
148
|
+
return this.jsc;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/** Set a single urlRegex breakpoint at line 1 of the entry script. */
|
|
152
|
+
private async setEntryBreakpoint(entryScript: string | null): Promise<string | null> {
|
|
153
|
+
if (!entryScript || !this.jsc) return null;
|
|
154
|
+
|
|
155
|
+
const parts = entryScript.split("/");
|
|
156
|
+
const filename = parts[parts.length - 1] ?? entryScript;
|
|
157
|
+
const urlRegex = `${escapeRegex(filename)}$`;
|
|
158
|
+
|
|
159
|
+
try {
|
|
160
|
+
const r = await this.jsc.send("Debugger.setBreakpointByUrl", {
|
|
161
|
+
urlRegex,
|
|
162
|
+
lineNumber: 1,
|
|
163
|
+
});
|
|
164
|
+
return r.breakpointId;
|
|
165
|
+
} catch {
|
|
166
|
+
return null;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
private findScriptId(scripts: Map<string, ScriptInfo>, url?: string): string | undefined {
|
|
171
|
+
if (!url) return undefined;
|
|
172
|
+
for (const [sid, info] of scripts) {
|
|
173
|
+
if (info.url === url) return sid;
|
|
174
|
+
}
|
|
175
|
+
return undefined;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
private resolveEntryScript(session: DebugSession): string | null {
|
|
179
|
+
if (!session.launchCommand) return null;
|
|
180
|
+
for (let i = session.launchCommand.length - 1; i >= 0; i--) {
|
|
181
|
+
const arg = session.launchCommand[i] as string;
|
|
182
|
+
if (!arg.startsWith("-")) return arg;
|
|
183
|
+
}
|
|
184
|
+
return null;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function escapeRegex(s: string): string {
|
|
189
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
190
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { RuntimeAdapter } from "../runtime-adapter.ts";
|
|
2
|
+
import { BunAdapter } from "./bun-adapter.ts";
|
|
3
|
+
import { NodeAdapter } from "./node-adapter.ts";
|
|
4
|
+
|
|
5
|
+
export function createAdapter(command: string[]): RuntimeAdapter {
|
|
6
|
+
const bin = command[0]?.split("/").pop();
|
|
7
|
+
if (bin === "bun" || bin === "bunx") return new BunAdapter();
|
|
8
|
+
// Default to NodeAdapter for "node", "nodejs", and unknown runtimes
|
|
9
|
+
return new NodeAdapter();
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export { BunAdapter } from "./bun-adapter.ts";
|
|
13
|
+
export { NodeAdapter } from "./node-adapter.ts";
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import type Protocol from "devtools-protocol/types/protocol.js";
|
|
2
|
+
import type { CdpClient } from "../../cdp/client.ts";
|
|
3
|
+
import type { RuntimeAdapter } from "../runtime-adapter.ts";
|
|
4
|
+
import type { DebugSession, ScriptInfo } from "../session.ts";
|
|
5
|
+
|
|
6
|
+
export class NodeAdapter implements RuntimeAdapter {
|
|
7
|
+
readonly name = "node" as const;
|
|
8
|
+
readonly internalUrlPrefix = "node:";
|
|
9
|
+
|
|
10
|
+
async preEnable(_cdp: CdpClient): Promise<void> {
|
|
11
|
+
// Node.js doesn't need anything before enableDomains()
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
async waitForBrkPause(session: DebugSession): Promise<void> {
|
|
15
|
+
// Give the Debugger.paused event a moment to arrive (older Node.js)
|
|
16
|
+
if (!session.isPaused()) {
|
|
17
|
+
await Bun.sleep(100);
|
|
18
|
+
}
|
|
19
|
+
// On Node.js v24+, --inspect-brk does not emit Debugger.paused when the
|
|
20
|
+
// debugger connects after the process is already paused. We request an
|
|
21
|
+
// explicit pause and then signal Runtime.runIfWaitingForDebugger so the
|
|
22
|
+
// process starts execution and immediately hits our pause request.
|
|
23
|
+
if (!session.isPaused() && session.cdp) {
|
|
24
|
+
await session.cdp.send("Debugger.pause");
|
|
25
|
+
await session.cdp.send("Runtime.runIfWaitingForDebugger");
|
|
26
|
+
const deadline = Date.now() + 2_000;
|
|
27
|
+
while (!session.isPaused() && Date.now() < deadline) {
|
|
28
|
+
await Bun.sleep(50);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
// On Node.js v24+, the initial --inspect-brk pause lands in an internal
|
|
32
|
+
// bootstrap module (node:internal/...) rather than the user script.
|
|
33
|
+
// Resume past internal pauses until we reach user code.
|
|
34
|
+
await this.skipInternalPauses(session);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async setBreakpointByLocation(
|
|
38
|
+
cdp: CdpClient,
|
|
39
|
+
params: {
|
|
40
|
+
file: string;
|
|
41
|
+
line: number;
|
|
42
|
+
column?: number;
|
|
43
|
+
condition?: string;
|
|
44
|
+
url?: string;
|
|
45
|
+
urlRegex?: string;
|
|
46
|
+
scriptId?: string;
|
|
47
|
+
scripts: Map<string, ScriptInfo>;
|
|
48
|
+
},
|
|
49
|
+
): Promise<{
|
|
50
|
+
breakpointId: string;
|
|
51
|
+
location?: { scriptId: string; lineNumber: number; columnNumber?: number };
|
|
52
|
+
url?: string;
|
|
53
|
+
}> {
|
|
54
|
+
const bpParams: Protocol.Debugger.SetBreakpointByUrlRequest = {
|
|
55
|
+
lineNumber: params.line - 1, // CDP uses 0-based lines
|
|
56
|
+
};
|
|
57
|
+
if (params.column !== undefined) {
|
|
58
|
+
bpParams.columnNumber = params.column;
|
|
59
|
+
}
|
|
60
|
+
if (params.urlRegex) {
|
|
61
|
+
bpParams.urlRegex = params.urlRegex;
|
|
62
|
+
} else if (params.url) {
|
|
63
|
+
bpParams.url = params.url;
|
|
64
|
+
}
|
|
65
|
+
if (params.condition) {
|
|
66
|
+
bpParams.condition = params.condition;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const r = await cdp.send("Debugger.setBreakpointByUrl", bpParams);
|
|
70
|
+
const loc = r.locations[0];
|
|
71
|
+
return {
|
|
72
|
+
breakpointId: r.breakpointId,
|
|
73
|
+
location: loc
|
|
74
|
+
? { scriptId: loc.scriptId, lineNumber: loc.lineNumber, columnNumber: loc.columnNumber }
|
|
75
|
+
: undefined,
|
|
76
|
+
url: params.url,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
async getBreakableLocations(
|
|
81
|
+
cdp: CdpClient,
|
|
82
|
+
scriptId: string,
|
|
83
|
+
startLine: number,
|
|
84
|
+
endLine: number,
|
|
85
|
+
): Promise<Array<{ line: number; column: number }>> {
|
|
86
|
+
const r = await cdp.send("Debugger.getPossibleBreakpoints", {
|
|
87
|
+
start: { scriptId, lineNumber: startLine - 1 },
|
|
88
|
+
end: { scriptId, lineNumber: endLine },
|
|
89
|
+
});
|
|
90
|
+
return r.locations.map((loc) => ({
|
|
91
|
+
line: loc.lineNumber + 1,
|
|
92
|
+
column: (loc.columnNumber ?? 0) + 1,
|
|
93
|
+
}));
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
async getProperties(
|
|
97
|
+
cdp: CdpClient,
|
|
98
|
+
params: Protocol.Runtime.GetPropertiesRequest,
|
|
99
|
+
): Promise<Protocol.Runtime.GetPropertiesResponse> {
|
|
100
|
+
return cdp.send("Runtime.getProperties", params);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
async setBlackboxPatterns(cdp: CdpClient, patterns: string[]): Promise<void> {
|
|
104
|
+
await cdp.send("Debugger.setBlackboxPatterns", { patterns });
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
private async skipInternalPauses(session: DebugSession): Promise<void> {
|
|
108
|
+
let skips = 0;
|
|
109
|
+
while (
|
|
110
|
+
session.isPaused() &&
|
|
111
|
+
session.cdp &&
|
|
112
|
+
session.pauseInfo?.url?.startsWith(this.internalUrlPrefix) &&
|
|
113
|
+
skips < 5
|
|
114
|
+
) {
|
|
115
|
+
skips++;
|
|
116
|
+
const waiter = session.createPauseWaiter(5_000);
|
|
117
|
+
await session.cdp.send("Debugger.resume");
|
|
118
|
+
await waiter;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type Protocol from "devtools-protocol/types/protocol.js";
|
|
2
|
+
import type { CdpClient } from "../cdp/client.ts";
|
|
3
|
+
import type { DebugSession, ScriptInfo } from "./session.ts";
|
|
4
|
+
|
|
5
|
+
export interface RuntimeAdapter {
|
|
6
|
+
readonly name: "node" | "bun" | "unknown";
|
|
7
|
+
readonly internalUrlPrefix: string;
|
|
8
|
+
|
|
9
|
+
/** Pre-enable hook: called after CDP connects, before enableDomains() */
|
|
10
|
+
preEnable(cdp: CdpClient): Promise<void>;
|
|
11
|
+
|
|
12
|
+
/** Handle --inspect-brk initial pause for this runtime */
|
|
13
|
+
waitForBrkPause(session: DebugSession): Promise<void>;
|
|
14
|
+
|
|
15
|
+
/** Get properties of a remote object, handling runtime-specific API differences */
|
|
16
|
+
getProperties(
|
|
17
|
+
cdp: CdpClient,
|
|
18
|
+
params: Protocol.Runtime.GetPropertiesRequest,
|
|
19
|
+
): Promise<Protocol.Runtime.GetPropertiesResponse>;
|
|
20
|
+
|
|
21
|
+
/** Set a breakpoint at a specific location, handling runtime-specific API differences */
|
|
22
|
+
setBreakpointByLocation(
|
|
23
|
+
cdp: CdpClient,
|
|
24
|
+
params: {
|
|
25
|
+
file: string;
|
|
26
|
+
line: number;
|
|
27
|
+
column?: number;
|
|
28
|
+
condition?: string;
|
|
29
|
+
url?: string;
|
|
30
|
+
urlRegex?: string;
|
|
31
|
+
scriptId?: string;
|
|
32
|
+
scripts: Map<string, ScriptInfo>;
|
|
33
|
+
},
|
|
34
|
+
): Promise<{
|
|
35
|
+
breakpointId: string;
|
|
36
|
+
location?: { scriptId: string; lineNumber: number; columnNumber?: number };
|
|
37
|
+
url?: string;
|
|
38
|
+
}>;
|
|
39
|
+
|
|
40
|
+
/** Get possible breakpoint locations in a script range */
|
|
41
|
+
getBreakableLocations(
|
|
42
|
+
cdp: CdpClient,
|
|
43
|
+
scriptId: string,
|
|
44
|
+
startLine: number,
|
|
45
|
+
endLine: number,
|
|
46
|
+
): Promise<Array<{ line: number; column: number }>>;
|
|
47
|
+
|
|
48
|
+
/** Apply blackbox patterns to the debugger */
|
|
49
|
+
setBlackboxPatterns(cdp: CdpClient, patterns: string[]): Promise<void>;
|
|
50
|
+
}
|
|
@@ -11,9 +11,7 @@ export async function addBlackbox(session: DebugSession, patterns: string[]): Pr
|
|
|
11
11
|
}
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
await session.
|
|
15
|
-
patterns: session.blackboxPatterns,
|
|
16
|
-
});
|
|
14
|
+
await session.adapter.setBlackboxPatterns(session.cdp, session.blackboxPatterns);
|
|
17
15
|
|
|
18
16
|
return [...session.blackboxPatterns];
|
|
19
17
|
}
|
|
@@ -33,9 +31,7 @@ export async function removeBlackbox(session: DebugSession, patterns: string[]):
|
|
|
33
31
|
session.blackboxPatterns = session.blackboxPatterns.filter((p) => !patterns.includes(p));
|
|
34
32
|
}
|
|
35
33
|
|
|
36
|
-
await session.
|
|
37
|
-
patterns: session.blackboxPatterns,
|
|
38
|
-
});
|
|
34
|
+
await session.adapter.setBlackboxPatterns(session.cdp, session.blackboxPatterns);
|
|
39
35
|
|
|
40
36
|
return [...session.blackboxPatterns];
|
|
41
37
|
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import type Protocol from "devtools-protocol/types/protocol.js";
|
|
2
1
|
import type { DebugSession } from "./session.ts";
|
|
3
2
|
|
|
4
3
|
export async function setBreakpoint(
|
|
@@ -20,6 +19,7 @@ export async function setBreakpoint(
|
|
|
20
19
|
let actualColumn: number | undefined =
|
|
21
20
|
options?.column !== undefined ? options.column - 1 : undefined; // user column is 1-based
|
|
22
21
|
let actualFile = file;
|
|
22
|
+
let generatedScriptId: string | null = null;
|
|
23
23
|
|
|
24
24
|
if (!options?.urlRegex) {
|
|
25
25
|
const generated = session.sourceMapResolver.toGenerated(file, line, actualColumn ?? 0);
|
|
@@ -28,6 +28,7 @@ export async function setBreakpoint(
|
|
|
28
28
|
originalLine = line;
|
|
29
29
|
actualLine = generated.line;
|
|
30
30
|
actualColumn = generated.column;
|
|
31
|
+
generatedScriptId = generated.scriptId;
|
|
31
32
|
// Find the URL of the generated script
|
|
32
33
|
const scriptInfo = session.scripts.get(generated.scriptId);
|
|
33
34
|
if (scriptInfo) {
|
|
@@ -36,33 +37,32 @@ export async function setBreakpoint(
|
|
|
36
37
|
}
|
|
37
38
|
}
|
|
38
39
|
|
|
39
|
-
const params: Protocol.Debugger.SetBreakpointByUrlRequest = {
|
|
40
|
-
lineNumber: actualLine - 1, // CDP uses 0-based lines
|
|
41
|
-
};
|
|
42
|
-
if (actualColumn !== undefined) {
|
|
43
|
-
params.columnNumber = actualColumn;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
40
|
let url: string | null = null;
|
|
41
|
+
let urlRegex: string | undefined;
|
|
47
42
|
if (options?.urlRegex) {
|
|
48
|
-
|
|
49
|
-
params.urlRegex = options.urlRegex;
|
|
43
|
+
urlRegex = options.urlRegex;
|
|
50
44
|
} else {
|
|
51
45
|
url = session.findScriptUrl(actualFile);
|
|
52
|
-
if (url) {
|
|
53
|
-
|
|
54
|
-
} else {
|
|
55
|
-
// Fall back to urlRegex to match partial paths
|
|
56
|
-
params.urlRegex = `${actualFile.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}$`;
|
|
46
|
+
if (!url && !generatedScriptId) {
|
|
47
|
+
urlRegex = `${actualFile.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}$`;
|
|
57
48
|
}
|
|
58
49
|
}
|
|
59
|
-
if (condition) {
|
|
60
|
-
params.condition = condition;
|
|
61
|
-
}
|
|
62
50
|
|
|
63
|
-
const r = await session.
|
|
51
|
+
const r = await session.adapter.setBreakpointByLocation(session.cdp, {
|
|
52
|
+
file,
|
|
53
|
+
line: actualLine,
|
|
54
|
+
column: actualColumn,
|
|
55
|
+
condition,
|
|
56
|
+
url: url ?? undefined,
|
|
57
|
+
urlRegex,
|
|
58
|
+
scriptId: generatedScriptId ?? undefined,
|
|
59
|
+
scripts: session.scripts,
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
const breakpointId = r.breakpointId;
|
|
63
|
+
const loc = r.location;
|
|
64
|
+
if (!url) url = r.url ?? session.findScriptUrl(actualFile);
|
|
64
65
|
|
|
65
|
-
const loc = r.locations[0];
|
|
66
66
|
const resolvedUrl = originalFile ?? url ?? file;
|
|
67
67
|
const resolvedLine = originalLine ?? (loc ? loc.lineNumber + 1 : line); // Convert back to 1-based
|
|
68
68
|
const resolvedColumn = loc?.columnNumber;
|
|
@@ -90,7 +90,7 @@ export async function setBreakpoint(
|
|
|
90
90
|
meta.urlRegex = options.urlRegex;
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
-
const ref = session.refs.addBreakpoint(
|
|
93
|
+
const ref = session.refs.addBreakpoint(breakpointId, meta);
|
|
94
94
|
|
|
95
95
|
const location: { url: string; line: number; column?: number } = {
|
|
96
96
|
url: resolvedUrl,
|
|
@@ -333,21 +333,26 @@ async function reEnableBreakpoint(
|
|
|
333
333
|
|
|
334
334
|
const builtCondition = session.buildBreakpointCondition(condition, hitCount);
|
|
335
335
|
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
if (builtCondition) {
|
|
347
|
-
bpParams.condition = builtCondition;
|
|
336
|
+
// Find scriptId for Bun adapter which needs it
|
|
337
|
+
let scriptId: string | undefined;
|
|
338
|
+
if (url) {
|
|
339
|
+
for (const [sid, info] of session.scripts) {
|
|
340
|
+
if (info.url === url) {
|
|
341
|
+
scriptId = sid;
|
|
342
|
+
break;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
348
345
|
}
|
|
349
346
|
|
|
350
|
-
const r = await session.
|
|
347
|
+
const r = await session.adapter.setBreakpointByLocation(session.cdp, {
|
|
348
|
+
file: (url ?? urlRegex ?? "") as string,
|
|
349
|
+
line,
|
|
350
|
+
condition: builtCondition,
|
|
351
|
+
url,
|
|
352
|
+
urlRegex,
|
|
353
|
+
scriptId,
|
|
354
|
+
scripts: session.scripts,
|
|
355
|
+
});
|
|
351
356
|
|
|
352
357
|
// Re-create the ref entry in the ref table
|
|
353
358
|
const type = (meta.type as string) === "LP" ? "LP" : "BP";
|
|
@@ -390,15 +395,7 @@ export async function getBreakableLocations(
|
|
|
390
395
|
throw new Error(`No scriptId found for "${file}"`);
|
|
391
396
|
}
|
|
392
397
|
|
|
393
|
-
|
|
394
|
-
start: { scriptId, lineNumber: startLine - 1 },
|
|
395
|
-
end: { scriptId, lineNumber: endLine },
|
|
396
|
-
});
|
|
397
|
-
|
|
398
|
-
return r.locations.map((loc) => ({
|
|
399
|
-
line: loc.lineNumber + 1, // Convert to 1-based
|
|
400
|
-
column: (loc.columnNumber ?? 0) + 1, // Convert to 1-based
|
|
401
|
-
}));
|
|
398
|
+
return session.adapter.getBreakableLocations(session.cdp, scriptId, startLine, endLine);
|
|
402
399
|
}
|
|
403
400
|
|
|
404
401
|
export async function setLogpoint(
|
|
@@ -423,19 +420,33 @@ export async function setLogpoint(
|
|
|
423
420
|
logExpr = `${logExpr}, false`;
|
|
424
421
|
}
|
|
425
422
|
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
}
|
|
423
|
+
let urlRegex: string | undefined;
|
|
424
|
+
if (!url) {
|
|
425
|
+
urlRegex = `${file.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}$`;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
// Find scriptId for Bun adapter
|
|
429
|
+
let scriptId: string | undefined;
|
|
430
430
|
if (url) {
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
431
|
+
for (const [sid, info] of session.scripts) {
|
|
432
|
+
if (info.url === url) {
|
|
433
|
+
scriptId = sid;
|
|
434
|
+
break;
|
|
435
|
+
}
|
|
436
|
+
}
|
|
434
437
|
}
|
|
435
438
|
|
|
436
|
-
const r = await session.
|
|
439
|
+
const r = await session.adapter.setBreakpointByLocation(session.cdp, {
|
|
440
|
+
file,
|
|
441
|
+
line,
|
|
442
|
+
condition: logExpr,
|
|
443
|
+
url: url ?? undefined,
|
|
444
|
+
urlRegex,
|
|
445
|
+
scriptId,
|
|
446
|
+
scripts: session.scripts,
|
|
447
|
+
});
|
|
437
448
|
|
|
438
|
-
const loc = r.
|
|
449
|
+
const loc = r.location;
|
|
439
450
|
const resolvedUrl = url ?? file;
|
|
440
451
|
const resolvedLine = loc ? loc.lineNumber + 1 : line;
|
|
441
452
|
const resolvedColumn = loc?.columnNumber;
|
|
@@ -478,7 +489,7 @@ export async function setExceptionPause(
|
|
|
478
489
|
|
|
479
490
|
// CDP only supports "none", "all", and "uncaught".
|
|
480
491
|
// Map "caught" to "all" since CDP does not have a "caught-only" mode.
|
|
481
|
-
let cdpState:
|
|
492
|
+
let cdpState: "none" | "all" | "uncaught";
|
|
482
493
|
switch (mode) {
|
|
483
494
|
case "all":
|
|
484
495
|
cdpState = "all";
|
|
@@ -188,7 +188,7 @@ export async function getVars(
|
|
|
188
188
|
const objectId = scopeObj.objectId;
|
|
189
189
|
if (!objectId) continue;
|
|
190
190
|
|
|
191
|
-
const propsResult = await session.
|
|
191
|
+
const propsResult = await session.adapter.getProperties(session.cdp, {
|
|
192
192
|
objectId,
|
|
193
193
|
ownProperties: true,
|
|
194
194
|
generatePreview: true,
|
|
@@ -274,7 +274,7 @@ export async function getProps(
|
|
|
274
274
|
propsParams.accessorPropertiesOnly = false;
|
|
275
275
|
}
|
|
276
276
|
|
|
277
|
-
const propsResult = await session.
|
|
277
|
+
const propsResult = await session.adapter.getProperties(session.cdp, propsParams);
|
|
278
278
|
const properties = propsResult.result ?? [];
|
|
279
279
|
const internalProps = options.internal ? (propsResult.internalProperties ?? []) : [];
|
|
280
280
|
|
|
@@ -209,7 +209,7 @@ export async function buildState(
|
|
|
209
209
|
const objectId = scopeObj.objectId;
|
|
210
210
|
if (!objectId) continue;
|
|
211
211
|
|
|
212
|
-
const propsResult = await session.
|
|
212
|
+
const propsResult = await session.adapter.getProperties(session.cdp, {
|
|
213
213
|
objectId,
|
|
214
214
|
ownProperties: true,
|
|
215
215
|
generatePreview: true,
|