@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,452 @@
|
|
|
1
|
+
// Module loading system for Vibe
|
|
2
|
+
// Handles loading both TypeScript and Vibe imports with cycle detection
|
|
3
|
+
|
|
4
|
+
import * as AST from '../ast';
|
|
5
|
+
import { parse } from '../parser/parse';
|
|
6
|
+
import type { RuntimeState, TsModule, VibeModule, ExportedItem, VibeValue } from './types';
|
|
7
|
+
import { createVibeValue } from './types';
|
|
8
|
+
import { resolve, dirname, join } from 'path';
|
|
9
|
+
|
|
10
|
+
// Map system module names to their implementation files
|
|
11
|
+
const SYSTEM_MODULES: Record<string, string> = {
|
|
12
|
+
'system': join(__dirname, 'stdlib', 'index.ts'),
|
|
13
|
+
'system/tools': join(__dirname, 'stdlib', 'tools', 'index.ts'),
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
// Blocked system module paths - these cannot be imported
|
|
17
|
+
// Core functions (print, env) are auto-imported and cannot be explicitly imported
|
|
18
|
+
const BLOCKED_SYSTEM_MODULES = new Set([
|
|
19
|
+
'system/core',
|
|
20
|
+
'core',
|
|
21
|
+
]);
|
|
22
|
+
|
|
23
|
+
// Check if an import source is a system module
|
|
24
|
+
function isSystemModule(source: string): boolean {
|
|
25
|
+
return source === 'system' || source.startsWith('system/');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Resolve a module path, handling system modules specially
|
|
29
|
+
function resolveModulePath(source: string, basePath: string): string {
|
|
30
|
+
// Block certain system module paths that should not be importable
|
|
31
|
+
if (BLOCKED_SYSTEM_MODULES.has(source)) {
|
|
32
|
+
throw new Error(
|
|
33
|
+
`Import error: '${source}' cannot be imported. Core functions like print() and env() are auto-imported and available without explicit import.`
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (isSystemModule(source)) {
|
|
38
|
+
const systemPath = SYSTEM_MODULES[source];
|
|
39
|
+
if (!systemPath) {
|
|
40
|
+
throw new Error(`Unknown system module: '${source}'`);
|
|
41
|
+
}
|
|
42
|
+
return systemPath;
|
|
43
|
+
}
|
|
44
|
+
return resolve(dirname(basePath), source);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Track modules currently being loaded (for cycle detection)
|
|
48
|
+
type LoadingSet = Set<string>;
|
|
49
|
+
|
|
50
|
+
// Load all imports from a program and return updated state
|
|
51
|
+
export async function loadImports(
|
|
52
|
+
state: RuntimeState,
|
|
53
|
+
basePath: string
|
|
54
|
+
): Promise<RuntimeState> {
|
|
55
|
+
// Start with empty loading set for cycle detection
|
|
56
|
+
return loadImportsRecursive(state, basePath, new Set());
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Internal recursive loader with cycle detection
|
|
60
|
+
async function loadImportsRecursive(
|
|
61
|
+
state: RuntimeState,
|
|
62
|
+
basePath: string,
|
|
63
|
+
loading: LoadingSet
|
|
64
|
+
): Promise<RuntimeState> {
|
|
65
|
+
const imports = state.program.body.filter(
|
|
66
|
+
(stmt): stmt is AST.ImportDeclaration => stmt.type === 'ImportDeclaration'
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
let newState = state;
|
|
70
|
+
|
|
71
|
+
for (const importDecl of imports) {
|
|
72
|
+
if (importDecl.sourceType === 'ts') {
|
|
73
|
+
newState = await loadTsModule(newState, importDecl, basePath);
|
|
74
|
+
} else {
|
|
75
|
+
newState = await loadVibeModuleRecursive(newState, importDecl, basePath, loading);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return newState;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Load a TypeScript module using Bun's import()
|
|
83
|
+
async function loadTsModule(
|
|
84
|
+
state: RuntimeState,
|
|
85
|
+
importDecl: AST.ImportDeclaration,
|
|
86
|
+
basePath: string
|
|
87
|
+
): Promise<RuntimeState> {
|
|
88
|
+
const modulePath = resolveModulePath(importDecl.source, basePath);
|
|
89
|
+
|
|
90
|
+
// Check if already loaded
|
|
91
|
+
if (state.tsModules[modulePath]) {
|
|
92
|
+
// Register the imported names (allows shared imports across modules)
|
|
93
|
+
return registerImportedNames(state, importDecl, modulePath, 'ts');
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Load the module using Bun's import()
|
|
97
|
+
const module = await import(modulePath);
|
|
98
|
+
|
|
99
|
+
// Extract the requested exports
|
|
100
|
+
const exports: Record<string, unknown> = {};
|
|
101
|
+
for (const spec of importDecl.specifiers) {
|
|
102
|
+
if (!(spec.imported in module)) {
|
|
103
|
+
throw new Error(
|
|
104
|
+
`Import error: '${spec.imported}' is not exported from '${importDecl.source}'`
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
exports[spec.local] = module[spec.imported];
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const tsModule: TsModule = { exports };
|
|
111
|
+
|
|
112
|
+
const newState: RuntimeState = {
|
|
113
|
+
...state,
|
|
114
|
+
tsModules: {
|
|
115
|
+
...state.tsModules,
|
|
116
|
+
[modulePath]: tsModule,
|
|
117
|
+
},
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
// Register the imported names
|
|
121
|
+
return registerImportedNames(newState, importDecl, modulePath, 'ts');
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Load a Vibe module with recursive import loading and cycle detection
|
|
125
|
+
async function loadVibeModuleRecursive(
|
|
126
|
+
state: RuntimeState,
|
|
127
|
+
importDecl: AST.ImportDeclaration,
|
|
128
|
+
basePath: string,
|
|
129
|
+
loading: LoadingSet
|
|
130
|
+
): Promise<RuntimeState> {
|
|
131
|
+
const modulePath = resolve(dirname(basePath), importDecl.source);
|
|
132
|
+
|
|
133
|
+
// Check for import cycle FIRST (before checking if loaded)
|
|
134
|
+
// This catches cycles even if the module was partially loaded
|
|
135
|
+
if (loading.has(modulePath)) {
|
|
136
|
+
// Build cycle path for error message
|
|
137
|
+
const cyclePath = [...loading, modulePath].join(' -> ');
|
|
138
|
+
throw new Error(
|
|
139
|
+
`Import error: Circular dependency detected: ${cyclePath}`
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Check if already loaded
|
|
144
|
+
if (state.vibeModules[modulePath]) {
|
|
145
|
+
// Register the imported names (allows shared imports across modules)
|
|
146
|
+
return registerImportedNames(state, importDecl, modulePath, 'vibe');
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Mark this module as being loaded
|
|
150
|
+
const newLoading = new Set(loading);
|
|
151
|
+
newLoading.add(modulePath);
|
|
152
|
+
|
|
153
|
+
// Read and parse the .vibe file
|
|
154
|
+
const source = await Bun.file(modulePath).text();
|
|
155
|
+
// Use import source path for error messages (relative path as written in import)
|
|
156
|
+
const program = parse(source, { file: importDecl.source });
|
|
157
|
+
|
|
158
|
+
// Extract exports from the program
|
|
159
|
+
const exports = extractVibeExports(program);
|
|
160
|
+
|
|
161
|
+
// Verify all requested imports exist
|
|
162
|
+
for (const spec of importDecl.specifiers) {
|
|
163
|
+
if (!(spec.imported in exports)) {
|
|
164
|
+
throw new Error(
|
|
165
|
+
`Import error: '${spec.imported}' is not exported from '${importDecl.source}'`
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Extract all module-level variables (for module scope isolation)
|
|
171
|
+
const globals = extractModuleGlobals(program);
|
|
172
|
+
|
|
173
|
+
const vibeModule: VibeModule = { exports, program, globals };
|
|
174
|
+
|
|
175
|
+
let newState: RuntimeState = {
|
|
176
|
+
...state,
|
|
177
|
+
vibeModules: {
|
|
178
|
+
...state.vibeModules,
|
|
179
|
+
[modulePath]: vibeModule,
|
|
180
|
+
},
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
// Recursively load this module's imports (these are NOT the main program's imports)
|
|
184
|
+
const moduleImports = program.body.filter(
|
|
185
|
+
(stmt): stmt is AST.ImportDeclaration => stmt.type === 'ImportDeclaration'
|
|
186
|
+
);
|
|
187
|
+
|
|
188
|
+
for (const nestedImport of moduleImports) {
|
|
189
|
+
if (nestedImport.sourceType === 'ts') {
|
|
190
|
+
newState = await loadTsModule(newState, nestedImport, modulePath);
|
|
191
|
+
} else {
|
|
192
|
+
newState = await loadVibeModuleRecursive(newState, nestedImport, modulePath, newLoading);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// Register the imported names
|
|
197
|
+
return registerImportedNames(newState, importDecl, modulePath, 'vibe');
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Extract exported items from a Vibe program
|
|
201
|
+
function extractVibeExports(program: AST.Program): Record<string, ExportedItem> {
|
|
202
|
+
const exports: Record<string, ExportedItem> = {};
|
|
203
|
+
|
|
204
|
+
for (const stmt of program.body) {
|
|
205
|
+
if (stmt.type !== 'ExportDeclaration') continue;
|
|
206
|
+
|
|
207
|
+
const decl = stmt.declaration;
|
|
208
|
+
|
|
209
|
+
switch (decl.type) {
|
|
210
|
+
case 'FunctionDeclaration':
|
|
211
|
+
exports[decl.name] = { kind: 'function', declaration: decl };
|
|
212
|
+
break;
|
|
213
|
+
|
|
214
|
+
case 'LetDeclaration':
|
|
215
|
+
exports[decl.name] = {
|
|
216
|
+
kind: 'variable',
|
|
217
|
+
name: decl.name,
|
|
218
|
+
value: null, // Will be evaluated when module runs
|
|
219
|
+
isConst: false,
|
|
220
|
+
typeAnnotation: decl.typeAnnotation,
|
|
221
|
+
};
|
|
222
|
+
break;
|
|
223
|
+
|
|
224
|
+
case 'ConstDeclaration':
|
|
225
|
+
exports[decl.name] = {
|
|
226
|
+
kind: 'variable',
|
|
227
|
+
name: decl.name,
|
|
228
|
+
value: null, // Will be evaluated when module runs
|
|
229
|
+
isConst: true,
|
|
230
|
+
typeAnnotation: decl.typeAnnotation,
|
|
231
|
+
};
|
|
232
|
+
break;
|
|
233
|
+
|
|
234
|
+
case 'ModelDeclaration':
|
|
235
|
+
exports[decl.name] = { kind: 'model', declaration: decl };
|
|
236
|
+
break;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
return exports;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// Extract all module-level variables (both exported and non-exported)
|
|
244
|
+
// These form the module's isolated global scope
|
|
245
|
+
function extractModuleGlobals(program: AST.Program): Record<string, VibeValue> {
|
|
246
|
+
const globals: Record<string, VibeValue> = {};
|
|
247
|
+
|
|
248
|
+
for (const stmt of program.body) {
|
|
249
|
+
// Handle direct declarations
|
|
250
|
+
if (stmt.type === 'LetDeclaration') {
|
|
251
|
+
globals[stmt.name] = createVibeValue(evaluateSimpleLiteral(stmt.initializer), {
|
|
252
|
+
isConst: false,
|
|
253
|
+
typeAnnotation: stmt.typeAnnotation,
|
|
254
|
+
});
|
|
255
|
+
} else if (stmt.type === 'ConstDeclaration') {
|
|
256
|
+
globals[stmt.name] = createVibeValue(evaluateSimpleLiteral(stmt.initializer), {
|
|
257
|
+
isConst: true,
|
|
258
|
+
typeAnnotation: stmt.typeAnnotation,
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
// Handle exported declarations
|
|
262
|
+
else if (stmt.type === 'ExportDeclaration') {
|
|
263
|
+
const decl = stmt.declaration;
|
|
264
|
+
if (decl.type === 'LetDeclaration') {
|
|
265
|
+
globals[decl.name] = createVibeValue(evaluateSimpleLiteral(decl.initializer), {
|
|
266
|
+
isConst: false,
|
|
267
|
+
typeAnnotation: decl.typeAnnotation,
|
|
268
|
+
});
|
|
269
|
+
} else if (decl.type === 'ConstDeclaration') {
|
|
270
|
+
globals[decl.name] = createVibeValue(evaluateSimpleLiteral(decl.initializer), {
|
|
271
|
+
isConst: true,
|
|
272
|
+
typeAnnotation: decl.typeAnnotation,
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
// Note: Functions and models are accessed via exports, not globals
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
return globals;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// Evaluate simple literal expressions for module initialization
|
|
283
|
+
// Complex expressions will be null (would need full runtime evaluation)
|
|
284
|
+
function evaluateSimpleLiteral(expr: AST.Expression | null): unknown {
|
|
285
|
+
if (!expr) return null;
|
|
286
|
+
|
|
287
|
+
switch (expr.type) {
|
|
288
|
+
case 'StringLiteral':
|
|
289
|
+
return expr.value;
|
|
290
|
+
case 'NumberLiteral':
|
|
291
|
+
return expr.value;
|
|
292
|
+
case 'BooleanLiteral':
|
|
293
|
+
return expr.value;
|
|
294
|
+
case 'NullLiteral':
|
|
295
|
+
return null;
|
|
296
|
+
case 'ArrayLiteral':
|
|
297
|
+
return expr.elements.map(e => evaluateSimpleLiteral(e));
|
|
298
|
+
case 'ObjectLiteral':
|
|
299
|
+
const obj: Record<string, unknown> = {};
|
|
300
|
+
for (const prop of expr.properties) {
|
|
301
|
+
obj[prop.key] = evaluateSimpleLiteral(prop.value);
|
|
302
|
+
}
|
|
303
|
+
return obj;
|
|
304
|
+
default:
|
|
305
|
+
// Complex expression - can't evaluate statically
|
|
306
|
+
return null;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// Register imported names in the state for lookup
|
|
311
|
+
function registerImportedNames(
|
|
312
|
+
state: RuntimeState,
|
|
313
|
+
importDecl: AST.ImportDeclaration,
|
|
314
|
+
modulePath: string,
|
|
315
|
+
sourceType: 'ts' | 'vibe'
|
|
316
|
+
): RuntimeState {
|
|
317
|
+
const newImportedNames = { ...state.importedNames };
|
|
318
|
+
|
|
319
|
+
for (const spec of importDecl.specifiers) {
|
|
320
|
+
// Check for name collision
|
|
321
|
+
if (newImportedNames[spec.local]) {
|
|
322
|
+
const existing = newImportedNames[spec.local];
|
|
323
|
+
// Allow same import from same source (nested modules can share imports)
|
|
324
|
+
if (existing.source === modulePath && existing.sourceType === sourceType) {
|
|
325
|
+
continue; // Already registered, skip
|
|
326
|
+
}
|
|
327
|
+
throw new Error(
|
|
328
|
+
`Import error: '${spec.local}' is already imported from '${existing.source}'`
|
|
329
|
+
);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
newImportedNames[spec.local] = {
|
|
333
|
+
source: modulePath,
|
|
334
|
+
sourceType,
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
return {
|
|
339
|
+
...state,
|
|
340
|
+
importedNames: newImportedNames,
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// Get an imported value by name
|
|
345
|
+
export function getImportedValue(
|
|
346
|
+
state: RuntimeState,
|
|
347
|
+
name: string
|
|
348
|
+
): unknown | undefined {
|
|
349
|
+
const importInfo = state.importedNames[name];
|
|
350
|
+
if (!importInfo) return undefined;
|
|
351
|
+
|
|
352
|
+
if (importInfo.sourceType === 'ts') {
|
|
353
|
+
const module = state.tsModules[importInfo.source];
|
|
354
|
+
return module?.exports[name];
|
|
355
|
+
} else {
|
|
356
|
+
const module = state.vibeModules[importInfo.source];
|
|
357
|
+
const exported = module?.exports[name];
|
|
358
|
+
if (!exported) return undefined;
|
|
359
|
+
|
|
360
|
+
if (exported.kind === 'function') {
|
|
361
|
+
// Return a marker that this is an imported Vibe function
|
|
362
|
+
return { __vibeImportedFunction: true, name, source: importInfo.source };
|
|
363
|
+
} else if (exported.kind === 'variable') {
|
|
364
|
+
return exported.value;
|
|
365
|
+
} else if (exported.kind === 'model') {
|
|
366
|
+
// Return the model config as a value
|
|
367
|
+
return { __vibeModel: true, ...exported.declaration.config };
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
return undefined;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// Check if a name is an imported TypeScript function
|
|
375
|
+
export function isImportedTsFunction(
|
|
376
|
+
state: RuntimeState,
|
|
377
|
+
name: string
|
|
378
|
+
): boolean {
|
|
379
|
+
const importInfo = state.importedNames[name];
|
|
380
|
+
if (!importInfo || importInfo.sourceType !== 'ts') return false;
|
|
381
|
+
|
|
382
|
+
const module = state.tsModules[importInfo.source];
|
|
383
|
+
const value = module?.exports[name];
|
|
384
|
+
return typeof value === 'function';
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// Check if a name is an imported Vibe function
|
|
388
|
+
export function isImportedVibeFunction(
|
|
389
|
+
state: RuntimeState,
|
|
390
|
+
name: string
|
|
391
|
+
): boolean {
|
|
392
|
+
const importInfo = state.importedNames[name];
|
|
393
|
+
if (!importInfo || importInfo.sourceType !== 'vibe') return false;
|
|
394
|
+
|
|
395
|
+
const module = state.vibeModules[importInfo.source];
|
|
396
|
+
const exported = module?.exports[name];
|
|
397
|
+
return exported?.kind === 'function';
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
// Get an imported Vibe function declaration
|
|
401
|
+
export function getImportedVibeFunction(
|
|
402
|
+
state: RuntimeState,
|
|
403
|
+
name: string
|
|
404
|
+
): AST.FunctionDeclaration | undefined {
|
|
405
|
+
const importInfo = state.importedNames[name];
|
|
406
|
+
if (!importInfo || importInfo.sourceType !== 'vibe') return undefined;
|
|
407
|
+
|
|
408
|
+
const module = state.vibeModules[importInfo.source];
|
|
409
|
+
const exported = module?.exports[name];
|
|
410
|
+
if (exported?.kind !== 'function') return undefined;
|
|
411
|
+
|
|
412
|
+
return exported.declaration;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
// Get an imported TypeScript function
|
|
416
|
+
export function getImportedTsFunction(
|
|
417
|
+
state: RuntimeState,
|
|
418
|
+
name: string
|
|
419
|
+
): ((...args: unknown[]) => unknown) | undefined {
|
|
420
|
+
const importInfo = state.importedNames[name];
|
|
421
|
+
if (!importInfo || importInfo.sourceType !== 'ts') return undefined;
|
|
422
|
+
|
|
423
|
+
const module = state.tsModules[importInfo.source];
|
|
424
|
+
const value = module?.exports[name];
|
|
425
|
+
if (typeof value !== 'function') return undefined;
|
|
426
|
+
|
|
427
|
+
return value as (...args: unknown[]) => unknown;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
// Get the module path for an imported Vibe function
|
|
431
|
+
// Returns undefined if not an imported Vibe function
|
|
432
|
+
export function getImportedVibeFunctionModulePath(
|
|
433
|
+
state: RuntimeState,
|
|
434
|
+
name: string
|
|
435
|
+
): string | undefined {
|
|
436
|
+
const importInfo = state.importedNames[name];
|
|
437
|
+
if (!importInfo || importInfo.sourceType !== 'vibe') return undefined;
|
|
438
|
+
|
|
439
|
+
const module = state.vibeModules[importInfo.source];
|
|
440
|
+
const exported = module?.exports[name];
|
|
441
|
+
if (exported?.kind !== 'function') return undefined;
|
|
442
|
+
|
|
443
|
+
return importInfo.source;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
// Get module globals by module path
|
|
447
|
+
export function getModuleGlobals(
|
|
448
|
+
state: RuntimeState,
|
|
449
|
+
modulePath: string
|
|
450
|
+
): Record<string, VibeValue> | undefined {
|
|
451
|
+
return state.vibeModules[modulePath]?.globals;
|
|
452
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import type { RuntimeState } from './types';
|
|
2
|
+
|
|
3
|
+
// Serialize runtime state to JSON string
|
|
4
|
+
export function serializeState(state: RuntimeState): string {
|
|
5
|
+
return JSON.stringify(state, null, 2);
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
// Deserialize runtime state from JSON string
|
|
9
|
+
export function deserializeState(json: string): RuntimeState {
|
|
10
|
+
const state = JSON.parse(json) as RuntimeState;
|
|
11
|
+
|
|
12
|
+
// Validate required fields
|
|
13
|
+
if (!state.status) {
|
|
14
|
+
throw new Error('Invalid state: missing status');
|
|
15
|
+
}
|
|
16
|
+
if (!state.program) {
|
|
17
|
+
throw new Error('Invalid state: missing program');
|
|
18
|
+
}
|
|
19
|
+
if (!Array.isArray(state.callStack)) {
|
|
20
|
+
throw new Error('Invalid state: missing or invalid callStack');
|
|
21
|
+
}
|
|
22
|
+
if (!Array.isArray(state.instructionStack)) {
|
|
23
|
+
throw new Error('Invalid state: missing or invalid instructionStack');
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return state;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Create a deep clone of the state (useful for debugging/testing)
|
|
30
|
+
export function cloneState(state: RuntimeState): RuntimeState {
|
|
31
|
+
return JSON.parse(JSON.stringify(state)) as RuntimeState;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Create a true deep clone of the state, properly handling Maps and Sets.
|
|
36
|
+
* Handles non-cloneable objects like Promises by omitting them.
|
|
37
|
+
*/
|
|
38
|
+
export function deepCloneState(state: RuntimeState): RuntimeState {
|
|
39
|
+
// Create a version of the state without non-cloneable objects (Promises)
|
|
40
|
+
// We need to strip promises from asyncOperations before cloning
|
|
41
|
+
const cleanAsyncOps = new Map<string, unknown>();
|
|
42
|
+
for (const [id, op] of state.asyncOperations) {
|
|
43
|
+
// Clone operation without the promise field
|
|
44
|
+
const { promise, ...rest } = op;
|
|
45
|
+
cleanAsyncOps.set(id, rest);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Create a clean state object for cloning
|
|
49
|
+
const stateForCloning = {
|
|
50
|
+
...state,
|
|
51
|
+
asyncOperations: cleanAsyncOps,
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
// Now we can safely use structuredClone
|
|
55
|
+
const cloned = structuredClone(stateForCloning);
|
|
56
|
+
|
|
57
|
+
// Restore proper typing for asyncOperations Map
|
|
58
|
+
return {
|
|
59
|
+
...cloned,
|
|
60
|
+
asyncOperations: cloned.asyncOperations as RuntimeState['asyncOperations'],
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Get a summary of the current state (for debugging)
|
|
65
|
+
export function getStateSummary(state: RuntimeState): {
|
|
66
|
+
status: string;
|
|
67
|
+
frameCount: number;
|
|
68
|
+
currentFrame: string;
|
|
69
|
+
instructionCount: number;
|
|
70
|
+
nextInstruction: string | null;
|
|
71
|
+
variables: Record<string, unknown>;
|
|
72
|
+
lastResult: unknown;
|
|
73
|
+
pendingAsyncCount: number;
|
|
74
|
+
} {
|
|
75
|
+
const currentFrame = state.callStack[state.callStack.length - 1];
|
|
76
|
+
const nextInstruction = state.instructionStack[0];
|
|
77
|
+
|
|
78
|
+
const variables: Record<string, unknown> = {};
|
|
79
|
+
if (currentFrame) {
|
|
80
|
+
for (const [name, variable] of Object.entries(currentFrame.locals)) {
|
|
81
|
+
// Check if this is a pending async variable
|
|
82
|
+
if (variable.asyncOperationId) {
|
|
83
|
+
const op = state.asyncOperations.get(variable.asyncOperationId);
|
|
84
|
+
if (op && (op.status === 'pending' || op.status === 'running')) {
|
|
85
|
+
variables[name] = `[pending: ${op.operationType}]`;
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
variables[name] = variable.value;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return {
|
|
94
|
+
status: state.status,
|
|
95
|
+
frameCount: state.callStack.length,
|
|
96
|
+
currentFrame: currentFrame?.name ?? 'none',
|
|
97
|
+
instructionCount: state.instructionStack.length,
|
|
98
|
+
nextInstruction: nextInstruction ? nextInstruction.op : null,
|
|
99
|
+
variables,
|
|
100
|
+
lastResult: state.lastResult,
|
|
101
|
+
pendingAsyncCount: state.pendingAsyncIds.size,
|
|
102
|
+
};
|
|
103
|
+
}
|