@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.
- package/package.json +46 -0
- package/src/ast/index.ts +375 -0
- package/src/ast.ts +2 -0
- package/src/debug/advanced-features.ts +482 -0
- package/src/debug/bun-inspector.ts +424 -0
- package/src/debug/handoff-manager.ts +283 -0
- package/src/debug/index.ts +150 -0
- package/src/debug/runner.ts +365 -0
- package/src/debug/server.ts +565 -0
- package/src/debug/stack-merger.ts +267 -0
- package/src/debug/state.ts +581 -0
- package/src/debug/test/advanced-features.test.ts +300 -0
- package/src/debug/test/e2e.test.ts +218 -0
- package/src/debug/test/handoff-manager.test.ts +256 -0
- package/src/debug/test/runner.test.ts +256 -0
- package/src/debug/test/stack-merger.test.ts +163 -0
- package/src/debug/test/state.test.ts +400 -0
- package/src/debug/test/ts-debug-integration.test.ts +374 -0
- package/src/debug/test/ts-import-tracker.test.ts +125 -0
- package/src/debug/test/ts-source-map.test.ts +169 -0
- package/src/debug/ts-import-tracker.ts +151 -0
- package/src/debug/ts-source-map.ts +171 -0
- package/src/errors/index.ts +124 -0
- package/src/index.ts +358 -0
- package/src/lexer/index.ts +348 -0
- package/src/lexer.ts +2 -0
- package/src/parser/index.ts +792 -0
- package/src/parser/parse.ts +45 -0
- package/src/parser/test/async.test.ts +248 -0
- package/src/parser/test/destructuring.test.ts +167 -0
- package/src/parser/test/do-expression.test.ts +486 -0
- package/src/parser/test/errors/do-expression.test.ts +95 -0
- package/src/parser/test/errors/error-locations.test.ts +230 -0
- package/src/parser/test/errors/invalid-expressions.test.ts +144 -0
- package/src/parser/test/errors/missing-tokens.test.ts +126 -0
- package/src/parser/test/errors/model-declaration.test.ts +185 -0
- package/src/parser/test/errors/nested-blocks.test.ts +226 -0
- package/src/parser/test/errors/unclosed-delimiters.test.ts +122 -0
- package/src/parser/test/errors/unexpected-tokens.test.ts +120 -0
- package/src/parser/test/import-export.test.ts +143 -0
- package/src/parser/test/literals.test.ts +404 -0
- package/src/parser/test/model-declaration.test.ts +161 -0
- package/src/parser/test/nested-blocks.test.ts +402 -0
- package/src/parser/test/parser.test.ts +743 -0
- package/src/parser/test/private.test.ts +136 -0
- package/src/parser/test/template-literal.test.ts +127 -0
- package/src/parser/test/tool-declaration.test.ts +302 -0
- package/src/parser/test/ts-block.test.ts +252 -0
- package/src/parser/test/type-annotations.test.ts +254 -0
- package/src/parser/visitor/helpers.ts +330 -0
- package/src/parser/visitor.ts +794 -0
- package/src/parser.ts +2 -0
- package/src/runtime/ai/cache-chunking.test.ts +69 -0
- package/src/runtime/ai/cache-chunking.ts +73 -0
- package/src/runtime/ai/client.ts +109 -0
- package/src/runtime/ai/context.ts +168 -0
- package/src/runtime/ai/formatters.ts +316 -0
- package/src/runtime/ai/index.ts +38 -0
- package/src/runtime/ai/language-ref.ts +38 -0
- package/src/runtime/ai/providers/anthropic.ts +253 -0
- package/src/runtime/ai/providers/google.ts +201 -0
- package/src/runtime/ai/providers/openai.ts +156 -0
- package/src/runtime/ai/retry.ts +100 -0
- package/src/runtime/ai/return-tools.ts +301 -0
- package/src/runtime/ai/test/client.test.ts +83 -0
- package/src/runtime/ai/test/formatters.test.ts +485 -0
- package/src/runtime/ai/test/retry.test.ts +137 -0
- package/src/runtime/ai/test/return-tools.test.ts +450 -0
- package/src/runtime/ai/test/tool-loop.test.ts +319 -0
- package/src/runtime/ai/test/tool-schema.test.ts +241 -0
- package/src/runtime/ai/tool-loop.ts +203 -0
- package/src/runtime/ai/tool-schema.ts +151 -0
- package/src/runtime/ai/types.ts +113 -0
- package/src/runtime/ai-logger.ts +255 -0
- package/src/runtime/ai-provider.ts +347 -0
- package/src/runtime/async/dependencies.ts +276 -0
- package/src/runtime/async/executor.ts +293 -0
- package/src/runtime/async/index.ts +43 -0
- package/src/runtime/async/scheduling.ts +163 -0
- package/src/runtime/async/test/dependencies.test.ts +284 -0
- package/src/runtime/async/test/executor.test.ts +388 -0
- package/src/runtime/context.ts +357 -0
- package/src/runtime/exec/ai.ts +139 -0
- package/src/runtime/exec/expressions.ts +475 -0
- package/src/runtime/exec/frames.ts +26 -0
- package/src/runtime/exec/functions.ts +305 -0
- package/src/runtime/exec/interpolation.ts +312 -0
- package/src/runtime/exec/statements.ts +604 -0
- package/src/runtime/exec/tools.ts +129 -0
- package/src/runtime/exec/typescript.ts +215 -0
- package/src/runtime/exec/variables.ts +279 -0
- package/src/runtime/index.ts +975 -0
- package/src/runtime/modules.ts +452 -0
- package/src/runtime/serialize.ts +103 -0
- package/src/runtime/state.ts +489 -0
- package/src/runtime/stdlib/core.ts +45 -0
- package/src/runtime/stdlib/directory.test.ts +156 -0
- package/src/runtime/stdlib/edit.test.ts +154 -0
- package/src/runtime/stdlib/fastEdit.test.ts +201 -0
- package/src/runtime/stdlib/glob.test.ts +106 -0
- package/src/runtime/stdlib/grep.test.ts +144 -0
- package/src/runtime/stdlib/index.ts +16 -0
- package/src/runtime/stdlib/readFile.test.ts +123 -0
- package/src/runtime/stdlib/tools/index.ts +707 -0
- package/src/runtime/stdlib/writeFile.test.ts +157 -0
- package/src/runtime/step.ts +969 -0
- package/src/runtime/test/ai-context.test.ts +1086 -0
- package/src/runtime/test/ai-result-object.test.ts +419 -0
- package/src/runtime/test/ai-tool-flow.test.ts +859 -0
- package/src/runtime/test/async-execution-order.test.ts +618 -0
- package/src/runtime/test/async-execution.test.ts +344 -0
- package/src/runtime/test/async-nested.test.ts +660 -0
- package/src/runtime/test/async-parallel-timing.test.ts +546 -0
- package/src/runtime/test/basic1.test.ts +154 -0
- package/src/runtime/test/binary-operators.test.ts +431 -0
- package/src/runtime/test/break-statement.test.ts +257 -0
- package/src/runtime/test/context-modes.test.ts +650 -0
- package/src/runtime/test/context.test.ts +466 -0
- package/src/runtime/test/core-functions.test.ts +228 -0
- package/src/runtime/test/e2e.test.ts +88 -0
- package/src/runtime/test/error-locations/error-locations.test.ts +80 -0
- package/src/runtime/test/error-locations/main-error.vibe +4 -0
- package/src/runtime/test/error-locations/main-import-error.vibe +3 -0
- package/src/runtime/test/error-locations/utils/helper.vibe +5 -0
- package/src/runtime/test/for-in.test.ts +312 -0
- package/src/runtime/test/helpers.ts +69 -0
- package/src/runtime/test/imports.test.ts +334 -0
- package/src/runtime/test/json-expressions.test.ts +232 -0
- package/src/runtime/test/literals.test.ts +372 -0
- package/src/runtime/test/logical-indexing.test.ts +478 -0
- package/src/runtime/test/member-methods.test.ts +324 -0
- package/src/runtime/test/model-config.test.ts +338 -0
- package/src/runtime/test/null-handling.test.ts +342 -0
- package/src/runtime/test/private-visibility.test.ts +332 -0
- package/src/runtime/test/runtime-state.test.ts +514 -0
- package/src/runtime/test/scoping.test.ts +370 -0
- package/src/runtime/test/string-interpolation.test.ts +354 -0
- package/src/runtime/test/template-literal.test.ts +181 -0
- package/src/runtime/test/tool-execution.test.ts +467 -0
- package/src/runtime/test/tool-schema-generation.test.ts +477 -0
- package/src/runtime/test/tostring.test.ts +210 -0
- package/src/runtime/test/ts-block.test.ts +594 -0
- package/src/runtime/test/ts-error-location.test.ts +231 -0
- package/src/runtime/test/types.test.ts +732 -0
- package/src/runtime/test/verbose-logger.test.ts +710 -0
- package/src/runtime/test/vibe-expression.test.ts +54 -0
- package/src/runtime/test/vibe-value-errors.test.ts +541 -0
- package/src/runtime/test/while.test.ts +232 -0
- package/src/runtime/tools/builtin.ts +30 -0
- package/src/runtime/tools/directory-tools.ts +70 -0
- package/src/runtime/tools/file-tools.ts +228 -0
- package/src/runtime/tools/index.ts +5 -0
- package/src/runtime/tools/registry.ts +48 -0
- package/src/runtime/tools/search-tools.ts +134 -0
- package/src/runtime/tools/security.ts +36 -0
- package/src/runtime/tools/system-tools.ts +312 -0
- package/src/runtime/tools/test/fixtures/base-types.ts +40 -0
- package/src/runtime/tools/test/fixtures/test-types.ts +132 -0
- package/src/runtime/tools/test/registry.test.ts +713 -0
- package/src/runtime/tools/test/security.test.ts +86 -0
- package/src/runtime/tools/test/system-tools.test.ts +679 -0
- package/src/runtime/tools/test/ts-schema.test.ts +357 -0
- package/src/runtime/tools/ts-schema.ts +341 -0
- package/src/runtime/tools/types.ts +89 -0
- package/src/runtime/tools/utility-tools.ts +198 -0
- package/src/runtime/ts-eval.ts +126 -0
- package/src/runtime/types.ts +797 -0
- package/src/runtime/validation.ts +160 -0
- package/src/runtime/verbose-logger.ts +459 -0
- package/src/runtime.ts +2 -0
- package/src/semantic/analyzer-context.ts +62 -0
- package/src/semantic/analyzer-validators.ts +575 -0
- package/src/semantic/analyzer-visitors.ts +534 -0
- package/src/semantic/analyzer.ts +83 -0
- package/src/semantic/index.ts +11 -0
- package/src/semantic/symbol-table.ts +58 -0
- package/src/semantic/test/async-validation.test.ts +301 -0
- package/src/semantic/test/compress-validation.test.ts +179 -0
- package/src/semantic/test/const-reassignment.test.ts +111 -0
- package/src/semantic/test/control-flow.test.ts +346 -0
- package/src/semantic/test/destructuring.test.ts +185 -0
- package/src/semantic/test/duplicate-declarations.test.ts +168 -0
- package/src/semantic/test/export-validation.test.ts +111 -0
- package/src/semantic/test/fixtures/math.ts +31 -0
- package/src/semantic/test/imports.test.ts +148 -0
- package/src/semantic/test/json-type.test.ts +68 -0
- package/src/semantic/test/literals.test.ts +127 -0
- package/src/semantic/test/model-validation.test.ts +179 -0
- package/src/semantic/test/prompt-validation.test.ts +343 -0
- package/src/semantic/test/scoping.test.ts +312 -0
- package/src/semantic/test/tool-validation.test.ts +306 -0
- package/src/semantic/test/ts-type-checking.test.ts +563 -0
- package/src/semantic/test/type-constraints.test.ts +111 -0
- package/src/semantic/test/type-inference.test.ts +87 -0
- package/src/semantic/test/type-validation.test.ts +552 -0
- package/src/semantic/test/undefined-variables.test.ts +163 -0
- package/src/semantic/ts-block-checker.ts +204 -0
- package/src/semantic/ts-signatures.ts +194 -0
- package/src/semantic/ts-types.ts +170 -0
- package/src/semantic/types.ts +58 -0
- package/tests/fixtures/conditional-logic.vibe +14 -0
- package/tests/fixtures/function-call.vibe +16 -0
- package/tests/fixtures/imports/cycle-detection/a.vibe +6 -0
- package/tests/fixtures/imports/cycle-detection/b.vibe +5 -0
- package/tests/fixtures/imports/cycle-detection/main.vibe +3 -0
- package/tests/fixtures/imports/module-isolation/main-b.vibe +8 -0
- package/tests/fixtures/imports/module-isolation/main.vibe +9 -0
- package/tests/fixtures/imports/module-isolation/moduleA.vibe +6 -0
- package/tests/fixtures/imports/module-isolation/moduleB.vibe +6 -0
- package/tests/fixtures/imports/nested-import/helper.vibe +6 -0
- package/tests/fixtures/imports/nested-import/main.vibe +3 -0
- package/tests/fixtures/imports/nested-import/utils.ts +3 -0
- package/tests/fixtures/imports/nested-isolation/file2.vibe +15 -0
- package/tests/fixtures/imports/nested-isolation/file3.vibe +10 -0
- package/tests/fixtures/imports/nested-isolation/main.vibe +21 -0
- package/tests/fixtures/imports/pure-cycle/a.vibe +5 -0
- package/tests/fixtures/imports/pure-cycle/b.vibe +5 -0
- package/tests/fixtures/imports/pure-cycle/main.vibe +3 -0
- package/tests/fixtures/imports/ts-boolean/checks.ts +14 -0
- package/tests/fixtures/imports/ts-boolean/main.vibe +10 -0
- package/tests/fixtures/imports/ts-boolean/type-mismatch.vibe +5 -0
- package/tests/fixtures/imports/ts-boolean/use-constant.vibe +18 -0
- package/tests/fixtures/imports/ts-error-handling/helpers.ts +42 -0
- package/tests/fixtures/imports/ts-error-handling/main.vibe +5 -0
- package/tests/fixtures/imports/ts-import/main.vibe +4 -0
- package/tests/fixtures/imports/ts-import/math.ts +9 -0
- package/tests/fixtures/imports/ts-variables/call-non-function.vibe +5 -0
- package/tests/fixtures/imports/ts-variables/data.ts +10 -0
- package/tests/fixtures/imports/ts-variables/import-json.vibe +5 -0
- package/tests/fixtures/imports/ts-variables/import-type-mismatch.vibe +5 -0
- package/tests/fixtures/imports/ts-variables/import-variable.vibe +5 -0
- package/tests/fixtures/imports/vibe-import/greet.vibe +5 -0
- package/tests/fixtures/imports/vibe-import/main.vibe +3 -0
- package/tests/fixtures/multiple-ai-calls.vibe +10 -0
- package/tests/fixtures/simple-greeting.vibe +6 -0
- package/tests/fixtures/template-literals.vibe +11 -0
- package/tests/integration/basic-ai/basic-ai.integration.test.ts +166 -0
- package/tests/integration/basic-ai/basic-ai.vibe +12 -0
- package/tests/integration/bug-fix/bug-fix.integration.test.ts +201 -0
- package/tests/integration/bug-fix/buggy-code.ts +22 -0
- package/tests/integration/bug-fix/fix-bug.vibe +21 -0
- package/tests/integration/compress/compress.integration.test.ts +206 -0
- package/tests/integration/destructuring/destructuring.integration.test.ts +92 -0
- package/tests/integration/hello-world-translator/hello-world-translator.integration.test.ts +61 -0
- package/tests/integration/line-annotator/context-modes.integration.test.ts +261 -0
- package/tests/integration/line-annotator/line-annotator.integration.test.ts +148 -0
- package/tests/integration/multi-feature/cumulative-sum.integration.test.ts +75 -0
- package/tests/integration/multi-feature/number-analyzer.integration.test.ts +191 -0
- package/tests/integration/multi-feature/number-analyzer.vibe +59 -0
- package/tests/integration/tool-calls/tool-calls.integration.test.ts +93 -0
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dependency detection for async operations.
|
|
3
|
+
* Scans expressions for variable references and builds execution waves.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type * as AST from '../../ast';
|
|
7
|
+
import type { AsyncOperation, AsyncWave, ContextEntry } from '../types';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Extracts all variable names referenced in an expression.
|
|
11
|
+
* Used to detect dependencies between async operations.
|
|
12
|
+
*/
|
|
13
|
+
export function getReferencedVariables(expr: AST.Expression): string[] {
|
|
14
|
+
const variables: string[] = [];
|
|
15
|
+
|
|
16
|
+
function visit(node: AST.Expression): void {
|
|
17
|
+
switch (node.type) {
|
|
18
|
+
case 'Identifier':
|
|
19
|
+
variables.push(node.name);
|
|
20
|
+
break;
|
|
21
|
+
|
|
22
|
+
case 'BinaryExpression':
|
|
23
|
+
visit(node.left);
|
|
24
|
+
visit(node.right);
|
|
25
|
+
break;
|
|
26
|
+
|
|
27
|
+
case 'UnaryExpression':
|
|
28
|
+
visit(node.operand);
|
|
29
|
+
break;
|
|
30
|
+
|
|
31
|
+
case 'CallExpression':
|
|
32
|
+
visit(node.callee);
|
|
33
|
+
node.arguments.forEach(visit);
|
|
34
|
+
break;
|
|
35
|
+
|
|
36
|
+
case 'MemberExpression':
|
|
37
|
+
visit(node.object);
|
|
38
|
+
break;
|
|
39
|
+
|
|
40
|
+
case 'IndexExpression':
|
|
41
|
+
visit(node.object);
|
|
42
|
+
visit(node.index);
|
|
43
|
+
break;
|
|
44
|
+
|
|
45
|
+
case 'SliceExpression':
|
|
46
|
+
visit(node.object);
|
|
47
|
+
if (node.start) visit(node.start);
|
|
48
|
+
if (node.end) visit(node.end);
|
|
49
|
+
break;
|
|
50
|
+
|
|
51
|
+
case 'ArrayLiteral':
|
|
52
|
+
node.elements.forEach(visit);
|
|
53
|
+
break;
|
|
54
|
+
|
|
55
|
+
case 'ObjectLiteral':
|
|
56
|
+
node.properties.forEach((prop) => visit(prop.value));
|
|
57
|
+
break;
|
|
58
|
+
|
|
59
|
+
case 'RangeExpression':
|
|
60
|
+
visit(node.start);
|
|
61
|
+
visit(node.end);
|
|
62
|
+
break;
|
|
63
|
+
|
|
64
|
+
case 'AssignmentExpression':
|
|
65
|
+
visit(node.value);
|
|
66
|
+
break;
|
|
67
|
+
|
|
68
|
+
case 'VibeExpression':
|
|
69
|
+
visit(node.prompt);
|
|
70
|
+
if (node.model) visit(node.model);
|
|
71
|
+
break;
|
|
72
|
+
|
|
73
|
+
case 'TsBlock':
|
|
74
|
+
// TsBlock params are variable references
|
|
75
|
+
variables.push(...node.params);
|
|
76
|
+
break;
|
|
77
|
+
|
|
78
|
+
case 'TemplateLiteral':
|
|
79
|
+
// Extract variables from template interpolations like {varName}
|
|
80
|
+
const matches = node.value.matchAll(/\{([a-zA-Z_][a-zA-Z0-9_]*)\}/g);
|
|
81
|
+
for (const match of matches) {
|
|
82
|
+
variables.push(match[1]);
|
|
83
|
+
}
|
|
84
|
+
break;
|
|
85
|
+
|
|
86
|
+
// Literals don't reference variables
|
|
87
|
+
case 'StringLiteral':
|
|
88
|
+
case 'NumberLiteral':
|
|
89
|
+
case 'BooleanLiteral':
|
|
90
|
+
case 'NullLiteral':
|
|
91
|
+
break;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
visit(expr);
|
|
96
|
+
return [...new Set(variables)]; // Remove duplicates
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Detects which async operations the given expression depends on.
|
|
101
|
+
* Returns the IDs of async operations that must complete before this expression can be evaluated.
|
|
102
|
+
*/
|
|
103
|
+
export function detectAsyncDependencies(
|
|
104
|
+
expr: AST.Expression,
|
|
105
|
+
asyncVarToOpId: Map<string, string>,
|
|
106
|
+
pendingAsyncIds: Set<string>
|
|
107
|
+
): string[] {
|
|
108
|
+
const referencedVars = getReferencedVariables(expr);
|
|
109
|
+
const dependencies: string[] = [];
|
|
110
|
+
|
|
111
|
+
for (const varName of referencedVars) {
|
|
112
|
+
const opId = asyncVarToOpId.get(varName);
|
|
113
|
+
if (opId && pendingAsyncIds.has(opId)) {
|
|
114
|
+
dependencies.push(opId);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return dependencies;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Builds execution waves from a list of async operations.
|
|
123
|
+
* Operations with no pending dependencies go in the same wave.
|
|
124
|
+
* Uses topological sort to determine execution order.
|
|
125
|
+
*/
|
|
126
|
+
export function buildExecutionWaves(
|
|
127
|
+
operations: AsyncOperation[],
|
|
128
|
+
contextSnapshot: ContextEntry[]
|
|
129
|
+
): AsyncWave[] {
|
|
130
|
+
const waves: AsyncWave[] = [];
|
|
131
|
+
const remaining = new Set(operations.map((op) => op.id));
|
|
132
|
+
const completed = new Set<string>();
|
|
133
|
+
|
|
134
|
+
// Map from operation ID to operation
|
|
135
|
+
const opById = new Map(operations.map((op) => [op.id, op]));
|
|
136
|
+
|
|
137
|
+
// Map from variable name to operation ID
|
|
138
|
+
const varToOp = new Map<string, string>();
|
|
139
|
+
for (const op of operations) {
|
|
140
|
+
if (op.variableName) {
|
|
141
|
+
varToOp.set(op.variableName, op.id);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
let waveId = 0;
|
|
146
|
+
while (remaining.size > 0) {
|
|
147
|
+
const waveOps: string[] = [];
|
|
148
|
+
|
|
149
|
+
for (const opId of remaining) {
|
|
150
|
+
const op = opById.get(opId)!;
|
|
151
|
+
|
|
152
|
+
// Check if all dependencies are completed (or not in our operation set)
|
|
153
|
+
const depsReady = op.dependencies.every((depVar) => {
|
|
154
|
+
const depOpId = varToOp.get(depVar);
|
|
155
|
+
return !depOpId || completed.has(depOpId) || !remaining.has(depOpId);
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
if (depsReady) {
|
|
159
|
+
waveOps.push(opId);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (waveOps.length === 0 && remaining.size > 0) {
|
|
164
|
+
// Circular dependency detected - should be caught at semantic analysis
|
|
165
|
+
throw new Error('Circular dependency detected in async operations');
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Create the wave
|
|
169
|
+
const wave: AsyncWave = {
|
|
170
|
+
id: waveId,
|
|
171
|
+
operationIds: waveOps,
|
|
172
|
+
contextSnapshot: [...contextSnapshot], // Copy context at wave creation
|
|
173
|
+
startTime: 0, // Will be set when wave executes
|
|
174
|
+
};
|
|
175
|
+
waves.push(wave);
|
|
176
|
+
|
|
177
|
+
// Mark operations as completed for next iteration
|
|
178
|
+
for (const opId of waveOps) {
|
|
179
|
+
remaining.delete(opId);
|
|
180
|
+
completed.add(opId);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
waveId++;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
return waves;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Gets variable names that an instruction references.
|
|
191
|
+
* Used to check if an instruction needs to await pending async operations.
|
|
192
|
+
*/
|
|
193
|
+
export function getInstructionDependencies(
|
|
194
|
+
instruction: { op: string; [key: string]: unknown },
|
|
195
|
+
asyncVarToOpId: Map<string, string>,
|
|
196
|
+
pendingAsyncIds: Set<string>
|
|
197
|
+
): string[] {
|
|
198
|
+
const dependencies: string[] = [];
|
|
199
|
+
|
|
200
|
+
// Check expression-based instructions
|
|
201
|
+
if ('expr' in instruction && instruction.expr) {
|
|
202
|
+
const expr = instruction.expr as AST.Expression;
|
|
203
|
+
const deps = detectAsyncDependencies(expr, asyncVarToOpId, pendingAsyncIds);
|
|
204
|
+
dependencies.push(...deps);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Check statement-based instructions
|
|
208
|
+
if ('stmt' in instruction && instruction.stmt) {
|
|
209
|
+
const stmt = instruction.stmt as AST.Statement;
|
|
210
|
+
const stmtDeps = getStatementDependencies(stmt, asyncVarToOpId, pendingAsyncIds);
|
|
211
|
+
dependencies.push(...stmtDeps);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
return [...new Set(dependencies)];
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Gets async operation IDs that a statement depends on.
|
|
219
|
+
*/
|
|
220
|
+
function getStatementDependencies(
|
|
221
|
+
stmt: AST.Statement,
|
|
222
|
+
asyncVarToOpId: Map<string, string>,
|
|
223
|
+
pendingAsyncIds: Set<string>
|
|
224
|
+
): string[] {
|
|
225
|
+
const dependencies: string[] = [];
|
|
226
|
+
|
|
227
|
+
switch (stmt.type) {
|
|
228
|
+
case 'LetDeclaration':
|
|
229
|
+
case 'ConstDeclaration':
|
|
230
|
+
if (stmt.initializer) {
|
|
231
|
+
dependencies.push(...detectAsyncDependencies(stmt.initializer, asyncVarToOpId, pendingAsyncIds));
|
|
232
|
+
}
|
|
233
|
+
break;
|
|
234
|
+
|
|
235
|
+
case 'DestructuringDeclaration':
|
|
236
|
+
dependencies.push(...detectAsyncDependencies(stmt.initializer, asyncVarToOpId, pendingAsyncIds));
|
|
237
|
+
break;
|
|
238
|
+
|
|
239
|
+
case 'ExpressionStatement':
|
|
240
|
+
dependencies.push(...detectAsyncDependencies(stmt.expression, asyncVarToOpId, pendingAsyncIds));
|
|
241
|
+
break;
|
|
242
|
+
|
|
243
|
+
case 'IfStatement':
|
|
244
|
+
dependencies.push(...detectAsyncDependencies(stmt.condition, asyncVarToOpId, pendingAsyncIds));
|
|
245
|
+
break;
|
|
246
|
+
|
|
247
|
+
case 'WhileStatement':
|
|
248
|
+
dependencies.push(...detectAsyncDependencies(stmt.condition, asyncVarToOpId, pendingAsyncIds));
|
|
249
|
+
break;
|
|
250
|
+
|
|
251
|
+
case 'ForInStatement':
|
|
252
|
+
dependencies.push(...detectAsyncDependencies(stmt.iterable, asyncVarToOpId, pendingAsyncIds));
|
|
253
|
+
break;
|
|
254
|
+
|
|
255
|
+
case 'ReturnStatement':
|
|
256
|
+
if (stmt.value) {
|
|
257
|
+
dependencies.push(...detectAsyncDependencies(stmt.value, asyncVarToOpId, pendingAsyncIds));
|
|
258
|
+
}
|
|
259
|
+
break;
|
|
260
|
+
|
|
261
|
+
case 'AsyncStatement':
|
|
262
|
+
dependencies.push(...detectAsyncDependencies(stmt.expression, asyncVarToOpId, pendingAsyncIds));
|
|
263
|
+
break;
|
|
264
|
+
|
|
265
|
+
// Declarations that don't have expression dependencies
|
|
266
|
+
case 'ImportDeclaration':
|
|
267
|
+
case 'ExportDeclaration':
|
|
268
|
+
case 'ModelDeclaration':
|
|
269
|
+
case 'FunctionDeclaration':
|
|
270
|
+
case 'ToolDeclaration':
|
|
271
|
+
case 'BlockStatement':
|
|
272
|
+
break;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
return dependencies;
|
|
276
|
+
}
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Async execution engine.
|
|
3
|
+
* Executes async operations in waves with throttling.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { RuntimeState, AsyncOperation, AsyncWave, VibeValue, VibeError, ContextEntry } from '../types';
|
|
7
|
+
import { createVibeValue, createVibeError } from '../types';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Simple semaphore for limiting concurrent operations.
|
|
11
|
+
*/
|
|
12
|
+
class Semaphore {
|
|
13
|
+
private permits: number;
|
|
14
|
+
private waiting: Array<() => void> = [];
|
|
15
|
+
|
|
16
|
+
constructor(permits: number) {
|
|
17
|
+
this.permits = permits;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
async acquire(): Promise<void> {
|
|
21
|
+
if (this.permits > 0) {
|
|
22
|
+
this.permits--;
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return new Promise((resolve) => {
|
|
27
|
+
this.waiting.push(resolve);
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
release(): void {
|
|
32
|
+
const next = this.waiting.shift();
|
|
33
|
+
if (next) {
|
|
34
|
+
next();
|
|
35
|
+
} else {
|
|
36
|
+
this.permits++;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Result of executing an async operation.
|
|
43
|
+
*/
|
|
44
|
+
export interface AsyncExecutionResult {
|
|
45
|
+
operationId: string;
|
|
46
|
+
result?: VibeValue;
|
|
47
|
+
error?: VibeError;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Executor function type for running a single async operation.
|
|
52
|
+
* This will be provided by the runtime to handle different operation types.
|
|
53
|
+
*/
|
|
54
|
+
export type AsyncOperationExecutor = (
|
|
55
|
+
operation: AsyncOperation,
|
|
56
|
+
contextSnapshot: ContextEntry[]
|
|
57
|
+
) => Promise<VibeValue>;
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Executes all operations in a wave with parallelism throttling.
|
|
61
|
+
* Returns the results of all operations.
|
|
62
|
+
*/
|
|
63
|
+
export async function executeWave(
|
|
64
|
+
wave: AsyncWave,
|
|
65
|
+
operations: Map<string, AsyncOperation>,
|
|
66
|
+
executor: AsyncOperationExecutor,
|
|
67
|
+
maxParallel: number
|
|
68
|
+
): Promise<AsyncExecutionResult[]> {
|
|
69
|
+
const semaphore = new Semaphore(maxParallel);
|
|
70
|
+
const startTime = Date.now();
|
|
71
|
+
|
|
72
|
+
// Update wave start time
|
|
73
|
+
wave.startTime = startTime;
|
|
74
|
+
|
|
75
|
+
const results = await Promise.all(
|
|
76
|
+
wave.operationIds.map(async (opId) => {
|
|
77
|
+
const operation = operations.get(opId);
|
|
78
|
+
if (!operation) {
|
|
79
|
+
return {
|
|
80
|
+
operationId: opId,
|
|
81
|
+
error: {
|
|
82
|
+
message: `Operation ${opId} not found`,
|
|
83
|
+
type: 'InternalError',
|
|
84
|
+
location: null,
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
await semaphore.acquire();
|
|
90
|
+
|
|
91
|
+
try {
|
|
92
|
+
// Update operation status
|
|
93
|
+
operation.status = 'running';
|
|
94
|
+
operation.startTime = Date.now();
|
|
95
|
+
|
|
96
|
+
// Execute the operation
|
|
97
|
+
const result = await executor(operation, wave.contextSnapshot);
|
|
98
|
+
|
|
99
|
+
// Update operation with result
|
|
100
|
+
operation.status = 'completed';
|
|
101
|
+
operation.endTime = Date.now();
|
|
102
|
+
operation.result = result;
|
|
103
|
+
|
|
104
|
+
return { operationId: opId, result };
|
|
105
|
+
} catch (err) {
|
|
106
|
+
// Handle exceptions (these crash the program per design)
|
|
107
|
+
const error: VibeError = {
|
|
108
|
+
message: err instanceof Error ? err.message : String(err),
|
|
109
|
+
type: err instanceof Error ? err.constructor.name : 'Error',
|
|
110
|
+
location: null,
|
|
111
|
+
stack: err instanceof Error ? err.stack : undefined,
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
operation.status = 'failed';
|
|
115
|
+
operation.endTime = Date.now();
|
|
116
|
+
operation.error = error;
|
|
117
|
+
|
|
118
|
+
return { operationId: opId, error };
|
|
119
|
+
} finally {
|
|
120
|
+
semaphore.release();
|
|
121
|
+
}
|
|
122
|
+
})
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
wave.endTime = Date.now();
|
|
126
|
+
return results;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Awaits specific async operations by their IDs.
|
|
131
|
+
* Returns when all specified operations are complete.
|
|
132
|
+
*/
|
|
133
|
+
export async function awaitOperations(
|
|
134
|
+
operationIds: string[],
|
|
135
|
+
operations: Map<string, AsyncOperation>
|
|
136
|
+
): Promise<Map<string, VibeValue>> {
|
|
137
|
+
const results = new Map<string, VibeValue>();
|
|
138
|
+
|
|
139
|
+
for (const opId of operationIds) {
|
|
140
|
+
const operation = operations.get(opId);
|
|
141
|
+
if (!operation) {
|
|
142
|
+
results.set(opId, createVibeError(`Operation ${opId} not found`));
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// If operation has a promise, wait for it
|
|
147
|
+
if (operation.promise) {
|
|
148
|
+
try {
|
|
149
|
+
const result = await operation.promise;
|
|
150
|
+
results.set(opId, result);
|
|
151
|
+
} catch (err) {
|
|
152
|
+
results.set(opId, createVibeError(err instanceof Error ? err : String(err)));
|
|
153
|
+
}
|
|
154
|
+
} else if (operation.result) {
|
|
155
|
+
// Already completed
|
|
156
|
+
results.set(opId, operation.result);
|
|
157
|
+
} else if (operation.error) {
|
|
158
|
+
// Already failed
|
|
159
|
+
results.set(opId, createVibeError(operation.error.message));
|
|
160
|
+
} else {
|
|
161
|
+
// Operation not started yet - this shouldn't happen
|
|
162
|
+
results.set(opId, createVibeError(`Operation ${opId} has not started`));
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return results;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Awaits all pending async operations in the state.
|
|
171
|
+
* Used at block boundaries and before sync instructions that need async results.
|
|
172
|
+
*/
|
|
173
|
+
export async function awaitAllPending(
|
|
174
|
+
state: RuntimeState
|
|
175
|
+
): Promise<Map<string, VibeValue>> {
|
|
176
|
+
const pendingIds = Array.from(state.pendingAsyncIds);
|
|
177
|
+
return awaitOperations(pendingIds, state.asyncOperations);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Awaits async operations that a variable depends on.
|
|
182
|
+
* Used for implicit await when a variable's value is needed.
|
|
183
|
+
*/
|
|
184
|
+
export async function awaitVariable(
|
|
185
|
+
variableName: string,
|
|
186
|
+
state: RuntimeState
|
|
187
|
+
): Promise<VibeValue | null> {
|
|
188
|
+
const opId = state.asyncVarToOpId.get(variableName);
|
|
189
|
+
if (!opId) {
|
|
190
|
+
return null; // Not an async variable
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (!state.pendingAsyncIds.has(opId)) {
|
|
194
|
+
// Already completed
|
|
195
|
+
const operation = state.asyncOperations.get(opId);
|
|
196
|
+
return operation?.result ?? null;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const results = await awaitOperations([opId], state.asyncOperations);
|
|
200
|
+
return results.get(opId) ?? null;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Generates a unique async operation ID.
|
|
205
|
+
* Note: Caller is responsible for incrementing state.nextAsyncId.
|
|
206
|
+
*/
|
|
207
|
+
export function generateAsyncId(state: RuntimeState): string {
|
|
208
|
+
return `async-${String(state.nextAsyncId).padStart(6, '0')}`;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Registers a new async operation in the state.
|
|
213
|
+
*/
|
|
214
|
+
export function registerAsyncOperation(
|
|
215
|
+
state: RuntimeState,
|
|
216
|
+
operation: Omit<AsyncOperation, 'id' | 'waveId'>
|
|
217
|
+
): AsyncOperation {
|
|
218
|
+
const id = generateAsyncId(state);
|
|
219
|
+
const fullOperation: AsyncOperation = {
|
|
220
|
+
...operation,
|
|
221
|
+
id,
|
|
222
|
+
waveId: state.currentWaveId,
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
state.asyncOperations.set(id, fullOperation);
|
|
226
|
+
state.pendingAsyncIds.add(id);
|
|
227
|
+
|
|
228
|
+
if (operation.variableName) {
|
|
229
|
+
state.asyncVarToOpId.set(operation.variableName, id);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
return fullOperation;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Marks an async operation as complete and removes from pending.
|
|
237
|
+
*/
|
|
238
|
+
export function completeAsyncOperation(
|
|
239
|
+
state: RuntimeState,
|
|
240
|
+
operationId: string,
|
|
241
|
+
result: VibeValue
|
|
242
|
+
): void {
|
|
243
|
+
const operation = state.asyncOperations.get(operationId);
|
|
244
|
+
if (operation) {
|
|
245
|
+
operation.status = 'completed';
|
|
246
|
+
operation.endTime = Date.now();
|
|
247
|
+
operation.result = result;
|
|
248
|
+
}
|
|
249
|
+
state.pendingAsyncIds.delete(operationId);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Marks an async operation as failed and removes from pending.
|
|
254
|
+
*/
|
|
255
|
+
export function failAsyncOperation(
|
|
256
|
+
state: RuntimeState,
|
|
257
|
+
operationId: string,
|
|
258
|
+
error: VibeError
|
|
259
|
+
): void {
|
|
260
|
+
const operation = state.asyncOperations.get(operationId);
|
|
261
|
+
if (operation) {
|
|
262
|
+
operation.status = 'failed';
|
|
263
|
+
operation.endTime = Date.now();
|
|
264
|
+
operation.error = error;
|
|
265
|
+
}
|
|
266
|
+
state.pendingAsyncIds.delete(operationId);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Checks if any async operations are still pending.
|
|
271
|
+
*/
|
|
272
|
+
export function hasPendingAsync(state: RuntimeState): boolean {
|
|
273
|
+
return state.pendingAsyncIds.size > 0;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Gets the IDs of pending async operations that a list of variables depend on.
|
|
278
|
+
*/
|
|
279
|
+
export function getPendingDependencies(
|
|
280
|
+
variableNames: string[],
|
|
281
|
+
state: RuntimeState
|
|
282
|
+
): string[] {
|
|
283
|
+
const deps: string[] = [];
|
|
284
|
+
|
|
285
|
+
for (const varName of variableNames) {
|
|
286
|
+
const opId = state.asyncVarToOpId.get(varName);
|
|
287
|
+
if (opId && state.pendingAsyncIds.has(opId)) {
|
|
288
|
+
deps.push(opId);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
return deps;
|
|
293
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Async execution module for parallel operations.
|
|
3
|
+
*
|
|
4
|
+
* This module provides:
|
|
5
|
+
* - Dependency detection for async operations
|
|
6
|
+
* - Wave-based execution with throttling
|
|
7
|
+
* - Async operation scheduling helpers
|
|
8
|
+
* - Utilities for managing async operation state
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
// Dependency detection
|
|
12
|
+
export {
|
|
13
|
+
getReferencedVariables,
|
|
14
|
+
detectAsyncDependencies,
|
|
15
|
+
buildExecutionWaves,
|
|
16
|
+
getInstructionDependencies,
|
|
17
|
+
} from './dependencies';
|
|
18
|
+
|
|
19
|
+
// Execution engine
|
|
20
|
+
export {
|
|
21
|
+
executeWave,
|
|
22
|
+
awaitOperations,
|
|
23
|
+
awaitAllPending,
|
|
24
|
+
awaitVariable,
|
|
25
|
+
generateAsyncId,
|
|
26
|
+
registerAsyncOperation,
|
|
27
|
+
completeAsyncOperation,
|
|
28
|
+
failAsyncOperation,
|
|
29
|
+
hasPendingAsync,
|
|
30
|
+
getPendingDependencies,
|
|
31
|
+
} from './executor';
|
|
32
|
+
|
|
33
|
+
// Scheduling helpers
|
|
34
|
+
export {
|
|
35
|
+
scheduleAsyncOperation,
|
|
36
|
+
clearAsyncContext,
|
|
37
|
+
isInAsyncContext,
|
|
38
|
+
createAsyncVibeError,
|
|
39
|
+
startAsyncOperation,
|
|
40
|
+
} from './scheduling';
|
|
41
|
+
|
|
42
|
+
export type { AsyncExecutionResult, AsyncOperationExecutor } from './executor';
|
|
43
|
+
export type { AsyncOperationDetails } from './scheduling';
|