@vibe-lang/runtime 0.2.5
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/package.json +46 -0
- package/src/ast/index.ts +375 -0
- package/src/ast.ts +2 -0
- package/src/debug/advanced-features.ts +482 -0
- package/src/debug/bun-inspector.ts +424 -0
- package/src/debug/handoff-manager.ts +283 -0
- package/src/debug/index.ts +150 -0
- package/src/debug/runner.ts +365 -0
- package/src/debug/server.ts +565 -0
- package/src/debug/stack-merger.ts +267 -0
- package/src/debug/state.ts +581 -0
- package/src/debug/test/advanced-features.test.ts +300 -0
- package/src/debug/test/e2e.test.ts +218 -0
- package/src/debug/test/handoff-manager.test.ts +256 -0
- package/src/debug/test/runner.test.ts +256 -0
- package/src/debug/test/stack-merger.test.ts +163 -0
- package/src/debug/test/state.test.ts +400 -0
- package/src/debug/test/ts-debug-integration.test.ts +374 -0
- package/src/debug/test/ts-import-tracker.test.ts +125 -0
- package/src/debug/test/ts-source-map.test.ts +169 -0
- package/src/debug/ts-import-tracker.ts +151 -0
- package/src/debug/ts-source-map.ts +171 -0
- package/src/errors/index.ts +124 -0
- package/src/index.ts +358 -0
- package/src/lexer/index.ts +348 -0
- package/src/lexer.ts +2 -0
- package/src/parser/index.ts +792 -0
- package/src/parser/parse.ts +45 -0
- package/src/parser/test/async.test.ts +248 -0
- package/src/parser/test/destructuring.test.ts +167 -0
- package/src/parser/test/do-expression.test.ts +486 -0
- package/src/parser/test/errors/do-expression.test.ts +95 -0
- package/src/parser/test/errors/error-locations.test.ts +230 -0
- package/src/parser/test/errors/invalid-expressions.test.ts +144 -0
- package/src/parser/test/errors/missing-tokens.test.ts +126 -0
- package/src/parser/test/errors/model-declaration.test.ts +185 -0
- package/src/parser/test/errors/nested-blocks.test.ts +226 -0
- package/src/parser/test/errors/unclosed-delimiters.test.ts +122 -0
- package/src/parser/test/errors/unexpected-tokens.test.ts +120 -0
- package/src/parser/test/import-export.test.ts +143 -0
- package/src/parser/test/literals.test.ts +404 -0
- package/src/parser/test/model-declaration.test.ts +161 -0
- package/src/parser/test/nested-blocks.test.ts +402 -0
- package/src/parser/test/parser.test.ts +743 -0
- package/src/parser/test/private.test.ts +136 -0
- package/src/parser/test/template-literal.test.ts +127 -0
- package/src/parser/test/tool-declaration.test.ts +302 -0
- package/src/parser/test/ts-block.test.ts +252 -0
- package/src/parser/test/type-annotations.test.ts +254 -0
- package/src/parser/visitor/helpers.ts +330 -0
- package/src/parser/visitor.ts +794 -0
- package/src/parser.ts +2 -0
- package/src/runtime/ai/cache-chunking.test.ts +69 -0
- package/src/runtime/ai/cache-chunking.ts +73 -0
- package/src/runtime/ai/client.ts +109 -0
- package/src/runtime/ai/context.ts +168 -0
- package/src/runtime/ai/formatters.ts +316 -0
- package/src/runtime/ai/index.ts +38 -0
- package/src/runtime/ai/language-ref.ts +38 -0
- package/src/runtime/ai/providers/anthropic.ts +253 -0
- package/src/runtime/ai/providers/google.ts +201 -0
- package/src/runtime/ai/providers/openai.ts +156 -0
- package/src/runtime/ai/retry.ts +100 -0
- package/src/runtime/ai/return-tools.ts +301 -0
- package/src/runtime/ai/test/client.test.ts +83 -0
- package/src/runtime/ai/test/formatters.test.ts +485 -0
- package/src/runtime/ai/test/retry.test.ts +137 -0
- package/src/runtime/ai/test/return-tools.test.ts +450 -0
- package/src/runtime/ai/test/tool-loop.test.ts +319 -0
- package/src/runtime/ai/test/tool-schema.test.ts +241 -0
- package/src/runtime/ai/tool-loop.ts +203 -0
- package/src/runtime/ai/tool-schema.ts +151 -0
- package/src/runtime/ai/types.ts +113 -0
- package/src/runtime/ai-logger.ts +255 -0
- package/src/runtime/ai-provider.ts +347 -0
- package/src/runtime/async/dependencies.ts +276 -0
- package/src/runtime/async/executor.ts +293 -0
- package/src/runtime/async/index.ts +43 -0
- package/src/runtime/async/scheduling.ts +163 -0
- package/src/runtime/async/test/dependencies.test.ts +284 -0
- package/src/runtime/async/test/executor.test.ts +388 -0
- package/src/runtime/context.ts +357 -0
- package/src/runtime/exec/ai.ts +139 -0
- package/src/runtime/exec/expressions.ts +475 -0
- package/src/runtime/exec/frames.ts +26 -0
- package/src/runtime/exec/functions.ts +305 -0
- package/src/runtime/exec/interpolation.ts +312 -0
- package/src/runtime/exec/statements.ts +604 -0
- package/src/runtime/exec/tools.ts +129 -0
- package/src/runtime/exec/typescript.ts +215 -0
- package/src/runtime/exec/variables.ts +279 -0
- package/src/runtime/index.ts +975 -0
- package/src/runtime/modules.ts +452 -0
- package/src/runtime/serialize.ts +103 -0
- package/src/runtime/state.ts +489 -0
- package/src/runtime/stdlib/core.ts +45 -0
- package/src/runtime/stdlib/directory.test.ts +156 -0
- package/src/runtime/stdlib/edit.test.ts +154 -0
- package/src/runtime/stdlib/fastEdit.test.ts +201 -0
- package/src/runtime/stdlib/glob.test.ts +106 -0
- package/src/runtime/stdlib/grep.test.ts +144 -0
- package/src/runtime/stdlib/index.ts +16 -0
- package/src/runtime/stdlib/readFile.test.ts +123 -0
- package/src/runtime/stdlib/tools/index.ts +707 -0
- package/src/runtime/stdlib/writeFile.test.ts +157 -0
- package/src/runtime/step.ts +969 -0
- package/src/runtime/test/ai-context.test.ts +1086 -0
- package/src/runtime/test/ai-result-object.test.ts +419 -0
- package/src/runtime/test/ai-tool-flow.test.ts +859 -0
- package/src/runtime/test/async-execution-order.test.ts +618 -0
- package/src/runtime/test/async-execution.test.ts +344 -0
- package/src/runtime/test/async-nested.test.ts +660 -0
- package/src/runtime/test/async-parallel-timing.test.ts +546 -0
- package/src/runtime/test/basic1.test.ts +154 -0
- package/src/runtime/test/binary-operators.test.ts +431 -0
- package/src/runtime/test/break-statement.test.ts +257 -0
- package/src/runtime/test/context-modes.test.ts +650 -0
- package/src/runtime/test/context.test.ts +466 -0
- package/src/runtime/test/core-functions.test.ts +228 -0
- package/src/runtime/test/e2e.test.ts +88 -0
- package/src/runtime/test/error-locations/error-locations.test.ts +80 -0
- package/src/runtime/test/error-locations/main-error.vibe +4 -0
- package/src/runtime/test/error-locations/main-import-error.vibe +3 -0
- package/src/runtime/test/error-locations/utils/helper.vibe +5 -0
- package/src/runtime/test/for-in.test.ts +312 -0
- package/src/runtime/test/helpers.ts +69 -0
- package/src/runtime/test/imports.test.ts +334 -0
- package/src/runtime/test/json-expressions.test.ts +232 -0
- package/src/runtime/test/literals.test.ts +372 -0
- package/src/runtime/test/logical-indexing.test.ts +478 -0
- package/src/runtime/test/member-methods.test.ts +324 -0
- package/src/runtime/test/model-config.test.ts +338 -0
- package/src/runtime/test/null-handling.test.ts +342 -0
- package/src/runtime/test/private-visibility.test.ts +332 -0
- package/src/runtime/test/runtime-state.test.ts +514 -0
- package/src/runtime/test/scoping.test.ts +370 -0
- package/src/runtime/test/string-interpolation.test.ts +354 -0
- package/src/runtime/test/template-literal.test.ts +181 -0
- package/src/runtime/test/tool-execution.test.ts +467 -0
- package/src/runtime/test/tool-schema-generation.test.ts +477 -0
- package/src/runtime/test/tostring.test.ts +210 -0
- package/src/runtime/test/ts-block.test.ts +594 -0
- package/src/runtime/test/ts-error-location.test.ts +231 -0
- package/src/runtime/test/types.test.ts +732 -0
- package/src/runtime/test/verbose-logger.test.ts +710 -0
- package/src/runtime/test/vibe-expression.test.ts +54 -0
- package/src/runtime/test/vibe-value-errors.test.ts +541 -0
- package/src/runtime/test/while.test.ts +232 -0
- package/src/runtime/tools/builtin.ts +30 -0
- package/src/runtime/tools/directory-tools.ts +70 -0
- package/src/runtime/tools/file-tools.ts +228 -0
- package/src/runtime/tools/index.ts +5 -0
- package/src/runtime/tools/registry.ts +48 -0
- package/src/runtime/tools/search-tools.ts +134 -0
- package/src/runtime/tools/security.ts +36 -0
- package/src/runtime/tools/system-tools.ts +312 -0
- package/src/runtime/tools/test/fixtures/base-types.ts +40 -0
- package/src/runtime/tools/test/fixtures/test-types.ts +132 -0
- package/src/runtime/tools/test/registry.test.ts +713 -0
- package/src/runtime/tools/test/security.test.ts +86 -0
- package/src/runtime/tools/test/system-tools.test.ts +679 -0
- package/src/runtime/tools/test/ts-schema.test.ts +357 -0
- package/src/runtime/tools/ts-schema.ts +341 -0
- package/src/runtime/tools/types.ts +89 -0
- package/src/runtime/tools/utility-tools.ts +198 -0
- package/src/runtime/ts-eval.ts +126 -0
- package/src/runtime/types.ts +797 -0
- package/src/runtime/validation.ts +160 -0
- package/src/runtime/verbose-logger.ts +459 -0
- package/src/runtime.ts +2 -0
- package/src/semantic/analyzer-context.ts +62 -0
- package/src/semantic/analyzer-validators.ts +575 -0
- package/src/semantic/analyzer-visitors.ts +534 -0
- package/src/semantic/analyzer.ts +83 -0
- package/src/semantic/index.ts +11 -0
- package/src/semantic/symbol-table.ts +58 -0
- package/src/semantic/test/async-validation.test.ts +301 -0
- package/src/semantic/test/compress-validation.test.ts +179 -0
- package/src/semantic/test/const-reassignment.test.ts +111 -0
- package/src/semantic/test/control-flow.test.ts +346 -0
- package/src/semantic/test/destructuring.test.ts +185 -0
- package/src/semantic/test/duplicate-declarations.test.ts +168 -0
- package/src/semantic/test/export-validation.test.ts +111 -0
- package/src/semantic/test/fixtures/math.ts +31 -0
- package/src/semantic/test/imports.test.ts +148 -0
- package/src/semantic/test/json-type.test.ts +68 -0
- package/src/semantic/test/literals.test.ts +127 -0
- package/src/semantic/test/model-validation.test.ts +179 -0
- package/src/semantic/test/prompt-validation.test.ts +343 -0
- package/src/semantic/test/scoping.test.ts +312 -0
- package/src/semantic/test/tool-validation.test.ts +306 -0
- package/src/semantic/test/ts-type-checking.test.ts +563 -0
- package/src/semantic/test/type-constraints.test.ts +111 -0
- package/src/semantic/test/type-inference.test.ts +87 -0
- package/src/semantic/test/type-validation.test.ts +552 -0
- package/src/semantic/test/undefined-variables.test.ts +163 -0
- package/src/semantic/ts-block-checker.ts +204 -0
- package/src/semantic/ts-signatures.ts +194 -0
- package/src/semantic/ts-types.ts +170 -0
- package/src/semantic/types.ts +58 -0
- package/tests/fixtures/conditional-logic.vibe +14 -0
- package/tests/fixtures/function-call.vibe +16 -0
- package/tests/fixtures/imports/cycle-detection/a.vibe +6 -0
- package/tests/fixtures/imports/cycle-detection/b.vibe +5 -0
- package/tests/fixtures/imports/cycle-detection/main.vibe +3 -0
- package/tests/fixtures/imports/module-isolation/main-b.vibe +8 -0
- package/tests/fixtures/imports/module-isolation/main.vibe +9 -0
- package/tests/fixtures/imports/module-isolation/moduleA.vibe +6 -0
- package/tests/fixtures/imports/module-isolation/moduleB.vibe +6 -0
- package/tests/fixtures/imports/nested-import/helper.vibe +6 -0
- package/tests/fixtures/imports/nested-import/main.vibe +3 -0
- package/tests/fixtures/imports/nested-import/utils.ts +3 -0
- package/tests/fixtures/imports/nested-isolation/file2.vibe +15 -0
- package/tests/fixtures/imports/nested-isolation/file3.vibe +10 -0
- package/tests/fixtures/imports/nested-isolation/main.vibe +21 -0
- package/tests/fixtures/imports/pure-cycle/a.vibe +5 -0
- package/tests/fixtures/imports/pure-cycle/b.vibe +5 -0
- package/tests/fixtures/imports/pure-cycle/main.vibe +3 -0
- package/tests/fixtures/imports/ts-boolean/checks.ts +14 -0
- package/tests/fixtures/imports/ts-boolean/main.vibe +10 -0
- package/tests/fixtures/imports/ts-boolean/type-mismatch.vibe +5 -0
- package/tests/fixtures/imports/ts-boolean/use-constant.vibe +18 -0
- package/tests/fixtures/imports/ts-error-handling/helpers.ts +42 -0
- package/tests/fixtures/imports/ts-error-handling/main.vibe +5 -0
- package/tests/fixtures/imports/ts-import/main.vibe +4 -0
- package/tests/fixtures/imports/ts-import/math.ts +9 -0
- package/tests/fixtures/imports/ts-variables/call-non-function.vibe +5 -0
- package/tests/fixtures/imports/ts-variables/data.ts +10 -0
- package/tests/fixtures/imports/ts-variables/import-json.vibe +5 -0
- package/tests/fixtures/imports/ts-variables/import-type-mismatch.vibe +5 -0
- package/tests/fixtures/imports/ts-variables/import-variable.vibe +5 -0
- package/tests/fixtures/imports/vibe-import/greet.vibe +5 -0
- package/tests/fixtures/imports/vibe-import/main.vibe +3 -0
- package/tests/fixtures/multiple-ai-calls.vibe +10 -0
- package/tests/fixtures/simple-greeting.vibe +6 -0
- package/tests/fixtures/template-literals.vibe +11 -0
- package/tests/integration/basic-ai/basic-ai.integration.test.ts +166 -0
- package/tests/integration/basic-ai/basic-ai.vibe +12 -0
- package/tests/integration/bug-fix/bug-fix.integration.test.ts +201 -0
- package/tests/integration/bug-fix/buggy-code.ts +22 -0
- package/tests/integration/bug-fix/fix-bug.vibe +21 -0
- package/tests/integration/compress/compress.integration.test.ts +206 -0
- package/tests/integration/destructuring/destructuring.integration.test.ts +92 -0
- package/tests/integration/hello-world-translator/hello-world-translator.integration.test.ts +61 -0
- package/tests/integration/line-annotator/context-modes.integration.test.ts +261 -0
- package/tests/integration/line-annotator/line-annotator.integration.test.ts +148 -0
- package/tests/integration/multi-feature/cumulative-sum.integration.test.ts +75 -0
- package/tests/integration/multi-feature/number-analyzer.integration.test.ts +191 -0
- package/tests/integration/multi-feature/number-analyzer.vibe +59 -0
- package/tests/integration/tool-calls/tool-calls.integration.test.ts +93 -0
|
@@ -0,0 +1,424 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bun Inspector Client
|
|
3
|
+
* Connects to Bun's debugger using Chrome DevTools Protocol over WebSocket
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { SourceLocation } from '../errors';
|
|
7
|
+
import type { StackFrame } from '@vibe-lang/debug-core';
|
|
8
|
+
import { findMappingByScriptId, mapTsLocationToVibe, type TsBlockMapping } from './ts-source-map';
|
|
9
|
+
|
|
10
|
+
// Chrome DevTools Protocol message types
|
|
11
|
+
interface CDPRequest {
|
|
12
|
+
id: number;
|
|
13
|
+
method: string;
|
|
14
|
+
params?: Record<string, unknown>;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
interface CDPResponse {
|
|
18
|
+
id: number;
|
|
19
|
+
result?: Record<string, unknown>;
|
|
20
|
+
error?: { code: number; message: string };
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
interface CDPEvent {
|
|
24
|
+
method: string;
|
|
25
|
+
params?: Record<string, unknown>;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
type CDPMessage = CDPResponse | CDPEvent;
|
|
29
|
+
|
|
30
|
+
// Debugger paused event params
|
|
31
|
+
interface DebuggerPausedParams {
|
|
32
|
+
callFrames: CDPCallFrame[];
|
|
33
|
+
reason: string;
|
|
34
|
+
hitBreakpoints?: string[];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// CDP Call Frame
|
|
38
|
+
interface CDPCallFrame {
|
|
39
|
+
callFrameId: string;
|
|
40
|
+
functionName: string;
|
|
41
|
+
location: {
|
|
42
|
+
scriptId: string;
|
|
43
|
+
lineNumber: number;
|
|
44
|
+
columnNumber: number;
|
|
45
|
+
};
|
|
46
|
+
url: string;
|
|
47
|
+
scopeChain: CDPScope[];
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// CDP Scope
|
|
51
|
+
interface CDPScope {
|
|
52
|
+
type: string;
|
|
53
|
+
object: { objectId: string };
|
|
54
|
+
name?: string;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Event handlers
|
|
58
|
+
type EventHandler = (params: Record<string, unknown>) => void;
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Bun Inspector Client
|
|
62
|
+
*/
|
|
63
|
+
export class BunInspectorClient {
|
|
64
|
+
private ws: WebSocket | null = null;
|
|
65
|
+
private messageId = 0;
|
|
66
|
+
private pendingRequests = new Map<number, {
|
|
67
|
+
resolve: (result: Record<string, unknown>) => void;
|
|
68
|
+
reject: (error: Error) => void;
|
|
69
|
+
}>();
|
|
70
|
+
private eventHandlers = new Map<string, EventHandler[]>();
|
|
71
|
+
private connected = false;
|
|
72
|
+
private scriptIdToUrl = new Map<string, string>();
|
|
73
|
+
|
|
74
|
+
constructor(private port: number = 9229) {}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Connect to Bun inspector
|
|
78
|
+
*/
|
|
79
|
+
async connect(): Promise<void> {
|
|
80
|
+
if (this.connected) return;
|
|
81
|
+
|
|
82
|
+
return new Promise((resolve, reject) => {
|
|
83
|
+
try {
|
|
84
|
+
// Bun's inspector uses WebSocket
|
|
85
|
+
this.ws = new WebSocket(`ws://127.0.0.1:${this.port}/ws`);
|
|
86
|
+
|
|
87
|
+
this.ws.onopen = () => {
|
|
88
|
+
this.connected = true;
|
|
89
|
+
// Enable debugger domain
|
|
90
|
+
this.send('Debugger.enable').then(() => {
|
|
91
|
+
resolve();
|
|
92
|
+
}).catch(reject);
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
this.ws.onmessage = (event) => {
|
|
96
|
+
this.handleMessage(JSON.parse(event.data));
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
this.ws.onerror = (error) => {
|
|
100
|
+
reject(new Error(`WebSocket error: ${error}`));
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
this.ws.onclose = () => {
|
|
104
|
+
this.connected = false;
|
|
105
|
+
};
|
|
106
|
+
} catch (error) {
|
|
107
|
+
reject(error);
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Disconnect from Bun inspector
|
|
114
|
+
*/
|
|
115
|
+
disconnect(): void {
|
|
116
|
+
if (this.ws) {
|
|
117
|
+
this.ws.close();
|
|
118
|
+
this.ws = null;
|
|
119
|
+
}
|
|
120
|
+
this.connected = false;
|
|
121
|
+
this.pendingRequests.clear();
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Check if connected
|
|
126
|
+
*/
|
|
127
|
+
isConnected(): boolean {
|
|
128
|
+
return this.connected;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Send a CDP request
|
|
133
|
+
*/
|
|
134
|
+
async send(method: string, params?: Record<string, unknown>): Promise<Record<string, unknown>> {
|
|
135
|
+
if (!this.ws || !this.connected) {
|
|
136
|
+
throw new Error('Not connected to Bun inspector');
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const id = ++this.messageId;
|
|
140
|
+
const request: CDPRequest = { id, method, params };
|
|
141
|
+
|
|
142
|
+
return new Promise((resolve, reject) => {
|
|
143
|
+
this.pendingRequests.set(id, { resolve, reject });
|
|
144
|
+
this.ws!.send(JSON.stringify(request));
|
|
145
|
+
|
|
146
|
+
// Timeout after 30 seconds
|
|
147
|
+
setTimeout(() => {
|
|
148
|
+
if (this.pendingRequests.has(id)) {
|
|
149
|
+
this.pendingRequests.delete(id);
|
|
150
|
+
reject(new Error(`Request timeout: ${method}`));
|
|
151
|
+
}
|
|
152
|
+
}, 30000);
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Handle incoming CDP message
|
|
158
|
+
*/
|
|
159
|
+
private handleMessage(message: CDPMessage): void {
|
|
160
|
+
if ('id' in message) {
|
|
161
|
+
// Response to a request
|
|
162
|
+
const handler = this.pendingRequests.get(message.id);
|
|
163
|
+
if (handler) {
|
|
164
|
+
this.pendingRequests.delete(message.id);
|
|
165
|
+
if (message.error) {
|
|
166
|
+
handler.reject(new Error(message.error.message));
|
|
167
|
+
} else {
|
|
168
|
+
handler.resolve(message.result ?? {});
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
} else if ('method' in message) {
|
|
172
|
+
// Event
|
|
173
|
+
this.emitEvent(message.method, message.params ?? {});
|
|
174
|
+
|
|
175
|
+
// Track script IDs
|
|
176
|
+
if (message.method === 'Debugger.scriptParsed') {
|
|
177
|
+
const params = message.params as { scriptId: string; url: string };
|
|
178
|
+
this.scriptIdToUrl.set(params.scriptId, params.url);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Register an event handler
|
|
185
|
+
*/
|
|
186
|
+
on(event: string, handler: EventHandler): void {
|
|
187
|
+
const handlers = this.eventHandlers.get(event) ?? [];
|
|
188
|
+
handlers.push(handler);
|
|
189
|
+
this.eventHandlers.set(event, handlers);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Remove an event handler
|
|
194
|
+
*/
|
|
195
|
+
off(event: string, handler: EventHandler): void {
|
|
196
|
+
const handlers = this.eventHandlers.get(event);
|
|
197
|
+
if (handlers) {
|
|
198
|
+
const index = handlers.indexOf(handler);
|
|
199
|
+
if (index !== -1) {
|
|
200
|
+
handlers.splice(index, 1);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Emit an event
|
|
207
|
+
*/
|
|
208
|
+
private emitEvent(event: string, params: Record<string, unknown>): void {
|
|
209
|
+
const handlers = this.eventHandlers.get(event) ?? [];
|
|
210
|
+
for (const handler of handlers) {
|
|
211
|
+
handler(params);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Debug commands
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Continue execution
|
|
219
|
+
*/
|
|
220
|
+
async continue(): Promise<void> {
|
|
221
|
+
await this.send('Debugger.resume');
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Step into
|
|
226
|
+
*/
|
|
227
|
+
async stepInto(): Promise<void> {
|
|
228
|
+
await this.send('Debugger.stepInto');
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Step over
|
|
233
|
+
*/
|
|
234
|
+
async stepOver(): Promise<void> {
|
|
235
|
+
await this.send('Debugger.stepOver');
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Step out
|
|
240
|
+
*/
|
|
241
|
+
async stepOut(): Promise<void> {
|
|
242
|
+
await this.send('Debugger.stepOut');
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Pause execution
|
|
247
|
+
*/
|
|
248
|
+
async pause(): Promise<void> {
|
|
249
|
+
await this.send('Debugger.pause');
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Set breakpoint
|
|
254
|
+
*/
|
|
255
|
+
async setBreakpoint(
|
|
256
|
+
scriptId: string,
|
|
257
|
+
lineNumber: number,
|
|
258
|
+
columnNumber?: number,
|
|
259
|
+
condition?: string
|
|
260
|
+
): Promise<{ breakpointId: string; actualLocation: { lineNumber: number; columnNumber: number } }> {
|
|
261
|
+
const result = await this.send('Debugger.setBreakpoint', {
|
|
262
|
+
location: { scriptId, lineNumber, columnNumber },
|
|
263
|
+
condition,
|
|
264
|
+
});
|
|
265
|
+
return result as any;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Set breakpoint by URL
|
|
270
|
+
*/
|
|
271
|
+
async setBreakpointByUrl(
|
|
272
|
+
url: string,
|
|
273
|
+
lineNumber: number,
|
|
274
|
+
columnNumber?: number,
|
|
275
|
+
condition?: string
|
|
276
|
+
): Promise<{ breakpointId: string; locations: Array<{ scriptId: string; lineNumber: number; columnNumber: number }> }> {
|
|
277
|
+
const result = await this.send('Debugger.setBreakpointByUrl', {
|
|
278
|
+
url,
|
|
279
|
+
lineNumber,
|
|
280
|
+
columnNumber,
|
|
281
|
+
condition,
|
|
282
|
+
});
|
|
283
|
+
return result as any;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Remove breakpoint
|
|
288
|
+
*/
|
|
289
|
+
async removeBreakpoint(breakpointId: string): Promise<void> {
|
|
290
|
+
await this.send('Debugger.removeBreakpoint', { breakpointId });
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Get stack frames from a paused event
|
|
295
|
+
*/
|
|
296
|
+
getStackFrames(pausedParams: DebuggerPausedParams): StackFrame[] {
|
|
297
|
+
return pausedParams.callFrames.map((frame, index) => {
|
|
298
|
+
const url = this.scriptIdToUrl.get(frame.location.scriptId) ?? frame.url;
|
|
299
|
+
|
|
300
|
+
// Check if this is a TS block and map location
|
|
301
|
+
const mapping = findMappingByScriptId(frame.location.scriptId);
|
|
302
|
+
let source: SourceLocation;
|
|
303
|
+
|
|
304
|
+
if (mapping) {
|
|
305
|
+
source = mapTsLocationToVibe(
|
|
306
|
+
mapping,
|
|
307
|
+
frame.location.lineNumber,
|
|
308
|
+
frame.location.columnNumber
|
|
309
|
+
);
|
|
310
|
+
} else {
|
|
311
|
+
source = {
|
|
312
|
+
file: url,
|
|
313
|
+
line: frame.location.lineNumber + 1, // CDP uses 0-based lines
|
|
314
|
+
column: frame.location.columnNumber + 1,
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
return {
|
|
319
|
+
id: index,
|
|
320
|
+
name: frame.functionName || '<anonymous>',
|
|
321
|
+
source,
|
|
322
|
+
isVibeCode: !!mapping, // True if this is a mapped TS block
|
|
323
|
+
};
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* Get variables from a scope
|
|
329
|
+
*/
|
|
330
|
+
async getVariables(objectId: string): Promise<Array<{ name: string; value: string; type: string }>> {
|
|
331
|
+
const result = await this.send('Runtime.getProperties', {
|
|
332
|
+
objectId,
|
|
333
|
+
ownProperties: true,
|
|
334
|
+
generatePreview: true,
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
const properties = (result as any).result ?? [];
|
|
338
|
+
return properties.map((prop: any) => ({
|
|
339
|
+
name: prop.name,
|
|
340
|
+
value: this.formatValue(prop.value),
|
|
341
|
+
type: prop.value?.type ?? 'undefined',
|
|
342
|
+
}));
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* Evaluate expression
|
|
347
|
+
*/
|
|
348
|
+
async evaluate(
|
|
349
|
+
expression: string,
|
|
350
|
+
callFrameId?: string
|
|
351
|
+
): Promise<{ result: string; type: string }> {
|
|
352
|
+
let result;
|
|
353
|
+
if (callFrameId) {
|
|
354
|
+
result = await this.send('Debugger.evaluateOnCallFrame', {
|
|
355
|
+
callFrameId,
|
|
356
|
+
expression,
|
|
357
|
+
generatePreview: true,
|
|
358
|
+
});
|
|
359
|
+
} else {
|
|
360
|
+
result = await this.send('Runtime.evaluate', {
|
|
361
|
+
expression,
|
|
362
|
+
generatePreview: true,
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
const value = (result as any).result;
|
|
367
|
+
return {
|
|
368
|
+
result: this.formatValue(value),
|
|
369
|
+
type: value?.type ?? 'undefined',
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* Format a CDP value for display
|
|
375
|
+
*/
|
|
376
|
+
private formatValue(value: any): string {
|
|
377
|
+
if (!value) return 'undefined';
|
|
378
|
+
|
|
379
|
+
switch (value.type) {
|
|
380
|
+
case 'undefined':
|
|
381
|
+
return 'undefined';
|
|
382
|
+
case 'null':
|
|
383
|
+
return 'null';
|
|
384
|
+
case 'boolean':
|
|
385
|
+
case 'number':
|
|
386
|
+
return String(value.value);
|
|
387
|
+
case 'string':
|
|
388
|
+
return `"${value.value}"`;
|
|
389
|
+
case 'object':
|
|
390
|
+
if (value.subtype === 'null') return 'null';
|
|
391
|
+
if (value.subtype === 'array') {
|
|
392
|
+
return value.description ?? 'Array';
|
|
393
|
+
}
|
|
394
|
+
return value.description ?? value.className ?? 'Object';
|
|
395
|
+
case 'function':
|
|
396
|
+
return value.description ?? 'function';
|
|
397
|
+
default:
|
|
398
|
+
return String(value.value ?? value.description ?? 'unknown');
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
// Singleton instance
|
|
404
|
+
let bunInspector: BunInspectorClient | null = null;
|
|
405
|
+
|
|
406
|
+
/**
|
|
407
|
+
* Get or create the Bun inspector client
|
|
408
|
+
*/
|
|
409
|
+
export function getBunInspector(port?: number): BunInspectorClient {
|
|
410
|
+
if (!bunInspector || (port && bunInspector['port'] !== port)) {
|
|
411
|
+
bunInspector = new BunInspectorClient(port);
|
|
412
|
+
}
|
|
413
|
+
return bunInspector;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
/**
|
|
417
|
+
* Close the Bun inspector client
|
|
418
|
+
*/
|
|
419
|
+
export function closeBunInspector(): void {
|
|
420
|
+
if (bunInspector) {
|
|
421
|
+
bunInspector.disconnect();
|
|
422
|
+
bunInspector = null;
|
|
423
|
+
}
|
|
424
|
+
}
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Handoff Manager
|
|
3
|
+
* Coordinates control transfer between Vibe runtime debugging and Bun/TS debugging
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { RuntimeState } from '../runtime/types';
|
|
7
|
+
import type { StackFrame, SourceLocation } from '@vibe-lang/debug-core';
|
|
8
|
+
import type { VibeDebugState } from './state';
|
|
9
|
+
import type { BunInspectorClient } from './bun-inspector';
|
|
10
|
+
import {
|
|
11
|
+
type DebugExecutionContext,
|
|
12
|
+
enterTsBlock,
|
|
13
|
+
enterTsImport,
|
|
14
|
+
exitTsMode,
|
|
15
|
+
decrementTsCallDepth,
|
|
16
|
+
incrementTsCallDepth,
|
|
17
|
+
createDebugExecutionContext,
|
|
18
|
+
} from './stack-merger';
|
|
19
|
+
import {
|
|
20
|
+
registerTsBlock,
|
|
21
|
+
setScriptId,
|
|
22
|
+
isLocationInTsBlock,
|
|
23
|
+
} from './ts-source-map';
|
|
24
|
+
import {
|
|
25
|
+
getTsImportInfo,
|
|
26
|
+
registerTempBreakpoint,
|
|
27
|
+
popTempBreakpoint,
|
|
28
|
+
clearTempBreakpoints,
|
|
29
|
+
} from './ts-import-tracker';
|
|
30
|
+
|
|
31
|
+
// Handoff state
|
|
32
|
+
export interface HandoffState {
|
|
33
|
+
// Current execution context
|
|
34
|
+
context: DebugExecutionContext;
|
|
35
|
+
// Whether a handoff is in progress
|
|
36
|
+
handoffInProgress: boolean;
|
|
37
|
+
// The reason for current handoff
|
|
38
|
+
handoffReason: 'ts_block' | 'ts_import' | null;
|
|
39
|
+
// Target location for handoff (where to break in TS)
|
|
40
|
+
handoffTarget: SourceLocation | null;
|
|
41
|
+
// Saved Vibe debug state when entering TS
|
|
42
|
+
savedVibeState: VibeDebugState | null;
|
|
43
|
+
// Saved Vibe runtime state when entering TS
|
|
44
|
+
savedRuntimeState: RuntimeState | null;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Create initial handoff state
|
|
49
|
+
*/
|
|
50
|
+
export function createHandoffState(): HandoffState {
|
|
51
|
+
return {
|
|
52
|
+
context: createDebugExecutionContext(),
|
|
53
|
+
handoffInProgress: false,
|
|
54
|
+
handoffReason: null,
|
|
55
|
+
handoffTarget: null,
|
|
56
|
+
savedVibeState: null,
|
|
57
|
+
savedRuntimeState: null,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Check if we should initiate a handoff to TS debugging
|
|
63
|
+
*/
|
|
64
|
+
export function shouldInitiateHandoff(
|
|
65
|
+
runtimeState: RuntimeState,
|
|
66
|
+
handoffState: HandoffState
|
|
67
|
+
): { shouldHandoff: boolean; reason: 'ts_block' | 'ts_import' | null } {
|
|
68
|
+
// Already in TS mode - no new handoff needed
|
|
69
|
+
if (handoffState.context.mode === 'typescript') {
|
|
70
|
+
return { shouldHandoff: false, reason: null };
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Check for pending TS block execution
|
|
74
|
+
if (runtimeState.status === 'awaiting_ts' && runtimeState.pendingTS) {
|
|
75
|
+
return { shouldHandoff: true, reason: 'ts_block' };
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Check for pending imported TS function call
|
|
79
|
+
if (runtimeState.status === 'awaiting_ts' && runtimeState.pendingImportedTsCall) {
|
|
80
|
+
return { shouldHandoff: true, reason: 'ts_import' };
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return { shouldHandoff: false, reason: null };
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Initiate handoff to TS debugging
|
|
88
|
+
*/
|
|
89
|
+
export function initiateHandoff(
|
|
90
|
+
runtimeState: RuntimeState,
|
|
91
|
+
debugState: VibeDebugState,
|
|
92
|
+
handoffState: HandoffState,
|
|
93
|
+
reason: 'ts_block' | 'ts_import'
|
|
94
|
+
): HandoffState {
|
|
95
|
+
const vibeCallDepth = runtimeState.callStack.length;
|
|
96
|
+
|
|
97
|
+
let newContext: DebugExecutionContext;
|
|
98
|
+
let target: SourceLocation | null = null;
|
|
99
|
+
|
|
100
|
+
if (reason === 'ts_block' && runtimeState.pendingTS) {
|
|
101
|
+
// Register the TS block for source mapping
|
|
102
|
+
const location = runtimeState.pendingTS.location ?? { file: '', line: 0, column: 0 };
|
|
103
|
+
const tsBlockId = registerTsBlock(
|
|
104
|
+
location.file ?? '',
|
|
105
|
+
location,
|
|
106
|
+
runtimeState.pendingTS.body,
|
|
107
|
+
runtimeState.pendingTS.params
|
|
108
|
+
);
|
|
109
|
+
newContext = enterTsBlock(handoffState.context, tsBlockId, vibeCallDepth);
|
|
110
|
+
target = { file: 'ts_block', line: 1, column: 0 }; // Entry of TS block
|
|
111
|
+
} else if (reason === 'ts_import' && runtimeState.pendingImportedTsCall) {
|
|
112
|
+
const { funcName } = runtimeState.pendingImportedTsCall;
|
|
113
|
+
newContext = enterTsImport(handoffState.context, funcName, vibeCallDepth);
|
|
114
|
+
// Target will be set when we resolve the function location via Bun inspector
|
|
115
|
+
} else {
|
|
116
|
+
newContext = handoffState.context;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return {
|
|
120
|
+
...handoffState,
|
|
121
|
+
context: newContext,
|
|
122
|
+
handoffInProgress: true,
|
|
123
|
+
handoffReason: reason,
|
|
124
|
+
handoffTarget: target,
|
|
125
|
+
savedVibeState: debugState,
|
|
126
|
+
savedRuntimeState: runtimeState,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Complete handoff (control now with TS debugger)
|
|
132
|
+
*/
|
|
133
|
+
export function completeHandoff(handoffState: HandoffState): HandoffState {
|
|
134
|
+
return {
|
|
135
|
+
...handoffState,
|
|
136
|
+
handoffInProgress: false,
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Check if we should return control to Vibe
|
|
142
|
+
* Called when TS execution hits a return or step out
|
|
143
|
+
*/
|
|
144
|
+
export function shouldReturnControl(
|
|
145
|
+
handoffState: HandoffState,
|
|
146
|
+
tsCallDepth: number
|
|
147
|
+
): boolean {
|
|
148
|
+
if (handoffState.context.mode !== 'typescript') {
|
|
149
|
+
return false;
|
|
150
|
+
}
|
|
151
|
+
// Return control when we've stepped out of all TS frames
|
|
152
|
+
return tsCallDepth <= 0;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Return control to Vibe debugging
|
|
157
|
+
*/
|
|
158
|
+
export function returnToVibe(handoffState: HandoffState): HandoffState {
|
|
159
|
+
return {
|
|
160
|
+
...handoffState,
|
|
161
|
+
context: exitTsMode(handoffState.context),
|
|
162
|
+
handoffInProgress: false,
|
|
163
|
+
handoffReason: null,
|
|
164
|
+
handoffTarget: null,
|
|
165
|
+
savedVibeState: null,
|
|
166
|
+
savedRuntimeState: null,
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Handle step into during TS execution
|
|
172
|
+
* Increments TS call depth
|
|
173
|
+
*/
|
|
174
|
+
export function handleTsStepIn(handoffState: HandoffState): HandoffState {
|
|
175
|
+
return {
|
|
176
|
+
...handoffState,
|
|
177
|
+
context: incrementTsCallDepth(handoffState.context),
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Handle step out during TS execution
|
|
183
|
+
* Decrements TS call depth, may trigger return to Vibe
|
|
184
|
+
*/
|
|
185
|
+
export function handleTsStepOut(handoffState: HandoffState): HandoffState {
|
|
186
|
+
const newContext = decrementTsCallDepth(handoffState.context);
|
|
187
|
+
|
|
188
|
+
// If we exited TS mode, clear the handoff state
|
|
189
|
+
if (newContext.mode === 'vibe') {
|
|
190
|
+
return returnToVibe(handoffState);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
return {
|
|
194
|
+
...handoffState,
|
|
195
|
+
context: newContext,
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Get current debug mode
|
|
201
|
+
*/
|
|
202
|
+
export function getCurrentMode(handoffState: HandoffState): 'vibe' | 'typescript' {
|
|
203
|
+
return handoffState.context.mode;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Check if currently in TS mode
|
|
208
|
+
*/
|
|
209
|
+
export function isInTsMode(handoffState: HandoffState): boolean {
|
|
210
|
+
return handoffState.context.mode === 'typescript';
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Get saved Vibe state (for continuing after TS returns)
|
|
215
|
+
*/
|
|
216
|
+
export function getSavedVibeState(handoffState: HandoffState): {
|
|
217
|
+
debugState: VibeDebugState | null;
|
|
218
|
+
runtimeState: RuntimeState | null;
|
|
219
|
+
} {
|
|
220
|
+
return {
|
|
221
|
+
debugState: handoffState.savedVibeState,
|
|
222
|
+
runtimeState: handoffState.savedRuntimeState,
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Setup temporary breakpoint for stepping into TS import
|
|
228
|
+
*/
|
|
229
|
+
export async function setupTsImportBreakpoint(
|
|
230
|
+
bunInspector: BunInspectorClient,
|
|
231
|
+
tsFile: string,
|
|
232
|
+
functionName: string,
|
|
233
|
+
entryLine: number
|
|
234
|
+
): Promise<string | null> {
|
|
235
|
+
try {
|
|
236
|
+
const result = await bunInspector.setBreakpointByUrl(
|
|
237
|
+
tsFile,
|
|
238
|
+
entryLine - 1, // CDP uses 0-based lines
|
|
239
|
+
0
|
|
240
|
+
);
|
|
241
|
+
if (result.breakpointId) {
|
|
242
|
+
registerTempBreakpoint(tsFile, entryLine, result.breakpointId);
|
|
243
|
+
return result.breakpointId;
|
|
244
|
+
}
|
|
245
|
+
} catch (error) {
|
|
246
|
+
console.error('Failed to set TS import breakpoint:', error);
|
|
247
|
+
}
|
|
248
|
+
return null;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Clean up temporary breakpoint after hitting it
|
|
253
|
+
*/
|
|
254
|
+
export async function cleanupTempBreakpoint(
|
|
255
|
+
bunInspector: BunInspectorClient,
|
|
256
|
+
tsFile: string,
|
|
257
|
+
line: number
|
|
258
|
+
): Promise<void> {
|
|
259
|
+
const breakpointId = popTempBreakpoint(tsFile, line);
|
|
260
|
+
if (breakpointId) {
|
|
261
|
+
try {
|
|
262
|
+
await bunInspector.removeBreakpoint(breakpointId);
|
|
263
|
+
} catch (error) {
|
|
264
|
+
// Ignore errors during cleanup
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Clean up all temporary breakpoints
|
|
271
|
+
*/
|
|
272
|
+
export async function cleanupAllTempBreakpoints(
|
|
273
|
+
bunInspector: BunInspectorClient
|
|
274
|
+
): Promise<void> {
|
|
275
|
+
const all = clearTempBreakpoints();
|
|
276
|
+
for (const breakpointId of all.values()) {
|
|
277
|
+
try {
|
|
278
|
+
await bunInspector.removeBreakpoint(breakpointId);
|
|
279
|
+
} catch (error) {
|
|
280
|
+
// Ignore errors during cleanup
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|