@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,388 @@
1
+ import { describe, expect, test } from 'bun:test';
2
+ import type { AsyncOperation, AsyncWave, RuntimeState, VibeValue } from '../../types';
3
+ import { createVibeValue } from '../../types';
4
+ import {
5
+ executeWave,
6
+ awaitOperations,
7
+ registerAsyncOperation,
8
+ completeAsyncOperation,
9
+ failAsyncOperation,
10
+ hasPendingAsync,
11
+ getPendingDependencies,
12
+ generateAsyncId,
13
+ } from '../executor';
14
+
15
+ describe('Async Executor', () => {
16
+ // Helper to create a mock runtime state
17
+ function createMockState(overrides: Partial<RuntimeState> = {}): RuntimeState {
18
+ return {
19
+ status: 'running',
20
+ program: { type: 'Program', body: [], location: { line: 1, column: 1 } },
21
+ functions: {},
22
+ tsModules: {},
23
+ vibeModules: {},
24
+ importedNames: {},
25
+ callStack: [],
26
+ instructionStack: [],
27
+ valueStack: [],
28
+ lastResult: null,
29
+ lastResultSource: null,
30
+ aiHistory: [],
31
+ executionLog: [],
32
+ logAiInteractions: false,
33
+ aiInteractions: [],
34
+ localContext: [],
35
+ globalContext: [],
36
+ pendingAI: null,
37
+ pendingCompress: null,
38
+ pendingTS: null,
39
+ pendingImportedTsCall: null,
40
+ pendingToolCall: null,
41
+ pendingDestructuring: null,
42
+ expectedFields: null,
43
+ lastUsedModel: null,
44
+ rootDir: '/tmp',
45
+ error: null,
46
+ errorObject: null,
47
+ asyncOperations: new Map(),
48
+ pendingAsyncIds: new Set(),
49
+ asyncVarToOpId: new Map(),
50
+ asyncWaves: [],
51
+ currentWaveId: 0,
52
+ maxParallel: 4,
53
+ nextAsyncId: 1,
54
+ ...overrides,
55
+ };
56
+ }
57
+
58
+ // Helper to create an async operation
59
+ function createOp(
60
+ id: string,
61
+ varName: string | null,
62
+ deps: string[] = []
63
+ ): AsyncOperation {
64
+ return {
65
+ id,
66
+ variableName: varName,
67
+ status: 'pending',
68
+ operationType: 'do',
69
+ dependencies: deps,
70
+ contextSnapshot: [],
71
+ waveId: 0,
72
+ };
73
+ }
74
+
75
+ describe('generateAsyncId', () => {
76
+ test('generates ID based on nextAsyncId', () => {
77
+ const state1 = createMockState({ nextAsyncId: 1 });
78
+ expect(generateAsyncId(state1)).toBe('async-000001');
79
+
80
+ const state2 = createMockState({ nextAsyncId: 2 });
81
+ expect(generateAsyncId(state2)).toBe('async-000002');
82
+
83
+ const state3 = createMockState({ nextAsyncId: 3 });
84
+ expect(generateAsyncId(state3)).toBe('async-000003');
85
+ });
86
+
87
+ test('pads ID with zeros', () => {
88
+ const state = createMockState({ nextAsyncId: 100 });
89
+ expect(generateAsyncId(state)).toBe('async-000100');
90
+ });
91
+
92
+ test('is a pure function (does not mutate state)', () => {
93
+ const state = createMockState({ nextAsyncId: 42 });
94
+ generateAsyncId(state);
95
+ expect(state.nextAsyncId).toBe(42); // Unchanged
96
+ });
97
+ });
98
+
99
+ describe('registerAsyncOperation', () => {
100
+ test('registers operation with generated ID', () => {
101
+ const state = createMockState();
102
+ const op = registerAsyncOperation(state, {
103
+ variableName: 'x',
104
+ status: 'pending',
105
+ operationType: 'do',
106
+ dependencies: [],
107
+ contextSnapshot: [],
108
+ });
109
+
110
+ expect(op.id).toBe('async-000001');
111
+ expect(state.asyncOperations.has('async-000001')).toBe(true);
112
+ });
113
+
114
+ test('adds operation to pendingAsyncIds', () => {
115
+ const state = createMockState();
116
+ const op = registerAsyncOperation(state, {
117
+ variableName: 'x',
118
+ status: 'pending',
119
+ operationType: 'do',
120
+ dependencies: [],
121
+ contextSnapshot: [],
122
+ });
123
+
124
+ expect(state.pendingAsyncIds.has(op.id)).toBe(true);
125
+ });
126
+
127
+ test('maps variable name to operation ID', () => {
128
+ const state = createMockState();
129
+ const op = registerAsyncOperation(state, {
130
+ variableName: 'myVar',
131
+ status: 'pending',
132
+ operationType: 'do',
133
+ dependencies: [],
134
+ contextSnapshot: [],
135
+ });
136
+
137
+ expect(state.asyncVarToOpId.get('myVar')).toBe(op.id);
138
+ });
139
+
140
+ test('handles null variableName (fire-and-forget)', () => {
141
+ const state = createMockState();
142
+ const op = registerAsyncOperation(state, {
143
+ variableName: null,
144
+ status: 'pending',
145
+ operationType: 'do',
146
+ dependencies: [],
147
+ contextSnapshot: [],
148
+ });
149
+
150
+ expect(op.id).toBeDefined();
151
+ expect(state.asyncVarToOpId.size).toBe(0);
152
+ });
153
+ });
154
+
155
+ describe('completeAsyncOperation', () => {
156
+ test('marks operation as completed', () => {
157
+ const state = createMockState();
158
+ const op = createOp('async-001', 'x');
159
+ state.asyncOperations.set('async-001', op);
160
+ state.pendingAsyncIds.add('async-001');
161
+
162
+ const result = createVibeValue('success');
163
+ completeAsyncOperation(state, 'async-001', result);
164
+
165
+ expect(op.status).toBe('completed');
166
+ expect(op.result).toBe(result);
167
+ expect(state.pendingAsyncIds.has('async-001')).toBe(false);
168
+ });
169
+
170
+ test('sets endTime', () => {
171
+ const state = createMockState();
172
+ const op = createOp('async-001', 'x');
173
+ state.asyncOperations.set('async-001', op);
174
+ state.pendingAsyncIds.add('async-001');
175
+
176
+ completeAsyncOperation(state, 'async-001', createVibeValue('success'));
177
+
178
+ expect(op.endTime).toBeDefined();
179
+ });
180
+ });
181
+
182
+ describe('failAsyncOperation', () => {
183
+ test('marks operation as failed', () => {
184
+ const state = createMockState();
185
+ const op = createOp('async-001', 'x');
186
+ state.asyncOperations.set('async-001', op);
187
+ state.pendingAsyncIds.add('async-001');
188
+
189
+ const error = { message: 'Test error', type: 'Error', location: null };
190
+ failAsyncOperation(state, 'async-001', error);
191
+
192
+ expect(op.status).toBe('failed');
193
+ expect(op.error).toBe(error);
194
+ expect(state.pendingAsyncIds.has('async-001')).toBe(false);
195
+ });
196
+ });
197
+
198
+ describe('hasPendingAsync', () => {
199
+ test('returns false when no pending operations', () => {
200
+ const state = createMockState();
201
+ expect(hasPendingAsync(state)).toBe(false);
202
+ });
203
+
204
+ test('returns true when operations are pending', () => {
205
+ const state = createMockState();
206
+ state.pendingAsyncIds.add('async-001');
207
+ expect(hasPendingAsync(state)).toBe(true);
208
+ });
209
+ });
210
+
211
+ describe('getPendingDependencies', () => {
212
+ test('returns empty array when no variables depend on async', () => {
213
+ const state = createMockState();
214
+ expect(getPendingDependencies(['x', 'y'], state)).toEqual([]);
215
+ });
216
+
217
+ test('returns pending operation IDs', () => {
218
+ const state = createMockState();
219
+ state.asyncVarToOpId.set('x', 'async-001');
220
+ state.asyncVarToOpId.set('y', 'async-002');
221
+ state.pendingAsyncIds.add('async-001');
222
+ state.pendingAsyncIds.add('async-002');
223
+
224
+ const deps = getPendingDependencies(['x', 'y', 'z'], state);
225
+ expect(deps).toContain('async-001');
226
+ expect(deps).toContain('async-002');
227
+ expect(deps.length).toBe(2);
228
+ });
229
+
230
+ test('excludes completed operations', () => {
231
+ const state = createMockState();
232
+ state.asyncVarToOpId.set('x', 'async-001');
233
+ state.asyncVarToOpId.set('y', 'async-002');
234
+ state.pendingAsyncIds.add('async-001');
235
+ // async-002 is not in pending (completed)
236
+
237
+ const deps = getPendingDependencies(['x', 'y'], state);
238
+ expect(deps).toEqual(['async-001']);
239
+ });
240
+ });
241
+
242
+ describe('executeWave', () => {
243
+ test('executes all operations in wave', async () => {
244
+ const operations = new Map<string, AsyncOperation>();
245
+ operations.set('async-001', createOp('async-001', 'a'));
246
+ operations.set('async-002', createOp('async-002', 'b'));
247
+
248
+ const wave: AsyncWave = {
249
+ id: 0,
250
+ operationIds: ['async-001', 'async-002'],
251
+ contextSnapshot: [],
252
+ startTime: 0,
253
+ };
254
+
255
+ const executor = async () => createVibeValue('result');
256
+ const results = await executeWave(wave, operations, executor, 4);
257
+
258
+ expect(results.length).toBe(2);
259
+ expect(results.every((r) => r.result?.value === 'result')).toBe(true);
260
+ });
261
+
262
+ test('respects maxParallel limit', async () => {
263
+ const operations = new Map<string, AsyncOperation>();
264
+ for (let i = 0; i < 10; i++) {
265
+ operations.set(`async-${i}`, createOp(`async-${i}`, `v${i}`));
266
+ }
267
+
268
+ const wave: AsyncWave = {
269
+ id: 0,
270
+ operationIds: Array.from(operations.keys()),
271
+ contextSnapshot: [],
272
+ startTime: 0,
273
+ };
274
+
275
+ let maxConcurrent = 0;
276
+ let currentConcurrent = 0;
277
+
278
+ const executor = async () => {
279
+ currentConcurrent++;
280
+ maxConcurrent = Math.max(maxConcurrent, currentConcurrent);
281
+ await new Promise((r) => setTimeout(r, 10));
282
+ currentConcurrent--;
283
+ return createVibeValue('result');
284
+ };
285
+
286
+ await executeWave(wave, operations, executor, 2);
287
+
288
+ expect(maxConcurrent).toBe(2);
289
+ });
290
+
291
+ test('handles operation errors', async () => {
292
+ const operations = new Map<string, AsyncOperation>();
293
+ operations.set('async-001', createOp('async-001', 'a'));
294
+
295
+ const wave: AsyncWave = {
296
+ id: 0,
297
+ operationIds: ['async-001'],
298
+ contextSnapshot: [],
299
+ startTime: 0,
300
+ };
301
+
302
+ const executor = async () => {
303
+ throw new Error('Test error');
304
+ };
305
+
306
+ const results = await executeWave(wave, operations, executor, 4);
307
+
308
+ expect(results.length).toBe(1);
309
+ expect(results[0].error?.message).toBe('Test error');
310
+ expect(operations.get('async-001')?.status).toBe('failed');
311
+ });
312
+
313
+ test('updates operation status to running during execution', async () => {
314
+ const operations = new Map<string, AsyncOperation>();
315
+ const op = createOp('async-001', 'a');
316
+ operations.set('async-001', op);
317
+
318
+ const wave: AsyncWave = {
319
+ id: 0,
320
+ operationIds: ['async-001'],
321
+ contextSnapshot: [],
322
+ startTime: 0,
323
+ };
324
+
325
+ let statusDuringExec = '';
326
+ const executor = async () => {
327
+ statusDuringExec = op.status;
328
+ return createVibeValue('result');
329
+ };
330
+
331
+ await executeWave(wave, operations, executor, 4);
332
+
333
+ expect(statusDuringExec).toBe('running');
334
+ expect(op.status).toBe('completed');
335
+ });
336
+
337
+ test('sets wave timing information', async () => {
338
+ const operations = new Map<string, AsyncOperation>();
339
+ operations.set('async-001', createOp('async-001', 'a'));
340
+
341
+ const wave: AsyncWave = {
342
+ id: 0,
343
+ operationIds: ['async-001'],
344
+ contextSnapshot: [],
345
+ startTime: 0,
346
+ };
347
+
348
+ const executor = async () => createVibeValue('result');
349
+ await executeWave(wave, operations, executor, 4);
350
+
351
+ expect(wave.startTime).toBeGreaterThan(0);
352
+ expect(wave.endTime).toBeGreaterThanOrEqual(wave.startTime);
353
+ });
354
+ });
355
+
356
+ describe('awaitOperations', () => {
357
+ test('returns results for completed operations', async () => {
358
+ const operations = new Map<string, AsyncOperation>();
359
+ const op = createOp('async-001', 'a');
360
+ op.status = 'completed';
361
+ op.result = createVibeValue('completed result');
362
+ operations.set('async-001', op);
363
+
364
+ const results = await awaitOperations(['async-001'], operations);
365
+
366
+ expect(results.get('async-001')?.value).toBe('completed result');
367
+ });
368
+
369
+ test('returns error for failed operations', async () => {
370
+ const operations = new Map<string, AsyncOperation>();
371
+ const op = createOp('async-001', 'a');
372
+ op.status = 'failed';
373
+ op.error = { message: 'Failed', type: 'Error', location: null };
374
+ operations.set('async-001', op);
375
+
376
+ const results = await awaitOperations(['async-001'], operations);
377
+
378
+ expect(results.get('async-001')?.err).toBeDefined();
379
+ });
380
+
381
+ test('returns error for missing operations', async () => {
382
+ const operations = new Map<string, AsyncOperation>();
383
+ const results = await awaitOperations(['nonexistent'], operations);
384
+
385
+ expect(results.get('nonexistent')?.err).toBeDefined();
386
+ });
387
+ });
388
+ });