@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,594 @@
|
|
|
1
|
+
import { describe, expect, test, beforeEach } from 'bun:test';
|
|
2
|
+
import { parse } from '../../parser/parse';
|
|
3
|
+
import { Runtime, AIProvider, clearFunctionCache, getFunctionCacheSize, TsBlockError } from '../index';
|
|
4
|
+
|
|
5
|
+
// Mock AI provider for testing
|
|
6
|
+
function createMockProvider(): AIProvider {
|
|
7
|
+
return {
|
|
8
|
+
async execute() {
|
|
9
|
+
return { value: 'ai response' };
|
|
10
|
+
},
|
|
11
|
+
async generateCode() {
|
|
12
|
+
return { value: 'generated code' };
|
|
13
|
+
},
|
|
14
|
+
async askUser(): Promise<string> {
|
|
15
|
+
return 'user input';
|
|
16
|
+
},
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
describe('Runtime - TypeScript Blocks', () => {
|
|
21
|
+
beforeEach(() => {
|
|
22
|
+
clearFunctionCache();
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
// ============================================================================
|
|
26
|
+
// Basic arithmetic
|
|
27
|
+
// ============================================================================
|
|
28
|
+
|
|
29
|
+
test('ts block with simple return', async () => {
|
|
30
|
+
const ast = parse('let x = ts() { return 42 }');
|
|
31
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
32
|
+
await runtime.run();
|
|
33
|
+
|
|
34
|
+
expect(runtime.getValue('x')).toBe(42);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test('ts block with addition', async () => {
|
|
38
|
+
const ast = parse(`
|
|
39
|
+
let a = "5"
|
|
40
|
+
let b = "3"
|
|
41
|
+
let sum = ts(a, b) { return Number(a) + Number(b) }
|
|
42
|
+
`);
|
|
43
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
44
|
+
await runtime.run();
|
|
45
|
+
|
|
46
|
+
expect(runtime.getValue('sum')).toBe(8);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
test('ts block with multiplication', async () => {
|
|
50
|
+
const ast = parse(`
|
|
51
|
+
let x = "7"
|
|
52
|
+
let doubled = ts(x) { return Number(x) * 2 }
|
|
53
|
+
`);
|
|
54
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
55
|
+
await runtime.run();
|
|
56
|
+
|
|
57
|
+
expect(runtime.getValue('doubled')).toBe(14);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
// ============================================================================
|
|
61
|
+
// String operations
|
|
62
|
+
// ============================================================================
|
|
63
|
+
|
|
64
|
+
test('ts block string manipulation', async () => {
|
|
65
|
+
const ast = parse(`
|
|
66
|
+
let name = "alice"
|
|
67
|
+
let upper = ts(name) { return name.toUpperCase() }
|
|
68
|
+
`);
|
|
69
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
70
|
+
await runtime.run();
|
|
71
|
+
|
|
72
|
+
expect(runtime.getValue('upper')).toBe('ALICE');
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
test('ts block string concatenation', async () => {
|
|
76
|
+
const ast = parse(`
|
|
77
|
+
let first = "Hello"
|
|
78
|
+
let second = "World"
|
|
79
|
+
let result = ts(first, second) { return first + " " + second }
|
|
80
|
+
`);
|
|
81
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
82
|
+
await runtime.run();
|
|
83
|
+
|
|
84
|
+
expect(runtime.getValue('result')).toBe('Hello World');
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
test('ts block template literal', async () => {
|
|
88
|
+
const ast = parse(`
|
|
89
|
+
let name = "Bob"
|
|
90
|
+
let greeting = ts(name) { return \`Hello, \${name}!\` }
|
|
91
|
+
`);
|
|
92
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
93
|
+
await runtime.run();
|
|
94
|
+
|
|
95
|
+
expect(runtime.getValue('greeting')).toBe('Hello, Bob!');
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
// ============================================================================
|
|
99
|
+
// Array operations
|
|
100
|
+
// ============================================================================
|
|
101
|
+
|
|
102
|
+
test('ts block array map', async () => {
|
|
103
|
+
const ast = parse(`
|
|
104
|
+
let items: text[] = ["a", "b", "c"]
|
|
105
|
+
let upper = ts(items) { return items.map(x => x.toUpperCase()) }
|
|
106
|
+
`);
|
|
107
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
108
|
+
await runtime.run();
|
|
109
|
+
|
|
110
|
+
expect(runtime.getValue('upper')).toEqual(['A', 'B', 'C']);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
test('ts block array filter', async () => {
|
|
114
|
+
const ast = parse(`
|
|
115
|
+
let numbers: text[] = ["1", "2", "3", "4", "5"]
|
|
116
|
+
let evens = ts(numbers) { return numbers.filter(x => Number(x) % 2 === 0) }
|
|
117
|
+
`);
|
|
118
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
119
|
+
await runtime.run();
|
|
120
|
+
|
|
121
|
+
expect(runtime.getValue('evens')).toEqual(['2', '4']);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
test('ts block array reduce', async () => {
|
|
125
|
+
const ast = parse(`
|
|
126
|
+
let numbers: text[] = ["1", "2", "3", "4", "5"]
|
|
127
|
+
let sum = ts(numbers) { return numbers.reduce((a, b) => a + Number(b), 0) }
|
|
128
|
+
`);
|
|
129
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
130
|
+
await runtime.run();
|
|
131
|
+
|
|
132
|
+
expect(runtime.getValue('sum')).toBe(15);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
// ============================================================================
|
|
136
|
+
// Object operations
|
|
137
|
+
// ============================================================================
|
|
138
|
+
|
|
139
|
+
test('ts block returns object', async () => {
|
|
140
|
+
const ast = parse(`
|
|
141
|
+
let name = "alice"
|
|
142
|
+
let age = "30"
|
|
143
|
+
let user = ts(name, age) { return { name: name, age: Number(age) } }
|
|
144
|
+
`);
|
|
145
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
146
|
+
await runtime.run();
|
|
147
|
+
|
|
148
|
+
expect(runtime.getValue('user')).toEqual({ name: 'alice', age: 30 });
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
test('ts block accesses object property', async () => {
|
|
152
|
+
const ast = parse(`
|
|
153
|
+
let user:json = {name: "bob", score: "100"}
|
|
154
|
+
let userName = ts(user) { return user.name }
|
|
155
|
+
`);
|
|
156
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
157
|
+
await runtime.run();
|
|
158
|
+
|
|
159
|
+
expect(runtime.getValue('userName')).toBe('bob');
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
test('ts block JSON stringify', async () => {
|
|
163
|
+
const ast = parse(`
|
|
164
|
+
let data:json = {key: "value"}
|
|
165
|
+
let jsonStr = ts(data) { return JSON.stringify(data) }
|
|
166
|
+
`);
|
|
167
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
168
|
+
await runtime.run();
|
|
169
|
+
|
|
170
|
+
expect(runtime.getValue('jsonStr')).toBe('{"key":"value"}');
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
// ============================================================================
|
|
174
|
+
// Math operations
|
|
175
|
+
// ============================================================================
|
|
176
|
+
|
|
177
|
+
test('ts block Math.max', async () => {
|
|
178
|
+
const ast = parse(`
|
|
179
|
+
let a = "5"
|
|
180
|
+
let b = "10"
|
|
181
|
+
let max = ts(a, b) { return Math.max(Number(a), Number(b)) }
|
|
182
|
+
`);
|
|
183
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
184
|
+
await runtime.run();
|
|
185
|
+
|
|
186
|
+
expect(runtime.getValue('max')).toBe(10);
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
test('ts block Math.round', async () => {
|
|
190
|
+
const ast = parse(`
|
|
191
|
+
let value = "3.7"
|
|
192
|
+
let rounded = ts(value) { return Math.round(Number(value)) }
|
|
193
|
+
`);
|
|
194
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
195
|
+
await runtime.run();
|
|
196
|
+
|
|
197
|
+
expect(runtime.getValue('rounded')).toBe(4);
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
// ============================================================================
|
|
201
|
+
// Conditionals in ts block
|
|
202
|
+
// ============================================================================
|
|
203
|
+
|
|
204
|
+
test('ts block with ternary', async () => {
|
|
205
|
+
const ast = parse(`
|
|
206
|
+
let x = "5"
|
|
207
|
+
let sign = ts(x) { return Number(x) > 0 ? "positive" : "non-positive" }
|
|
208
|
+
`);
|
|
209
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
210
|
+
await runtime.run();
|
|
211
|
+
|
|
212
|
+
expect(runtime.getValue('sign')).toBe('positive');
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
test('ts block with if statement', async () => {
|
|
216
|
+
const ast = parse(`
|
|
217
|
+
let x = "-3"
|
|
218
|
+
let abs = ts(x) {
|
|
219
|
+
const n = Number(x)
|
|
220
|
+
if (n < 0) { return -n }
|
|
221
|
+
return n
|
|
222
|
+
}
|
|
223
|
+
`);
|
|
224
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
225
|
+
await runtime.run();
|
|
226
|
+
|
|
227
|
+
expect(runtime.getValue('abs')).toBe(3);
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
// ============================================================================
|
|
231
|
+
// Multiple ts blocks in sequence
|
|
232
|
+
// ============================================================================
|
|
233
|
+
|
|
234
|
+
test('chained ts blocks', async () => {
|
|
235
|
+
const ast = parse(`
|
|
236
|
+
let x = "5"
|
|
237
|
+
let doubled = ts(x) { return Number(x) * 2 }
|
|
238
|
+
let squared = ts(doubled) { return doubled * doubled }
|
|
239
|
+
`);
|
|
240
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
241
|
+
await runtime.run();
|
|
242
|
+
|
|
243
|
+
expect(runtime.getValue('doubled')).toBe(10);
|
|
244
|
+
expect(runtime.getValue('squared')).toBe(100);
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
// ============================================================================
|
|
248
|
+
// ts block with async/await
|
|
249
|
+
// ============================================================================
|
|
250
|
+
|
|
251
|
+
test('ts block with async operation', async () => {
|
|
252
|
+
const ast = parse(`
|
|
253
|
+
let result = ts() {
|
|
254
|
+
return await Promise.resolve(42)
|
|
255
|
+
}
|
|
256
|
+
`);
|
|
257
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
258
|
+
await runtime.run();
|
|
259
|
+
|
|
260
|
+
expect(runtime.getValue('result')).toBe(42);
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
test('ts block with delayed async', async () => {
|
|
264
|
+
const ast = parse(`
|
|
265
|
+
let result = ts() {
|
|
266
|
+
return await new Promise(resolve => setTimeout(() => resolve("done"), 10))
|
|
267
|
+
}
|
|
268
|
+
`);
|
|
269
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
270
|
+
await runtime.run();
|
|
271
|
+
|
|
272
|
+
expect(runtime.getValue('result')).toBe('done');
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
// ============================================================================
|
|
276
|
+
// Function caching
|
|
277
|
+
// ============================================================================
|
|
278
|
+
|
|
279
|
+
test('function cache stores compiled functions', async () => {
|
|
280
|
+
expect(getFunctionCacheSize()).toBe(0);
|
|
281
|
+
|
|
282
|
+
const ast = parse(`
|
|
283
|
+
let a = "1"
|
|
284
|
+
let b = "2"
|
|
285
|
+
let sum1 = ts(a, b) { return Number(a) + Number(b) }
|
|
286
|
+
let sum2 = ts(a, b) { return Number(a) + Number(b) }
|
|
287
|
+
`);
|
|
288
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
289
|
+
await runtime.run();
|
|
290
|
+
|
|
291
|
+
// Same function body should be cached and reused
|
|
292
|
+
expect(getFunctionCacheSize()).toBe(1);
|
|
293
|
+
expect(runtime.getValue('sum1')).toBe(3);
|
|
294
|
+
expect(runtime.getValue('sum2')).toBe(3);
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
test('different function bodies create different cache entries', async () => {
|
|
298
|
+
expect(getFunctionCacheSize()).toBe(0);
|
|
299
|
+
|
|
300
|
+
const ast = parse(`
|
|
301
|
+
let x = "5"
|
|
302
|
+
let doubled = ts(x) { return Number(x) * 2 }
|
|
303
|
+
let tripled = ts(x) { return Number(x) * 3 }
|
|
304
|
+
`);
|
|
305
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
306
|
+
await runtime.run();
|
|
307
|
+
|
|
308
|
+
expect(getFunctionCacheSize()).toBe(2);
|
|
309
|
+
expect(runtime.getValue('doubled')).toBe(10);
|
|
310
|
+
expect(runtime.getValue('tripled')).toBe(15);
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
// ============================================================================
|
|
314
|
+
// Error handling
|
|
315
|
+
// ============================================================================
|
|
316
|
+
|
|
317
|
+
test('ts block throws on undefined variable', async () => {
|
|
318
|
+
const ast = parse(`
|
|
319
|
+
let result = ts(undefinedVar) { return undefinedVar }
|
|
320
|
+
`);
|
|
321
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
322
|
+
|
|
323
|
+
await expect(runtime.run()).rejects.toThrow("'undefinedVar' is not defined");
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
test('ts block can throw and catch errors', async () => {
|
|
327
|
+
const ast = parse(`
|
|
328
|
+
let result = ts() {
|
|
329
|
+
try {
|
|
330
|
+
throw new Error("test error")
|
|
331
|
+
} catch (e) {
|
|
332
|
+
return "caught"
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
`);
|
|
336
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
337
|
+
await runtime.run();
|
|
338
|
+
|
|
339
|
+
expect(runtime.getValue('result')).toBe('caught');
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
// ============================================================================
|
|
343
|
+
// Integration with AI calls
|
|
344
|
+
// ============================================================================
|
|
345
|
+
|
|
346
|
+
test('ts block processes AI response', async () => {
|
|
347
|
+
const provider: AIProvider = {
|
|
348
|
+
async execute() {
|
|
349
|
+
return { value: '{"name": "alice", "score": 95}' };
|
|
350
|
+
},
|
|
351
|
+
async generateCode() {
|
|
352
|
+
return { value: '' };
|
|
353
|
+
},
|
|
354
|
+
async askUser(): Promise<string> {
|
|
355
|
+
return '';
|
|
356
|
+
},
|
|
357
|
+
};
|
|
358
|
+
|
|
359
|
+
const ast = parse(`
|
|
360
|
+
model gpt = { name: "gpt-4", apiKey: "key", url: "http://test" }
|
|
361
|
+
let response = vibe "get user" gpt default
|
|
362
|
+
let parsed:json = ts(response) { return JSON.parse(response) }
|
|
363
|
+
let score = ts(parsed) { return parsed.score }
|
|
364
|
+
`);
|
|
365
|
+
const runtime = new Runtime(ast, provider);
|
|
366
|
+
await runtime.run();
|
|
367
|
+
|
|
368
|
+
expect(runtime.getValue('parsed')).toEqual({ name: 'alice', score: 95 });
|
|
369
|
+
expect(runtime.getValue('score')).toBe(95);
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
// ============================================================================
|
|
373
|
+
// TsBlockError - Enhanced error handling
|
|
374
|
+
// ============================================================================
|
|
375
|
+
|
|
376
|
+
test('ts block runtime error includes code snippet', async () => {
|
|
377
|
+
const ast = parse(`
|
|
378
|
+
let result = ts() { throw new Error("intentional error") }
|
|
379
|
+
`);
|
|
380
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
381
|
+
|
|
382
|
+
try {
|
|
383
|
+
await runtime.run();
|
|
384
|
+
expect.unreachable('should have thrown');
|
|
385
|
+
} catch (error) {
|
|
386
|
+
expect(error).toBeInstanceOf(TsBlockError);
|
|
387
|
+
expect((error as TsBlockError).message).toContain('runtime error');
|
|
388
|
+
expect((error as TsBlockError).message).toContain('intentional error');
|
|
389
|
+
expect((error as TsBlockError).message).toContain('Code:');
|
|
390
|
+
}
|
|
391
|
+
});
|
|
392
|
+
|
|
393
|
+
test('ts block syntax error provides helpful message', async () => {
|
|
394
|
+
// Note: Vibe parser extracts raw TS body, so we need valid Vibe syntax
|
|
395
|
+
// but invalid TS syntax. Using return with no semicolon and trailing content
|
|
396
|
+
const ast = parse(`
|
|
397
|
+
let result = ts() { return const const const }
|
|
398
|
+
`);
|
|
399
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
400
|
+
|
|
401
|
+
try {
|
|
402
|
+
await runtime.run();
|
|
403
|
+
expect.unreachable('should have thrown');
|
|
404
|
+
} catch (error) {
|
|
405
|
+
expect(error).toBeInstanceOf(TsBlockError);
|
|
406
|
+
expect((error as TsBlockError).message).toContain('compilation error');
|
|
407
|
+
}
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
test('TsBlockError preserves original error', async () => {
|
|
411
|
+
const ast = parse(`
|
|
412
|
+
let result = ts() { throw new TypeError("type mismatch") }
|
|
413
|
+
`);
|
|
414
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
415
|
+
|
|
416
|
+
try {
|
|
417
|
+
await runtime.run();
|
|
418
|
+
expect.unreachable('should have thrown');
|
|
419
|
+
} catch (error) {
|
|
420
|
+
expect(error).toBeInstanceOf(TsBlockError);
|
|
421
|
+
const tsError = error as TsBlockError;
|
|
422
|
+
expect(tsError.originalError).toBeDefined();
|
|
423
|
+
expect(tsError.originalError.message).toBe('type mismatch');
|
|
424
|
+
}
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
test('TsBlockError includes params and body', async () => {
|
|
428
|
+
const ast = parse(`
|
|
429
|
+
let x = "5"
|
|
430
|
+
let result = ts(x) { return x.nonExistentMethod() }
|
|
431
|
+
`);
|
|
432
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
433
|
+
|
|
434
|
+
try {
|
|
435
|
+
await runtime.run();
|
|
436
|
+
expect.unreachable('should have thrown');
|
|
437
|
+
} catch (error) {
|
|
438
|
+
expect(error).toBeInstanceOf(TsBlockError);
|
|
439
|
+
const tsError = error as TsBlockError;
|
|
440
|
+
expect(tsError.params).toEqual(['x']);
|
|
441
|
+
expect(tsError.body).toContain('nonExistentMethod');
|
|
442
|
+
}
|
|
443
|
+
});
|
|
444
|
+
|
|
445
|
+
test('TsBlockError includes source location', async () => {
|
|
446
|
+
const ast = parse(`
|
|
447
|
+
let x = "5"
|
|
448
|
+
let result = ts(x) { throw new Error("test") }
|
|
449
|
+
`);
|
|
450
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
451
|
+
|
|
452
|
+
try {
|
|
453
|
+
await runtime.run();
|
|
454
|
+
expect.unreachable('should have thrown');
|
|
455
|
+
} catch (error) {
|
|
456
|
+
expect(error).toBeInstanceOf(TsBlockError);
|
|
457
|
+
const tsError = error as TsBlockError;
|
|
458
|
+
expect(tsError.location).toBeDefined();
|
|
459
|
+
expect(tsError.location?.line).toBeGreaterThan(0);
|
|
460
|
+
expect(tsError.location?.column).toBeGreaterThan(0);
|
|
461
|
+
}
|
|
462
|
+
});
|
|
463
|
+
|
|
464
|
+
test('TsBlockError.format() includes source location', async () => {
|
|
465
|
+
const ast = parse(`let result = ts() { throw new Error("test") }`, { file: 'test.vibe' });
|
|
466
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
467
|
+
|
|
468
|
+
try {
|
|
469
|
+
await runtime.run();
|
|
470
|
+
expect.unreachable('should have thrown');
|
|
471
|
+
} catch (error) {
|
|
472
|
+
expect(error).toBeInstanceOf(TsBlockError);
|
|
473
|
+
const tsError = error as TsBlockError;
|
|
474
|
+
const formatted = tsError.format();
|
|
475
|
+
expect(formatted).toContain('[test.vibe:');
|
|
476
|
+
expect(formatted).toContain('ts block runtime error');
|
|
477
|
+
}
|
|
478
|
+
});
|
|
479
|
+
|
|
480
|
+
// ============================================================================
|
|
481
|
+
// Const object mutation prevention
|
|
482
|
+
// ============================================================================
|
|
483
|
+
|
|
484
|
+
test('const object cannot be mutated in ts block', async () => {
|
|
485
|
+
const ast = parse(`
|
|
486
|
+
const data:json = {name: "alice", count: 5}
|
|
487
|
+
let result = ts(data) {
|
|
488
|
+
data.name = "modified"
|
|
489
|
+
return data.name
|
|
490
|
+
}
|
|
491
|
+
`);
|
|
492
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
493
|
+
|
|
494
|
+
// Bun uses "Attempted to assign to readonly property"
|
|
495
|
+
await expect(runtime.run()).rejects.toThrow(/readonly property/);
|
|
496
|
+
});
|
|
497
|
+
|
|
498
|
+
test('const array cannot be mutated in ts block', async () => {
|
|
499
|
+
const ast = parse(`
|
|
500
|
+
const items: number[] = [1, 2, 3]
|
|
501
|
+
let result = ts(items) {
|
|
502
|
+
items.push(4)
|
|
503
|
+
return items
|
|
504
|
+
}
|
|
505
|
+
`);
|
|
506
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
507
|
+
|
|
508
|
+
await expect(runtime.run()).rejects.toThrow(/readonly property/);
|
|
509
|
+
});
|
|
510
|
+
|
|
511
|
+
test('const nested object cannot be mutated in ts block', async () => {
|
|
512
|
+
const ast = parse(`
|
|
513
|
+
const data:json = {user: {name: "alice", profile: {age: 30}}}
|
|
514
|
+
let result = ts(data) {
|
|
515
|
+
data.user.profile.age = 99
|
|
516
|
+
return data.user.profile.age
|
|
517
|
+
}
|
|
518
|
+
`);
|
|
519
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
520
|
+
|
|
521
|
+
await expect(runtime.run()).rejects.toThrow(/readonly property/);
|
|
522
|
+
});
|
|
523
|
+
|
|
524
|
+
test('let object CAN be mutated in ts block', async () => {
|
|
525
|
+
const ast = parse(`
|
|
526
|
+
let data:json = {name: "alice", count: 5}
|
|
527
|
+
let result = ts(data) {
|
|
528
|
+
data.name = "modified"
|
|
529
|
+
return data.name
|
|
530
|
+
}
|
|
531
|
+
`);
|
|
532
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
533
|
+
await runtime.run();
|
|
534
|
+
|
|
535
|
+
expect(runtime.getValue('result')).toBe('modified');
|
|
536
|
+
});
|
|
537
|
+
|
|
538
|
+
test('const object can be read but not modified', async () => {
|
|
539
|
+
const ast = parse(`
|
|
540
|
+
const data:json = {items: [1, 2, 3], name: "test"}
|
|
541
|
+
let length = ts(data) { return data.items.length }
|
|
542
|
+
let name = ts(data) { return data.name }
|
|
543
|
+
`);
|
|
544
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
545
|
+
await runtime.run();
|
|
546
|
+
|
|
547
|
+
expect(runtime.getValue('length')).toBe(3);
|
|
548
|
+
expect(runtime.getValue('name')).toBe('test');
|
|
549
|
+
});
|
|
550
|
+
|
|
551
|
+
test('const array elements cannot be replaced in ts block', async () => {
|
|
552
|
+
const ast = parse(`
|
|
553
|
+
const items: number[] = [1, 2, 3]
|
|
554
|
+
let result = ts(items) {
|
|
555
|
+
items[0] = 999
|
|
556
|
+
return items[0]
|
|
557
|
+
}
|
|
558
|
+
`);
|
|
559
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
560
|
+
|
|
561
|
+
await expect(runtime.run()).rejects.toThrow(/readonly property/);
|
|
562
|
+
});
|
|
563
|
+
|
|
564
|
+
test('const object with array cannot have array items pushed', async () => {
|
|
565
|
+
const ast = parse(`
|
|
566
|
+
const data:json = {items: [1, 2, 3]}
|
|
567
|
+
let result = ts(data) {
|
|
568
|
+
data.items.push(4)
|
|
569
|
+
return data.items.length
|
|
570
|
+
}
|
|
571
|
+
`);
|
|
572
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
573
|
+
|
|
574
|
+
await expect(runtime.run()).rejects.toThrow(/readonly property/);
|
|
575
|
+
});
|
|
576
|
+
|
|
577
|
+
// ============================================================================
|
|
578
|
+
// Dynamic imports in ts blocks
|
|
579
|
+
// ============================================================================
|
|
580
|
+
|
|
581
|
+
test('ts block can use dynamic import for Node built-ins', async () => {
|
|
582
|
+
const ast = parse(`
|
|
583
|
+
let result = ts() {
|
|
584
|
+
const { join } = await import('path');
|
|
585
|
+
return join('foo', 'bar');
|
|
586
|
+
}
|
|
587
|
+
`);
|
|
588
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
589
|
+
await runtime.run();
|
|
590
|
+
|
|
591
|
+
// Path separator varies by OS (/ on Unix, \ on Windows)
|
|
592
|
+
expect(runtime.getValue('result')).toMatch(/foo[\/\\]bar/);
|
|
593
|
+
});
|
|
594
|
+
});
|