@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,482 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Advanced Debug Features
|
|
3
|
+
* Conditional breakpoints, logpoints, watch expressions, exception breakpoints
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { RuntimeState } from '../runtime/types';
|
|
7
|
+
import type { VibeDebugState } from './state';
|
|
8
|
+
import type { SourceLocation, Breakpoint } from '@vibe-lang/debug-core';
|
|
9
|
+
|
|
10
|
+
// Extended breakpoint with advanced features
|
|
11
|
+
export interface AdvancedBreakpoint extends Breakpoint {
|
|
12
|
+
// Condition expression (only break if true)
|
|
13
|
+
condition?: string;
|
|
14
|
+
// Hit count condition (e.g., ">=5", "==10", "%3")
|
|
15
|
+
hitCondition?: string;
|
|
16
|
+
// Log message instead of breaking (logpoint)
|
|
17
|
+
logMessage?: string;
|
|
18
|
+
// Current hit count
|
|
19
|
+
hitCount: number;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Watch expression
|
|
23
|
+
export interface WatchExpression {
|
|
24
|
+
id: number;
|
|
25
|
+
expression: string;
|
|
26
|
+
// Last evaluated value
|
|
27
|
+
lastValue?: string;
|
|
28
|
+
// Error if evaluation failed
|
|
29
|
+
error?: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Exception breakpoint configuration
|
|
33
|
+
export interface ExceptionBreakpointConfig {
|
|
34
|
+
// Break on all exceptions
|
|
35
|
+
all: boolean;
|
|
36
|
+
// Break on uncaught exceptions only
|
|
37
|
+
uncaught: boolean;
|
|
38
|
+
// Break on specific exception types
|
|
39
|
+
filters: string[];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Advanced debug state
|
|
43
|
+
export interface AdvancedDebugState {
|
|
44
|
+
// Advanced breakpoints (keyed by file:line)
|
|
45
|
+
advancedBreakpoints: Map<string, AdvancedBreakpoint>;
|
|
46
|
+
// Watch expressions
|
|
47
|
+
watchExpressions: WatchExpression[];
|
|
48
|
+
nextWatchId: number;
|
|
49
|
+
// Exception breakpoint settings
|
|
50
|
+
exceptionBreakpoints: ExceptionBreakpointConfig;
|
|
51
|
+
// Log output buffer for logpoints
|
|
52
|
+
logOutput: string[];
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Create initial advanced debug state
|
|
57
|
+
*/
|
|
58
|
+
export function createAdvancedDebugState(): AdvancedDebugState {
|
|
59
|
+
return {
|
|
60
|
+
advancedBreakpoints: new Map(),
|
|
61
|
+
watchExpressions: [],
|
|
62
|
+
nextWatchId: 1,
|
|
63
|
+
exceptionBreakpoints: {
|
|
64
|
+
all: false,
|
|
65
|
+
uncaught: true,
|
|
66
|
+
filters: [],
|
|
67
|
+
},
|
|
68
|
+
logOutput: [],
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Set an advanced breakpoint
|
|
74
|
+
*/
|
|
75
|
+
export function setAdvancedBreakpoint(
|
|
76
|
+
state: AdvancedDebugState,
|
|
77
|
+
file: string,
|
|
78
|
+
line: number,
|
|
79
|
+
options?: {
|
|
80
|
+
condition?: string;
|
|
81
|
+
hitCondition?: string;
|
|
82
|
+
logMessage?: string;
|
|
83
|
+
}
|
|
84
|
+
): { state: AdvancedDebugState; breakpoint: AdvancedBreakpoint } {
|
|
85
|
+
const key = `${normalizeFilePath(file)}:${line}`;
|
|
86
|
+
|
|
87
|
+
const breakpoint: AdvancedBreakpoint = {
|
|
88
|
+
id: Date.now(), // Simple unique ID
|
|
89
|
+
file,
|
|
90
|
+
line,
|
|
91
|
+
verified: true,
|
|
92
|
+
condition: options?.condition,
|
|
93
|
+
hitCondition: options?.hitCondition,
|
|
94
|
+
logMessage: options?.logMessage,
|
|
95
|
+
hitCount: 0,
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
const newBreakpoints = new Map(state.advancedBreakpoints);
|
|
99
|
+
newBreakpoints.set(key, breakpoint);
|
|
100
|
+
|
|
101
|
+
return {
|
|
102
|
+
state: { ...state, advancedBreakpoints: newBreakpoints },
|
|
103
|
+
breakpoint,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Remove an advanced breakpoint
|
|
109
|
+
*/
|
|
110
|
+
export function removeAdvancedBreakpoint(
|
|
111
|
+
state: AdvancedDebugState,
|
|
112
|
+
file: string,
|
|
113
|
+
line: number
|
|
114
|
+
): AdvancedDebugState {
|
|
115
|
+
const key = `${normalizeFilePath(file)}:${line}`;
|
|
116
|
+
const newBreakpoints = new Map(state.advancedBreakpoints);
|
|
117
|
+
newBreakpoints.delete(key);
|
|
118
|
+
return { ...state, advancedBreakpoints: newBreakpoints };
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Get advanced breakpoint at location
|
|
123
|
+
*/
|
|
124
|
+
export function getAdvancedBreakpoint(
|
|
125
|
+
state: AdvancedDebugState,
|
|
126
|
+
file: string,
|
|
127
|
+
line: number
|
|
128
|
+
): AdvancedBreakpoint | undefined {
|
|
129
|
+
const key = `${normalizeFilePath(file)}:${line}`;
|
|
130
|
+
return state.advancedBreakpoints.get(key);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Evaluate condition expression
|
|
135
|
+
* Returns true if condition passes (or no condition)
|
|
136
|
+
*/
|
|
137
|
+
export function evaluateCondition(
|
|
138
|
+
condition: string | undefined,
|
|
139
|
+
runtimeState: RuntimeState
|
|
140
|
+
): boolean {
|
|
141
|
+
if (!condition) return true;
|
|
142
|
+
|
|
143
|
+
try {
|
|
144
|
+
// Get current frame's variables
|
|
145
|
+
const frame = runtimeState.callStack[runtimeState.callStack.length - 1];
|
|
146
|
+
if (!frame) return true;
|
|
147
|
+
|
|
148
|
+
// Build context for evaluation
|
|
149
|
+
const context: Record<string, unknown> = {};
|
|
150
|
+
for (const [name, variable] of Object.entries(frame.locals)) {
|
|
151
|
+
context[name] = variable.value;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Simple expression evaluation
|
|
155
|
+
// Supports: variable comparisons, logical operators, simple math
|
|
156
|
+
return evaluateSimpleExpression(condition, context);
|
|
157
|
+
} catch {
|
|
158
|
+
// If evaluation fails, don't break
|
|
159
|
+
return false;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Check hit count condition
|
|
165
|
+
*/
|
|
166
|
+
export function checkHitCondition(
|
|
167
|
+
hitCondition: string | undefined,
|
|
168
|
+
hitCount: number
|
|
169
|
+
): boolean {
|
|
170
|
+
if (!hitCondition) return true;
|
|
171
|
+
|
|
172
|
+
const trimmed = hitCondition.trim();
|
|
173
|
+
|
|
174
|
+
// Check different hit condition formats
|
|
175
|
+
if (trimmed.startsWith('>=')) {
|
|
176
|
+
const value = parseInt(trimmed.slice(2), 10);
|
|
177
|
+
return hitCount >= value;
|
|
178
|
+
}
|
|
179
|
+
if (trimmed.startsWith('<=')) {
|
|
180
|
+
const value = parseInt(trimmed.slice(2), 10);
|
|
181
|
+
return hitCount <= value;
|
|
182
|
+
}
|
|
183
|
+
if (trimmed.startsWith('>')) {
|
|
184
|
+
const value = parseInt(trimmed.slice(1), 10);
|
|
185
|
+
return hitCount > value;
|
|
186
|
+
}
|
|
187
|
+
if (trimmed.startsWith('<')) {
|
|
188
|
+
const value = parseInt(trimmed.slice(1), 10);
|
|
189
|
+
return hitCount < value;
|
|
190
|
+
}
|
|
191
|
+
if (trimmed.startsWith('==') || trimmed.startsWith('=')) {
|
|
192
|
+
const value = parseInt(trimmed.replace(/^==?/, ''), 10);
|
|
193
|
+
return hitCount === value;
|
|
194
|
+
}
|
|
195
|
+
if (trimmed.startsWith('%')) {
|
|
196
|
+
const value = parseInt(trimmed.slice(1), 10);
|
|
197
|
+
return value > 0 && hitCount % value === 0;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Default: break when hit count reaches the value
|
|
201
|
+
const value = parseInt(trimmed, 10);
|
|
202
|
+
return hitCount >= value;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Check if breakpoint should trigger
|
|
207
|
+
* Handles condition, hit count, and logpoint
|
|
208
|
+
*/
|
|
209
|
+
export function shouldBreakpointTrigger(
|
|
210
|
+
state: AdvancedDebugState,
|
|
211
|
+
runtimeState: RuntimeState,
|
|
212
|
+
location: SourceLocation
|
|
213
|
+
): { shouldBreak: boolean; logMessage?: string; newState: AdvancedDebugState } {
|
|
214
|
+
const bp = getAdvancedBreakpoint(state, location.file, location.line);
|
|
215
|
+
if (!bp) {
|
|
216
|
+
return { shouldBreak: false, newState: state };
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// Increment hit count
|
|
220
|
+
const newBp: AdvancedBreakpoint = { ...bp, hitCount: bp.hitCount + 1 };
|
|
221
|
+
const newBreakpoints = new Map(state.advancedBreakpoints);
|
|
222
|
+
newBreakpoints.set(`${normalizeFilePath(location.file)}:${location.line}`, newBp);
|
|
223
|
+
let newState: AdvancedDebugState = { ...state, advancedBreakpoints: newBreakpoints };
|
|
224
|
+
|
|
225
|
+
// Check condition
|
|
226
|
+
if (!evaluateCondition(bp.condition, runtimeState)) {
|
|
227
|
+
return { shouldBreak: false, newState };
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// Check hit condition
|
|
231
|
+
if (!checkHitCondition(bp.hitCondition, newBp.hitCount)) {
|
|
232
|
+
return { shouldBreak: false, newState };
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// Handle logpoint
|
|
236
|
+
if (bp.logMessage) {
|
|
237
|
+
const message = interpolateLogMessage(bp.logMessage, runtimeState);
|
|
238
|
+
newState = {
|
|
239
|
+
...newState,
|
|
240
|
+
logOutput: [...newState.logOutput, message],
|
|
241
|
+
};
|
|
242
|
+
// Logpoints don't break
|
|
243
|
+
return { shouldBreak: false, logMessage: message, newState };
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
return { shouldBreak: true, newState };
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Interpolate variables in log message
|
|
251
|
+
* Supports {variableName} syntax
|
|
252
|
+
*/
|
|
253
|
+
export function interpolateLogMessage(
|
|
254
|
+
message: string,
|
|
255
|
+
runtimeState: RuntimeState
|
|
256
|
+
): string {
|
|
257
|
+
const frame = runtimeState.callStack[runtimeState.callStack.length - 1];
|
|
258
|
+
if (!frame) return message;
|
|
259
|
+
|
|
260
|
+
return message.replace(/\{(\w+)\}/g, (match, varName) => {
|
|
261
|
+
const variable = frame.locals[varName];
|
|
262
|
+
if (variable) {
|
|
263
|
+
return formatValueForLog(variable.value);
|
|
264
|
+
}
|
|
265
|
+
return match; // Keep original if variable not found
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Add a watch expression
|
|
271
|
+
*/
|
|
272
|
+
export function addWatchExpression(
|
|
273
|
+
state: AdvancedDebugState,
|
|
274
|
+
expression: string
|
|
275
|
+
): { state: AdvancedDebugState; watch: WatchExpression } {
|
|
276
|
+
const watch: WatchExpression = {
|
|
277
|
+
id: state.nextWatchId,
|
|
278
|
+
expression,
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
return {
|
|
282
|
+
state: {
|
|
283
|
+
...state,
|
|
284
|
+
watchExpressions: [...state.watchExpressions, watch],
|
|
285
|
+
nextWatchId: state.nextWatchId + 1,
|
|
286
|
+
},
|
|
287
|
+
watch,
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Remove a watch expression
|
|
293
|
+
*/
|
|
294
|
+
export function removeWatchExpression(
|
|
295
|
+
state: AdvancedDebugState,
|
|
296
|
+
watchId: number
|
|
297
|
+
): AdvancedDebugState {
|
|
298
|
+
return {
|
|
299
|
+
...state,
|
|
300
|
+
watchExpressions: state.watchExpressions.filter(w => w.id !== watchId),
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Evaluate all watch expressions
|
|
306
|
+
*/
|
|
307
|
+
export function evaluateWatchExpressions(
|
|
308
|
+
state: AdvancedDebugState,
|
|
309
|
+
runtimeState: RuntimeState
|
|
310
|
+
): AdvancedDebugState {
|
|
311
|
+
const evaluated = state.watchExpressions.map(watch => {
|
|
312
|
+
try {
|
|
313
|
+
const frame = runtimeState.callStack[runtimeState.callStack.length - 1];
|
|
314
|
+
if (!frame) {
|
|
315
|
+
return { ...watch, lastValue: undefined, error: 'No active frame' };
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
const context: Record<string, unknown> = {};
|
|
319
|
+
for (const [name, variable] of Object.entries(frame.locals)) {
|
|
320
|
+
context[name] = variable.value;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
const value = evaluateExpression(watch.expression, context);
|
|
324
|
+
return {
|
|
325
|
+
...watch,
|
|
326
|
+
lastValue: formatValueForLog(value),
|
|
327
|
+
error: undefined,
|
|
328
|
+
};
|
|
329
|
+
} catch (error) {
|
|
330
|
+
return {
|
|
331
|
+
...watch,
|
|
332
|
+
lastValue: undefined,
|
|
333
|
+
error: error instanceof Error ? error.message : String(error),
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
return { ...state, watchExpressions: evaluated };
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* Configure exception breakpoints
|
|
343
|
+
*/
|
|
344
|
+
export function setExceptionBreakpoints(
|
|
345
|
+
state: AdvancedDebugState,
|
|
346
|
+
config: Partial<ExceptionBreakpointConfig>
|
|
347
|
+
): AdvancedDebugState {
|
|
348
|
+
return {
|
|
349
|
+
...state,
|
|
350
|
+
exceptionBreakpoints: {
|
|
351
|
+
...state.exceptionBreakpoints,
|
|
352
|
+
...config,
|
|
353
|
+
},
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* Check if we should break on an exception
|
|
359
|
+
*/
|
|
360
|
+
export function shouldBreakOnException(
|
|
361
|
+
state: AdvancedDebugState,
|
|
362
|
+
error: Error,
|
|
363
|
+
isCaught: boolean
|
|
364
|
+
): boolean {
|
|
365
|
+
const config = state.exceptionBreakpoints;
|
|
366
|
+
|
|
367
|
+
// Break on all exceptions
|
|
368
|
+
if (config.all) {
|
|
369
|
+
return true;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// Break on uncaught only
|
|
373
|
+
if (config.uncaught && !isCaught) {
|
|
374
|
+
return true;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// Check filters
|
|
378
|
+
if (config.filters.length > 0) {
|
|
379
|
+
const errorType = error.constructor.name;
|
|
380
|
+
return config.filters.some(f =>
|
|
381
|
+
errorType.toLowerCase().includes(f.toLowerCase()) ||
|
|
382
|
+
error.message.toLowerCase().includes(f.toLowerCase())
|
|
383
|
+
);
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
return false;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
/**
|
|
390
|
+
* Get log output and clear buffer
|
|
391
|
+
*/
|
|
392
|
+
export function flushLogOutput(state: AdvancedDebugState): {
|
|
393
|
+
state: AdvancedDebugState;
|
|
394
|
+
logs: string[];
|
|
395
|
+
} {
|
|
396
|
+
return {
|
|
397
|
+
state: { ...state, logOutput: [] },
|
|
398
|
+
logs: state.logOutput,
|
|
399
|
+
};
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
// Helper functions
|
|
403
|
+
|
|
404
|
+
function normalizeFilePath(path: string): string {
|
|
405
|
+
return path.replace(/\\/g, '/').toLowerCase();
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
function formatValueForLog(value: unknown): string {
|
|
409
|
+
if (value === null) return 'null';
|
|
410
|
+
if (value === undefined) return 'undefined';
|
|
411
|
+
if (typeof value === 'string') return value;
|
|
412
|
+
if (typeof value === 'number' || typeof value === 'boolean') return String(value);
|
|
413
|
+
if (Array.isArray(value)) return JSON.stringify(value);
|
|
414
|
+
if (typeof value === 'object') {
|
|
415
|
+
// Check for VibeValue
|
|
416
|
+
if ('value' in value) {
|
|
417
|
+
return formatValueForLog((value as any).value);
|
|
418
|
+
}
|
|
419
|
+
return JSON.stringify(value);
|
|
420
|
+
}
|
|
421
|
+
return String(value);
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
function evaluateSimpleExpression(
|
|
425
|
+
expression: string,
|
|
426
|
+
context: Record<string, unknown>
|
|
427
|
+
): boolean {
|
|
428
|
+
// Replace variable names with their values
|
|
429
|
+
let evaluated = expression;
|
|
430
|
+
|
|
431
|
+
for (const [name, value] of Object.entries(context)) {
|
|
432
|
+
const regex = new RegExp(`\\b${name}\\b`, 'g');
|
|
433
|
+
evaluated = evaluated.replace(regex, JSON.stringify(value));
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
// Simple expression evaluation using Function constructor
|
|
437
|
+
// Only supports basic comparisons and logical ops
|
|
438
|
+
try {
|
|
439
|
+
// Sanitize - only allow safe operators and values
|
|
440
|
+
if (!/^[\s\d\w"'<>=!&|+\-*/%().]+$/.test(evaluated)) {
|
|
441
|
+
return false;
|
|
442
|
+
}
|
|
443
|
+
const fn = new Function(`return ${evaluated}`);
|
|
444
|
+
return !!fn();
|
|
445
|
+
} catch {
|
|
446
|
+
return false;
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
function evaluateExpression(
|
|
451
|
+
expression: string,
|
|
452
|
+
context: Record<string, unknown>
|
|
453
|
+
): unknown {
|
|
454
|
+
// Simple variable lookup
|
|
455
|
+
if (context.hasOwnProperty(expression)) {
|
|
456
|
+
return context[expression];
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
// Property access (e.g., "obj.prop")
|
|
460
|
+
const parts = expression.split('.');
|
|
461
|
+
if (parts.length > 1) {
|
|
462
|
+
let value: unknown = context[parts[0]];
|
|
463
|
+
for (let i = 1; i < parts.length && value != null; i++) {
|
|
464
|
+
value = (value as any)[parts[i]];
|
|
465
|
+
}
|
|
466
|
+
return value;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
// Try evaluating as expression
|
|
470
|
+
let evaluated = expression;
|
|
471
|
+
for (const [name, value] of Object.entries(context)) {
|
|
472
|
+
const regex = new RegExp(`\\b${name}\\b`, 'g');
|
|
473
|
+
evaluated = evaluated.replace(regex, JSON.stringify(value));
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
try {
|
|
477
|
+
const fn = new Function(`return ${evaluated}`);
|
|
478
|
+
return fn();
|
|
479
|
+
} catch {
|
|
480
|
+
throw new Error(`Cannot evaluate: ${expression}`);
|
|
481
|
+
}
|
|
482
|
+
}
|