@vibe-lang/runtime 0.2.8 → 0.2.10

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 (33) hide show
  1. package/package.json +1 -1
  2. package/src/ast/index.ts +6 -0
  3. package/src/lexer/index.ts +2 -0
  4. package/src/parser/index.ts +14 -0
  5. package/src/parser/parse.ts +138 -1
  6. package/src/parser/test/errors/missing-tokens.test.ts +25 -0
  7. package/src/parser/test/errors/model-declaration.test.ts +14 -8
  8. package/src/parser/test/errors/unclosed-delimiters.test.ts +200 -34
  9. package/src/parser/test/errors/unexpected-tokens.test.ts +15 -1
  10. package/src/parser/test/literals.test.ts +29 -0
  11. package/src/parser/test/model-declaration.test.ts +14 -0
  12. package/src/parser/visitor.ts +9 -0
  13. package/src/runtime/async/dependencies.ts +8 -1
  14. package/src/runtime/async/test/dependencies.test.ts +27 -1
  15. package/src/runtime/exec/statements.ts +51 -1
  16. package/src/runtime/index.ts +2 -3
  17. package/src/runtime/modules.ts +1 -1
  18. package/src/runtime/stdlib/index.ts +7 -11
  19. package/src/runtime/stdlib/tools/index.ts +5 -122
  20. package/src/runtime/stdlib/utils/index.ts +58 -0
  21. package/src/runtime/step.ts +4 -0
  22. package/src/runtime/test/core-functions.test.ts +19 -10
  23. package/src/runtime/test/throw.test.ts +220 -0
  24. package/src/runtime/test/tool-execution.test.ts +30 -30
  25. package/src/runtime/types.ts +4 -1
  26. package/src/runtime/validation.ts +6 -0
  27. package/src/semantic/analyzer-context.ts +2 -0
  28. package/src/semantic/analyzer-visitors.ts +149 -2
  29. package/src/semantic/analyzer.ts +1 -0
  30. package/src/semantic/test/fixtures/exports.vibe +25 -0
  31. package/src/semantic/test/function-return-check.test.ts +215 -0
  32. package/src/semantic/test/imports.test.ts +66 -2
  33. package/src/semantic/test/prompt-validation.test.ts +44 -0
@@ -110,6 +110,20 @@ model myModel = {
110
110
  expect(model.config.apiKey.value).toBe('sk-test');
111
111
  expect(model.config.url.value).toBe('https://api.openai.com');
112
112
  });
113
+
114
+ test('model with trailing comma', () => {
115
+ const ast = parse(`
116
+ model myModel = {
117
+ name: "gpt-4",
118
+ apiKey: "sk-test",
119
+ url: "https://api.openai.com",
120
+ }
121
+ `);
122
+ expect(ast.body).toHaveLength(1);
123
+ const model = ast.body[0] as any;
124
+ expect(model.type).toBe('ModelDeclaration');
125
+ expect(model.config.providedFields).toEqual(['name', 'apiKey', 'url']);
126
+ });
113
127
  });
114
128
 
