@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,732 @@
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 - Type Validation', () => {
6
+ // Mock AI provider
7
+ const mockProvider: AIProvider = {
8
+ execute: async (prompt: string) => ({ value: prompt }),
9
+ generateCode: async () => ({ value: 'let result = "generated"' }),
10
+ askUser: async () => 'user response',
11
+ };
12
+
13
+ function createRuntime(code: string): Runtime {
14
+ const ast = parse(code);
15
+ return new Runtime(ast, mockProvider);
16
+ }
17
+
18
+ // ============================================================================
19
+ // Text type (passthrough)
20
+ // ============================================================================
21
+
22
+ test('text type keeps string as string', async () => {
23
+ const runtime = createRuntime('let x: text = "hello"');
24
+ await runtime.run();
25
+ expect(runtime.getValue('x')).toBe('hello');
26
+ });
27
+
28
+ test('no type keeps string as string', async () => {
29
+ const runtime = createRuntime('let x = "hello"');
30
+ await runtime.run();
31
+ expect(runtime.getValue('x')).toBe('hello');
32
+ });
33
+
34
+ // ============================================================================
35
+ // JSON type - parsing string literals
36
+ // ============================================================================
37
+
38
+ test('json type parses JSON object string', async () => {
39
+ const runtime = createRuntime('let x: json = "{\\"name\\": \\"test\\"}"');
40
+ await runtime.run();
41
+ expect(runtime.getValue('x')).toEqual({ name: 'test' });
42
+ });
43
+
44
+ test('json type rejects JSON array string (use json[] for arrays)', async () => {
45
+ const runtime = createRuntime('let x: json = "[1, 2, 3]"');
46
+ await expect(runtime.run()).rejects.toThrow('json type expects an object, not an array');
47
+ });
48
+
49
+ test('json type parses empty object', async () => {
50
+ const runtime = createRuntime('let x: json = "{}"');
51
+ await runtime.run();
52
+ expect(runtime.getValue('x')).toEqual({});
53
+ });
54
+
55
+ test('json type rejects empty array string (use json[] for arrays)', async () => {
56
+ const runtime = createRuntime('let x: json = "[]"');
57
+ await expect(runtime.run()).rejects.toThrow('json type expects an object, not an array');
58
+ });
59
+
60
+ test('json type parses nested objects', async () => {
61
+ const runtime = createRuntime('let x: json = "{\\"user\\": {\\"name\\": \\"test\\", \\"age\\": 30}}"');
62
+ await runtime.run();
63
+ expect(runtime.getValue('x')).toEqual({ user: { name: 'test', age: 30 } });
64
+ });
65
+
66
+ test('json const type parses correctly', async () => {
67
+ const runtime = createRuntime('const x: json = "{\\"key\\": \\"value\\"}"');
68
+ await runtime.run();
69
+ expect(runtime.getValue('x')).toEqual({ key: 'value' });
70
+ });
71
+
72
+ // ============================================================================
73
+ // JSON type - invalid values
74
+ // ============================================================================
75
+
76
+ test('json type throws on invalid JSON string', async () => {
77
+ const runtime = createRuntime('let x: json = "{invalid}"');
78
+ await expect(runtime.run()).rejects.toThrow("Variable 'x': invalid JSON string");
79
+ });
80
+
81
+ test('json type accepts null (uninitialized)', async () => {
82
+ const runtime = createRuntime(`
83
+ let x: json
84
+ `);
85
+ await runtime.run();
86
+ // Uninitialized json variable gets null, which is valid for any typed variable
87
+ expect(runtime.getValue('x')).toBe(null);
88
+ });
89
+
90
+ // ============================================================================
91
+ // JSON type - assignment validation
92
+ // ============================================================================
93
+
94
+ test('json variable assignment parses valid JSON', async () => {
95
+ const runtime = createRuntime(`
96
+ let x: json = "{}"
97
+ x = "{\\"updated\\": true}"
98
+ `);
99
+ await runtime.run();
100
+ expect(runtime.getValue('x')).toEqual({ updated: true });
101
+ });
102
+
103
+ test('json variable assignment throws on invalid JSON', async () => {
104
+ const runtime = createRuntime(`
105
+ let x: json = "{}"
106
+ x = "not json"
107
+ `);
108
+ await expect(runtime.run()).rejects.toThrow("Variable 'x': invalid JSON string");
109
+ });
110
+
111
+ // ============================================================================
112
+ // Type validation errors
113
+ // ============================================================================
114
+
115
+ test('json type rejects plain text string', async () => {
116
+ const runtime = createRuntime('let x: json = "hello world"');
117
+ await expect(runtime.run()).rejects.toThrow("Variable 'x': invalid JSON string");
118
+ });
119
+
120
+ test('json type rejects malformed JSON string', async () => {
121
+ const runtime = createRuntime('let x: json = "{name: value}"');
122
+ await expect(runtime.run()).rejects.toThrow("Variable 'x': invalid JSON string");
123
+ });
124
+
125
+ test('json type rejects JSON primitive string (number)', async () => {
126
+ // "42" is valid JSON but parses to a primitive, not object
127
+ const runtime = createRuntime('let x: json = "42"');
128
+ await expect(runtime.run()).rejects.toThrow(/expected json \(object\)/);
129
+ });
130
+
131
+ test('json type rejects JSON primitive string (boolean)', async () => {
132
+ const runtime = createRuntime('let x: json = "true"');
133
+ await expect(runtime.run()).rejects.toThrow(/expected json \(object\)/);
134
+ });
135
+
136
+ test('json type rejects JSON primitive string (null)', async () => {
137
+ const runtime = createRuntime('let x: json = "null"');
138
+ await expect(runtime.run()).rejects.toThrow(/expected json \(object\)/);
139
+ });
140
+
141
+ test('json type accepts object literal directly', async () => {
142
+ const runtime = createRuntime('let x: json = {key: "value"}');
143
+ await runtime.run();
144
+ expect(runtime.getValue('x')).toEqual({ key: 'value' });
145
+ });
146
+
147
+ test('json type rejects array literal (use json[] for arrays)', async () => {
148
+ const runtime = createRuntime('let x: json = ["a", "b"]');
149
+ await expect(runtime.run()).rejects.toThrow('json type expects an object, not an array');
150
+ });
151
+
152
+ test('json[] type accepts array literal', async () => {
153
+ const runtime = createRuntime('let x: json[] = [{a: 1}, {b: 2}]');
154
+ await runtime.run();
155
+ expect(runtime.getValue('x')).toEqual([{ a: 1 }, { b: 2 }]);
156
+ });
157
+
158
+ test('reassigning json variable with invalid string throws', async () => {
159
+ const runtime = createRuntime(`
160
+ let x: json = {initial: "value"}
161
+ x = "not valid json"
162
+ `);
163
+ await expect(runtime.run()).rejects.toThrow("Variable 'x': invalid JSON string");
164
+ });
165
+
166
+ test('reassigning json variable with valid JSON string works', async () => {
167
+ const runtime = createRuntime(`
168
+ let x: json = {initial: "value"}
169
+ x = "{\\"updated\\": true}"
170
+ `);
171
+ await runtime.run();
172
+ expect(runtime.getValue('x')).toEqual({ updated: true });
173
+ });
174
+
175
+ test('reassigning json variable with object literal works', async () => {
176
+ const runtime = createRuntime(`
177
+ let x: json = {initial: "value"}
178
+ x = {replaced: "new"}
179
+ `);
180
+ await runtime.run();
181
+ expect(runtime.getValue('x')).toEqual({ replaced: 'new' });
182
+ });
183
+
184
+ // ============================================================================
185
+ // JSON type with AI responses
186
+ // ============================================================================
187
+
188
+ test('json type parses AI response as JSON', async () => {
189
+ const jsonProvider: AIProvider = {
190
+ execute: async () => ({ value: '{"result": "from AI"}' }),
191
+ generateCode: async () => ({ value: '' }),
192
+ askUser: async () => '',
193
+ };
194
+
195
+ const code = `
196
+ model myModel = {
197
+ name: "test",
198
+ apiKey: "key",
199
+ url: "http://example.com"
200
+ }
201
+ let x: json = vibe "return json" myModel default
202
+ `;
203
+ const ast = parse(code);
204
+ const runtime = new Runtime(ast, jsonProvider);
205
+ await runtime.run();
206
+ expect(runtime.getValue('x')).toEqual({ result: 'from AI' });
207
+ });
208
+
209
+ test('json type throws if AI returns invalid JSON', async () => {
210
+ const invalidProvider: AIProvider = {
211
+ execute: async () => ({ value: 'not valid json' }),
212
+ generateCode: async () => ({ value: '' }),
213
+ askUser: async () => '',
214
+ };
215
+
216
+ const code = `
217
+ model myModel = {
218
+ name: "test",
219
+ apiKey: "key",
220
+ url: "http://example.com"
221
+ }
222
+ let x: json = vibe "return json" myModel default
223
+ `;
224
+ const ast = parse(code);
225
+ const runtime = new Runtime(ast, invalidProvider);
226
+ await expect(runtime.run()).rejects.toThrow("Variable 'x': invalid JSON string");
227
+ });
228
+
229
+ // ============================================================================
230
+ // Boolean type - variable declarations
231
+ // ============================================================================
232
+
233
+ test('boolean type accepts true literal', async () => {
234
+ const runtime = createRuntime('let x: boolean = true');
235
+ await runtime.run();
236
+ expect(runtime.getValue('x')).toBe(true);
237
+ });
238
+
239
+ test('boolean type accepts false literal', async () => {
240
+ const runtime = createRuntime('let x: boolean = false');
241
+ await runtime.run();
242
+ expect(runtime.getValue('x')).toBe(false);
243
+ });
244
+
245
+ test('boolean const type works', async () => {
246
+ const runtime = createRuntime('const DEBUG: boolean = true');
247
+ await runtime.run();
248
+ expect(runtime.getValue('DEBUG')).toBe(true);
249
+ });
250
+
251
+ test('boolean type throws on string value', async () => {
252
+ const runtime = createRuntime('let x: boolean = "true"');
253
+ await expect(runtime.run()).rejects.toThrow("Variable 'x': expected boolean, got string");
254
+ });
255
+
256
+ test('boolean type throws on empty string', async () => {
257
+ const runtime = createRuntime('let x: boolean = ""');
258
+ await expect(runtime.run()).rejects.toThrow("Variable 'x': expected boolean, got string");
259
+ });
260
+
261
+ // ============================================================================
262
+ // Boolean type - variable assignment
263
+ // ============================================================================
264
+
265
+ test('boolean variable assignment accepts boolean', async () => {
266
+ const runtime = createRuntime(`
267
+ let x: boolean = true
268
+ x = false
269
+ `);
270
+ await runtime.run();
271
+ expect(runtime.getValue('x')).toBe(false);
272
+ });
273
+
274
+ test('boolean variable assignment throws on string', async () => {
275
+ const runtime = createRuntime(`
276
+ let x: boolean = true
277
+ x = "false"
278
+ `);
279
+ await expect(runtime.run()).rejects.toThrow("Variable 'x': expected boolean, got string");
280
+ });
281
+
282
+ test('runtime error includes correct line number', async () => {
283
+ const runtime = createRuntime(`
284
+ let first = "ok"
285
+ let second = "also ok"
286
+ let bad: boolean = "not a boolean"
287
+ let fourth = "never reached"
288
+ `);
289
+ try {
290
+ await runtime.run();
291
+ expect.unreachable('Should have thrown');
292
+ } catch (e) {
293
+ expect(e).toBeInstanceOf(Error);
294
+ const err = e as Error & { location?: { line: number; column: number } };
295
+ expect(err.location).toBeDefined();
296
+ expect(err.location?.line).toBe(4); // line 4: let bad: boolean = "not a boolean"
297
+ }
298
+ });
299
+
300
+ // ============================================================================
301
+ // Boolean type - function returns
302
+ // ============================================================================
303
+
304
+ test('vibe function returning boolean works', async () => {
305
+ const runtime = createRuntime(`
306
+ function isValid(): boolean {
307
+ return true
308
+ }
309
+ let result = isValid()
310
+ `);
311
+ await runtime.run();
312
+ expect(runtime.getValue('result')).toBe(true);
313
+ });
314
+
315
+ test('vibe function returning boolean false works', async () => {
316
+ const runtime = createRuntime(`
317
+ function check(): boolean {
318
+ return false
319
+ }
320
+ let result = check()
321
+ `);
322
+ await runtime.run();
323
+ expect(runtime.getValue('result')).toBe(false);
324
+ });
325
+
326
+ test('vibe function with boolean return type throws on string return', async () => {
327
+ const runtime = createRuntime(`
328
+ function isValid(): boolean {
329
+ return "yes"
330
+ }
331
+ let result = isValid()
332
+ `);
333
+ await expect(runtime.run()).rejects.toThrow(/expected boolean, got string/);
334
+ });
335
+
336
+ test('vibe function with boolean return type throws on empty string', async () => {
337
+ const runtime = createRuntime(`
338
+ function check(): boolean {
339
+ return ""
340
+ }
341
+ let result = check()
342
+ `);
343
+ await expect(runtime.run()).rejects.toThrow(/expected boolean, got string/);
344
+ });
345
+
346
+ // ============================================================================
347
+ // Boolean type - function parameters
348
+ // ============================================================================
349
+
350
+ test('function with boolean parameter accepts boolean', async () => {
351
+ const runtime = createRuntime(`
352
+ function test(flag: boolean): boolean {
353
+ return flag
354
+ }
355
+ let result = test(true)
356
+ `);
357
+ await runtime.run();
358
+ expect(runtime.getValue('result')).toBe(true);
359
+ });
360
+
361
+ test('function with boolean parameter throws on string', async () => {
362
+ const runtime = createRuntime(`
363
+ function test(flag: boolean): boolean {
364
+ return flag
365
+ }
366
+ let result = test("true")
367
+ `);
368
+ await expect(runtime.run()).rejects.toThrow(/expected boolean, got string/);
369
+ });
370
+
371
+ // ============================================================================
372
+ // Boolean type - ts block returns
373
+ // ============================================================================
374
+
375
+ test('ts block returning boolean to boolean variable works', async () => {
376
+ const runtime = createRuntime(`
377
+ let x: boolean = ts() { return true }
378
+ `);
379
+ await runtime.run();
380
+ expect(runtime.getValue('x')).toBe(true);
381
+ });
382
+
383
+ test('ts block returning false to boolean variable works', async () => {
384
+ const runtime = createRuntime(`
385
+ let x: boolean = ts() { return false }
386
+ `);
387
+ await runtime.run();
388
+ expect(runtime.getValue('x')).toBe(false);
389
+ });
390
+
391
+ test('ts block with comparison returns boolean', async () => {
392
+ const runtime = createRuntime(`
393
+ let a = "5"
394
+ let isPositive: boolean = ts(a) { return Number(a) > 0 }
395
+ `);
396
+ await runtime.run();
397
+ expect(runtime.getValue('isPositive')).toBe(true);
398
+ });
399
+
400
+ test('ts block returning string to boolean variable throws', async () => {
401
+ const runtime = createRuntime(`
402
+ let x: boolean = ts() { return "true" }
403
+ `);
404
+ await expect(runtime.run()).rejects.toThrow("Variable 'x': expected boolean, got string");
405
+ });
406
+
407
+ test('ts block returning number to boolean variable throws', async () => {
408
+ const runtime = createRuntime(`
409
+ let x: boolean = ts() { return 1 }
410
+ `);
411
+ await expect(runtime.run()).rejects.toThrow("Variable 'x': expected boolean, got number");
412
+ });
413
+
414
+ // ============================================================================
415
+ // Boolean type - if statement strict checking
416
+ // ============================================================================
417
+
418
+ test('if statement accepts boolean true', async () => {
419
+ const runtime = createRuntime(`
420
+ let result = "no"
421
+ if true {
422
+ result = "yes"
423
+ }
424
+ `);
425
+ await runtime.run();
426
+ expect(runtime.getValue('result')).toBe('yes');
427
+ });
428
+
429
+ test('if statement accepts boolean false', async () => {
430
+ const runtime = createRuntime(`
431
+ let result = "no"
432
+ if false {
433
+ result = "yes"
434
+ }
435
+ `);
436
+ await runtime.run();
437
+ expect(runtime.getValue('result')).toBe('no');
438
+ });
439
+
440
+ test('if statement accepts boolean variable', async () => {
441
+ const runtime = createRuntime(`
442
+ let flag: boolean = true
443
+ let result = "no"
444
+ if flag {
445
+ result = "yes"
446
+ }
447
+ `);
448
+ await runtime.run();
449
+ expect(runtime.getValue('result')).toBe('yes');
450
+ });
451
+
452
+ test('if statement throws on string condition', async () => {
453
+ const runtime = createRuntime(`
454
+ let x = "hello"
455
+ if x {
456
+ let y = "inside"
457
+ }
458
+ `);
459
+ await expect(runtime.run()).rejects.toThrow('if condition must be a boolean, got string');
460
+ });
461
+
462
+ test('if statement throws on empty string condition', async () => {
463
+ const runtime = createRuntime(`
464
+ let x = ""
465
+ if x {
466
+ let y = "inside"
467
+ }
468
+ `);
469
+ await expect(runtime.run()).rejects.toThrow('if condition must be a boolean, got string');
470
+ });
471
+
472
+ test('if statement with function returning boolean works', async () => {
473
+ const runtime = createRuntime(`
474
+ function isActive(): boolean {
475
+ return true
476
+ }
477
+ let result = "no"
478
+ if isActive() {
479
+ result = "yes"
480
+ }
481
+ `);
482
+ await runtime.run();
483
+ expect(runtime.getValue('result')).toBe('yes');
484
+ });
485
+
486
+ test('if statement with ts block returning boolean works', async () => {
487
+ const runtime = createRuntime(`
488
+ let num = "5"
489
+ let result = "no"
490
+ let isPositive: boolean = ts(num) { return Number(num) > 0 }
491
+ if isPositive {
492
+ result = "yes"
493
+ }
494
+ `);
495
+ await runtime.run();
496
+ expect(runtime.getValue('result')).toBe('yes');
497
+ });
498
+
499
+ // ============================================================================
500
+ // Number type - variable declarations
501
+ // ============================================================================
502
+
503
+ test('number type with integer value', async () => {
504
+ const runtime = createRuntime('let x: number = 42');
505
+ await runtime.run();
506
+ expect(runtime.getValue('x')).toBe(42);
507
+ });
508
+
509
+ test('number type with decimal value', async () => {
510
+ const runtime = createRuntime('let x: number = 3.14');
511
+ await runtime.run();
512
+ expect(runtime.getValue('x')).toBe(3.14);
513
+ });
514
+
515
+ test('number type with negative value', async () => {
516
+ const runtime = createRuntime('let x: number = -5');
517
+ await runtime.run();
518
+ expect(runtime.getValue('x')).toBe(-5);
519
+ });
520
+
521
+ test('const number type', async () => {
522
+ const runtime = createRuntime('const MAX: number = 100');
523
+ await runtime.run();
524
+ expect(runtime.getValue('MAX')).toBe(100);
525
+ });
526
+
527
+ test('number type inferred from literal', async () => {
528
+ const runtime = createRuntime('let x = 42');
529
+ await runtime.run();
530
+ expect(runtime.getValue('x')).toBe(42);
531
+ });
532
+
533
+ test('number type throws on string literal', async () => {
534
+ const runtime = createRuntime('let x: number = "pizza"');
535
+ await expect(runtime.run()).rejects.toThrow("expected number, got string");
536
+ });
537
+
538
+ test('number type throws on boolean literal', async () => {
539
+ const runtime = createRuntime('let x: number = true');
540
+ await expect(runtime.run()).rejects.toThrow("expected number, got boolean");
541
+ });
542
+
543
+ test('const number throws on string literal', async () => {
544
+ const runtime = createRuntime('const MAX: number = "one hundred"');
545
+ await expect(runtime.run()).rejects.toThrow("expected number, got string");
546
+ });
547
+
548
+ // ============================================================================
549
+ // Number type - function return types
550
+ // ============================================================================
551
+
552
+ test('function with number return type works', async () => {
553
+ const runtime = createRuntime(`
554
+ function getCount(): number {
555
+ return 42
556
+ }
557
+ let result = getCount()
558
+ `);
559
+ await runtime.run();
560
+ expect(runtime.getValue('result')).toBe(42);
561
+ });
562
+
563
+ test('function with number return type throws on string return', async () => {
564
+ const runtime = createRuntime(`
565
+ function getCount(): number {
566
+ return "forty-two"
567
+ }
568
+ let result = getCount()
569
+ `);
570
+ await expect(runtime.run()).rejects.toThrow(/expected number, got string/);
571
+ });
572
+
573
+ test('function with number return type throws on boolean return', async () => {
574
+ const runtime = createRuntime(`
575
+ function getCount(): number {
576
+ return true
577
+ }
578
+ let result = getCount()
579
+ `);
580
+ await expect(runtime.run()).rejects.toThrow(/expected number, got boolean/);
581
+ });
582
+
583
+ // ============================================================================
584
+ // Number type - function parameters
585
+ // ============================================================================
586
+
587
+ test('function with number parameter accepts number', async () => {
588
+ const runtime = createRuntime(`
589
+ function double(n: number): number {
590
+ return 42
591
+ }
592
+ let result = double(21)
593
+ `);
594
+ await runtime.run();
595
+ expect(runtime.getValue('result')).toBe(42);
596
+ });
597
+
598
+ test('function with number parameter throws on string', async () => {
599
+ const runtime = createRuntime(`
600
+ function double(n: number): number {
601
+ return 42
602
+ }
603
+ let result = double("21")
604
+ `);
605
+ await expect(runtime.run()).rejects.toThrow(/expected number, got string/);
606
+ });
607
+
608
+ // ============================================================================
609
+ // Number type - ts block returns
610
+ // ============================================================================
611
+
612
+ test('ts block returning number to number variable works', async () => {
613
+ const runtime = createRuntime(`
614
+ let x: number = ts() { return 42 }
615
+ `);
616
+ await runtime.run();
617
+ expect(runtime.getValue('x')).toBe(42);
618
+ });
619
+
620
+ test('ts block returning string to number variable throws', async () => {
621
+ const runtime = createRuntime(`
622
+ let x: number = ts() { return "42" }
623
+ `);
624
+ await expect(runtime.run()).rejects.toThrow("Variable 'x': expected number, got string");
625
+ });
626
+
627
+ test('ts block returning Infinity to number variable throws', async () => {
628
+ const runtime = createRuntime(`
629
+ let x: number = ts() { return Infinity }
630
+ `);
631
+ await expect(runtime.run()).rejects.toThrow("must be finite");
632
+ });
633
+
634
+ test('ts block returning NaN to number variable throws', async () => {
635
+ const runtime = createRuntime(`
636
+ let x: number = ts() { return NaN }
637
+ `);
638
+ await expect(runtime.run()).rejects.toThrow("must be finite");
639
+ });
640
+
641
+ // ============================================================================
642
+ // Array types
643
+ // ============================================================================
644
+
645
+ test('text[] type with valid string array', async () => {
646
+ const runtime = createRuntime('let items: text[] = ["a", "b", "c"]');
647
+ await runtime.run();
648
+ expect(runtime.getValue('items')).toEqual(['a', 'b', 'c']);
649
+ });
650
+
651
+ test('text[] type with empty array', async () => {
652
+ const runtime = createRuntime('let items: text[] = []');
653
+ await runtime.run();
654
+ expect(runtime.getValue('items')).toEqual([]);
655
+ });
656
+
657
+ test('boolean[] type with valid boolean array', async () => {
658
+ const runtime = createRuntime('let flags: boolean[] = [true, false, true]');
659
+ await runtime.run();
660
+ expect(runtime.getValue('flags')).toEqual([true, false, true]);
661
+ });
662
+
663
+ test('number[] type with valid number array', async () => {
664
+ const runtime = createRuntime('let nums: number[] = [1, 2, 3]');
665
+ await runtime.run();
666
+ expect(runtime.getValue('nums')).toEqual([1, 2, 3]);
667
+ });
668
+
669
+ test('number[] type with mixed integers and decimals', async () => {
670
+ const runtime = createRuntime('let nums: number[] = [1, 2.5, -3]');
671
+ await runtime.run();
672
+ expect(runtime.getValue('nums')).toEqual([1, 2.5, -3]);
673
+ });
674
+
675
+ test('number[] type throws on string element', async () => {
676
+ const runtime = createRuntime('let nums: number[] = [1, 2, "three"]');
677
+ await expect(runtime.run()).rejects.toThrow("nums[2]");
678
+ await expect(runtime.run()).rejects.toThrow("expected number");
679
+ });
680
+
681
+ test('text[][] nested array type', async () => {
682
+ const runtime = createRuntime('let matrix: text[][] = [["a", "b"], ["c"]]');
683
+ await runtime.run();
684
+ expect(runtime.getValue('matrix')).toEqual([['a', 'b'], ['c']]);
685
+ });
686
+
687
+ test('json[] type with array of objects', async () => {
688
+ const runtime = createRuntime('let items: json[] = [{name: "a"}, {name: "b"}]');
689
+ await runtime.run();
690
+ expect(runtime.getValue('items')).toEqual([{ name: 'a' }, { name: 'b' }]);
691
+ });
692
+
693
+ test('text[] type errors on non-array string value', async () => {
694
+ const runtime = createRuntime('let items: text[] = "not an array"');
695
+ // String that isn't valid JSON array throws invalid JSON error
696
+ await expect(runtime.run()).rejects.toThrow("invalid JSON array string");
697
+ });
698
+
699
+ test('text[] type errors on element type mismatch', async () => {
700
+ const runtime = createRuntime('let items: text[] = [true, false]');
701
+ await expect(runtime.run()).rejects.toThrow("items[0]");
702
+ await expect(runtime.run()).rejects.toThrow("expected text");
703
+ });
704
+
705
+ test('boolean[] type errors on string elements', async () => {
706
+ const runtime = createRuntime('let flags: boolean[] = ["yes", "no"]');
707
+ await expect(runtime.run()).rejects.toThrow("flags[0]");
708
+ await expect(runtime.run()).rejects.toThrow("expected boolean");
709
+ });
710
+
711
+ test('function parameter with array type', async () => {
712
+ const runtime = createRuntime(`
713
+ function first(items: text[]): text {
714
+ return "got array"
715
+ }
716
+ let result = first(["a", "b"])
717
+ `);
718
+ await runtime.run();
719
+ expect(runtime.getValue('result')).toBe('got array');
720
+ });
721
+
722
+ test('function parameter array type validation', async () => {
723
+ const runtime = createRuntime(`
724
+ function process(items: text[]) {
725
+ return "done"
726
+ }
727
+ process("not an array")
728
+ `);
729
+ // String that isn't valid JSON array throws invalid JSON error
730
+ await expect(runtime.run()).rejects.toThrow("invalid JSON array string");
731
+ });
732
+ });