@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,334 @@
1
+ import { describe, expect, test } from 'bun:test';
2
+ import { readFileSync } from 'fs';
3
+ import { join } from 'path';
4
+ import { parse } from '../../parser/parse';
5
+ import { createInitialState } from '../state';
6
+ import { loadImports } from '../modules';
7
+ import { runUntilPause, step } from '../step';
8
+ import { resumeWithAIResponse, resumeWithImportedTsResult } from '../state';
9
+ import { Runtime } from '../index';
10
+ import { resolveValue } from '../types';
11
+
12
+ // Get the package root directory (relative to this test file)
13
+ // src/runtime/test -> runtime -> src -> runtime (package root)
14
+ const packageRoot = join(import.meta.dir, '..', '..', '..');
15
+
16
+ // Helper to load and run a vibe script with imports
17
+ async function loadAndRun(
18
+ relativePath: string,
19
+ aiResponses: Record<string, string> = {}
20
+ ): Promise<{ state: Awaited<ReturnType<typeof loadImports>>; result: unknown }> {
21
+ const scriptPath = join(packageRoot, 'tests', 'fixtures', 'imports', relativePath);
22
+ const source = readFileSync(scriptPath, 'utf-8');
23
+ const ast = parse(source);
24
+ let state = createInitialState(ast);
25
+
26
+ // Load imports
27
+ state = await loadImports(state, scriptPath);
28
+
29
+ // Run until pause
30
+ state = runUntilPause(state);
31
+
32
+ // Handle any async operations
33
+ while (state.status === 'awaiting_ai' || state.status === 'awaiting_ts') {
34
+ if (state.status === 'awaiting_ai') {
35
+ const response = aiResponses[state.pendingAI?.prompt ?? ''] ?? 'mock response';
36
+ state = resumeWithAIResponse(state, response);
37
+ } else if (state.status === 'awaiting_ts') {
38
+ if (state.pendingImportedTsCall) {
39
+ // Get the function from the loaded modules
40
+ const { funcName, args } = state.pendingImportedTsCall;
41
+ const importInfo = state.importedNames[funcName];
42
+ if (importInfo && importInfo.sourceType === 'ts') {
43
+ const module = state.tsModules[importInfo.source];
44
+ const fn = module?.exports[funcName] as (...args: unknown[]) => unknown;
45
+ const result = await fn(...args);
46
+ state = resumeWithImportedTsResult(state, result);
47
+ }
48
+ }
49
+ }
50
+ state = runUntilPause(state);
51
+ }
52
+
53
+ return { state, result: resolveValue(state.lastResult) };
54
+ }
55
+
56
+ describe('Runtime - TypeScript Imports', () => {
57
+ test('can import and call TypeScript functions', async () => {
58
+ const { state, result } = await loadAndRun('ts-import/main.vibe');
59
+
60
+ expect(state.status).toBe('completed');
61
+ // The last statement is product = multiply(4, 7) = 28
62
+ expect(result).toBe(28);
63
+ });
64
+
65
+ test('imported TS functions are registered in state', async () => {
66
+ const scriptPath = join(packageRoot, 'tests', 'fixtures', 'imports', 'ts-import', 'main.vibe');
67
+ const source = readFileSync(scriptPath, 'utf-8');
68
+ const ast = parse(source);
69
+ let state = createInitialState(ast);
70
+
71
+ state = await loadImports(state, scriptPath);
72
+
73
+ expect(state.importedNames['add']).toBeDefined();
74
+ expect(state.importedNames['add'].sourceType).toBe('ts');
75
+ expect(state.importedNames['multiply']).toBeDefined();
76
+ });
77
+ });
78
+
79
+ describe('Runtime - Vibe Imports', () => {
80
+ test('can import and call Vibe functions', async () => {
81
+ // {name} is left as reference in vibe expression
82
+ const { state, result } = await loadAndRun('vibe-import/main.vibe', {
83
+ 'Say hello to {name}': 'Hello, Alice!',
84
+ });
85
+
86
+ expect(state.status).toBe('completed');
87
+ expect(result).toBe('Hello, Alice!');
88
+ });
89
+
90
+ test('imported Vibe functions are registered in state', async () => {
91
+ const scriptPath = join(packageRoot, 'tests', 'fixtures', 'imports', 'vibe-import', 'main.vibe');
92
+ const source = readFileSync(scriptPath, 'utf-8');
93
+ const ast = parse(source);
94
+ let state = createInitialState(ast);
95
+
96
+ state = await loadImports(state, scriptPath);
97
+
98
+ expect(state.importedNames['greet']).toBeDefined();
99
+ expect(state.importedNames['greet'].sourceType).toBe('vibe');
100
+ });
101
+ });
102
+
103
+ describe('Runtime - Nested Imports', () => {
104
+ test('can handle nested imports (vibe importing ts)', async () => {
105
+ const { state, result } = await loadAndRun('nested-import/main.vibe');
106
+
107
+ expect(state.status).toBe('completed');
108
+ expect(result).toBe('John Doe');
109
+ });
110
+
111
+ test('nested imports load all dependencies', async () => {
112
+ const scriptPath = join(packageRoot, 'tests', 'fixtures', 'imports', 'nested-import', 'main.vibe');
113
+ const source = readFileSync(scriptPath, 'utf-8');
114
+ const ast = parse(source);
115
+ let state = createInitialState(ast);
116
+
117
+ state = await loadImports(state, scriptPath);
118
+
119
+ // formatGreeting should be imported
120
+ expect(state.importedNames['formatGreeting']).toBeDefined();
121
+ expect(state.importedNames['formatGreeting'].sourceType).toBe('vibe');
122
+
123
+ // The helper.vibe's TS import (formatName) should be loaded in tsModules
124
+ const helperPath = join(packageRoot, 'tests', 'fixtures', 'imports', 'nested-import', 'helper.vibe');
125
+ const utilsPath = join(packageRoot, 'tests', 'fixtures', 'imports', 'nested-import', 'utils.ts');
126
+ expect(state.tsModules[utilsPath]).toBeDefined();
127
+ });
128
+ });
129
+
130
+ describe('Runtime - Import Error Detection', () => {
131
+ test('detects circular dependency when modules import each other', async () => {
132
+ // a.vibe imports from b.vibe and b.vibe imports from a.vibe
133
+ // This creates a circular dependency
134
+ const scriptPath = join(packageRoot, 'tests', 'fixtures', 'imports', 'cycle-detection', 'main.vibe');
135
+ const source = readFileSync(scriptPath, 'utf-8');
136
+ const ast = parse(source);
137
+ let state = createInitialState(ast);
138
+
139
+ await expect(loadImports(state, scriptPath)).rejects.toThrow(/Circular dependency/);
140
+ });
141
+
142
+ test('detects circular dependency in import chain', async () => {
143
+ // main.vibe -> b.vibe -> a.vibe -> b.vibe (cycle!)
144
+ const scriptPath = join(packageRoot, 'tests', 'fixtures', 'imports', 'pure-cycle', 'main.vibe');
145
+ const source = readFileSync(scriptPath, 'utf-8');
146
+ const ast = parse(source);
147
+ let state = createInitialState(ast);
148
+
149
+ await expect(loadImports(state, scriptPath)).rejects.toThrow(/Circular dependency/);
150
+ });
151
+ });
152
+
153
+ describe('Runtime - Runtime class with imports', () => {
154
+ test('Runtime.run() loads imports automatically', async () => {
155
+ const scriptPath = join(packageRoot, 'tests', 'fixtures', 'imports', 'ts-import', 'main.vibe');
156
+ const source = readFileSync(scriptPath, 'utf-8');
157
+ const ast = parse(source);
158
+
159
+ const runtime = new Runtime(
160
+ ast,
161
+ {
162
+ execute: async (prompt: string) => ({ value: 'mock' }),
163
+ generateCode: async (prompt: string) => ({ value: 'mock' }),
164
+ askUser: async (prompt: string) => 'mock',
165
+ },
166
+ { basePath: scriptPath }
167
+ );
168
+
169
+ const result = await runtime.run();
170
+ expect(result).toBe(28);
171
+ });
172
+ });
173
+
174
+ describe('Runtime - TypeScript Boolean Imports', () => {
175
+ test('can import TS boolean constant and use in if condition', async () => {
176
+ const { state, result } = await loadAndRun('ts-boolean/main.vibe');
177
+
178
+ expect(state.status).toBe('completed');
179
+ expect(result).toBe('enabled');
180
+
181
+ const enabled = state.callStack[0].locals['enabled'];
182
+ expect(enabled.value).toBe(true);
183
+ expect(enabled.typeAnnotation).toBe('boolean');
184
+ });
185
+
186
+ test('imported TS boolean constant is registered', async () => {
187
+ const scriptPath = join(packageRoot, 'tests', 'fixtures', 'imports', 'ts-boolean', 'main.vibe');
188
+ const source = readFileSync(scriptPath, 'utf-8');
189
+ const ast = parse(source);
190
+ let state = createInitialState(ast);
191
+
192
+ state = await loadImports(state, scriptPath);
193
+
194
+ expect(state.importedNames['FEATURE_ENABLED']).toBeDefined();
195
+ expect(state.importedNames['FEATURE_ENABLED'].sourceType).toBe('ts');
196
+ });
197
+
198
+ test('can import TS function returning boolean and use in if condition', async () => {
199
+ const { state, result } = await loadAndRun('ts-boolean/use-constant.vibe');
200
+
201
+ expect(state.status).toBe('completed');
202
+ expect(result).toBe('not empty');
203
+
204
+ // Verify boolean variables were assigned correctly
205
+ const check1 = state.callStack[0].locals['check1'];
206
+ expect(check1.value).toBe(true);
207
+ expect(check1.typeAnnotation).toBe('boolean');
208
+
209
+ const check2 = state.callStack[0].locals['check2'];
210
+ expect(check2.value).toBe(true);
211
+ expect(check2.typeAnnotation).toBe('boolean');
212
+
213
+ // Verify if conditions worked
214
+ expect(state.callStack[0].locals['result1'].value).toBe('passed');
215
+ expect(state.callStack[0].locals['result2'].value).toBe('not empty');
216
+ });
217
+ });
218
+
219
+ describe('Runtime - Module Scope Isolation', () => {
220
+ test('imported function sees its own module globals, not caller globals', async () => {
221
+ // main.vibe has const x = "MAIN"
222
+ // moduleA.vibe has const x = "A" and exports getX() which returns x
223
+ // When main calls getX(), it should return "A", not "MAIN"
224
+ const { state, result } = await loadAndRun('module-isolation/main.vibe');
225
+
226
+ expect(state.status).toBe('completed');
227
+ expect(result).toBe('A'); // From moduleA's global, not main's
228
+ });
229
+
230
+ test('different modules with same variable name are isolated', async () => {
231
+ // main-b.vibe has const x = "MAIN"
232
+ // moduleB.vibe has const x = "B" and exports getX() which returns x
233
+ // When main calls getX(), it should return "B"
234
+ const { state, result } = await loadAndRun('module-isolation/main-b.vibe');
235
+
236
+ expect(state.status).toBe('completed');
237
+ expect(result).toBe('B'); // From moduleB's global
238
+ });
239
+
240
+ test('nested imports maintain correct scope isolation', async () => {
241
+ // main.vibe imports from file2 and file3
242
+ // file2.vibe imports from file3
243
+ // Each has its own x variable
244
+ //
245
+ // Expected results:
246
+ // - getB() returns "B" (file2's x)
247
+ // - getC() returns "C" (file3's x)
248
+ // - getCTwice() returns "CC" (file3's x + x)
249
+ // - getBAndC() returns "BC" (file2's x + getC() which returns file3's x)
250
+ // - Final result: "B" + "C" + "CC" + "BC" = "BCCCBC"
251
+ const { state, result } = await loadAndRun('nested-isolation/main.vibe');
252
+
253
+ expect(state.status).toBe('completed');
254
+
255
+ // Verify individual results
256
+ expect(state.callStack[0].locals['resultB'].value).toBe('B');
257
+ expect(state.callStack[0].locals['resultC'].value).toBe('C');
258
+ expect(state.callStack[0].locals['resultCC'].value).toBe('CC');
259
+ expect(state.callStack[0].locals['resultBC'].value).toBe('BC');
260
+
261
+ // Verify final combined result from locals
262
+ expect(state.callStack[0].locals['result'].value).toBe('BCCCBC');
263
+ });
264
+
265
+ test('module globals are stored in vibeModules', async () => {
266
+ const scriptPath = join(packageRoot, 'tests', 'fixtures', 'imports', 'module-isolation', 'main.vibe');
267
+ const source = readFileSync(scriptPath, 'utf-8');
268
+ const ast = parse(source);
269
+ let state = createInitialState(ast);
270
+
271
+ state = await loadImports(state, scriptPath);
272
+
273
+ // Check that moduleA has its globals
274
+ const moduleAPath = join(packageRoot, 'tests', 'fixtures', 'imports', 'module-isolation', 'moduleA.vibe');
275
+ const moduleA = state.vibeModules[moduleAPath];
276
+ expect(moduleA).toBeDefined();
277
+ expect(moduleA.globals).toBeDefined();
278
+ expect(moduleA.globals['x']).toBeDefined();
279
+ expect(moduleA.globals['x'].value).toBe('A');
280
+ expect(moduleA.globals['x'].isConst).toBe(true);
281
+ });
282
+ });
283
+
284
+ describe('Runtime - TypeScript Variable Imports', () => {
285
+ test('can import TS variable and assign to text type', async () => {
286
+ const { state, result } = await loadAndRun('ts-variables/import-variable.vibe');
287
+
288
+ expect(state.status).toBe('completed');
289
+ expect(result).toBe('Hello from TypeScript');
290
+
291
+ // Verify the variable was assigned with correct type
292
+ const greeting = state.callStack[0].locals['greeting'];
293
+ expect(greeting.value).toBe('Hello from TypeScript');
294
+ expect(greeting.typeAnnotation).toBe('text');
295
+ });
296
+
297
+ test('can import TS object and assign to json type', async () => {
298
+ const { state, result } = await loadAndRun('ts-variables/import-json.vibe');
299
+
300
+ expect(state.status).toBe('completed');
301
+ expect(result).toEqual({ name: 'test', version: '1.0' });
302
+
303
+ // Verify the variable was assigned with correct type
304
+ const config = state.callStack[0].locals['config'];
305
+ expect(config.value).toEqual({ name: 'test', version: '1.0' });
306
+ expect(config.typeAnnotation).toBe('json');
307
+ });
308
+
309
+ test('throws error when assigning object to text type', async () => {
310
+ const scriptPath = join(packageRoot, 'tests', 'fixtures', 'imports', 'ts-variables', 'import-type-mismatch.vibe');
311
+ const source = readFileSync(scriptPath, 'utf-8');
312
+ const ast = parse(source);
313
+ let state = createInitialState(ast);
314
+
315
+ state = await loadImports(state, scriptPath);
316
+ state = runUntilPause(state);
317
+
318
+ expect(state.status).toBe('error');
319
+ expect(state.error).toMatch(/expected text \(string\)/);
320
+ });
321
+
322
+ test('throws error when calling non-function import', async () => {
323
+ const scriptPath = join(packageRoot, 'tests', 'fixtures', 'imports', 'ts-variables', 'call-non-function.vibe');
324
+ const source = readFileSync(scriptPath, 'utf-8');
325
+ const ast = parse(source);
326
+ let state = createInitialState(ast);
327
+
328
+ state = await loadImports(state, scriptPath);
329
+ state = runUntilPause(state);
330
+
331
+ expect(state.status).toBe('error');
332
+ expect(state.error).toBe('TypeError: Cannot call non-function');
333
+ });
334
+ });
@@ -0,0 +1,232 @@
1
+ import { describe, expect, test } from 'bun:test';
2
+ import { parse } from '../../parser/parse';
3
+ import { Runtime, type AIProvider } from '../index';
4
+
5
+ describe('Runtime - JSON Objects with Expressions', () => {
6
+ const mockProvider: AIProvider = {
7
+ execute: async (prompt: string) => ({ value: prompt }),
8
+ generateCode: async () => ({ value: '' }),
9
+ askUser: async () => '',
10
+ };
11
+
12
+ function createRuntime(code: string): Runtime {
13
+ const ast = parse(code);
14
+ return new Runtime(ast, mockProvider);
15
+ }
16
+
17
+ // ============================================================================
18
+ // JSON with ts() expressions
19
+ // ============================================================================
20
+
21
+ test('json object with ts() expression value', async () => {
22
+ const runtime = createRuntime(`
23
+ let obj:json = {
24
+ key: ts() { return "computed-value"; }
25
+ }
26
+ `);
27
+ await runtime.run();
28
+ expect(runtime.getValue('obj')).toEqual({ key: 'computed-value' });
29
+ });
30
+
31
+ test('json object with multiple ts() expression values', async () => {
32
+ const runtime = createRuntime(`
33
+ let obj:json = {
34
+ a: ts() { return "value-a"; },
35
+ b: ts() { return "value-b"; },
36
+ c: "literal"
37
+ }
38
+ `);
39
+ await runtime.run();
40
+ expect(runtime.getValue('obj')).toEqual({
41
+ a: 'value-a',
42
+ b: 'value-b',
43
+ c: 'literal',
44
+ });
45
+ });
46
+
47
+ test('nested json with ts() expression', async () => {
48
+ const runtime = createRuntime(`
49
+ let obj:json = {
50
+ outer: {
51
+ inner: ts() { return "nested-computed"; }
52
+ }
53
+ }
54
+ `);
55
+ await runtime.run();
56
+ expect(runtime.getValue('obj')).toEqual({
57
+ outer: { inner: 'nested-computed' },
58
+ });
59
+ });
60
+
61
+ // ============================================================================
62
+ // JSON with env() function calls
63
+ // ============================================================================
64
+
65
+ test('json object with env() function call', async () => {
66
+ process.env.TEST_JSON_VAR = 'env-value';
67
+
68
+ // env() is auto-imported, no import needed
69
+ const runtime = createRuntime(`
70
+ let obj:json = {
71
+ fromEnv: env("TEST_JSON_VAR")
72
+ }
73
+ `);
74
+ await runtime.run();
75
+ expect(runtime.getValue('obj')).toEqual({ fromEnv: 'env-value' });
76
+
77
+ delete process.env.TEST_JSON_VAR;
78
+ });
79
+
80
+ test('json object with env() and default value', async () => {
81
+ // env() is auto-imported, no import needed
82
+ const runtime = createRuntime(`
83
+ let obj:json = {
84
+ value: env("NONEXISTENT_VAR", "default-value")
85
+ }
86
+ `);
87
+ await runtime.run();
88
+ expect(runtime.getValue('obj')).toEqual({ value: 'default-value' });
89
+ });
90
+
91
+ // ============================================================================
92
+ // JSON with vibe function calls
93
+ // ============================================================================
94
+
95
+ test('json object with vibe function call', async () => {
96
+ const runtime = createRuntime(`
97
+ function getValue(): text {
98
+ return "from-function"
99
+ }
100
+
101
+ let obj:json = {
102
+ key: getValue()
103
+ }
104
+ `);
105
+ await runtime.run();
106
+ expect(runtime.getValue('obj')).toEqual({ key: 'from-function' });
107
+ });
108
+
109
+ test('json object with parameterized function call', async () => {
110
+ const runtime = createRuntime(`
111
+ function format(prefix: text, value: text): text {
112
+ return ts(prefix, value) { return prefix + "-" + value; }
113
+ }
114
+
115
+ let obj:json = {
116
+ formatted: format("pre", "val")
117
+ }
118
+ `);
119
+ await runtime.run();
120
+ expect(runtime.getValue('obj')).toEqual({ formatted: 'pre-val' });
121
+ });
122
+
123
+ // ============================================================================
124
+ // JSON arrays with expressions
125
+ // ============================================================================
126
+
127
+ test('text[] array with ts() expression elements', async () => {
128
+ const runtime = createRuntime(`
129
+ let arr: text[] = [
130
+ ts() { return "first"; },
131
+ "literal",
132
+ ts() { return "third"; }
133
+ ]
134
+ `);
135
+ await runtime.run();
136
+ expect(runtime.getValue('arr')).toEqual(['first', 'literal', 'third']);
137
+ });
138
+
139
+ test('text[] array with function call elements', async () => {
140
+ const runtime = createRuntime(`
141
+ function item(n: number): text {
142
+ return ts(n) { return "item-" + n; }
143
+ }
144
+
145
+ let arr: text[] = [item(1), item(2), item(3)]
146
+ `);
147
+ await runtime.run();
148
+ expect(runtime.getValue('arr')).toEqual(['item-1', 'item-2', 'item-3']);
149
+ });
150
+
151
+ // ============================================================================
152
+ // Mixed scenarios
153
+ // ============================================================================
154
+
155
+ test('json with variable references and expressions', async () => {
156
+ const runtime = createRuntime(`
157
+ let prefix = "hello"
158
+
159
+ let obj:json = {
160
+ static: prefix,
161
+ computed: ts(prefix) { return prefix.toUpperCase(); }
162
+ }
163
+ `);
164
+ await runtime.run();
165
+ expect(runtime.getValue('obj')).toEqual({
166
+ static: 'hello',
167
+ computed: 'HELLO',
168
+ });
169
+ });
170
+
171
+ test('complex nested json with mixed expressions', async () => {
172
+ process.env.TEST_NESTED_VAR = 'from-env';
173
+
174
+ // env() is auto-imported, no import needed
175
+ const runtime = createRuntime(`
176
+ function getVersion(): text {
177
+ return "1.0.0"
178
+ }
179
+
180
+ let config:json = {
181
+ meta: {
182
+ version: getVersion(),
183
+ env: env("TEST_NESTED_VAR")
184
+ },
185
+ computed: ts() { return 42; },
186
+ literal: "plain"
187
+ }
188
+ `);
189
+ await runtime.run();
190
+ expect(runtime.getValue('config')).toEqual({
191
+ meta: {
192
+ version: '1.0.0',
193
+ env: 'from-env',
194
+ },
195
+ computed: 42,
196
+ literal: 'plain',
197
+ });
198
+
199
+ delete process.env.TEST_NESTED_VAR;
200
+ });
201
+
202
+ test('deeply nested object with env() at leaf level', async () => {
203
+ process.env.DEEP_SECRET = 'secret-value';
204
+
205
+ // env() is auto-imported, no import needed
206
+ const runtime = createRuntime(`
207
+ let config:json = {
208
+ level1: {
209
+ level2: {
210
+ level3: {
211
+ secret: env("DEEP_SECRET"),
212
+ computed: ts() { return "deep-computed"; }
213
+ }
214
+ }
215
+ }
216
+ }
217
+ `);
218
+ await runtime.run();
219
+ expect(runtime.getValue('config')).toEqual({
220
+ level1: {
221
+ level2: {
222
+ level3: {
223
+ secret: 'secret-value',
224
+ computed: 'deep-computed',
225
+ },
226
+ },
227
+ },
228
+ });
229
+
230
+ delete process.env.DEEP_SECRET;
231
+ });
232
+ });