@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,163 @@
|
|
|
1
|
+
import { describe, expect, test } from 'bun:test';
|
|
2
|
+
import { parse } from '../../parser/parse';
|
|
3
|
+
import { analyze } from '../index';
|
|
4
|
+
|
|
5
|
+
describe('Semantic Errors - Undefined Variables', () => {
|
|
6
|
+
// ============================================================================
|
|
7
|
+
// Basic undefined variable usage
|
|
8
|
+
// ============================================================================
|
|
9
|
+
|
|
10
|
+
test('using undefined variable in expression', () => {
|
|
11
|
+
const ast = parse(`
|
|
12
|
+
let x = undefinedVar
|
|
13
|
+
`);
|
|
14
|
+
const errors = analyze(ast);
|
|
15
|
+
expect(errors.length).toBe(1);
|
|
16
|
+
expect(errors[0].message).toBe("'undefinedVar' is not defined");
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
test('using undefined variable in function call', () => {
|
|
20
|
+
const ast = parse(`
|
|
21
|
+
let x = unknownFunc()
|
|
22
|
+
`);
|
|
23
|
+
const errors = analyze(ast);
|
|
24
|
+
expect(errors.length).toBe(1);
|
|
25
|
+
expect(errors[0].message).toBe("'unknownFunc' is not defined");
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
test('using undefined variable as function argument', () => {
|
|
29
|
+
const ast = parse(`
|
|
30
|
+
function greet(name: text): text {
|
|
31
|
+
return name
|
|
32
|
+
}
|
|
33
|
+
let x = greet(unknownArg)
|
|
34
|
+
`);
|
|
35
|
+
const errors = analyze(ast);
|
|
36
|
+
expect(errors.length).toBe(1);
|
|
37
|
+
expect(errors[0].message).toBe("'unknownArg' is not defined");
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
// ============================================================================
|
|
41
|
+
// Undefined in vibe expressions
|
|
42
|
+
// ============================================================================
|
|
43
|
+
|
|
44
|
+
test('using undefined variable as vibe prompt', () => {
|
|
45
|
+
const ast = parse(`
|
|
46
|
+
model myModel = { name: "test", apiKey: "key", url: "http://test" }
|
|
47
|
+
let x: text = vibe undefinedPrompt myModel default
|
|
48
|
+
`);
|
|
49
|
+
const errors = analyze(ast);
|
|
50
|
+
expect(errors.length).toBe(1);
|
|
51
|
+
expect(errors[0].message).toBe("'undefinedPrompt' is not defined");
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
test('using undefined model in vibe expression', () => {
|
|
55
|
+
const ast = parse(`
|
|
56
|
+
let x: text = vibe "prompt" undefinedModel default
|
|
57
|
+
`);
|
|
58
|
+
const errors = analyze(ast);
|
|
59
|
+
expect(errors.length).toBe(1);
|
|
60
|
+
expect(errors[0].message).toBe("'undefinedModel' is not defined");
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test('using undefined context variable in vibe expression', () => {
|
|
64
|
+
const ast = parse(`
|
|
65
|
+
model myModel = { name: "test", apiKey: "key", url: "http://test" }
|
|
66
|
+
let x: text = vibe "prompt" myModel undefinedContext
|
|
67
|
+
`);
|
|
68
|
+
const errors = analyze(ast);
|
|
69
|
+
expect(errors.length).toBe(1);
|
|
70
|
+
expect(errors[0].message).toBe("'undefinedContext' is not defined");
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
// ============================================================================
|
|
74
|
+
// Undefined in if statements
|
|
75
|
+
// ============================================================================
|
|
76
|
+
|
|
77
|
+
test('using undefined variable in if condition', () => {
|
|
78
|
+
const ast = parse(`
|
|
79
|
+
if undefinedCond {
|
|
80
|
+
let x = "yes"
|
|
81
|
+
}
|
|
82
|
+
`);
|
|
83
|
+
const errors = analyze(ast);
|
|
84
|
+
expect(errors.length).toBe(1);
|
|
85
|
+
expect(errors[0].message).toBe("'undefinedCond' is not defined");
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
// ============================================================================
|
|
89
|
+
// Undefined in return statements
|
|
90
|
+
// ============================================================================
|
|
91
|
+
|
|
92
|
+
test('returning undefined variable', () => {
|
|
93
|
+
const ast = parse(`
|
|
94
|
+
function test() {
|
|
95
|
+
return undefinedReturn
|
|
96
|
+
}
|
|
97
|
+
`);
|
|
98
|
+
const errors = analyze(ast);
|
|
99
|
+
expect(errors.length).toBe(1);
|
|
100
|
+
expect(errors[0].message).toBe("'undefinedReturn' is not defined");
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
// ============================================================================
|
|
104
|
+
// Multiple undefined variables
|
|
105
|
+
// ============================================================================
|
|
106
|
+
|
|
107
|
+
test('multiple undefined variables', () => {
|
|
108
|
+
const ast = parse(`
|
|
109
|
+
let a = undefined1
|
|
110
|
+
let b = undefined2
|
|
111
|
+
`);
|
|
112
|
+
const errors = analyze(ast);
|
|
113
|
+
expect(errors.length).toBe(2);
|
|
114
|
+
expect(errors[0].message).toBe("'undefined1' is not defined");
|
|
115
|
+
expect(errors[1].message).toBe("'undefined2' is not defined");
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
// ============================================================================
|
|
119
|
+
// Variable used before declaration
|
|
120
|
+
// ============================================================================
|
|
121
|
+
|
|
122
|
+
test('variable used before declaration', () => {
|
|
123
|
+
const ast = parse(`
|
|
124
|
+
let x = y
|
|
125
|
+
let y = "hello"
|
|
126
|
+
`);
|
|
127
|
+
const errors = analyze(ast);
|
|
128
|
+
expect(errors.length).toBe(1);
|
|
129
|
+
expect(errors[0].message).toBe("'y' is not defined");
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
// ============================================================================
|
|
133
|
+
// Valid cases (should have no errors)
|
|
134
|
+
// ============================================================================
|
|
135
|
+
|
|
136
|
+
test('using declared variable', () => {
|
|
137
|
+
const ast = parse(`
|
|
138
|
+
let x = "hello"
|
|
139
|
+
let y = x
|
|
140
|
+
`);
|
|
141
|
+
const errors = analyze(ast);
|
|
142
|
+
expect(errors.length).toBe(0);
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
test('using function parameter', () => {
|
|
146
|
+
const ast = parse(`
|
|
147
|
+
function greet(name: text): text {
|
|
148
|
+
return name
|
|
149
|
+
}
|
|
150
|
+
`);
|
|
151
|
+
const errors = analyze(ast);
|
|
152
|
+
expect(errors.length).toBe(0);
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
test('using model in vibe expression', () => {
|
|
156
|
+
const ast = parse(`
|
|
157
|
+
model myModel = { name: "test", apiKey: "key", url: "http://test" }
|
|
158
|
+
let x: text = vibe "prompt" myModel default
|
|
159
|
+
`);
|
|
160
|
+
const errors = analyze(ast);
|
|
161
|
+
expect(errors.length).toBe(0);
|
|
162
|
+
});
|
|
163
|
+
});
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TypeScript Block Type Checker
|
|
3
|
+
*
|
|
4
|
+
* Type-checks ts() block bodies by compiling virtual TypeScript in memory.
|
|
5
|
+
* This catches type errors at compile time before the Vibe program runs.
|
|
6
|
+
*/
|
|
7
|
+
import ts from 'typescript';
|
|
8
|
+
import type { SourceLocation } from '../errors';
|
|
9
|
+
import { vibeTypeToTs, tsTypeToVibe } from './ts-types';
|
|
10
|
+
|
|
11
|
+
export interface TsBlockError {
|
|
12
|
+
message: string;
|
|
13
|
+
location: SourceLocation;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Parameter info for ts() block type checking.
|
|
18
|
+
*/
|
|
19
|
+
export interface TsBlockParam {
|
|
20
|
+
name: string;
|
|
21
|
+
vibeType: string | null;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Create compiler options for virtual TypeScript compilation.
|
|
26
|
+
*/
|
|
27
|
+
function createCompilerOptions(): ts.CompilerOptions {
|
|
28
|
+
return {
|
|
29
|
+
target: ts.ScriptTarget.ESNext,
|
|
30
|
+
module: ts.ModuleKind.ESNext,
|
|
31
|
+
noEmit: true,
|
|
32
|
+
strict: false, // Less strict to avoid spurious errors
|
|
33
|
+
skipLibCheck: true,
|
|
34
|
+
noImplicitAny: false, // Allow implicit any for external references
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Create a virtual compiler host that serves in-memory TypeScript code.
|
|
40
|
+
*/
|
|
41
|
+
function createVirtualCompilerHost(
|
|
42
|
+
fileName: string,
|
|
43
|
+
code: string,
|
|
44
|
+
options: ts.CompilerOptions
|
|
45
|
+
): { host: ts.CompilerHost; sourceFile: ts.SourceFile } {
|
|
46
|
+
const sourceFile = ts.createSourceFile(
|
|
47
|
+
fileName,
|
|
48
|
+
code,
|
|
49
|
+
ts.ScriptTarget.ESNext,
|
|
50
|
+
true
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
const defaultHost = ts.createCompilerHost(options);
|
|
54
|
+
const host: ts.CompilerHost = {
|
|
55
|
+
...defaultHost,
|
|
56
|
+
getSourceFile: (name, languageVersion) => {
|
|
57
|
+
if (name === fileName) {
|
|
58
|
+
return sourceFile;
|
|
59
|
+
}
|
|
60
|
+
return defaultHost.getSourceFile(name, languageVersion);
|
|
61
|
+
},
|
|
62
|
+
fileExists: (name) => {
|
|
63
|
+
if (name === fileName) return true;
|
|
64
|
+
return defaultHost.fileExists(name);
|
|
65
|
+
},
|
|
66
|
+
readFile: (name) => {
|
|
67
|
+
if (name === fileName) return code;
|
|
68
|
+
return defaultHost.readFile(name);
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
return { host, sourceFile };
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Generate virtual TypeScript function code from ts() block params and body.
|
|
77
|
+
*/
|
|
78
|
+
function generateVirtualCode(params: TsBlockParam[], body: string): string {
|
|
79
|
+
const paramList = params
|
|
80
|
+
.map((p) => `${p.name}: ${vibeTypeToTs(p.vibeType)}`)
|
|
81
|
+
.join(', ');
|
|
82
|
+
return `function __tsBlock(${paramList}) {\n${body}\n}`;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Type-check a ts() block body with the given parameters.
|
|
87
|
+
*
|
|
88
|
+
* Only checks for type errors related to the provided parameters.
|
|
89
|
+
* External function calls and variables are not checked since we don't
|
|
90
|
+
* have context about what's available in the runtime environment.
|
|
91
|
+
*
|
|
92
|
+
* @param params - Array of parameter names and their Vibe types
|
|
93
|
+
* @param body - The TypeScript code inside the ts() block
|
|
94
|
+
* @param blockLocation - Location of the ts() block in the Vibe source
|
|
95
|
+
* @returns Array of type errors found
|
|
96
|
+
*/
|
|
97
|
+
export function checkTsBlockTypes(
|
|
98
|
+
params: TsBlockParam[],
|
|
99
|
+
body: string,
|
|
100
|
+
blockLocation: SourceLocation
|
|
101
|
+
): TsBlockError[] {
|
|
102
|
+
// Skip type checking if there are no typed parameters
|
|
103
|
+
// We can only meaningfully check types when we have parameter type info
|
|
104
|
+
const typedParams = params.filter(p => p.vibeType !== null);
|
|
105
|
+
if (typedParams.length === 0) {
|
|
106
|
+
return [];
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const virtualFileName = '__virtual_ts_block__.ts';
|
|
110
|
+
const virtualCode = generateVirtualCode(params, body);
|
|
111
|
+
const compilerOptions = createCompilerOptions();
|
|
112
|
+
const { host } = createVirtualCompilerHost(virtualFileName, virtualCode, compilerOptions);
|
|
113
|
+
|
|
114
|
+
// Create program and get diagnostics
|
|
115
|
+
const program = ts.createProgram([virtualFileName], compilerOptions, host);
|
|
116
|
+
const diagnostics = ts.getPreEmitDiagnostics(program);
|
|
117
|
+
|
|
118
|
+
// Filter diagnostics to only include type errors related to our parameters
|
|
119
|
+
// Error code 2304 is "Cannot find name" - we ignore these for external refs
|
|
120
|
+
const paramNames = new Set(params.map(p => p.name));
|
|
121
|
+
const relevantErrors: TsBlockError[] = [];
|
|
122
|
+
|
|
123
|
+
for (const d of diagnostics) {
|
|
124
|
+
if (d.category !== ts.DiagnosticCategory.Error) continue;
|
|
125
|
+
|
|
126
|
+
const message = ts.flattenDiagnosticMessageText(d.messageText, '\n');
|
|
127
|
+
|
|
128
|
+
// Skip "Cannot find name" errors for external functions/variables
|
|
129
|
+
// These are expected since ts() blocks can call external code
|
|
130
|
+
if (d.code === 2304) {
|
|
131
|
+
// Only report if it's about one of our parameters (shouldn't happen
|
|
132
|
+
// since we define them, but just in case)
|
|
133
|
+
const match = message.match(/Cannot find name '(\w+)'/);
|
|
134
|
+
if (match && !paramNames.has(match[1])) {
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Map location back to original block
|
|
140
|
+
let errorLocation = blockLocation;
|
|
141
|
+
if (d.file && d.start !== undefined) {
|
|
142
|
+
const { line } = d.file.getLineAndCharacterOfPosition(d.start);
|
|
143
|
+
const originalLine = line - 1;
|
|
144
|
+
if (originalLine >= 0) {
|
|
145
|
+
errorLocation = {
|
|
146
|
+
...blockLocation,
|
|
147
|
+
line: blockLocation.line + originalLine,
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
relevantErrors.push({
|
|
153
|
+
message: `TypeScript error in ts() block: ${message}`,
|
|
154
|
+
location: errorLocation,
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
return relevantErrors;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Infer the return type of a ts() block by compiling it and extracting the return type.
|
|
163
|
+
*
|
|
164
|
+
* @param params - Array of parameter names and their Vibe types
|
|
165
|
+
* @param body - The TypeScript code inside the ts() block
|
|
166
|
+
* @returns The inferred Vibe type, or null if it cannot be determined
|
|
167
|
+
*/
|
|
168
|
+
export function inferTsBlockReturnType(
|
|
169
|
+
params: TsBlockParam[],
|
|
170
|
+
body: string
|
|
171
|
+
): string | null {
|
|
172
|
+
const virtualFileName = '__virtual_ts_block_infer__.ts';
|
|
173
|
+
const virtualCode = generateVirtualCode(params, body);
|
|
174
|
+
const compilerOptions = createCompilerOptions();
|
|
175
|
+
const { host } = createVirtualCompilerHost(virtualFileName, virtualCode, compilerOptions);
|
|
176
|
+
|
|
177
|
+
const program = ts.createProgram([virtualFileName], compilerOptions, host);
|
|
178
|
+
const checker = program.getTypeChecker();
|
|
179
|
+
const source = program.getSourceFile(virtualFileName);
|
|
180
|
+
|
|
181
|
+
if (!source) return null;
|
|
182
|
+
|
|
183
|
+
// Find the function declaration and get its return type
|
|
184
|
+
let returnType: string | null = null;
|
|
185
|
+
|
|
186
|
+
function visit(node: ts.Node) {
|
|
187
|
+
if (returnType !== null) return;
|
|
188
|
+
|
|
189
|
+
if (ts.isFunctionDeclaration(node) && node.name?.text === '__tsBlock') {
|
|
190
|
+
const signature = checker.getSignatureFromDeclaration(node);
|
|
191
|
+
if (signature) {
|
|
192
|
+
const tsReturnType = checker.getReturnTypeOfSignature(signature);
|
|
193
|
+
const tsTypeString = checker.typeToString(tsReturnType);
|
|
194
|
+
returnType = tsTypeToVibe(tsTypeString);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
ts.forEachChild(node, visit);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
visit(source);
|
|
202
|
+
|
|
203
|
+
return returnType;
|
|
204
|
+
}
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TypeScript Signature Extraction Utility
|
|
3
|
+
*
|
|
4
|
+
* Extracts function signatures from TypeScript files at compile time
|
|
5
|
+
* for use in semantic analysis of Vibe programs.
|
|
6
|
+
*/
|
|
7
|
+
import ts from 'typescript';
|
|
8
|
+
|
|
9
|
+
export interface TsFunctionSignature {
|
|
10
|
+
name: string;
|
|
11
|
+
params: Array<{ name: string; tsType: string; optional: boolean }>;
|
|
12
|
+
returnType: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// Cache to avoid re-parsing same file
|
|
16
|
+
const signatureCache = new Map<string, Map<string, TsFunctionSignature>>();
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Extract a function signature from a TypeScript file.
|
|
20
|
+
*
|
|
21
|
+
* @param sourceFile - Path to the TypeScript source file
|
|
22
|
+
* @param funcName - Name of the function to extract
|
|
23
|
+
* @returns The function signature, or undefined if not found
|
|
24
|
+
*/
|
|
25
|
+
export function extractFunctionSignature(
|
|
26
|
+
sourceFile: string,
|
|
27
|
+
funcName: string
|
|
28
|
+
): TsFunctionSignature | undefined {
|
|
29
|
+
// Check cache first
|
|
30
|
+
const fileCache = signatureCache.get(sourceFile);
|
|
31
|
+
if (fileCache?.has(funcName)) {
|
|
32
|
+
return fileCache.get(funcName);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Create TypeScript program to parse the file
|
|
36
|
+
const program = ts.createProgram([sourceFile], {
|
|
37
|
+
target: ts.ScriptTarget.ESNext,
|
|
38
|
+
module: ts.ModuleKind.ESNext,
|
|
39
|
+
moduleResolution: ts.ModuleResolutionKind.NodeNext,
|
|
40
|
+
strict: true,
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
const checker = program.getTypeChecker();
|
|
44
|
+
const source = program.getSourceFile(sourceFile);
|
|
45
|
+
|
|
46
|
+
if (!source) {
|
|
47
|
+
return undefined;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Walk AST to find function declaration or arrow function export
|
|
51
|
+
let result: TsFunctionSignature | undefined;
|
|
52
|
+
|
|
53
|
+
function visit(node: ts.Node) {
|
|
54
|
+
if (result) return; // Already found
|
|
55
|
+
|
|
56
|
+
// Handle: export function funcName(...) { }
|
|
57
|
+
if (ts.isFunctionDeclaration(node) && node.name?.text === funcName) {
|
|
58
|
+
result = extractSignatureFromFunction(node, checker, funcName);
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Handle: export const funcName = (...) => { }
|
|
63
|
+
// or: export const funcName = function(...) { }
|
|
64
|
+
if (ts.isVariableStatement(node)) {
|
|
65
|
+
for (const decl of node.declarationList.declarations) {
|
|
66
|
+
if (ts.isIdentifier(decl.name) && decl.name.text === funcName && decl.initializer) {
|
|
67
|
+
if (ts.isArrowFunction(decl.initializer) || ts.isFunctionExpression(decl.initializer)) {
|
|
68
|
+
result = extractSignatureFromFunction(decl.initializer, checker, funcName);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
ts.forEachChild(node, visit);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
visit(source);
|
|
79
|
+
|
|
80
|
+
// Cache the result
|
|
81
|
+
if (result) {
|
|
82
|
+
if (!signatureCache.has(sourceFile)) {
|
|
83
|
+
signatureCache.set(sourceFile, new Map());
|
|
84
|
+
}
|
|
85
|
+
signatureCache.get(sourceFile)!.set(funcName, result);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return result;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Extract signature from a function-like declaration.
|
|
93
|
+
*/
|
|
94
|
+
function extractSignatureFromFunction(
|
|
95
|
+
node: ts.FunctionDeclaration | ts.ArrowFunction | ts.FunctionExpression,
|
|
96
|
+
checker: ts.TypeChecker,
|
|
97
|
+
funcName: string
|
|
98
|
+
): TsFunctionSignature {
|
|
99
|
+
const params = node.parameters.map((param) => {
|
|
100
|
+
const paramType = checker.getTypeAtLocation(param);
|
|
101
|
+
return {
|
|
102
|
+
name: param.name.getText(),
|
|
103
|
+
tsType: checker.typeToString(paramType),
|
|
104
|
+
optional: !!param.questionToken || !!param.initializer,
|
|
105
|
+
};
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
// Get return type from signature
|
|
109
|
+
const sig = checker.getSignatureFromDeclaration(node);
|
|
110
|
+
const returnType = sig ? checker.typeToString(sig.getReturnType()) : 'void';
|
|
111
|
+
|
|
112
|
+
return {
|
|
113
|
+
name: funcName,
|
|
114
|
+
params,
|
|
115
|
+
returnType,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Extract all exported function signatures from a TypeScript file.
|
|
121
|
+
*
|
|
122
|
+
* @param sourceFile - Path to the TypeScript source file
|
|
123
|
+
* @returns Map of function name to signature
|
|
124
|
+
*/
|
|
125
|
+
export function extractAllFunctionSignatures(
|
|
126
|
+
sourceFile: string
|
|
127
|
+
): Map<string, TsFunctionSignature> {
|
|
128
|
+
const results = new Map<string, TsFunctionSignature>();
|
|
129
|
+
|
|
130
|
+
const program = ts.createProgram([sourceFile], {
|
|
131
|
+
target: ts.ScriptTarget.ESNext,
|
|
132
|
+
module: ts.ModuleKind.ESNext,
|
|
133
|
+
moduleResolution: ts.ModuleResolutionKind.NodeNext,
|
|
134
|
+
strict: true,
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
const checker = program.getTypeChecker();
|
|
138
|
+
const source = program.getSourceFile(sourceFile);
|
|
139
|
+
|
|
140
|
+
if (!source) {
|
|
141
|
+
return results;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function visit(node: ts.Node) {
|
|
145
|
+
// Handle: export function funcName(...) { }
|
|
146
|
+
if (ts.isFunctionDeclaration(node) && node.name) {
|
|
147
|
+
const isExported = node.modifiers?.some(
|
|
148
|
+
(m) => m.kind === ts.SyntaxKind.ExportKeyword
|
|
149
|
+
);
|
|
150
|
+
if (isExported) {
|
|
151
|
+
const sig = extractSignatureFromFunction(node, checker, node.name.text);
|
|
152
|
+
results.set(node.name.text, sig);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Handle: export const funcName = (...) => { }
|
|
157
|
+
if (ts.isVariableStatement(node)) {
|
|
158
|
+
const isExported = node.modifiers?.some(
|
|
159
|
+
(m) => m.kind === ts.SyntaxKind.ExportKeyword
|
|
160
|
+
);
|
|
161
|
+
if (isExported) {
|
|
162
|
+
for (const decl of node.declarationList.declarations) {
|
|
163
|
+
if (ts.isIdentifier(decl.name) && decl.initializer) {
|
|
164
|
+
if (ts.isArrowFunction(decl.initializer) || ts.isFunctionExpression(decl.initializer)) {
|
|
165
|
+
const sig = extractSignatureFromFunction(decl.initializer, checker, decl.name.text);
|
|
166
|
+
results.set(decl.name.text, sig);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
ts.forEachChild(node, visit);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
visit(source);
|
|
177
|
+
|
|
178
|
+
// Update cache
|
|
179
|
+
if (!signatureCache.has(sourceFile)) {
|
|
180
|
+
signatureCache.set(sourceFile, new Map());
|
|
181
|
+
}
|
|
182
|
+
for (const [name, sig] of results) {
|
|
183
|
+
signatureCache.get(sourceFile)!.set(name, sig);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
return results;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Clear the signature cache (useful for testing or when files change).
|
|
191
|
+
*/
|
|
192
|
+
export function clearSignatureCache(): void {
|
|
193
|
+
signatureCache.clear();
|
|
194
|
+
}
|