@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,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
+ });