115
129
  describe('Syntax Errors - Model Declaration', () => {
@@ -61,6 +61,7 @@ class VibeAstVisitor extends BaseVibeVisitor {
61
61
  if (ctx.toolDeclaration) return this.visit(ctx.toolDeclaration);
62
62
  if (ctx.returnStatement) return this.visit(ctx.returnStatement);
63
63
  if (ctx.breakStatement) return this.visit(ctx.breakStatement);
64
+ if (ctx.throwStatement) return this.visit(ctx.throwStatement);
64
65
  if (ctx.ifStatement) return this.visit(ctx.ifStatement);
65
66
  if (ctx.forInStatement) return this.visit(ctx.forInStatement);
66
67
  if (ctx.whileStatement) return this.visit(ctx.whileStatement);
@@ -409,6 +410,14 @@ class VibeAstVisitor extends BaseVibeVisitor {
409
410
  return { type: 'BreakStatement', location: tokenLocation(ctx.Break[0]) };
410
411
  }
411
412
 
413
+ throwStatement(ctx: { Throw: IToken[]; expression: CstNode[] }): AST.ThrowStatement {
414
+ return {
415
+ type: 'ThrowStatement',
416
+ message: this.visit(ctx.expression),
417
+ location: tokenLocation(ctx.Throw[0]),
418
+ };
419
+ }
420
+
412
421
  ifStatement(ctx: { If: IToken[]; expression: CstNode[]; blockStatement: CstNode[]; ifStatement?: CstNode[] }): AST.IfStatement {
413
422
  const alternate = ctx.ifStatement ? this.visit(ctx.ifStatement) : ctx.blockStatement.length > 1 ? this.visit(ctx.blockStatement[1]) : null;
414
423
  return { type: 'IfStatement', condition: this.visit(ctx.expression), consequent: this.visit(ctx.blockStatement[0]), alternate, location: tokenLocation(ctx.If[0]) };
@@ -83,8 +83,15 @@ export function getReferencedVariables(expr: AST.Expression): string[] {
83
83
  }
84
84
  break;
85
85
 
86
- // Literals don't reference variables
87
86
  case 'StringLiteral':
87
+ // Extract variables from {varName} and !{varName} interpolation in strings
88
+ const stringMatches = node.value.matchAll(/!?\{([a-zA-Z_][a-zA-Z0-9_]*)\}/g);
89
+ for (const match of stringMatches) {
90
+ variables.push(match[1]);
91
+ }
92
+ break;
93
+
94
+ // These literals don't reference variables
88
95
  case 'NumberLiteral':
89
96
  case 'BooleanLiteral':
90
97
  case 'NullLiteral':
@@ -22,11 +22,37 @@ describe('Async Dependency Detection', () => {
22
22
  expect(getReferencedVariables(expr)).toEqual(['myVar']);
23
23
  });
24
24
 
25
- test('extracts no variables from string literal', () => {
25
+ test('extracts no variables from plain string literal', () => {
26
26
  const expr = parseExpr('"hello"');
27
27
  expect(getReferencedVariables(expr)).toEqual([]);
28
28
  });
29
29
 
30
+ test('extracts variables from string literal with {var} interpolation', () => {
31
+ const expr = parseExpr('"Hello {name}!"');
32
+ const vars = getReferencedVariables(expr);
33
+ expect(vars).toContain('name');
34
+ });
35
+
36
+ test('extracts variables from string literal with !{var} expansion', () => {
37
+ const expr = parseExpr('"Process this: !{data}"');
38
+ const vars = getReferencedVariables(expr);
39
+ expect(vars).toContain('data');
40
+ });
41
+
42
+ test('extracts multiple variables from string literal interpolation', () => {
43
+ const expr = parseExpr('"Hello {greeting} {name}, your data is !{info}"');
44
+ const vars = getReferencedVariables(expr);
45
+ expect(vars).toContain('greeting');
46
+ expect(vars).toContain('name');
47
+ expect(vars).toContain('info');
48
+ });
49
+
50
+ test('extracts variables from single-quoted string with interpolation', () => {
51
+ const expr = parseExpr("'User: {user}'");
52
+ const vars = getReferencedVariables(expr);
53
+ expect(vars).toContain('user');
54
+ });
55
+
30
56
  test('extracts no variables from number literal', () => {
31
57
  const expr = parseExpr('42');
32
58
  expect(getReferencedVariables(expr)).toEqual([]);
@@ -3,7 +3,7 @@
3
3
  import * as AST from '../../ast';
4
4
  import type { SourceLocation } from '../../errors';
5
5
  import type { RuntimeState, VibeValue } from '../types';
6
- import { createVibeValue, resolveValue, isVibeValue } from '../types';
6
+ import { createVibeValue, createVibeError, resolveValue, isVibeValue } from '../types';
7
7
  import { currentFrame } from '../state';
8
8
  import { requireBoolean, validateAndCoerce } from '../validation';
9
9
  import { execDeclareVar } from './variables';
@@ -441,6 +441,53 @@ export function execReturnValue(state: RuntimeState): RuntimeState {
441
441
  return { ...state, callStack: newCallStack, instructionStack: newInstructionStack, lastResult: validatedReturnValue };
442
442
  }
443
443
 
444
+ /**
445
+ * Throw statement - evaluate message and throw error.
446
+ */
447
+ export function execThrowStatement(state: RuntimeState, stmt: AST.ThrowStatement): RuntimeState {
448
+ return {
449
+ ...state,
450
+ instructionStack: [
451
+ { op: 'exec_expression', expr: stmt.message, location: stmt.message.location },
452
+ { op: 'throw_error', location: stmt.location },
453
+ ...state.instructionStack,
454
+ ],
455
+ };
456
+ }
457
+
458
+ /**
459
+ * Throw error - create error value and unwind to function boundary.
460
+ * Uses lastResult as the error message.
461
+ */
462
+ export function execThrowError(state: RuntimeState, location: SourceLocation): RuntimeState {
463
+ // Get the error message from lastResult
464
+ const messageValue = resolveValue(state.lastResult);
465
+ const message = typeof messageValue === 'string' ? messageValue : String(messageValue);
466
+
467
+ // Create error VibeValue
468
+ const errorValue = createVibeError(message, location);
469
+
470
+ // Check if we're at top level (only main frame) or in a function
471
+ const isTopLevel = state.callStack.length === 1;
472
+
473
+ if (isTopLevel) {
474
+ // At top level - complete with error but keep the frame for variable access
475
+ return { ...state, status: 'completed', instructionStack: [], lastResult: errorValue };
476
+ }
477
+
478
+ // In a function - unwind like return: pop frame and skip to after pop_frame instruction
479
+ const newCallStack = state.callStack.slice(0, -1);
480
+
481
+ // Find and skip past the pop_frame instruction
482
+ let newInstructionStack = state.instructionStack;
483
+ const popFrameIndex = newInstructionStack.findIndex((i) => i.op === 'pop_frame');
484
+ if (popFrameIndex !== -1) {
485
+ newInstructionStack = newInstructionStack.slice(popFrameIndex + 1);
486
+ }
487
+
488
+ return { ...state, callStack: newCallStack, instructionStack: newInstructionStack, lastResult: errorValue };
489
+ }
490
+
444
491
  /**
445
492
  * Execute statements at index - sequential statement execution.
446
493
  */
@@ -598,6 +645,9 @@ export function execStatement(state: RuntimeState, stmt: AST.Statement): Runtime
598
645
  case 'BreakStatement':
599
646
  return execBreakStatement(state, stmt);
600
647
 
648
+ case 'ThrowStatement':
649
+ return execThrowStatement(state, stmt);
650
+
601
651
  default:
602
652
  throw new Error(`Unknown statement type: ${(stmt as AST.Statement).type}`);
603
653
  }
@@ -185,13 +185,12 @@ export class Runtime {
185
185
  return resolveValue(variable?.value);
186
186
  }
187
187
 
188
- // Get raw value including VibeValue wrapper if present
188
+ // Get raw VibeValue wrapper (for testing error state, toolCalls, etc.)
189
189
  getRawValue(name: string): unknown {
190
190
  const frame = this.state.callStack[this.state.callStack.length - 1];
191
191
  if (!frame) return undefined;
192
192
 
193
- const variable = frame.locals[name];
194
- return variable?.value;
193
+ return frame.locals[name];
195
194
  }
196
195
 
197
196
  // Get all AI interactions (for debugging)
@@ -9,7 +9,7 @@ import { resolve, dirname, join } from 'path';
9
9
 
10
10
  // Map system module names to their implementation files
11
11
  const SYSTEM_MODULES: Record<string, string> = {
12
- 'system': join(__dirname, 'stdlib', 'index.ts'),
12
+ 'system/utils': join(__dirname, 'stdlib', 'utils', 'index.ts'),
13
13
  'system/tools': join(__dirname, 'stdlib', 'tools', 'index.ts'),
14
14
  };
15
15
 
@@ -1,16 +1,12 @@
1
- // Standard library functions for Vibe scripts
2
- // Import with: import { uuid } from "system"
1
+ // System module - re-exports utility functions for convenience
2
+ // Prefer importing from "system/utils" directly.
3
+ //
4
+ // Import with: import { uuid, random, now } from "system"
5
+ // Or directly: import { uuid, random, now } from "system/utils"
3
6
  //
4
- // These are TypeScript functions that can be called directly from Vibe scripts.
5
7
  // For AI tools, use: import { allTools } from "system/tools"
6
8
  //
7
9
  // NOTE: print() and env() are auto-imported core functions.
8
- // They are available everywhere without import and CANNOT be imported from "system".
10
+ // They are available everywhere without import.
9
11
 
10
- /**
11
- * Generate a UUID v4.
12
- * @returns A new UUID string
13
- */
14
- export function uuid(): string {
15
- return crypto.randomUUID();
16
- }
12
+ export { uuid, now, random, jsonParse, jsonStringify } from './utils';
@@ -2,7 +2,7 @@
2
2
  // Import with: import { allTools, readFile, writeFile, ... } from "system/tools"
3
3
  //
4
4
  // These are tools that AI models can use via the tools parameter.
5
- // For direct script use, import functions from "system" instead.
5
+ // For utility functions (uuid, random, now, etc.), use: import { ... } from "system/utils"
6
6
 
7
7
  import type { VibeToolValue, ToolContext } from '../../tools/types';
8
8
  import { validatePathInSandbox } from '../../tools/security';
@@ -386,117 +386,6 @@ export const dirExists: VibeToolValue = {
386
386
  },
387
387
  };
388
388
 
389
- // =============================================================================
390
- // Utility Tools
391
- // =============================================================================
392
-
393
- export const env: VibeToolValue = {
394
- __vibeTool: true,
395
- name: 'env',
396
- schema: {
397
- name: 'env',
398
- description: 'Get an environment variable.',
399
- parameters: [
400
- { name: 'name', type: { type: 'string' }, description: 'The environment variable name', required: true },
401
- { name: 'defaultValue', type: { type: 'string' }, description: 'Default value if not set', required: false },
402
- ],
403
- returns: { type: 'string' },
404
- },
405
- executor: async (args: Record<string, unknown>) => {
406
- const name = args.name as string;
407
- const defaultValue = args.defaultValue as string | undefined;
408
- return process.env[name] ?? defaultValue ?? '';
409
- },
410
- };
411
-
412
- export const now: VibeToolValue = {
413
- __vibeTool: true,
414
- name: 'now',
415
- schema: {
416
- name: 'now',
417
- description: 'Get the current timestamp in milliseconds.',
418
- parameters: [],
419
- returns: { type: 'number' },
420
- },
421
- executor: async () => {
422
- return Date.now();
423
- },
424
- };
425
-
426
- export const jsonParse: VibeToolValue = {
427
- __vibeTool: true,
428
- name: 'jsonParse',
429
- schema: {
430
- name: 'jsonParse',
431
- description: 'Parse a JSON string into an object.',
432
- parameters: [
433
- { name: 'text', type: { type: 'string' }, description: 'The JSON string to parse', required: true },
434
- ],
435
- returns: { type: 'object', additionalProperties: true },
436
- },
437
- executor: async (args: Record<string, unknown>) => {
438
- const text = args.text as string;
439
- return JSON.parse(text);
440
- },
441
- };
442
-
443
- export const jsonStringify: VibeToolValue = {
444
- __vibeTool: true,
445
- name: 'jsonStringify',
446
- schema: {
447
- name: 'jsonStringify',
448
- description: 'Convert an object to a JSON string.',
449
- parameters: [
450
- { name: 'value', type: { type: 'object', additionalProperties: true }, description: 'The value to stringify', required: true },
451
- { name: 'pretty', type: { type: 'boolean' }, description: 'Whether to format with indentation', required: false },
452
- ],
453
- returns: { type: 'string' },
454
- },
455
- executor: async (args: Record<string, unknown>) => {
456
- const value = args.value;
457
- const pretty = args.pretty as boolean | undefined;
458
- return pretty ? JSON.stringify(value, null, 2) : JSON.stringify(value);
459
- },
460
- };
461
-
462
- export const random: VibeToolValue = {
463
- __vibeTool: true,
464
- name: 'random',
465
- schema: {
466
- name: 'random',
467
- description: 'Generate a random number. Without arguments, returns 0-1. With min/max, returns integer in range.',
468
- parameters: [
469
- { name: 'min', type: { type: 'number' }, description: 'Minimum value (inclusive)', required: false },
470
- { name: 'max', type: { type: 'number' }, description: 'Maximum value (inclusive)', required: false },
471
- ],
472
- returns: { type: 'number' },
473
- },
474
- executor: async (args: Record<string, unknown>) => {
475
- const min = args.min as number | undefined;
476
- const max = args.max as number | undefined;
477
-
478
- if (min !== undefined && max !== undefined) {
479
- return Math.floor(Math.random() * (max - min + 1)) + min;
480
- }
481
-
482
- return Math.random();
483
- },
484
- };
485
-
486
- export const uuid: VibeToolValue = {
487
- __vibeTool: true,
488
- name: 'uuid',
489
- schema: {
490
- name: 'uuid',
491
- description: 'Generate a UUID v4.',
492
- parameters: [],
493
- returns: { type: 'string' },
494
- },
495
- executor: async () => {
496
- return crypto.randomUUID();
497
- },
498
- };
499
-
500
389
  // =============================================================================
501
390
  // System Tools
502
391
  // =============================================================================
@@ -567,12 +456,12 @@ export const runCode: VibeToolValue = {
567
456
  schema: {
568
457
  name: 'runCode',
569
458
  description:
570
- 'Execute TypeScript/JavaScript code in a sandboxed subprocess. ' +
459
+ 'Execute TypeScript code in a sandboxed subprocess. IMPORTANT: Only write TypeScript code, never Python or other languages. ' +
571
460
  'All scope variables are automatically available as local variables. ' +
572
461
  'Use `return value` to pass results back. Bun APIs are available. ' +
573
462
  'Each execution creates a unique folder in .vibe-cache/ for intermediate files.',
574
463
  parameters: [
575
- { name: 'code', type: { type: 'string' }, description: 'TypeScript/JavaScript code to execute', required: true },
464
+ { name: 'code', type: { type: 'string' }, description: 'TypeScript code to execute (not Python or other languages)', required: true },
576
465
  { name: 'scope', type: { type: 'object', additionalProperties: true }, description: 'Variables to make available in the code', required: false },
577
466
  { name: 'timeout', type: { type: 'number' }, description: 'Timeout in milliseconds (default: 30000)', required: false },
578
467
  ],
@@ -659,8 +548,8 @@ console.log('__VIBE_RESULT__' + JSON.stringify(__result));
659
548
  // =============================================================================
660
549
 
661
550
  /**
662
- * All tools - the complete set of standard tools.
663
- * Includes all file, search, directory, utility, and system tools.
551
+ * All tools - the complete set of standard AI tools.
552
+ * Includes file, search, directory, and system tools.
664
553
  */
665
554
  export const allTools: VibeToolValue[] = [
666
555
  // File tools
@@ -669,8 +558,6 @@ export const allTools: VibeToolValue[] = [
669
558
  glob, grep,
670
559
  // Directory tools
671
560
  mkdir, dirExists,
672
- // Utility tools
673
- env, now, jsonParse, jsonStringify, random, uuid,
674
561
  // System tools
675
562
  bash, runCode,
676
563
  ];
@@ -686,8 +573,6 @@ export const readonlyTools: VibeToolValue[] = [
686
573
  glob, grep,
687
574
  // Directory tools (read-only)
688
575
  dirExists,
689
- // Utility tools
690
- env, now, jsonParse, jsonStringify, random, uuid,
691
576
  ];
692
577
 
693
578
  /**
@@ -701,7 +586,5 @@ export const safeTools: VibeToolValue[] = [
701
586
  glob, grep,
702
587
  // Directory tools
703
588
  mkdir, dirExists,
704
- // Utility tools
705
- env, now, jsonParse, jsonStringify, random, uuid,
706
589
  ];
707
590
 
@@ -0,0 +1,58 @@
1
+ // System utility functions for Vibe scripts
2
+ // Import with: import { uuid, now, random, jsonParse, jsonStringify } from "system/utils"
3
+ //
4
+ // These are utility functions that can be called directly from Vibe scripts.
5
+ // For AI tools, use: import { readFile, writeFile, ... } from "system/tools"
6
+ //
7
+ // NOTE: print() and env() are auto-imported core functions.
8
+ // They are available everywhere without import.
9
+
10
+ /**
11
+ * Generate a UUID v4.
12
+ * @returns A new UUID string
13
+ */
14
+ export function uuid(): string {
15
+ return crypto.randomUUID();
16
+ }
17
+
18
+ /**
19
+ * Get the current timestamp in milliseconds.
20
+ * @returns Unix timestamp in milliseconds
21
+ */
22
+ export function now(): number {
23
+ return Date.now();
24
+ }
25
+
26
+ /**
27
+ * Generate a random number.
28
+ * Without arguments, returns a float between 0 and 1.
29
+ * With min/max, returns an integer in the range [min, max] inclusive.
30
+ * @param min - Minimum value (inclusive)
31
+ * @param max - Maximum value (inclusive)
32
+ * @returns Random number
33
+ */
34
+ export function random(min?: number, max?: number): number {
35
+ if (min !== undefined && max !== undefined) {
36
+ return Math.floor(Math.random() * (max - min + 1)) + min;
37
+ }
38
+ return Math.random();
39
+ }
40
+
41
+ /**
42
+ * Parse a JSON string into an object.
43
+ * @param text - The JSON string to parse
44
+ * @returns Parsed object
45
+ */
46
+ export function jsonParse(text: string): unknown {
47
+ return JSON.parse(text);
48
+ }
49
+
50
+ /**
51
+ * Convert a value to a JSON string.
52
+ * @param value - The value to stringify
53
+ * @param pretty - Whether to format with indentation
54
+ * @returns JSON string
55
+ */
56
+ export function jsonStringify(value: unknown, pretty?: boolean): string {
57
+ return pretty ? JSON.stringify(value, null, 2) : JSON.stringify(value);
58
+ }
@@ -10,6 +10,7 @@ import {
10
10
  execStatement,
11
11
  execStatements,
12
12
  execReturnValue,
13
+ execThrowError,
13
14
  execIfBranch,
14
15
  execEnterBlock,
15
16
  execExitBlock,
@@ -317,6 +318,9 @@ function executeInstruction(state: RuntimeState, instruction: Instruction): Runt
317
318
  case 'return_value':
318
319
  return execReturnValue(state);
319
320
 
321
+ case 'throw_error':
322
+ return execThrowError(state, instruction.location);
323
+
320
324
  case 'enter_block':
321
325
  return execEnterBlock(state, instruction.savedKeys);
322
326
 
@@ -176,22 +176,22 @@ if true {
176
176
  });
177
177
 
178
178
  describe('core functions cannot be imported', () => {
179
- test('importing env from system fails', async () => {
179
+ test('importing env from system/utils fails (env is core function)', async () => {
180
180
  const ast = parse(`
181
- import { env } from "system"
181
+ import { env } from "system/utils"
182
182
  let x = 1
183
183
  `);
184
184
  const runtime = new Runtime(ast, createMockProvider());
185
- await expect(runtime.run()).rejects.toThrow("'env' is not exported from 'system'");
185
+ await expect(runtime.run()).rejects.toThrow("'env' is not exported from 'system/utils'");
186
186
  });
187
187
 
188
- test('importing print from system fails', async () => {
188
+ test('importing print from system/utils fails (print is core function)', async () => {
189
189
  const ast = parse(`
190
- import { print } from "system"
190
+ import { print } from "system/utils"
191
191
  let x = 1
192
192
  `);
193
193
  const runtime = new Runtime(ast, createMockProvider());
194
- await expect(runtime.run()).rejects.toThrow("'print' is not exported from 'system'");
194
+ await expect(runtime.run()).rejects.toThrow("'print' is not exported from 'system/utils'");
195
195
  });
196
196
 
197
197
  test('importing from system/core is blocked', async () => {
@@ -202,10 +202,19 @@ let x = 1
202
202
  const runtime = new Runtime(ast, createMockProvider());
203
203
  await expect(runtime.run()).rejects.toThrow("'system/core' cannot be imported");
204
204
  });
205
+
206
+ test('importing from bare "system" fails (not a valid module)', async () => {
207
+ const ast = parse(`
208
+ import { uuid } from "system"
209
+ let x = 1
210
+ `);
211
+ const runtime = new Runtime(ast, createMockProvider());
212
+ await expect(runtime.run()).rejects.toThrow("Unknown system module: 'system'");
213
+ });
205
214
  });
206
215
 
207
- describe('library functions still require import', () => {
208
- test('uuid requires import from system', async () => {
216
+ describe('utility functions require import from system/utils', () => {
217
+ test('uuid requires import', async () => {
209
218
  const ast = parse(`
210
219
  let id = uuid()
211
220
  `);
@@ -213,9 +222,9 @@ let id = uuid()
213
222
  await expect(runtime.run()).rejects.toThrow("'uuid' is not defined");
214
223
  });
215
224
 
216
- test('uuid works when imported from system', async () => {
225
+ test('uuid works when imported from system/utils', async () => {
217
226
  const ast = parse(`
218
- import { uuid } from "system"
227
+ import { uuid } from "system/utils"
219
228
  let id = uuid()
220
229
  `);
221
230
  const runtime = new Runtime(ast, createMockProvider());