@@ -391,6 +391,225 @@ describe('Function Execute API Route', () => {
391391 } )
392392 } )
393393
394+ describe ( 'Enhanced Error Handling' , ( ) => {
395+ it ( 'should provide detailed syntax error with line content' , async ( ) => {
396+ // Mock VM Script to throw a syntax error
397+ const mockScript = vi . fn ( ) . mockImplementation ( ( ) => {
398+ const error = new Error ( 'Invalid or unexpected token' )
399+ error . name = 'SyntaxError'
400+ error . stack = `user-function.js:5
401+ description: "This has a missing closing quote
402+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
403+
404+ SyntaxError: Invalid or unexpected token
405+ at new Script (node:vm:117:7)
406+ at POST (/path/to/route.ts:123:24)`
407+ throw error
408+ } )
409+
410+ vi . doMock ( 'vm' , ( ) => ( {
411+ createContext : mockCreateContext ,
412+ Script : mockScript ,
413+ } ) )
414+
415+ const req = createMockRequest ( 'POST' , {
416+ code : 'const obj = {\n name: "test",\n description: "This has a missing closing quote\n};\nreturn obj;' ,
417+ timeout : 5000 ,
418+ } )
419+
420+ const { POST } = await import ( './route' )
421+ const response = await POST ( req )
422+ const data = await response . json ( )
423+
424+ expect ( response . status ) . toBe ( 500 )
425+ expect ( data . success ) . toBe ( false )
426+ expect ( data . error ) . toContain ( 'Syntax Error' )
427+ expect ( data . error ) . toContain ( 'Line 3' )
428+ expect ( data . error ) . toContain ( 'description: "This has a missing closing quote' )
429+ expect ( data . error ) . toContain ( 'Invalid or unexpected token' )
430+ expect ( data . error ) . toContain ( '(Check for missing quotes, brackets, or semicolons)' )
431+
432+ // Check debug information
433+ expect ( data . debug ) . toBeDefined ( )
434+ expect ( data . debug . line ) . toBe ( 3 )
435+ expect ( data . debug . errorType ) . toBe ( 'SyntaxError' )
436+ expect ( data . debug . lineContent ) . toBe ( 'description: "This has a missing closing quote' )
437+ } )
438+
439+ it ( 'should provide detailed runtime error with line and column' , async ( ) => {
440+ // Create the error object first
441+ const runtimeError = new Error ( "Cannot read properties of null (reading 'someMethod')" )
442+ runtimeError . name = 'TypeError'
443+ runtimeError . stack = `TypeError: Cannot read properties of null (reading 'someMethod')
444+ at user-function.js:4:16
445+ at user-function.js:9:3
446+ at Script.runInContext (node:vm:147:14)`
447+
448+ // Mock successful script creation but runtime error
449+ const mockScript = vi . fn ( ) . mockImplementation ( ( ) => ( {
450+ runInContext : vi . fn ( ) . mockRejectedValue ( runtimeError ) ,
451+ } ) )
452+
453+ vi . doMock ( 'vm' , ( ) => ( {
454+ createContext : mockCreateContext ,
455+ Script : mockScript ,
456+ } ) )
457+
458+ const req = createMockRequest ( 'POST' , {
459+ code : 'const obj = null;\nreturn obj.someMethod();' ,
460+ timeout : 5000 ,
461+ } )
462+
463+ const { POST } = await import ( './route' )
464+ const response = await POST ( req )
465+ const data = await response . json ( )
466+
467+ expect ( response . status ) . toBe ( 500 )
468+ expect ( data . success ) . toBe ( false )
469+ expect ( data . error ) . toContain ( 'Type Error' )
470+ expect ( data . error ) . toContain ( 'Line 2' )
471+ expect ( data . error ) . toContain ( 'return obj.someMethod();' )
472+ expect ( data . error ) . toContain ( 'Cannot read properties of null' )
473+
474+ // Check debug information
475+ expect ( data . debug ) . toBeDefined ( )
476+ expect ( data . debug . line ) . toBe ( 2 )
477+ expect ( data . debug . column ) . toBe ( 16 )
478+ expect ( data . debug . errorType ) . toBe ( 'TypeError' )
479+ expect ( data . debug . lineContent ) . toBe ( 'return obj.someMethod();' )
480+ } )
481+
482+ it ( 'should handle ReferenceError with enhanced details' , async ( ) => {
483+ // Create the error object first
484+ const referenceError = new Error ( 'undefinedVariable is not defined' )
485+ referenceError . name = 'ReferenceError'
486+ referenceError . stack = `ReferenceError: undefinedVariable is not defined
487+ at user-function.js:4:8
488+ at Script.runInContext (node:vm:147:14)`
489+
490+ const mockScript = vi . fn ( ) . mockImplementation ( ( ) => ( {
491+ runInContext : vi . fn ( ) . mockRejectedValue ( referenceError ) ,
492+ } ) )
493+
494+ vi . doMock ( 'vm' , ( ) => ( {
495+ createContext : mockCreateContext ,
496+ Script : mockScript ,
497+ } ) )
498+
499+ const req = createMockRequest ( 'POST' , {
500+ code : 'const x = 42;\nreturn undefinedVariable + x;' ,
501+ timeout : 5000 ,
502+ } )
503+
504+ const { POST } = await import ( './route' )
505+ const response = await POST ( req )
506+ const data = await response . json ( )
507+
508+ expect ( response . status ) . toBe ( 500 )
509+ expect ( data . success ) . toBe ( false )
510+ expect ( data . error ) . toContain ( 'Reference Error' )
511+ expect ( data . error ) . toContain ( 'Line 2' )
512+ expect ( data . error ) . toContain ( 'return undefinedVariable + x;' )
513+ expect ( data . error ) . toContain ( 'undefinedVariable is not defined' )
514+ } )
515+
516+ it ( 'should handle errors without line content gracefully' , async ( ) => {
517+ const mockScript = vi . fn ( ) . mockImplementation ( ( ) => {
518+ const error = new Error ( 'Generic error without stack trace' )
519+ error . name = 'Error'
520+ // No stack trace
521+ throw error
522+ } )
523+
524+ vi . doMock ( 'vm' , ( ) => ( {
525+ createContext : mockCreateContext ,
526+ Script : mockScript ,
527+ } ) )
528+
529+ const req = createMockRequest ( 'POST' , {
530+ code : 'return "test";' ,
531+ timeout : 5000 ,
532+ } )
533+
534+ const { POST } = await import ( './route' )
535+ const response = await POST ( req )
536+ const data = await response . json ( )
537+
538+ expect ( response . status ) . toBe ( 500 )
539+ expect ( data . success ) . toBe ( false )
540+ expect ( data . error ) . toBe ( 'Generic error without stack trace' )
541+
542+ // Should still have debug info, but without line details
543+ expect ( data . debug ) . toBeDefined ( )
544+ expect ( data . debug . errorType ) . toBe ( 'Error' )
545+ expect ( data . debug . line ) . toBeUndefined ( )
546+ expect ( data . debug . lineContent ) . toBeUndefined ( )
547+ } )
548+
549+ it ( 'should extract line numbers from different stack trace formats' , async ( ) => {
550+ const mockScript = vi . fn ( ) . mockImplementation ( ( ) => {
551+ const error = new Error ( 'Test error' )
552+ error . name = 'Error'
553+ error . stack = `Error: Test error
554+ at user-function.js:7:25
555+ at async function
556+ at Script.runInContext (node:vm:147:14)`
557+ throw error
558+ } )
559+
560+ vi . doMock ( 'vm' , ( ) => ( {
561+ createContext : mockCreateContext ,
562+ Script : mockScript ,
563+ } ) )
564+
565+ const req = createMockRequest ( 'POST' , {
566+ code : 'const a = 1;\nconst b = 2;\nconst c = 3;\nconst d = 4;\nreturn a + b + c + d;' ,
567+ timeout : 5000 ,
568+ } )
569+
570+ const { POST } = await import ( './route' )
571+ const response = await POST ( req )
572+ const data = await response . json ( )
573+
574+ expect ( response . status ) . toBe ( 500 )
575+ expect ( data . success ) . toBe ( false )
576+
577+ // Line 7 in VM should map to line 5 in user code (7 - 3 + 1 = 5)
578+ expect ( data . debug . line ) . toBe ( 5 )
579+ expect ( data . debug . column ) . toBe ( 25 )
580+ expect ( data . debug . lineContent ) . toBe ( 'return a + b + c + d;' )
581+ } )
582+
583+ it ( 'should provide helpful suggestions for common syntax errors' , async ( ) => {
584+ const mockScript = vi . fn ( ) . mockImplementation ( ( ) => {
585+ const error = new Error ( 'Unexpected end of input' )
586+ error . name = 'SyntaxError'
587+ error . stack = 'user-function.js:4\nSyntaxError: Unexpected end of input'
588+ throw error
589+ } )
590+
591+ vi . doMock ( 'vm' , ( ) => ( {
592+ createContext : mockCreateContext ,
593+ Script : mockScript ,
594+ } ) )
595+
596+ const req = createMockRequest ( 'POST' , {
597+ code : 'const obj = {\n name: "test"\n// Missing closing brace' ,
598+ timeout : 5000 ,
599+ } )
600+
601+ const { POST } = await import ( './route' )
602+ const response = await POST ( req )
603+ const data = await response . json ( )
604+
605+ expect ( response . status ) . toBe ( 500 )
606+ expect ( data . success ) . toBe ( false )
607+ expect ( data . error ) . toContain ( 'Syntax Error' )
608+ expect ( data . error ) . toContain ( 'Unexpected end of input' )
609+ expect ( data . error ) . toContain ( '(Check for missing closing brackets or braces)' )
610+ } )
611+ } )
612+
394613 describe ( 'Utility Functions' , ( ) => {
395614 it ( 'should properly escape regex special characters' , async ( ) => {
396615 // This tests the escapeRegExp function indirectly
0 commit comments