@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.
Files changed (250) hide show
  1. package/package.json +46 -0
  2. package/src/ast/index.ts +375 -0
  3. package/src/ast.ts +2 -0
  4. package/src/debug/advanced-features.ts +482 -0
  5. package/src/debug/bun-inspector.ts +424 -0
  6. package/src/debug/handoff-manager.ts +283 -0
  7. package/src/debug/index.ts +150 -0
  8. package/src/debug/runner.ts +365 -0
  9. package/src/debug/server.ts +565 -0
  10. package/src/debug/stack-merger.ts +267 -0
  11. package/src/debug/state.ts +581 -0
  12. package/src/debug/test/advanced-features.test.ts +300 -0
  13. package/src/debug/test/e2e.test.ts +218 -0
  14. package/src/debug/test/handoff-manager.test.ts +256 -0
  15. package/src/debug/test/runner.test.ts +256 -0
  16. package/src/debug/test/stack-merger.test.ts +163 -0
  17. package/src/debug/test/state.test.ts +400 -0
  18. package/src/debug/test/ts-debug-integration.test.ts +374 -0
  19. package/src/debug/test/ts-import-tracker.test.ts +125 -0
  20. package/src/debug/test/ts-source-map.test.ts +169 -0
  21. package/src/debug/ts-import-tracker.ts +151 -0
  22. package/src/debug/ts-source-map.ts +171 -0
  23. package/src/errors/index.ts +124 -0
  24. package/src/index.ts +358 -0
  25. package/src/lexer/index.ts +348 -0
  26. package/src/lexer.ts +2 -0
  27. package/src/parser/index.ts +792 -0
  28. package/src/parser/parse.ts +45 -0
  29. package/src/parser/test/async.test.ts +248 -0
  30. package/src/parser/test/destructuring.test.ts +167 -0
  31. package/src/parser/test/do-expression.test.ts +486 -0
  32. package/src/parser/test/errors/do-expression.test.ts +95 -0
  33. package/src/parser/test/errors/error-locations.test.ts +230 -0
  34. package/src/parser/test/errors/invalid-expressions.test.ts +144 -0
  35. package/src/parser/test/errors/missing-tokens.test.ts +126 -0
  36. package/src/parser/test/errors/model-declaration.test.ts +185 -0
  37. package/src/parser/test/errors/nested-blocks.test.ts +226 -0
  38. package/src/parser/test/errors/unclosed-delimiters.test.ts +122 -0
  39. package/src/parser/test/errors/unexpected-tokens.test.ts +120 -0
  40. package/src/parser/test/import-export.test.ts +143 -0
  41. package/src/parser/test/literals.test.ts +404 -0
  42. package/src/parser/test/model-declaration.test.ts +161 -0
  43. package/src/parser/test/nested-blocks.test.ts +402 -0
  44. package/src/parser/test/parser.test.ts +743 -0
  45. package/src/parser/test/private.test.ts +136 -0
  46. package/src/parser/test/template-literal.test.ts +127 -0
  47. package/src/parser/test/tool-declaration.test.ts +302 -0
  48. package/src/parser/test/ts-block.test.ts +252 -0
  49. package/src/parser/test/type-annotations.test.ts +254 -0
  50. package/src/parser/visitor/helpers.ts +330 -0
  51. package/src/parser/visitor.ts +794 -0
  52. package/src/parser.ts +2 -0
  53. package/src/runtime/ai/cache-chunking.test.ts +69 -0
  54. package/src/runtime/ai/cache-chunking.ts +73 -0
  55. package/src/runtime/ai/client.ts +109 -0
  56. package/src/runtime/ai/context.ts +168 -0
  57. package/src/runtime/ai/formatters.ts +316 -0
  58. package/src/runtime/ai/index.ts +38 -0
  59. package/src/runtime/ai/language-ref.ts +38 -0
  60. package/src/runtime/ai/providers/anthropic.ts +253 -0
  61. package/src/runtime/ai/providers/google.ts +201 -0
  62. package/src/runtime/ai/providers/openai.ts +156 -0
  63. package/src/runtime/ai/retry.ts +100 -0
  64. package/src/runtime/ai/return-tools.ts +301 -0
  65. package/src/runtime/ai/test/client.test.ts +83 -0
  66. package/src/runtime/ai/test/formatters.test.ts +485 -0
  67. package/src/runtime/ai/test/retry.test.ts +137 -0
  68. package/src/runtime/ai/test/return-tools.test.ts +450 -0
  69. package/src/runtime/ai/test/tool-loop.test.ts +319 -0
  70. package/src/runtime/ai/test/tool-schema.test.ts +241 -0
  71. package/src/runtime/ai/tool-loop.ts +203 -0
  72. package/src/runtime/ai/tool-schema.ts +151 -0
  73. package/src/runtime/ai/types.ts +113 -0
  74. package/src/runtime/ai-logger.ts +255 -0
  75. package/src/runtime/ai-provider.ts +347 -0
  76. package/src/runtime/async/dependencies.ts +276 -0
  77. package/src/runtime/async/executor.ts +293 -0
  78. package/src/runtime/async/index.ts +43 -0
  79. package/src/runtime/async/scheduling.ts +163 -0
  80. package/src/runtime/async/test/dependencies.test.ts +284 -0
  81. package/src/runtime/async/test/executor.test.ts +388 -0
  82. package/src/runtime/context.ts +357 -0
  83. package/src/runtime/exec/ai.ts +139 -0
  84. package/src/runtime/exec/expressions.ts +475 -0
  85. package/src/runtime/exec/frames.ts +26 -0
  86. package/src/runtime/exec/functions.ts +305 -0
  87. package/src/runtime/exec/interpolation.ts +312 -0
  88. package/src/runtime/exec/statements.ts +604 -0
  89. package/src/runtime/exec/tools.ts +129 -0
  90. package/src/runtime/exec/typescript.ts +215 -0
  91. package/src/runtime/exec/variables.ts +279 -0
  92. package/src/runtime/index.ts +975 -0
  93. package/src/runtime/modules.ts +452 -0
  94. package/src/runtime/serialize.ts +103 -0
  95. package/src/runtime/state.ts +489 -0
  96. package/src/runtime/stdlib/core.ts +45 -0
  97. package/src/runtime/stdlib/directory.test.ts +156 -0
  98. package/src/runtime/stdlib/edit.test.ts +154 -0
  99. package/src/runtime/stdlib/fastEdit.test.ts +201 -0
  100. package/src/runtime/stdlib/glob.test.ts +106 -0
  101. package/src/runtime/stdlib/grep.test.ts +144 -0
  102. package/src/runtime/stdlib/index.ts +16 -0
  103. package/src/runtime/stdlib/readFile.test.ts +123 -0
  104. package/src/runtime/stdlib/tools/index.ts +707 -0
  105. package/src/runtime/stdlib/writeFile.test.ts +157 -0
  106. package/src/runtime/step.ts +969 -0
  107. package/src/runtime/test/ai-context.test.ts +1086 -0
  108. package/src/runtime/test/ai-result-object.test.ts +419 -0
  109. package/src/runtime/test/ai-tool-flow.test.ts +859 -0
  110. package/src/runtime/test/async-execution-order.test.ts +618 -0
  111. package/src/runtime/test/async-execution.test.ts +344 -0
  112. package/src/runtime/test/async-nested.test.ts +660 -0
  113. package/src/runtime/test/async-parallel-timing.test.ts +546 -0
  114. package/src/runtime/test/basic1.test.ts +154 -0
  115. package/src/runtime/test/binary-operators.test.ts +431 -0
  116. package/src/runtime/test/break-statement.test.ts +257 -0
  117. package/src/runtime/test/context-modes.test.ts +650 -0
  118. package/src/runtime/test/context.test.ts +466 -0
  119. package/src/runtime/test/core-functions.test.ts +228 -0
  120. package/src/runtime/test/e2e.test.ts +88 -0
  121. package/src/runtime/test/error-locations/error-locations.test.ts +80 -0
  122. package/src/runtime/test/error-locations/main-error.vibe +4 -0
  123. package/src/runtime/test/error-locations/main-import-error.vibe +3 -0
  124. package/src/runtime/test/error-locations/utils/helper.vibe +5 -0
  125. package/src/runtime/test/for-in.test.ts +312 -0
  126. package/src/runtime/test/helpers.ts +69 -0
  127. package/src/runtime/test/imports.test.ts +334 -0
  128. package/src/runtime/test/json-expressions.test.ts +232 -0
  129. package/src/runtime/test/literals.test.ts +372 -0
  130. package/src/runtime/test/logical-indexing.test.ts +478 -0
  131. package/src/runtime/test/member-methods.test.ts +324 -0
  132. package/src/runtime/test/model-config.test.ts +338 -0
  133. package/src/runtime/test/null-handling.test.ts +342 -0
  134. package/src/runtime/test/private-visibility.test.ts +332 -0
  135. package/src/runtime/test/runtime-state.test.ts +514 -0
  136. package/src/runtime/test/scoping.test.ts +370 -0
  137. package/src/runtime/test/string-interpolation.test.ts +354 -0
  138. package/src/runtime/test/template-literal.test.ts +181 -0
  139. package/src/runtime/test/tool-execution.test.ts +467 -0
  140. package/src/runtime/test/tool-schema-generation.test.ts +477 -0
  141. package/src/runtime/test/tostring.test.ts +210 -0
  142. package/src/runtime/test/ts-block.test.ts +594 -0
  143. package/src/runtime/test/ts-error-location.test.ts +231 -0
  144. package/src/runtime/test/types.test.ts +732 -0
  145. package/src/runtime/test/verbose-logger.test.ts +710 -0
  146. package/src/runtime/test/vibe-expression.test.ts +54 -0
  147. package/src/runtime/test/vibe-value-errors.test.ts +541 -0
  148. package/src/runtime/test/while.test.ts +232 -0
  149. package/src/runtime/tools/builtin.ts +30 -0
  150. package/src/runtime/tools/directory-tools.ts +70 -0
  151. package/src/runtime/tools/file-tools.ts +228 -0
  152. package/src/runtime/tools/index.ts +5 -0
  153. package/src/runtime/tools/registry.ts +48 -0
  154. package/src/runtime/tools/search-tools.ts +134 -0
  155. package/src/runtime/tools/security.ts +36 -0
  156. package/src/runtime/tools/system-tools.ts +312 -0
  157. package/src/runtime/tools/test/fixtures/base-types.ts +40 -0
  158. package/src/runtime/tools/test/fixtures/test-types.ts +132 -0
  159. package/src/runtime/tools/test/registry.test.ts +713 -0
  160. package/src/runtime/tools/test/security.test.ts +86 -0
  161. package/src/runtime/tools/test/system-tools.test.ts +679 -0
  162. package/src/runtime/tools/test/ts-schema.test.ts +357 -0
  163. package/src/runtime/tools/ts-schema.ts +341 -0
  164. package/src/runtime/tools/types.ts +89 -0
  165. package/src/runtime/tools/utility-tools.ts +198 -0
  166. package/src/runtime/ts-eval.ts +126 -0
  167. package/src/runtime/types.ts +797 -0
  168. package/src/runtime/validation.ts +160 -0
  169. package/src/runtime/verbose-logger.ts +459 -0
  170. package/src/runtime.ts +2 -0
  171. package/src/semantic/analyzer-context.ts +62 -0
  172. package/src/semantic/analyzer-validators.ts +575 -0
  173. package/src/semantic/analyzer-visitors.ts +534 -0
  174. package/src/semantic/analyzer.ts +83 -0
  175. package/src/semantic/index.ts +11 -0
  176. package/src/semantic/symbol-table.ts +58 -0
  177. package/src/semantic/test/async-validation.test.ts +301 -0
  178. package/src/semantic/test/compress-validation.test.ts +179 -0
  179. package/src/semantic/test/const-reassignment.test.ts +111 -0
  180. package/src/semantic/test/control-flow.test.ts +346 -0
  181. package/src/semantic/test/destructuring.test.ts +185 -0
  182. package/src/semantic/test/duplicate-declarations.test.ts +168 -0
  183. package/src/semantic/test/export-validation.test.ts +111 -0
  184. package/src/semantic/test/fixtures/math.ts +31 -0
  185. package/src/semantic/test/imports.test.ts +148 -0
  186. package/src/semantic/test/json-type.test.ts +68 -0
  187. package/src/semantic/test/literals.test.ts +127 -0
  188. package/src/semantic/test/model-validation.test.ts +179 -0
  189. package/src/semantic/test/prompt-validation.test.ts +343 -0
  190. package/src/semantic/test/scoping.test.ts +312 -0
  191. package/src/semantic/test/tool-validation.test.ts +306 -0
  192. package/src/semantic/test/ts-type-checking.test.ts +563 -0
  193. package/src/semantic/test/type-constraints.test.ts +111 -0
  194. package/src/semantic/test/type-inference.test.ts +87 -0
  195. package/src/semantic/test/type-validation.test.ts +552 -0
  196. package/src/semantic/test/undefined-variables.test.ts +163 -0
  197. package/src/semantic/ts-block-checker.ts +204 -0
  198. package/src/semantic/ts-signatures.ts +194 -0
  199. package/src/semantic/ts-types.ts +170 -0
  200. package/src/semantic/types.ts +58 -0
  201. package/tests/fixtures/conditional-logic.vibe +14 -0
  202. package/tests/fixtures/function-call.vibe +16 -0
  203. package/tests/fixtures/imports/cycle-detection/a.vibe +6 -0
  204. package/tests/fixtures/imports/cycle-detection/b.vibe +5 -0
  205. package/tests/fixtures/imports/cycle-detection/main.vibe +3 -0
  206. package/tests/fixtures/imports/module-isolation/main-b.vibe +8 -0
  207. package/tests/fixtures/imports/module-isolation/main.vibe +9 -0
  208. package/tests/fixtures/imports/module-isolation/moduleA.vibe +6 -0
  209. package/tests/fixtures/imports/module-isolation/moduleB.vibe +6 -0
  210. package/tests/fixtures/imports/nested-import/helper.vibe +6 -0
  211. package/tests/fixtures/imports/nested-import/main.vibe +3 -0
  212. package/tests/fixtures/imports/nested-import/utils.ts +3 -0
  213. package/tests/fixtures/imports/nested-isolation/file2.vibe +15 -0
  214. package/tests/fixtures/imports/nested-isolation/file3.vibe +10 -0
  215. package/tests/fixtures/imports/nested-isolation/main.vibe +21 -0
  216. package/tests/fixtures/imports/pure-cycle/a.vibe +5 -0
  217. package/tests/fixtures/imports/pure-cycle/b.vibe +5 -0
  218. package/tests/fixtures/imports/pure-cycle/main.vibe +3 -0
  219. package/tests/fixtures/imports/ts-boolean/checks.ts +14 -0
  220. package/tests/fixtures/imports/ts-boolean/main.vibe +10 -0
  221. package/tests/fixtures/imports/ts-boolean/type-mismatch.vibe +5 -0
  222. package/tests/fixtures/imports/ts-boolean/use-constant.vibe +18 -0
  223. package/tests/fixtures/imports/ts-error-handling/helpers.ts +42 -0
  224. package/tests/fixtures/imports/ts-error-handling/main.vibe +5 -0
  225. package/tests/fixtures/imports/ts-import/main.vibe +4 -0
  226. package/tests/fixtures/imports/ts-import/math.ts +9 -0
  227. package/tests/fixtures/imports/ts-variables/call-non-function.vibe +5 -0
  228. package/tests/fixtures/imports/ts-variables/data.ts +10 -0
  229. package/tests/fixtures/imports/ts-variables/import-json.vibe +5 -0
  230. package/tests/fixtures/imports/ts-variables/import-type-mismatch.vibe +5 -0
  231. package/tests/fixtures/imports/ts-variables/import-variable.vibe +5 -0
  232. package/tests/fixtures/imports/vibe-import/greet.vibe +5 -0
  233. package/tests/fixtures/imports/vibe-import/main.vibe +3 -0
  234. package/tests/fixtures/multiple-ai-calls.vibe +10 -0
  235. package/tests/fixtures/simple-greeting.vibe +6 -0
  236. package/tests/fixtures/template-literals.vibe +11 -0
  237. package/tests/integration/basic-ai/basic-ai.integration.test.ts +166 -0
  238. package/tests/integration/basic-ai/basic-ai.vibe +12 -0
  239. package/tests/integration/bug-fix/bug-fix.integration.test.ts +201 -0
  240. package/tests/integration/bug-fix/buggy-code.ts +22 -0
  241. package/tests/integration/bug-fix/fix-bug.vibe +21 -0
  242. package/tests/integration/compress/compress.integration.test.ts +206 -0
  243. package/tests/integration/destructuring/destructuring.integration.test.ts +92 -0
  244. package/tests/integration/hello-world-translator/hello-world-translator.integration.test.ts +61 -0
  245. package/tests/integration/line-annotator/context-modes.integration.test.ts +261 -0
  246. package/tests/integration/line-annotator/line-annotator.integration.test.ts +148 -0
  247. package/tests/integration/multi-feature/cumulative-sum.integration.test.ts +75 -0
  248. package/tests/integration/multi-feature/number-analyzer.integration.test.ts +191 -0
  249. package/tests/integration/multi-feature/number-analyzer.vibe +59 -0
  250. 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
+ }