1
0
mirror of https://github.com/pocketbase/pocketbase.git synced 2025-11-25 07:34:10 +02:00

[#6102] fixed JSVM exception -> Go error unwrapping

This commit is contained in:
Gani Georgiev
2024-12-13 17:54:43 +02:00
parent 4f35fb74c8
commit 3098c2dcd8
33 changed files with 119 additions and 60 deletions

View File

@@ -81,14 +81,14 @@ func hooksBinds(app core.App, loader *goja.Runtime, executors *vmsPool) {
res, err := executor.RunProgram(pr)
executor.Set("__args", goja.Undefined())
// check for returned error value
// (legacy) check for returned Go error value
if res != nil {
if resErr, ok := res.Export().(error); ok {
return resErr
}
}
return err
return normalizeException(err)
})
return []reflect.Value{reflect.ValueOf(&err).Elem()}
@@ -197,14 +197,14 @@ func wrapHandlerFunc(executors *vmsPool, handler goja.Value) (func(*core.Request
res, err := executor.RunProgram(pr)
executor.Set("__args", goja.Undefined())
// check for returned error
// (legacy) check for returned Go error value
if res != nil {
if v, ok := res.Export().(error); ok {
return v
}
}
return err
return normalizeException(err)
})
}
@@ -215,9 +215,9 @@ func wrapHandlerFunc(executors *vmsPool, handler goja.Value) (func(*core.Request
}
type gojaHookHandler struct {
priority int
id string
serializedFunc string
priority int
}
func wrapMiddlewares(executors *vmsPool, rawMiddlewares ...goja.Value) ([]*hook.Handler[*core.RequestEvent], error) {
@@ -254,14 +254,14 @@ func wrapMiddlewares(executors *vmsPool, rawMiddlewares ...goja.Value) ([]*hook.
res, err := executor.RunProgram(pr)
executor.Set("__args", goja.Undefined())
// check for returned error
// (legacy) check for returned Go error value
if res != nil {
if v, ok := res.Export().(error); ok {
return v
}
}
return err
return normalizeException(err)
})
},
}
@@ -276,14 +276,14 @@ func wrapMiddlewares(executors *vmsPool, rawMiddlewares ...goja.Value) ([]*hook.
res, err := executor.RunProgram(pr)
executor.Set("__args", goja.Undefined())
// check for returned error
// (legacy) check for returned Go error value
if res != nil {
if v, ok := res.Export().(error); ok {
return v
}
}
return err
return normalizeException(err)
})
},
}
@@ -1019,3 +1019,29 @@ func newDynamicModel(shape map[string]any) any {
return elem.Addr().Interface()
}
// normalizeException checks if the provided error is a goja.Exception
// and attempts to return its underlying Go error.
//
// note: using just goja.Exception.Unwrap() is insufficient and may falsely result in nil.
func normalizeException(err error) error {
if err == nil {
return nil
}
jsException, ok := err.(*goja.Exception)
if !ok {
return err // no exception
}
switch v := jsException.Value().Export().(type) {
case error:
err = v
case map[string]any: // goja.GoError
if vErr, ok := v["value"].(error); ok {
err = vErr
}
}
return err
}

View File

@@ -2,6 +2,7 @@ package jsvm
import (
"encoding/json"
"errors"
"fmt"
"io"
"mime/multipart"
@@ -1456,6 +1457,47 @@ func TestHooksBinds(t *testing.T) {
}
}
func TestHooksExceptionUnwrapping(t *testing.T) {
app, _ := tests.NewTestApp()
defer app.Cleanup()
goErr := errors.New("test")
vmFactory := func() *goja.Runtime {
vm := goja.New()
baseBinds(vm)
vm.Set("$app", app)
vm.Set("goErr", goErr)
return vm
}
pool := newPool(1, vmFactory)
vm := vmFactory()
hooksBinds(app, vm, pool)
_, err := vm.RunString(`
onModelUpdate((e) => {
throw goErr
}, "demo1")
`)
if err != nil {
t.Fatal(err)
}
record, err := app.FindFirstRecordByFilter("demo1", "1=1")
if err != nil {
t.Fatal(err)
}
record.Set("text", "update")
err = app.Save(record)
if !errors.Is(err, goErr) {
t.Fatalf("Expected goError, got %v", err)
}
}
func TestRouterBindsCount(t *testing.T) {
app, _ := tests.NewTestApp()
defer app.Cleanup()

View File

@@ -326,21 +326,7 @@ func (p *plugin) normalizeServeExceptions(e *core.RequestEvent) error {
return err // no error or already committed
}
jsException, ok := err.(*goja.Exception)
if !ok {
return err // no exception
}
switch v := jsException.Value().Export().(type) {
case error:
err = v
case map[string]any: // goja.GoError
if vErr, ok := v["value"].(error); ok {
err = vErr
}
}
return err
return normalizeException(err)
}
// watchHooks initializes a hooks file watcher that will restart the