@vibe-lang/runtime 0.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (250) hide show
  1. package/package.json +46 -0
  2. package/src/ast/index.ts +375 -0
  3. package/src/ast.ts +2 -0
  4. package/src/debug/advanced-features.ts +482 -0
  5. package/src/debug/bun-inspector.ts +424 -0
  6. package/src/debug/handoff-manager.ts +283 -0
  7. package/src/debug/index.ts +150 -0
  8. package/src/debug/runner.ts +365 -0
  9. package/src/debug/server.ts +565 -0
  10. package/src/debug/stack-merger.ts +267 -0
  11. package/src/debug/state.ts +581 -0
  12. package/src/debug/test/advanced-features.test.ts +300 -0
  13. package/src/debug/test/e2e.test.ts +218 -0
  14. package/src/debug/test/handoff-manager.test.ts +256 -0
  15. package/src/debug/test/runner.test.ts +256 -0
  16. package/src/debug/test/stack-merger.test.ts +163 -0
  17. package/src/debug/test/state.test.ts +400 -0
  18. package/src/debug/test/ts-debug-integration.test.ts +374 -0
  19. package/src/debug/test/ts-import-tracker.test.ts +125 -0
  20. package/src/debug/test/ts-source-map.test.ts +169 -0
  21. package/src/debug/ts-import-tracker.ts +151 -0
  22. package/src/debug/ts-source-map.ts +171 -0
  23. package/src/errors/index.ts +124 -0
  24. package/src/index.ts +358 -0
  25. package/src/lexer/index.ts +348 -0
  26. package/src/lexer.ts +2 -0
  27. package/src/parser/index.ts +792 -0
  28. package/src/parser/parse.ts +45 -0
  29. package/src/parser/test/async.test.ts +248 -0
  30. package/src/parser/test/destructuring.test.ts +167 -0
  31. package/src/parser/test/do-expression.test.ts +486 -0
  32. package/src/parser/test/errors/do-expression.test.ts +95 -0
  33. package/src/parser/test/errors/error-locations.test.ts +230 -0
  34. package/src/parser/test/errors/invalid-expressions.test.ts +144 -0
  35. package/src/parser/test/errors/missing-tokens.test.ts +126 -0
  36. package/src/parser/test/errors/model-declaration.test.ts +185 -0
  37. package/src/parser/test/errors/nested-blocks.test.ts +226 -0
  38. package/src/parser/test/errors/unclosed-delimiters.test.ts +122 -0
  39. package/src/parser/test/errors/unexpected-tokens.test.ts +120 -0
  40. package/src/parser/test/import-export.test.ts +143 -0
  41. package/src/parser/test/literals.test.ts +404 -0
  42. package/src/parser/test/model-declaration.test.ts +161 -0
  43. package/src/parser/test/nested-blocks.test.ts +402 -0
  44. package/src/parser/test/parser.test.ts +743 -0
  45. package/src/parser/test/private.test.ts +136 -0
  46. package/src/parser/test/template-literal.test.ts +127 -0
  47. package/src/parser/test/tool-declaration.test.ts +302 -0
  48. package/src/parser/test/ts-block.test.ts +252 -0
  49. package/src/parser/test/type-annotations.test.ts +254 -0
  50. package/src/parser/visitor/helpers.ts +330 -0
  51. package/src/parser/visitor.ts +794 -0
  52. package/src/parser.ts +2 -0
  53. package/src/runtime/ai/cache-chunking.test.ts +69 -0
  54. package/src/runtime/ai/cache-chunking.ts +73 -0
  55. package/src/runtime/ai/client.ts +109 -0
  56. package/src/runtime/ai/context.ts +168 -0
  57. package/src/runtime/ai/formatters.ts +316 -0
  58. package/src/runtime/ai/index.ts +38 -0
  59. package/src/runtime/ai/language-ref.ts +38 -0
  60. package/src/runtime/ai/providers/anthropic.ts +253 -0
  61. package/src/runtime/ai/providers/google.ts +201 -0
  62. package/src/runtime/ai/providers/openai.ts +156 -0
  63. package/src/runtime/ai/retry.ts +100 -0
  64. package/src/runtime/ai/return-tools.ts +301 -0
  65. package/src/runtime/ai/test/client.test.ts +83 -0
  66. package/src/runtime/ai/test/formatters.test.ts +485 -0
  67. package/src/runtime/ai/test/retry.test.ts +137 -0
  68. package/src/runtime/ai/test/return-tools.test.ts +450 -0
  69. package/src/runtime/ai/test/tool-loop.test.ts +319 -0
  70. package/src/runtime/ai/test/tool-schema.test.ts +241 -0
  71. package/src/runtime/ai/tool-loop.ts +203 -0
  72. package/src/runtime/ai/tool-schema.ts +151 -0
  73. package/src/runtime/ai/types.ts +113 -0
  74. package/src/runtime/ai-logger.ts +255 -0
  75. package/src/runtime/ai-provider.ts +347 -0
  76. package/src/runtime/async/dependencies.ts +276 -0
  77. package/src/runtime/async/executor.ts +293 -0
  78. package/src/runtime/async/index.ts +43 -0
  79. package/src/runtime/async/scheduling.ts +163 -0
  80. package/src/runtime/async/test/dependencies.test.ts +284 -0
  81. package/src/runtime/async/test/executor.test.ts +388 -0
  82. package/src/runtime/context.ts +357 -0
  83. package/src/runtime/exec/ai.ts +139 -0
  84. package/src/runtime/exec/expressions.ts +475 -0
  85. package/src/runtime/exec/frames.ts +26 -0
  86. package/src/runtime/exec/functions.ts +305 -0
  87. package/src/runtime/exec/interpolation.ts +312 -0
  88. package/src/runtime/exec/statements.ts +604 -0
  89. package/src/runtime/exec/tools.ts +129 -0
  90. package/src/runtime/exec/typescript.ts +215 -0
  91. package/src/runtime/exec/variables.ts +279 -0
  92. package/src/runtime/index.ts +975 -0
  93. package/src/runtime/modules.ts +452 -0
  94. package/src/runtime/serialize.ts +103 -0
  95. package/src/runtime/state.ts +489 -0
  96. package/src/runtime/stdlib/core.ts +45 -0
  97. package/src/runtime/stdlib/directory.test.ts +156 -0
  98. package/src/runtime/stdlib/edit.test.ts +154 -0
  99. package/src/runtime/stdlib/fastEdit.test.ts +201 -0
  100. package/src/runtime/stdlib/glob.test.ts +106 -0
  101. package/src/runtime/stdlib/grep.test.ts +144 -0
  102. package/src/runtime/stdlib/index.ts +16 -0
  103. package/src/runtime/stdlib/readFile.test.ts +123 -0
  104. package/src/runtime/stdlib/tools/index.ts +707 -0
  105. package/src/runtime/stdlib/writeFile.test.ts +157 -0
  106. package/src/runtime/step.ts +969 -0
  107. package/src/runtime/test/ai-context.test.ts +1086 -0
  108. package/src/runtime/test/ai-result-object.test.ts +419 -0
  109. package/src/runtime/test/ai-tool-flow.test.ts +859 -0
  110. package/src/runtime/test/async-execution-order.test.ts +618 -0
  111. package/src/runtime/test/async-execution.test.ts +344 -0
  112. package/src/runtime/test/async-nested.test.ts +660 -0
  113. package/src/runtime/test/async-parallel-timing.test.ts +546 -0
  114. package/src/runtime/test/basic1.test.ts +154 -0
  115. package/src/runtime/test/binary-operators.test.ts +431 -0
  116. package/src/runtime/test/break-statement.test.ts +257 -0
  117. package/src/runtime/test/context-modes.test.ts +650 -0
  118. package/src/runtime/test/context.test.ts +466 -0
  119. package/src/runtime/test/core-functions.test.ts +228 -0
  120. package/src/runtime/test/e2e.test.ts +88 -0
  121. package/src/runtime/test/error-locations/error-locations.test.ts +80 -0
  122. package/src/runtime/test/error-locations/main-error.vibe +4 -0
  123. package/src/runtime/test/error-locations/main-import-error.vibe +3 -0
  124. package/src/runtime/test/error-locations/utils/helper.vibe +5 -0
  125. package/src/runtime/test/for-in.test.ts +312 -0
  126. package/src/runtime/test/helpers.ts +69 -0
  127. package/src/runtime/test/imports.test.ts +334 -0
  128. package/src/runtime/test/json-expressions.test.ts +232 -0
  129. package/src/runtime/test/literals.test.ts +372 -0
  130. package/src/runtime/test/logical-indexing.test.ts +478 -0
  131. package/src/runtime/test/member-methods.test.ts +324 -0
  132. package/src/runtime/test/model-config.test.ts +338 -0
  133. package/src/runtime/test/null-handling.test.ts +342 -0
  134. package/src/runtime/test/private-visibility.test.ts +332 -0
  135. package/src/runtime/test/runtime-state.test.ts +514 -0
  136. package/src/runtime/test/scoping.test.ts +370 -0
  137. package/src/runtime/test/string-interpolation.test.ts +354 -0
  138. package/src/runtime/test/template-literal.test.ts +181 -0
  139. package/src/runtime/test/tool-execution.test.ts +467 -0
  140. package/src/runtime/test/tool-schema-generation.test.ts +477 -0
  141. package/src/runtime/test/tostring.test.ts +210 -0
  142. package/src/runtime/test/ts-block.test.ts +594 -0
  143. package/src/runtime/test/ts-error-location.test.ts +231 -0
  144. package/src/runtime/test/types.test.ts +732 -0
  145. package/src/runtime/test/verbose-logger.test.ts +710 -0
  146. package/src/runtime/test/vibe-expression.test.ts +54 -0
  147. package/src/runtime/test/vibe-value-errors.test.ts +541 -0
  148. package/src/runtime/test/while.test.ts +232 -0
  149. package/src/runtime/tools/builtin.ts +30 -0
  150. package/src/runtime/tools/directory-tools.ts +70 -0
  151. package/src/runtime/tools/file-tools.ts +228 -0
  152. package/src/runtime/tools/index.ts +5 -0
  153. package/src/runtime/tools/registry.ts +48 -0
  154. package/src/runtime/tools/search-tools.ts +134 -0
  155. package/src/runtime/tools/security.ts +36 -0
  156. package/src/runtime/tools/system-tools.ts +312 -0
  157. package/src/runtime/tools/test/fixtures/base-types.ts +40 -0
  158. package/src/runtime/tools/test/fixtures/test-types.ts +132 -0
  159. package/src/runtime/tools/test/registry.test.ts +713 -0
  160. package/src/runtime/tools/test/security.test.ts +86 -0
  161. package/src/runtime/tools/test/system-tools.test.ts +679 -0
  162. package/src/runtime/tools/test/ts-schema.test.ts +357 -0
  163. package/src/runtime/tools/ts-schema.ts +341 -0
  164. package/src/runtime/tools/types.ts +89 -0
  165. package/src/runtime/tools/utility-tools.ts +198 -0
  166. package/src/runtime/ts-eval.ts +126 -0
  167. package/src/runtime/types.ts +797 -0
  168. package/src/runtime/validation.ts +160 -0
  169. package/src/runtime/verbose-logger.ts +459 -0
  170. package/src/runtime.ts +2 -0
  171. package/src/semantic/analyzer-context.ts +62 -0
  172. package/src/semantic/analyzer-validators.ts +575 -0
  173. package/src/semantic/analyzer-visitors.ts +534 -0
  174. package/src/semantic/analyzer.ts +83 -0
  175. package/src/semantic/index.ts +11 -0
  176. package/src/semantic/symbol-table.ts +58 -0
  177. package/src/semantic/test/async-validation.test.ts +301 -0
  178. package/src/semantic/test/compress-validation.test.ts +179 -0
  179. package/src/semantic/test/const-reassignment.test.ts +111 -0
  180. package/src/semantic/test/control-flow.test.ts +346 -0
  181. package/src/semantic/test/destructuring.test.ts +185 -0
  182. package/src/semantic/test/duplicate-declarations.test.ts +168 -0
  183. package/src/semantic/test/export-validation.test.ts +111 -0
  184. package/src/semantic/test/fixtures/math.ts +31 -0
  185. package/src/semantic/test/imports.test.ts +148 -0
  186. package/src/semantic/test/json-type.test.ts +68 -0
  187. package/src/semantic/test/literals.test.ts +127 -0
  188. package/src/semantic/test/model-validation.test.ts +179 -0
  189. package/src/semantic/test/prompt-validation.test.ts +343 -0
  190. package/src/semantic/test/scoping.test.ts +312 -0
  191. package/src/semantic/test/tool-validation.test.ts +306 -0
  192. package/src/semantic/test/ts-type-checking.test.ts +563 -0
  193. package/src/semantic/test/type-constraints.test.ts +111 -0
  194. package/src/semantic/test/type-inference.test.ts +87 -0
  195. package/src/semantic/test/type-validation.test.ts +552 -0
  196. package/src/semantic/test/undefined-variables.test.ts +163 -0
  197. package/src/semantic/ts-block-checker.ts +204 -0
  198. package/src/semantic/ts-signatures.ts +194 -0
  199. package/src/semantic/ts-types.ts +170 -0
  200. package/src/semantic/types.ts +58 -0
  201. package/tests/fixtures/conditional-logic.vibe +14 -0
  202. package/tests/fixtures/function-call.vibe +16 -0
  203. package/tests/fixtures/imports/cycle-detection/a.vibe +6 -0
  204. package/tests/fixtures/imports/cycle-detection/b.vibe +5 -0
  205. package/tests/fixtures/imports/cycle-detection/main.vibe +3 -0
  206. package/tests/fixtures/imports/module-isolation/main-b.vibe +8 -0
  207. package/tests/fixtures/imports/module-isolation/main.vibe +9 -0
  208. package/tests/fixtures/imports/module-isolation/moduleA.vibe +6 -0
  209. package/tests/fixtures/imports/module-isolation/moduleB.vibe +6 -0
  210. package/tests/fixtures/imports/nested-import/helper.vibe +6 -0
  211. package/tests/fixtures/imports/nested-import/main.vibe +3 -0
  212. package/tests/fixtures/imports/nested-import/utils.ts +3 -0
  213. package/tests/fixtures/imports/nested-isolation/file2.vibe +15 -0
  214. package/tests/fixtures/imports/nested-isolation/file3.vibe +10 -0
  215. package/tests/fixtures/imports/nested-isolation/main.vibe +21 -0
  216. package/tests/fixtures/imports/pure-cycle/a.vibe +5 -0
  217. package/tests/fixtures/imports/pure-cycle/b.vibe +5 -0
  218. package/tests/fixtures/imports/pure-cycle/main.vibe +3 -0
  219. package/tests/fixtures/imports/ts-boolean/checks.ts +14 -0
  220. package/tests/fixtures/imports/ts-boolean/main.vibe +10 -0
  221. package/tests/fixtures/imports/ts-boolean/type-mismatch.vibe +5 -0
  222. package/tests/fixtures/imports/ts-boolean/use-constant.vibe +18 -0
  223. package/tests/fixtures/imports/ts-error-handling/helpers.ts +42 -0
  224. package/tests/fixtures/imports/ts-error-handling/main.vibe +5 -0
  225. package/tests/fixtures/imports/ts-import/main.vibe +4 -0
  226. package/tests/fixtures/imports/ts-import/math.ts +9 -0
  227. package/tests/fixtures/imports/ts-variables/call-non-function.vibe +5 -0
  228. package/tests/fixtures/imports/ts-variables/data.ts +10 -0
  229. package/tests/fixtures/imports/ts-variables/import-json.vibe +5 -0
  230. package/tests/fixtures/imports/ts-variables/import-type-mismatch.vibe +5 -0
  231. package/tests/fixtures/imports/ts-variables/import-variable.vibe +5 -0
  232. package/tests/fixtures/imports/vibe-import/greet.vibe +5 -0
  233. package/tests/fixtures/imports/vibe-import/main.vibe +3 -0
  234. package/tests/fixtures/multiple-ai-calls.vibe +10 -0
  235. package/tests/fixtures/simple-greeting.vibe +6 -0
  236. package/tests/fixtures/template-literals.vibe +11 -0
  237. package/tests/integration/basic-ai/basic-ai.integration.test.ts +166 -0
  238. package/tests/integration/basic-ai/basic-ai.vibe +12 -0
  239. package/tests/integration/bug-fix/bug-fix.integration.test.ts +201 -0
  240. package/tests/integration/bug-fix/buggy-code.ts +22 -0
  241. package/tests/integration/bug-fix/fix-bug.vibe +21 -0
  242. package/tests/integration/compress/compress.integration.test.ts +206 -0
  243. package/tests/integration/destructuring/destructuring.integration.test.ts +92 -0
  244. package/tests/integration/hello-world-translator/hello-world-translator.integration.test.ts +61 -0
  245. package/tests/integration/line-annotator/context-modes.integration.test.ts +261 -0
  246. package/tests/integration/line-annotator/line-annotator.integration.test.ts +148 -0
  247. package/tests/integration/multi-feature/cumulative-sum.integration.test.ts +75 -0
  248. package/tests/integration/multi-feature/number-analyzer.integration.test.ts +191 -0
  249. package/tests/integration/multi-feature/number-analyzer.vibe +59 -0
  250. package/tests/integration/tool-calls/tool-calls.integration.test.ts +93 -0
@@ -0,0 +1,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
+ }