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