@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,45 @@
1
+ import { tokenize } from '../lexer';
2
+ import { vibeParser } from './index';
3
+ import { vibeAstVisitor } from './visitor';
4
+ import { setCurrentFile } from './visitor/helpers';
5
+ import { ParserError } from '../errors';
6
+ import type { Program } from '../ast';
7
+
8
+ export interface ParseOptions {
9
+ /** File path to include in source locations (for error reporting) */
10
+ file?: string;
11
+ }
12
+
13
+ /**
14
+ * Parse a Vibe source code string into an AST
15
+ */
16
+ export function parse(source: string, options?: ParseOptions): Program {
17
+ // Set current file for location tracking
18
+ setCurrentFile(options?.file);
19
+
20
+ // Tokenize
21
+ const tokens = tokenize(source);
22
+
23
+ // Parse to CST
24
+ vibeParser.input = tokens;
25
+ const cst = vibeParser.program();
26
+
27
+ // Check for parse errors
28
+ if (vibeParser.errors.length > 0) {
29
+ const error = vibeParser.errors[0];
30
+ throw new ParserError(
31
+ error.message,
32
+ error.token.image,
33
+ { line: error.token.startLine ?? 1, column: error.token.startColumn ?? 1, file: options?.file },
34
+ source
35
+ );
36
+ }
37
+
38
+ // Transform CST to AST
39
+ const ast = vibeAstVisitor.visit(cst);
40
+
41
+ // Clear current file after parsing
42
+ setCurrentFile(undefined);
43
+
44
+ return ast;
45
+ }
@@ -0,0 +1,248 @@
1
+ import { describe, it, expect } from 'bun:test';
2
+ import { parse } from '../parse';
3
+ import type * as AST from '../../ast';
4
+
5
+ describe('async keyword parsing', () => {
6
+ describe('async let declarations', () => {
7
+ it('parses async let with do expression', () => {
8
+ const code = 'async let x = do "prompt" myModel';
9
+ const ast = parse(code);
10
+
11
+ expect(ast.body.length).toBe(1);
12
+ const decl = ast.body[0] as AST.LetDeclaration;
13
+ expect(decl.type).toBe('LetDeclaration');
14
+ expect(decl.name).toBe('x');
15
+ expect(decl.isAsync).toBe(true);
16
+ expect(decl.initializer?.type).toBe('VibeExpression');
17
+ });
18
+
19
+ it('parses async let with vibe expression', () => {
20
+ const code = 'async let result = vibe "analyze this" myModel';
21
+ const ast = parse(code);
22
+
23
+ const decl = ast.body[0] as AST.LetDeclaration;
24
+ expect(decl.type).toBe('LetDeclaration');
25
+ expect(decl.name).toBe('result');
26
+ expect(decl.isAsync).toBe(true);
27
+ const vibeExpr = decl.initializer as AST.VibeExpression;
28
+ expect(vibeExpr.operationType).toBe('vibe');
29
+ });
30
+
31
+ it('parses async let with type annotation', () => {
32
+ const code = 'async let x: text = do "prompt" myModel';
33
+ const ast = parse(code);
34
+
35
+ const decl = ast.body[0] as AST.LetDeclaration;
36
+ expect(decl.type).toBe('LetDeclaration');
37
+ expect(decl.name).toBe('x');
38
+ expect(decl.typeAnnotation).toBe('text');
39
+ expect(decl.isAsync).toBe(true);
40
+ });
41
+
42
+ it('parses async let with private modifier', () => {
43
+ const code = 'async let private secret = do "get secret" myModel';
44
+ const ast = parse(code);
45
+
46
+ const decl = ast.body[0] as AST.LetDeclaration;
47
+ expect(decl.type).toBe('LetDeclaration');
48
+ expect(decl.name).toBe('secret');
49
+ expect(decl.isAsync).toBe(true);
50
+ expect(decl.isPrivate).toBe(true);
51
+ });
52
+
53
+ it('parses async let with ts block', () => {
54
+ const code = 'async let data = ts() { return fetchData(); }';
55
+ const ast = parse(code);
56
+
57
+ const decl = ast.body[0] as AST.LetDeclaration;
58
+ expect(decl.type).toBe('LetDeclaration');
59
+ expect(decl.name).toBe('data');
60
+ expect(decl.isAsync).toBe(true);
61
+ expect(decl.initializer?.type).toBe('TsBlock');
62
+ });
63
+
64
+ it('parses async let with function call', () => {
65
+ const code = 'async let result = fetchFromAPI()';
66
+ const ast = parse(code);
67
+
68
+ const decl = ast.body[0] as AST.LetDeclaration;
69
+ expect(decl.type).toBe('LetDeclaration');
70
+ expect(decl.name).toBe('result');
71
+ expect(decl.isAsync).toBe(true);
72
+ expect(decl.initializer?.type).toBe('CallExpression');
73
+ });
74
+ });
75
+
76
+ describe('async const declarations', () => {
77
+ it('parses async const with do expression', () => {
78
+ const code = 'async const x = do "prompt" myModel';
79
+ const ast = parse(code);
80
+
81
+ const decl = ast.body[0] as AST.ConstDeclaration;
82
+ expect(decl.type).toBe('ConstDeclaration');
83
+ expect(decl.name).toBe('x');
84
+ expect(decl.isAsync).toBe(true);
85
+ });
86
+
87
+ it('parses async const with type annotation', () => {
88
+ const code = 'async const x: number = do "give number" myModel';
89
+ const ast = parse(code);
90
+
91
+ const decl = ast.body[0] as AST.ConstDeclaration;
92
+ expect(decl.type).toBe('ConstDeclaration');
93
+ expect(decl.name).toBe('x');
94
+ expect(decl.typeAnnotation).toBe('number');
95
+ expect(decl.isAsync).toBe(true);
96
+ });
97
+
98
+ it('parses async const with private modifier', () => {
99
+ const code = 'async const private API_KEY = fetchKey()';
100
+ const ast = parse(code);
101
+
102
+ const decl = ast.body[0] as AST.ConstDeclaration;
103
+ expect(decl.type).toBe('ConstDeclaration');
104
+ expect(decl.name).toBe('API_KEY');
105
+ expect(decl.isAsync).toBe(true);
106
+ expect(decl.isPrivate).toBe(true);
107
+ });
108
+ });
109
+
110
+ describe('async destructuring declarations', () => {
111
+ it('parses async let destructuring', () => {
112
+ const code = 'async let {name: text, age: number} = do "get person" myModel';
113
+ const ast = parse(code);
114
+
115
+ const decl = ast.body[0] as AST.DestructuringDeclaration;
116
+ expect(decl.type).toBe('DestructuringDeclaration');
117
+ expect(decl.isConst).toBe(false);
118
+ expect(decl.isAsync).toBe(true);
119
+ expect(decl.fields.length).toBe(2);
120
+ expect(decl.fields[0].name).toBe('name');
121
+ expect(decl.fields[1].name).toBe('age');
122
+ });
123
+
124
+ it('parses async const destructuring', () => {
125
+ const code = 'async const {x: number, y: number} = do "get coords" myModel';
126
+ const ast = parse(code);
127
+
128
+ const decl = ast.body[0] as AST.DestructuringDeclaration;
129
+ expect(decl.type).toBe('DestructuringDeclaration');
130
+ expect(decl.isConst).toBe(true);
131
+ expect(decl.isAsync).toBe(true);
132
+ });
133
+
134
+ it('parses async destructuring with private fields', () => {
135
+ const code = 'async let {private secret: text, public_data: json} = do "get data" myModel';
136
+ const ast = parse(code);
137
+
138
+ const decl = ast.body[0] as AST.DestructuringDeclaration;
139
+ expect(decl.type).toBe('DestructuringDeclaration');
140
+ expect(decl.isAsync).toBe(true);
141
+ expect(decl.fields[0].isPrivate).toBe(true);
142
+ expect(decl.fields[1].isPrivate).toBeUndefined();
143
+ });
144
+ });
145
+
146
+ describe('async standalone statements (fire-and-forget)', () => {
147
+ it('parses async do statement', () => {
148
+ const code = 'async do "log something" myModel';
149
+ const ast = parse(code);
150
+
151
+ const stmt = ast.body[0] as AST.AsyncStatement;
152
+ expect(stmt.type).toBe('AsyncStatement');
153
+ const expr = stmt.expression as AST.VibeExpression;
154
+ expect(expr.type).toBe('VibeExpression');
155
+ expect(expr.operationType).toBe('do');
156
+ });
157
+
158
+ it('parses async vibe statement', () => {
159
+ const code = 'async vibe "process data" myModel';
160
+ const ast = parse(code);
161
+
162
+ const stmt = ast.body[0] as AST.AsyncStatement;
163
+ expect(stmt.type).toBe('AsyncStatement');
164
+ const expr = stmt.expression as AST.VibeExpression;
165
+ expect(expr.type).toBe('VibeExpression');
166
+ expect(expr.operationType).toBe('vibe');
167
+ });
168
+
169
+ it('parses async ts block statement', () => {
170
+ const code = 'async ts() { console.log("fire and forget"); }';
171
+ const ast = parse(code);
172
+
173
+ const stmt = ast.body[0] as AST.AsyncStatement;
174
+ expect(stmt.type).toBe('AsyncStatement');
175
+ expect(stmt.expression.type).toBe('TsBlock');
176
+ });
177
+
178
+ it('parses async function call statement', () => {
179
+ const code = 'async logToAnalytics("event")';
180
+ const ast = parse(code);
181
+
182
+ const stmt = ast.body[0] as AST.AsyncStatement;
183
+ expect(stmt.type).toBe('AsyncStatement');
184
+ expect(stmt.expression.type).toBe('CallExpression');
185
+ });
186
+
187
+ it('parses async method call statement', () => {
188
+ const code = 'async api.sendNotification("done")';
189
+ const ast = parse(code);
190
+
191
+ const stmt = ast.body[0] as AST.AsyncStatement;
192
+ expect(stmt.type).toBe('AsyncStatement');
193
+ expect(stmt.expression.type).toBe('CallExpression');
194
+ });
195
+ });
196
+
197
+ describe('multiple async declarations', () => {
198
+ it('parses multiple async operations in sequence', () => {
199
+ const code = `
200
+ async let a = do "1" myModel
201
+ async let b = do "2" myModel
202
+ async let c = tsFunc()
203
+ let result = a + b + c
204
+ `;
205
+ const ast = parse(code);
206
+
207
+ expect(ast.body.length).toBe(4);
208
+ expect((ast.body[0] as AST.LetDeclaration).isAsync).toBe(true);
209
+ expect((ast.body[1] as AST.LetDeclaration).isAsync).toBe(true);
210
+ expect((ast.body[2] as AST.LetDeclaration).isAsync).toBe(true);
211
+ expect((ast.body[3] as AST.LetDeclaration).isAsync).toBeUndefined();
212
+ });
213
+
214
+ it('parses mixed async and sync declarations', () => {
215
+ const code = `
216
+ let sync1 = "hello"
217
+ async let async1 = do "prompt" myModel
218
+ const sync2 = 42
219
+ async const async2 = fetchData()
220
+ `;
221
+ const ast = parse(code);
222
+
223
+ expect(ast.body.length).toBe(4);
224
+ expect((ast.body[0] as AST.LetDeclaration).isAsync).toBeUndefined();
225
+ expect((ast.body[1] as AST.LetDeclaration).isAsync).toBe(true);
226
+ expect((ast.body[2] as AST.ConstDeclaration).isAsync).toBeUndefined();
227
+ expect((ast.body[3] as AST.ConstDeclaration).isAsync).toBe(true);
228
+ });
229
+ });
230
+
231
+ describe('regular declarations are not async', () => {
232
+ it('regular let is not async', () => {
233
+ const code = 'let x = do "prompt" myModel';
234
+ const ast = parse(code);
235
+
236
+ const decl = ast.body[0] as AST.LetDeclaration;
237
+ expect(decl.isAsync).toBeUndefined();
238
+ });
239
+
240
+ it('regular const is not async', () => {
241
+ const code = 'const x = 42';
242
+ const ast = parse(code);
243
+
244
+ const decl = ast.body[0] as AST.ConstDeclaration;
245
+ expect(decl.isAsync).toBeUndefined();
246
+ });
247
+ });
248
+ });
@@ -0,0 +1,167 @@
1
+ import { describe, expect, test } from 'bun:test';
2
+ import { parse } from '../parse';
3
+
4
+ describe('Parser - Destructuring Declarations', () => {
5
+ // ============================================================================
6
+ // Const destructuring
7
+ // ============================================================================
8
+
9
+ test('const destructuring with single field', () => {
10
+ const ast = parse('const {name: text} = do "get name" myModel');
11
+ expect(ast.body).toHaveLength(1);
12
+ expect(ast.body[0]).toMatchObject({
13
+ type: 'DestructuringDeclaration',
14
+ isConst: true,
15
+ fields: [{ name: 'name', type: 'text' }],
16
+ initializer: {
17
+ type: 'VibeExpression',
18
+ operationType: 'do',
19
+ },
20
+ });
21
+ });
22
+
23
+ test('const destructuring with multiple fields', () => {
24
+ const ast = parse('const {name: text, age: number} = do "get info" myModel');
25
+ expect(ast.body).toHaveLength(1);
26
+ expect(ast.body[0]).toMatchObject({
27
+ type: 'DestructuringDeclaration',
28
+ isConst: true,
29
+ fields: [
30
+ { name: 'name', type: 'text' },
31
+ { name: 'age', type: 'number' },
32
+ ],
33
+ initializer: {
34
+ type: 'VibeExpression',
35
+ operationType: 'do',
36
+ },
37
+ });
38
+ });
39
+
40
+ test('const destructuring with three fields', () => {
41
+ const ast = parse('const {valid: boolean, reason: text, score: number} = do "validate" myModel');
42
+ expect(ast.body).toHaveLength(1);
43
+ expect(ast.body[0]).toMatchObject({
44
+ type: 'DestructuringDeclaration',
45
+ isConst: true,
46
+ fields: [
47
+ { name: 'valid', type: 'boolean' },
48
+ { name: 'reason', type: 'text' },
49
+ { name: 'score', type: 'number' },
50
+ ],
51
+ });
52
+ });
53
+
54
+ // ============================================================================
55
+ // Let destructuring
56
+ // ============================================================================
57
+
58
+ test('let destructuring with single field', () => {
59
+ const ast = parse('let {count: number} = do "get count" myModel');
60
+ expect(ast.body).toHaveLength(1);
61
+ expect(ast.body[0]).toMatchObject({
62
+ type: 'DestructuringDeclaration',
63
+ isConst: false,
64
+ fields: [{ name: 'count', type: 'number' }],
65
+ initializer: {
66
+ type: 'VibeExpression',
67
+ operationType: 'do',
68
+ },
69
+ });
70
+ });
71
+
72
+ test('let destructuring with multiple fields', () => {
73
+ const ast = parse('let {x: number, y: number} = do "get coords" myModel');
74
+ expect(ast.body).toHaveLength(1);
75
+ expect(ast.body[0]).toMatchObject({
76
+ type: 'DestructuringDeclaration',
77
+ isConst: false,
78
+ fields: [
79
+ { name: 'x', type: 'number' },
80
+ { name: 'y', type: 'number' },
81
+ ],
82
+ });
83
+ });
84
+
85
+ // ============================================================================
86
+ // Various field types
87
+ // ============================================================================
88
+
89
+ test('destructuring with json type', () => {
90
+ const ast = parse('const {data: json} = do "get data" myModel');
91
+ expect(ast.body).toHaveLength(1);
92
+ expect(ast.body[0]).toMatchObject({
93
+ type: 'DestructuringDeclaration',
94
+ fields: [{ name: 'data', type: 'json' }],
95
+ });
96
+ });
97
+
98
+ test('destructuring with array types', () => {
99
+ const ast = parse('const {items: text[], counts: number[]} = do "get lists" myModel');
100
+ expect(ast.body).toHaveLength(1);
101
+ expect(ast.body[0]).toMatchObject({
102
+ type: 'DestructuringDeclaration',
103
+ fields: [
104
+ { name: 'items', type: 'text[]' },
105
+ { name: 'counts', type: 'number[]' },
106
+ ],
107
+ });
108
+ });
109
+
110
+ test('destructuring with boolean array', () => {
111
+ const ast = parse('const {flags: boolean[]} = do "get flags" myModel');
112
+ expect(ast.body).toHaveLength(1);
113
+ expect(ast.body[0]).toMatchObject({
114
+ type: 'DestructuringDeclaration',
115
+ fields: [{ name: 'flags', type: 'boolean[]' }],
116
+ });
117
+ });
118
+
119
+ test('destructuring with json array', () => {
120
+ const ast = parse('const {users: json[]} = do "get users" myModel');
121
+ expect(ast.body).toHaveLength(1);
122
+ expect(ast.body[0]).toMatchObject({
123
+ type: 'DestructuringDeclaration',
124
+ fields: [{ name: 'users', type: 'json[]' }],
125
+ });
126
+ });
127
+
128
+ // ============================================================================
129
+ // Mixed with regular declarations
130
+ // ============================================================================
131
+
132
+ test('destructuring followed by regular declaration', () => {
133
+ const ast = parse(`
134
+ const {name: text} = do "get name" myModel
135
+ let x = 42
136
+ `);
137
+ expect(ast.body).toHaveLength(2);
138
+ expect(ast.body[0].type).toBe('DestructuringDeclaration');
139
+ expect(ast.body[1].type).toBe('LetDeclaration');
140
+ });
141
+
142
+ test('regular declaration followed by destructuring', () => {
143
+ const ast = parse(`
144
+ let x = 42
145
+ const {name: text} = do "get name" myModel
146
+ `);
147
+ expect(ast.body).toHaveLength(2);
148
+ expect(ast.body[0].type).toBe('LetDeclaration');
149
+ expect(ast.body[1].type).toBe('DestructuringDeclaration');
150
+ });
151
+
152
+ // ============================================================================
153
+ // Vibe expression types
154
+ // ============================================================================
155
+
156
+ test('destructuring with vibe expression', () => {
157
+ const ast = parse('const {code: text} = vibe "generate code" myModel');
158
+ expect(ast.body).toHaveLength(1);
159
+ expect(ast.body[0]).toMatchObject({
160
+ type: 'DestructuringDeclaration',
161
+ initializer: {
162
+ type: 'VibeExpression',
163
+ operationType: 'vibe',
164
+ },
165
+ });
166
+ });
167
+ });