agent-dbg 0.1.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/.bin/ndbg +0 -0
- package/.claude/settings.local.json +21 -0
- package/.claude/skills/ndbg-debugger/ndbg-debugger/SKILL.md +116 -0
- package/.claude/skills/ndbg-debugger/ndbg-debugger/references/commands.md +173 -0
- package/CLAUDE.md +43 -0
- package/PROGRESS.md +261 -0
- package/README.md +67 -0
- package/biome.json +41 -0
- package/ndbg-spec.md +958 -0
- package/package.json +30 -0
- package/src/cdp/client.ts +198 -0
- package/src/cdp/types.ts +16 -0
- package/src/cli/parser.ts +287 -0
- package/src/cli/registry.ts +7 -0
- package/src/cli/types.ts +24 -0
- package/src/commands/attach.ts +47 -0
- package/src/commands/blackbox-ls.ts +38 -0
- package/src/commands/blackbox-rm.ts +57 -0
- package/src/commands/blackbox.ts +48 -0
- package/src/commands/break-ls.ts +57 -0
- package/src/commands/break-rm.ts +40 -0
- package/src/commands/break-toggle.ts +42 -0
- package/src/commands/break.ts +145 -0
- package/src/commands/breakable.ts +69 -0
- package/src/commands/catch.ts +38 -0
- package/src/commands/console.ts +61 -0
- package/src/commands/continue.ts +46 -0
- package/src/commands/eval.ts +70 -0
- package/src/commands/exceptions.ts +61 -0
- package/src/commands/hotpatch.ts +67 -0
- package/src/commands/launch.ts +69 -0
- package/src/commands/logpoint.ts +78 -0
- package/src/commands/pause.ts +46 -0
- package/src/commands/props.ts +77 -0
- package/src/commands/restart-frame.ts +36 -0
- package/src/commands/run-to.ts +70 -0
- package/src/commands/scripts.ts +57 -0
- package/src/commands/search.ts +73 -0
- package/src/commands/sessions.ts +71 -0
- package/src/commands/set-return.ts +49 -0
- package/src/commands/set.ts +61 -0
- package/src/commands/source.ts +59 -0
- package/src/commands/sourcemap.ts +66 -0
- package/src/commands/stack.ts +64 -0
- package/src/commands/state.ts +124 -0
- package/src/commands/status.ts +57 -0
- package/src/commands/step.ts +50 -0
- package/src/commands/stop.ts +27 -0
- package/src/commands/vars.ts +71 -0
- package/src/daemon/client.ts +147 -0
- package/src/daemon/entry.ts +242 -0
- package/src/daemon/paths.ts +26 -0
- package/src/daemon/server.ts +185 -0
- package/src/daemon/session-blackbox.ts +41 -0
- package/src/daemon/session-breakpoints.ts +492 -0
- package/src/daemon/session-execution.ts +121 -0
- package/src/daemon/session-inspection.ts +701 -0
- package/src/daemon/session-mutation.ts +197 -0
- package/src/daemon/session-state.ts +258 -0
- package/src/daemon/session.ts +938 -0
- package/src/daemon/spawn.ts +53 -0
- package/src/formatter/errors.ts +15 -0
- package/src/formatter/source.ts +74 -0
- package/src/formatter/stack.ts +70 -0
- package/src/formatter/values.ts +269 -0
- package/src/formatter/variables.ts +20 -0
- package/src/main.ts +45 -0
- package/src/protocol/messages.ts +316 -0
- package/src/refs/ref-table.ts +120 -0
- package/src/refs/resolver.ts +24 -0
- package/src/sourcemap/resolver.ts +318 -0
- package/tests/fixtures/async-app.js +34 -0
- package/tests/fixtures/console-app.js +12 -0
- package/tests/fixtures/error-app.js +28 -0
- package/tests/fixtures/exception-app.js +6 -0
- package/tests/fixtures/inspect-app.js +10 -0
- package/tests/fixtures/mutation-app.js +9 -0
- package/tests/fixtures/simple-app.js +50 -0
- package/tests/fixtures/step-app.js +13 -0
- package/tests/fixtures/ts-app/src/app.ts +21 -0
- package/tests/fixtures/ts-app/tsconfig.json +14 -0
- package/tests/integration/blackbox.test.ts +135 -0
- package/tests/integration/break-extras.test.ts +241 -0
- package/tests/integration/breakpoint.test.ts +217 -0
- package/tests/integration/console.test.ts +275 -0
- package/tests/integration/execution.test.ts +247 -0
- package/tests/integration/inspection.test.ts +311 -0
- package/tests/integration/mutation.test.ts +178 -0
- package/tests/integration/session.test.ts +223 -0
- package/tests/integration/source.test.ts +209 -0
- package/tests/integration/sourcemap.test.ts +214 -0
- package/tests/integration/state.test.ts +208 -0
- package/tests/unit/cdp-client.test.ts +422 -0
- package/tests/unit/daemon.test.ts +286 -0
- package/tests/unit/formatter.test.ts +716 -0
- package/tests/unit/parser.test.ts +105 -0
- package/tests/unit/refs.test.ts +383 -0
- package/tests/unit/sourcemap.test.ts +236 -0
- package/tsconfig.json +32 -0
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, test } from "bun:test";
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
import { SourceMapResolver } from "../../src/sourcemap/resolver.ts";
|
|
4
|
+
|
|
5
|
+
const FIXTURE_DIR = resolve(import.meta.dir, "../fixtures/ts-app");
|
|
6
|
+
const DIST_DIR = resolve(FIXTURE_DIR, "dist");
|
|
7
|
+
const APP_JS = resolve(DIST_DIR, "app.js");
|
|
8
|
+
const APP_JS_MAP = resolve(DIST_DIR, "app.js.map");
|
|
9
|
+
|
|
10
|
+
describe("SourceMapResolver", () => {
|
|
11
|
+
let resolver: SourceMapResolver;
|
|
12
|
+
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
resolver = new SourceMapResolver();
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
describe("loadSourceMap", () => {
|
|
18
|
+
test("loads file-based source map successfully", async () => {
|
|
19
|
+
const loaded = await resolver.loadSourceMap("1", APP_JS, "app.js.map");
|
|
20
|
+
expect(loaded).toBe(true);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
test("loads inline data: URI source map (base64)", async () => {
|
|
24
|
+
// Read the actual map file and encode as base64 data URI
|
|
25
|
+
const mapContent = await Bun.file(APP_JS_MAP).text();
|
|
26
|
+
const b64 = Buffer.from(mapContent).toString("base64");
|
|
27
|
+
const dataUri = `data:application/json;charset=utf-8;base64,${b64}`;
|
|
28
|
+
|
|
29
|
+
const loaded = await resolver.loadSourceMap("2", APP_JS, dataUri);
|
|
30
|
+
expect(loaded).toBe(true);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test("loads inline data: URI source map (percent-encoded)", async () => {
|
|
34
|
+
const mapContent = await Bun.file(APP_JS_MAP).text();
|
|
35
|
+
const encoded = encodeURIComponent(mapContent);
|
|
36
|
+
const dataUri = `data:application/json,${encoded}`;
|
|
37
|
+
|
|
38
|
+
const loaded = await resolver.loadSourceMap("3", APP_JS, dataUri);
|
|
39
|
+
expect(loaded).toBe(true);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test("returns false for missing source map file", async () => {
|
|
43
|
+
const loaded = await resolver.loadSourceMap("4", APP_JS, "nonexistent.map");
|
|
44
|
+
expect(loaded).toBe(false);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test("returns false for invalid JSON in source map", async () => {
|
|
48
|
+
// Create a temp file with invalid content
|
|
49
|
+
const tmpPath = resolve(DIST_DIR, "invalid.js.map");
|
|
50
|
+
await Bun.write(tmpPath, "not json{{{");
|
|
51
|
+
try {
|
|
52
|
+
const loaded = await resolver.loadSourceMap(
|
|
53
|
+
"5",
|
|
54
|
+
resolve(DIST_DIR, "invalid.js"),
|
|
55
|
+
"invalid.js.map",
|
|
56
|
+
);
|
|
57
|
+
expect(loaded).toBe(false);
|
|
58
|
+
} finally {
|
|
59
|
+
// Cleanup
|
|
60
|
+
const file = Bun.file(tmpPath);
|
|
61
|
+
if (await file.exists()) {
|
|
62
|
+
await Bun.write(tmpPath, ""); // Clear it
|
|
63
|
+
const { unlink } = await import("node:fs/promises");
|
|
64
|
+
await unlink(tmpPath);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test("returns false when disabled", async () => {
|
|
70
|
+
resolver.setDisabled(true);
|
|
71
|
+
const loaded = await resolver.loadSourceMap("6", APP_JS, "app.js.map");
|
|
72
|
+
expect(loaded).toBe(false);
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
describe("toOriginal", () => {
|
|
77
|
+
test("translates generated position to original TS position", async () => {
|
|
78
|
+
await resolver.loadSourceMap("1", APP_JS, "app.js.map");
|
|
79
|
+
|
|
80
|
+
// Line 2 of app.js is "function greet(person) {"
|
|
81
|
+
// which maps to line 7 of app.ts "function greet(person: Person): string {"
|
|
82
|
+
const original = resolver.toOriginal("1", 2, 0);
|
|
83
|
+
expect(original).not.toBeNull();
|
|
84
|
+
expect(original!.source).toContain("app.ts");
|
|
85
|
+
expect(original!.line).toBe(7);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
test("returns null for script without source map", () => {
|
|
89
|
+
const original = resolver.toOriginal("999", 1, 0);
|
|
90
|
+
expect(original).toBeNull();
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
test("returns null when disabled", async () => {
|
|
94
|
+
await resolver.loadSourceMap("1", APP_JS, "app.js.map");
|
|
95
|
+
resolver.setDisabled(true);
|
|
96
|
+
const original = resolver.toOriginal("1", 2, 0);
|
|
97
|
+
expect(original).toBeNull();
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
describe("toGenerated", () => {
|
|
102
|
+
test("translates original TS position to generated JS position", async () => {
|
|
103
|
+
await resolver.loadSourceMap("1", APP_JS, "app.js.map");
|
|
104
|
+
|
|
105
|
+
// Line 7 col 0 of app.ts (greet function) should map to line 2 of app.js
|
|
106
|
+
const generated = resolver.toGenerated("../src/app.ts", 7, 0);
|
|
107
|
+
expect(generated).not.toBeNull();
|
|
108
|
+
expect(generated!.scriptId).toBe("1");
|
|
109
|
+
expect(generated!.line).toBe(2);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
test("works with suffix matching", async () => {
|
|
113
|
+
await resolver.loadSourceMap("1", APP_JS, "app.js.map");
|
|
114
|
+
|
|
115
|
+
// Should find via suffix matching
|
|
116
|
+
const generated = resolver.toGenerated("src/app.ts", 7, 0);
|
|
117
|
+
expect(generated).not.toBeNull();
|
|
118
|
+
expect(generated!.scriptId).toBe("1");
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
test("returns null for unknown source", async () => {
|
|
122
|
+
await resolver.loadSourceMap("1", APP_JS, "app.js.map");
|
|
123
|
+
const generated = resolver.toGenerated("unknown.ts", 1, 0);
|
|
124
|
+
expect(generated).toBeNull();
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
test("returns null when disabled", async () => {
|
|
128
|
+
await resolver.loadSourceMap("1", APP_JS, "app.js.map");
|
|
129
|
+
resolver.setDisabled(true);
|
|
130
|
+
const generated = resolver.toGenerated("../src/app.ts", 7, 0);
|
|
131
|
+
expect(generated).toBeNull();
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
describe("getOriginalSource", () => {
|
|
136
|
+
test("returns original TS source from sourcesContent", async () => {
|
|
137
|
+
await resolver.loadSourceMap("1", APP_JS, "app.js.map");
|
|
138
|
+
const source = resolver.getOriginalSource("1", "app.ts");
|
|
139
|
+
expect(source).not.toBeNull();
|
|
140
|
+
expect(source!).toContain("interface Person");
|
|
141
|
+
expect(source!).toContain("person: Person");
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
test("returns null for unknown script", () => {
|
|
145
|
+
const source = resolver.getOriginalSource("999", "app.ts");
|
|
146
|
+
expect(source).toBeNull();
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
test("returns null when disabled", async () => {
|
|
150
|
+
await resolver.loadSourceMap("1", APP_JS, "app.js.map");
|
|
151
|
+
resolver.setDisabled(true);
|
|
152
|
+
const source = resolver.getOriginalSource("1", "app.ts");
|
|
153
|
+
expect(source).toBeNull();
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
describe("findScriptForSource", () => {
|
|
158
|
+
test("finds script by suffix match", async () => {
|
|
159
|
+
await resolver.loadSourceMap("1", APP_JS, "app.js.map");
|
|
160
|
+
|
|
161
|
+
const result = resolver.findScriptForSource("app.ts");
|
|
162
|
+
expect(result).not.toBeNull();
|
|
163
|
+
expect(result!.scriptId).toBe("1");
|
|
164
|
+
expect(result!.url).toBe(APP_JS);
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
test("finds script by path suffix", async () => {
|
|
168
|
+
await resolver.loadSourceMap("1", APP_JS, "app.js.map");
|
|
169
|
+
|
|
170
|
+
const result = resolver.findScriptForSource("src/app.ts");
|
|
171
|
+
expect(result).not.toBeNull();
|
|
172
|
+
expect(result!.scriptId).toBe("1");
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
test("returns null for unknown path", async () => {
|
|
176
|
+
await resolver.loadSourceMap("1", APP_JS, "app.js.map");
|
|
177
|
+
const result = resolver.findScriptForSource("unknown.ts");
|
|
178
|
+
expect(result).toBeNull();
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
test("returns null when disabled", async () => {
|
|
182
|
+
await resolver.loadSourceMap("1", APP_JS, "app.js.map");
|
|
183
|
+
resolver.setDisabled(true);
|
|
184
|
+
const result = resolver.findScriptForSource("app.ts");
|
|
185
|
+
expect(result).toBeNull();
|
|
186
|
+
});
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
describe("getInfo / getAllInfos", () => {
|
|
190
|
+
test("getInfo returns source map info for loaded script", async () => {
|
|
191
|
+
await resolver.loadSourceMap("1", APP_JS, "app.js.map");
|
|
192
|
+
|
|
193
|
+
const info = resolver.getInfo("1");
|
|
194
|
+
expect(info).not.toBeNull();
|
|
195
|
+
expect(info!.scriptId).toBe("1");
|
|
196
|
+
expect(info!.generatedUrl).toBe(APP_JS);
|
|
197
|
+
expect(info!.mapUrl).toBe("app.js.map");
|
|
198
|
+
expect(info!.sources.length).toBeGreaterThan(0);
|
|
199
|
+
expect(info!.hasSourcesContent).toBe(true);
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
test("getInfo returns null for unknown script", () => {
|
|
203
|
+
const info = resolver.getInfo("999");
|
|
204
|
+
expect(info).toBeNull();
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
test("getAllInfos returns all loaded source maps", async () => {
|
|
208
|
+
await resolver.loadSourceMap("1", APP_JS, "app.js.map");
|
|
209
|
+
|
|
210
|
+
const infos = resolver.getAllInfos();
|
|
211
|
+
expect(infos.length).toBe(1);
|
|
212
|
+
expect(infos[0]!.scriptId).toBe("1");
|
|
213
|
+
});
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
describe("setDisabled / clear", () => {
|
|
217
|
+
test("setDisabled toggles disabled state", () => {
|
|
218
|
+
expect(resolver.isDisabled()).toBe(false);
|
|
219
|
+
resolver.setDisabled(true);
|
|
220
|
+
expect(resolver.isDisabled()).toBe(true);
|
|
221
|
+
resolver.setDisabled(false);
|
|
222
|
+
expect(resolver.isDisabled()).toBe(false);
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
test("clear resets all caches", async () => {
|
|
226
|
+
await resolver.loadSourceMap("1", APP_JS, "app.js.map");
|
|
227
|
+
expect(resolver.getInfo("1")).not.toBeNull();
|
|
228
|
+
|
|
229
|
+
resolver.clear();
|
|
230
|
+
|
|
231
|
+
expect(resolver.getInfo("1")).toBeNull();
|
|
232
|
+
expect(resolver.getAllInfos().length).toBe(0);
|
|
233
|
+
expect(resolver.findScriptForSource("app.ts")).toBeNull();
|
|
234
|
+
});
|
|
235
|
+
});
|
|
236
|
+
});
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"lib": ["ESNext"],
|
|
4
|
+
"target": "ESNext",
|
|
5
|
+
"module": "Preserve",
|
|
6
|
+
"moduleDetection": "force",
|
|
7
|
+
"allowJs": false,
|
|
8
|
+
|
|
9
|
+
"moduleResolution": "bundler",
|
|
10
|
+
"allowImportingTsExtensions": true,
|
|
11
|
+
"verbatimModuleSyntax": true,
|
|
12
|
+
"noEmit": true,
|
|
13
|
+
|
|
14
|
+
"strict": true,
|
|
15
|
+
"skipLibCheck": true,
|
|
16
|
+
"noFallthroughCasesInSwitch": true,
|
|
17
|
+
"noUncheckedIndexedAccess": true,
|
|
18
|
+
"noImplicitOverride": true,
|
|
19
|
+
"noUnusedLocals": true,
|
|
20
|
+
"noUnusedParameters": true,
|
|
21
|
+
"noPropertyAccessFromIndexSignature": false,
|
|
22
|
+
|
|
23
|
+
"outDir": "dist",
|
|
24
|
+
"rootDir": ".",
|
|
25
|
+
"baseUrl": ".",
|
|
26
|
+
"paths": {
|
|
27
|
+
"@/*": ["src/*"]
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"include": ["src/**/*.ts", "tests/**/*.ts"],
|
|
31
|
+
"exclude": ["node_modules", "dist"]
|
|
32
|
+
}
|