You've already forked pocketbase
mirror of
https://github.com/pocketbase/pocketbase.git
synced 2025-11-27 16:28:27 +02:00
[#6935] added toBytes JSVM helper
This commit is contained in:
@@ -1,3 +1,8 @@
|
||||
## v0.29.0 (WIP)
|
||||
|
||||
- Added global JSVM `toBytes()` helper to return the bytes slice representation of a value such as io.Reader or string (_other types are first serialized to Go string_).
|
||||
|
||||
|
||||
## v0.28.3
|
||||
|
||||
- Skip sending empty `Range` header when fetching blobs from S3 ([#6914](https://github.com/pocketbase/pocketbase/pull/6914)).
|
||||
|
||||
@@ -309,6 +309,44 @@ func baseBinds(vm *goja.Runtime) {
|
||||
return string(bodyBytes), nil
|
||||
})
|
||||
|
||||
// note: throw only on reader error
|
||||
vm.Set("toBytes", func(raw any, maxReaderBytes int) ([]byte, error) {
|
||||
switch v := raw.(type) {
|
||||
case nil:
|
||||
return nil, nil
|
||||
case string:
|
||||
return []byte(v), nil
|
||||
case []byte:
|
||||
return v, nil
|
||||
case types.JSONRaw:
|
||||
return v, nil
|
||||
case io.Reader:
|
||||
if maxReaderBytes == 0 {
|
||||
maxReaderBytes = router.DefaultMaxMemory
|
||||
}
|
||||
|
||||
limitReader := io.LimitReader(v, int64(maxReaderBytes))
|
||||
|
||||
return io.ReadAll(limitReader)
|
||||
default:
|
||||
b, err := cast.ToUint8SliceE(v)
|
||||
if err == nil {
|
||||
return b, nil
|
||||
}
|
||||
|
||||
str, err := cast.ToStringE(v)
|
||||
if err == nil {
|
||||
return []byte(str), nil
|
||||
}
|
||||
|
||||
// as a last attempt try to json encode the value
|
||||
rawBytes, _ := json.Marshal(raw)
|
||||
|
||||
return rawBytes, nil
|
||||
}
|
||||
})
|
||||
|
||||
// note: throw only on reader error
|
||||
vm.Set("toString", func(raw any, maxReaderBytes int) (string, error) {
|
||||
switch v := raw.(type) {
|
||||
case io.Reader:
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package jsvm
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
@@ -22,6 +23,7 @@ import (
|
||||
"github.com/pocketbase/pocketbase/tools/filesystem"
|
||||
"github.com/pocketbase/pocketbase/tools/mailer"
|
||||
"github.com/pocketbase/pocketbase/tools/router"
|
||||
"github.com/pocketbase/pocketbase/tools/types"
|
||||
"github.com/spf13/cast"
|
||||
)
|
||||
|
||||
@@ -44,7 +46,7 @@ func TestBaseBindsCount(t *testing.T) {
|
||||
vm := goja.New()
|
||||
baseBinds(vm)
|
||||
|
||||
testBindsCount(vm, "this", 34, t)
|
||||
testBindsCount(vm, "this", 35, t)
|
||||
}
|
||||
|
||||
func TestBaseBindsSleep(t *testing.T) {
|
||||
@@ -83,7 +85,7 @@ func TestBaseBindsReaderToString(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestBaseBindsToStringAndToBytes(t *testing.T) {
|
||||
func TestBaseBindsToString(t *testing.T) {
|
||||
vm := goja.New()
|
||||
baseBinds(vm)
|
||||
vm.Set("scenarios", []struct {
|
||||
@@ -106,10 +108,49 @@ func TestBaseBindsToStringAndToBytes(t *testing.T) {
|
||||
|
||||
_, err := vm.RunString(`
|
||||
for (let s of scenarios) {
|
||||
let result = toString(s.value)
|
||||
let str = toString(s.value)
|
||||
if (str != s.expected) {
|
||||
throw new Error('[' + s.name + '] Expected string ' + s.expected + ', got ' + str);
|
||||
}
|
||||
}
|
||||
`)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
if (result != s.expected) {
|
||||
throw new Error('[' + s.name + '] Expected string ' + s.expected + ', got ' + result);
|
||||
func TestBaseBindsToBytes(t *testing.T) {
|
||||
vm := goja.New()
|
||||
baseBinds(vm)
|
||||
vm.Set("bytesEqual", bytes.Equal)
|
||||
vm.Set("scenarios", []struct {
|
||||
Name string
|
||||
Value any
|
||||
Expected []byte
|
||||
}{
|
||||
{"null", nil, nil},
|
||||
{"string", "test", []byte("test")},
|
||||
{"number", -12.4, []byte("-12.4")},
|
||||
{"bool", true, []byte("true")},
|
||||
{"arr", []int{1, 2, 3}, []byte{1, 2, 3}},
|
||||
{"jsonraw", types.JSONRaw{1, 2, 3}, []byte{1, 2, 3}},
|
||||
{"reader", strings.NewReader("test"), []byte("test")},
|
||||
{"obj", map[string]any{"test": 123}, []byte(`{"test":123}`)},
|
||||
{"struct", struct {
|
||||
Name string
|
||||
private string
|
||||
}{Name: "123", private: "456"}, []byte(`{"Name":"123"}`)},
|
||||
})
|
||||
|
||||
_, err := vm.RunString(`
|
||||
for (let s of scenarios) {
|
||||
let b = toBytes(s.value)
|
||||
if (!Array.isArray(b)) {
|
||||
throw new Error('[' + s.name + '] Expected toBytes to return an array');
|
||||
}
|
||||
|
||||
if (!bytesEqual(b, s.expected)) {
|
||||
throw new Error('[' + s.name + '] Expected bytes ' + s.expected + ', got ' + b);
|
||||
}
|
||||
}
|
||||
`)
|
||||
|
||||
5116
plugins/jsvm/internal/types/generated/types.d.ts
vendored
5116
plugins/jsvm/internal/types/generated/types.d.ts
vendored
File diff suppressed because it is too large
Load Diff
@@ -194,6 +194,32 @@ declare function readerToString(reader: any, maxBytes?: number): string;
|
||||
*/
|
||||
declare function toString(val: any, maxBytes?: number): string;
|
||||
|
||||
/**
|
||||
* toBytes converts the specified value into a bytes slice.
|
||||
*
|
||||
* Support optional second maxBytes argument to limit the max read bytes
|
||||
* when the value is a io.Reader (default to 32MB).
|
||||
*
|
||||
* Types that don't have Go slice representation (bool, objects, etc.)
|
||||
* are serialized to UTF8 string and its bytes slice is returned.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ` + "```" + `js
|
||||
* // io.Reader
|
||||
* const ex1 = toBytes(e.request.body)
|
||||
*
|
||||
* // string
|
||||
* const ex2 = toBytes("hello") // [104 101 108 108 111]
|
||||
*
|
||||
* // object (the same as the string '{"test":1}')
|
||||
* const ex2 = toBytes({"test":1}) // [123 34 116 101 115 116 34 58 49 125]
|
||||
* ` + "```" + `
|
||||
*
|
||||
* @group PocketBase
|
||||
*/
|
||||
declare function toBytes(val: any, maxBytes?: number): Array<number>;
|
||||
|
||||
/**
|
||||
* sleep pauses the current goroutine for at least the specified user duration (in ms).
|
||||
* A zero or negative duration returns immediately.
|
||||
|
||||
Reference in New Issue
Block a user