mirror of
https://github.com/syumai/workers.git
synced 2025-03-11 09:49:12 +00:00
Merge pull request #14 from syumai/split-cloudflare-package
split cloudflare package
This commit is contained in:
commit
af65c8d1ea
@ -1,9 +1,11 @@
|
|||||||
package workers
|
package cloudflare
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"syscall/js"
|
"syscall/js"
|
||||||
|
|
||||||
|
"github.com/syumai/workers/internal/jsutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
// KVNamespace represents interface of Cloudflare Worker's KV namespace instance.
|
// KVNamespace represents interface of Cloudflare Worker's KV namespace instance.
|
||||||
@ -15,7 +17,7 @@ type KVNamespace struct {
|
|||||||
|
|
||||||
// NewKVNamespace returns KVNamespace for given variable name.
|
// NewKVNamespace returns KVNamespace for given variable name.
|
||||||
// - variable name must be defined in wrangler.toml as kv_namespace's binding.
|
// - variable name must be defined in wrangler.toml as kv_namespace's binding.
|
||||||
// - if the given variable name doesn't exist on global object, returns error.
|
// - if the given variable name doesn't exist on Global object, returns error.
|
||||||
func NewKVNamespace(varName string) (*KVNamespace, error) {
|
func NewKVNamespace(varName string) (*KVNamespace, error) {
|
||||||
inst := js.Global().Get(varName)
|
inst := js.Global().Get(varName)
|
||||||
if inst.IsUndefined() {
|
if inst.IsUndefined() {
|
||||||
@ -31,7 +33,7 @@ type KVNamespaceGetOptions struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (opts *KVNamespaceGetOptions) toJS(type_ string) js.Value {
|
func (opts *KVNamespaceGetOptions) toJS(type_ string) js.Value {
|
||||||
obj := newObject()
|
obj := jsutil.NewObject()
|
||||||
obj.Set("type", type_)
|
obj.Set("type", type_)
|
||||||
if opts == nil {
|
if opts == nil {
|
||||||
return obj
|
return obj
|
||||||
@ -46,7 +48,7 @@ func (opts *KVNamespaceGetOptions) toJS(type_ string) js.Value {
|
|||||||
// - if a network error happens, returns error.
|
// - if a network error happens, returns error.
|
||||||
func (kv *KVNamespace) GetString(key string, opts *KVNamespaceGetOptions) (string, error) {
|
func (kv *KVNamespace) GetString(key string, opts *KVNamespaceGetOptions) (string, error) {
|
||||||
p := kv.instance.Call("get", key, opts.toJS("text"))
|
p := kv.instance.Call("get", key, opts.toJS("text"))
|
||||||
v, err := awaitPromise(p)
|
v, err := jsutil.AwaitPromise(p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
@ -57,12 +59,12 @@ func (kv *KVNamespace) GetString(key string, opts *KVNamespaceGetOptions) (strin
|
|||||||
// - if a network error happens, returns error.
|
// - if a network error happens, returns error.
|
||||||
func (kv *KVNamespace) GetReader(key string, opts *KVNamespaceGetOptions) (io.Reader, error) {
|
func (kv *KVNamespace) GetReader(key string, opts *KVNamespaceGetOptions) (io.Reader, error) {
|
||||||
p := kv.instance.Call("get", key, opts.toJS("stream"))
|
p := kv.instance.Call("get", key, opts.toJS("stream"))
|
||||||
v, err := awaitPromise(p)
|
v, err := jsutil.AwaitPromise(p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
global.Get("console").Call("log", v)
|
jsutil.Global.Get("console").Call("log", v)
|
||||||
return convertStreamReaderToReader(v.Call("getReader")), nil
|
return jsutil.ConvertStreamReaderToReader(v.Call("getReader")), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// KVNamespaceListOptions represents Cloudflare KV namespace list options.
|
// KVNamespaceListOptions represents Cloudflare KV namespace list options.
|
||||||
@ -77,7 +79,7 @@ func (opts *KVNamespaceListOptions) toJS() js.Value {
|
|||||||
if opts == nil {
|
if opts == nil {
|
||||||
return js.Undefined()
|
return js.Undefined()
|
||||||
}
|
}
|
||||||
obj := newObject()
|
obj := jsutil.NewObject()
|
||||||
if opts.Limit != 0 {
|
if opts.Limit != 0 {
|
||||||
obj.Set("limit", opts.Limit)
|
obj.Set("limit", opts.Limit)
|
||||||
}
|
}
|
||||||
@ -151,7 +153,7 @@ func toKVNamespaceListResult(v js.Value) (*KVNamespaceListResult, error) {
|
|||||||
// List lists keys stored into the KV namespace.
|
// List lists keys stored into the KV namespace.
|
||||||
func (kv *KVNamespace) List(opts *KVNamespaceListOptions) (*KVNamespaceListResult, error) {
|
func (kv *KVNamespace) List(opts *KVNamespaceListOptions) (*KVNamespaceListResult, error) {
|
||||||
p := kv.instance.Call("list", opts.toJS())
|
p := kv.instance.Call("list", opts.toJS())
|
||||||
v, err := awaitPromise(p)
|
v, err := jsutil.AwaitPromise(p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -170,7 +172,7 @@ func (opts *KVNamespacePutOptions) toJS() js.Value {
|
|||||||
if opts == nil {
|
if opts == nil {
|
||||||
return js.Undefined()
|
return js.Undefined()
|
||||||
}
|
}
|
||||||
obj := newObject()
|
obj := jsutil.NewObject()
|
||||||
if opts.Expiration != 0 {
|
if opts.Expiration != 0 {
|
||||||
obj.Set("expiration", opts.Expiration)
|
obj.Set("expiration", opts.Expiration)
|
||||||
}
|
}
|
||||||
@ -184,7 +186,7 @@ func (opts *KVNamespacePutOptions) toJS() js.Value {
|
|||||||
// - if a network error happens, returns error.
|
// - if a network error happens, returns error.
|
||||||
func (kv *KVNamespace) PutString(key string, value string, opts *KVNamespacePutOptions) error {
|
func (kv *KVNamespace) PutString(key string, value string, opts *KVNamespacePutOptions) error {
|
||||||
p := kv.instance.Call("put", key, value, opts.toJS())
|
p := kv.instance.Call("put", key, value, opts.toJS())
|
||||||
_, err := awaitPromise(p)
|
_, err := jsutil.AwaitPromise(p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -200,10 +202,10 @@ func (kv *KVNamespace) PutReader(key string, value io.Reader, opts *KVNamespaceP
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
ua := newUint8Array(len(b))
|
ua := jsutil.NewUint8Array(len(b))
|
||||||
js.CopyBytesToJS(ua, b)
|
js.CopyBytesToJS(ua, b)
|
||||||
p := kv.instance.Call("put", key, ua.Get("buffer"), opts.toJS())
|
p := kv.instance.Call("put", key, ua.Get("buffer"), opts.toJS())
|
||||||
_, err = awaitPromise(p)
|
_, err = jsutil.AwaitPromise(p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -214,7 +216,7 @@ func (kv *KVNamespace) PutReader(key string, value io.Reader, opts *KVNamespaceP
|
|||||||
// - if a network error happens, returns error.
|
// - if a network error happens, returns error.
|
||||||
func (kv *KVNamespace) Delete(key string) error {
|
func (kv *KVNamespace) Delete(key string) error {
|
||||||
p := kv.instance.Call("delete", key)
|
p := kv.instance.Call("delete", key)
|
||||||
_, err := awaitPromise(p)
|
_, err := jsutil.AwaitPromise(p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
@ -1,9 +1,11 @@
|
|||||||
package workers
|
package cloudflare
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"syscall/js"
|
"syscall/js"
|
||||||
|
|
||||||
|
"github.com/syumai/workers/internal/jsutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
// R2Bucket represents interface of Cloudflare Worker's R2 Bucket instance.
|
// R2Bucket represents interface of Cloudflare Worker's R2 Bucket instance.
|
||||||
@ -15,8 +17,8 @@ type R2Bucket struct {
|
|||||||
|
|
||||||
// NewR2Bucket returns R2Bucket for given variable name.
|
// NewR2Bucket returns R2Bucket for given variable name.
|
||||||
// - variable name must be defined in wrangler.toml.
|
// - variable name must be defined in wrangler.toml.
|
||||||
// - see example: https://github.com/syumai/workers/tree/main/examples/r2-image-viewer
|
// - see example: https://github.com/syumai/workers/tree/main/examples/r2-image-viewer
|
||||||
// - if the given variable name doesn't exist on global object, returns error.
|
// - if the given variable name doesn't exist on Global object, returns error.
|
||||||
func NewR2Bucket(varName string) (*R2Bucket, error) {
|
func NewR2Bucket(varName string) (*R2Bucket, error) {
|
||||||
inst := js.Global().Get(varName)
|
inst := js.Global().Get(varName)
|
||||||
if inst.IsUndefined() {
|
if inst.IsUndefined() {
|
||||||
@ -31,7 +33,7 @@ func NewR2Bucket(varName string) (*R2Bucket, error) {
|
|||||||
// - if a network error happens, returns error.
|
// - if a network error happens, returns error.
|
||||||
func (r *R2Bucket) Head(key string) (*R2Object, error) {
|
func (r *R2Bucket) Head(key string) (*R2Object, error) {
|
||||||
p := r.instance.Call("head", key)
|
p := r.instance.Call("head", key)
|
||||||
v, err := awaitPromise(p)
|
v, err := jsutil.AwaitPromise(p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -46,7 +48,7 @@ func (r *R2Bucket) Head(key string) (*R2Object, error) {
|
|||||||
// - if a network error happens, returns error.
|
// - if a network error happens, returns error.
|
||||||
func (r *R2Bucket) Get(key string) (*R2Object, error) {
|
func (r *R2Bucket) Get(key string) (*R2Object, error) {
|
||||||
p := r.instance.Call("get", key)
|
p := r.instance.Call("get", key)
|
||||||
v, err := awaitPromise(p)
|
v, err := jsutil.AwaitPromise(p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -68,7 +70,7 @@ func (opts *R2PutOptions) toJS() js.Value {
|
|||||||
if opts == nil {
|
if opts == nil {
|
||||||
return js.Undefined()
|
return js.Undefined()
|
||||||
}
|
}
|
||||||
obj := newObject()
|
obj := jsutil.NewObject()
|
||||||
if opts.HTTPMetadata != (R2HTTPMetadata{}) {
|
if opts.HTTPMetadata != (R2HTTPMetadata{}) {
|
||||||
obj.Set("httpMetadata", opts.HTTPMetadata.toJS())
|
obj.Set("httpMetadata", opts.HTTPMetadata.toJS())
|
||||||
}
|
}
|
||||||
@ -99,10 +101,10 @@ func (r *R2Bucket) Put(key string, value io.ReadCloser, opts *R2PutOptions) (*R2
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer value.Close()
|
defer value.Close()
|
||||||
ua := newUint8Array(len(b))
|
ua := jsutil.NewUint8Array(len(b))
|
||||||
js.CopyBytesToJS(ua, b)
|
js.CopyBytesToJS(ua, b)
|
||||||
p := r.instance.Call("put", key, ua.Get("buffer"), opts.toJS())
|
p := r.instance.Call("put", key, ua.Get("buffer"), opts.toJS())
|
||||||
v, err := awaitPromise(p)
|
v, err := jsutil.AwaitPromise(p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -113,7 +115,7 @@ func (r *R2Bucket) Put(key string, value io.ReadCloser, opts *R2PutOptions) (*R2
|
|||||||
// - if a network error happens, returns error.
|
// - if a network error happens, returns error.
|
||||||
func (r *R2Bucket) Delete(key string) error {
|
func (r *R2Bucket) Delete(key string) error {
|
||||||
p := r.instance.Call("delete", key)
|
p := r.instance.Call("delete", key)
|
||||||
if _, err := awaitPromise(p); err != nil {
|
if _, err := jsutil.AwaitPromise(p); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@ -123,7 +125,7 @@ func (r *R2Bucket) Delete(key string) error {
|
|||||||
// - if a network error happens, returns error.
|
// - if a network error happens, returns error.
|
||||||
func (r *R2Bucket) List() (*R2Objects, error) {
|
func (r *R2Bucket) List() (*R2Objects, error) {
|
||||||
p := r.instance.Call("list")
|
p := r.instance.Call("list")
|
||||||
v, err := awaitPromise(p)
|
v, err := jsutil.AwaitPromise(p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package workers
|
package cloudflare
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
@ -6,6 +6,8 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"syscall/js"
|
"syscall/js"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/syumai/workers/internal/jsutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
// R2Object represents Cloudflare R2 object.
|
// R2Object represents Cloudflare R2 object.
|
||||||
@ -41,7 +43,7 @@ func (o *R2Object) BodyUsed() (bool, error) {
|
|||||||
// toR2Object converts JavaScript side's R2Object to *R2Object.
|
// toR2Object converts JavaScript side's R2Object to *R2Object.
|
||||||
// - https://github.com/cloudflare/workers-types/blob/3012f263fb1239825e5f0061b267c8650d01b717/index.d.ts#L1094
|
// - https://github.com/cloudflare/workers-types/blob/3012f263fb1239825e5f0061b267c8650d01b717/index.d.ts#L1094
|
||||||
func toR2Object(v js.Value) (*R2Object, error) {
|
func toR2Object(v js.Value) (*R2Object, error) {
|
||||||
uploaded, err := dateToTime(v.Get("uploaded"))
|
uploaded, err := jsutil.DateToTime(v.Get("uploaded"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error converting uploaded: %w", err)
|
return nil, fmt.Errorf("error converting uploaded: %w", err)
|
||||||
}
|
}
|
||||||
@ -52,7 +54,7 @@ func toR2Object(v js.Value) (*R2Object, error) {
|
|||||||
bodyVal := v.Get("body")
|
bodyVal := v.Get("body")
|
||||||
var body io.Reader
|
var body io.Reader
|
||||||
if !bodyVal.IsUndefined() {
|
if !bodyVal.IsUndefined() {
|
||||||
body = convertStreamReaderToReader(v.Get("body").Call("getReader"))
|
body = jsutil.ConvertStreamReaderToReader(v.Get("body").Call("getReader"))
|
||||||
}
|
}
|
||||||
return &R2Object{
|
return &R2Object{
|
||||||
instance: v,
|
instance: v,
|
||||||
@ -63,7 +65,7 @@ func toR2Object(v js.Value) (*R2Object, error) {
|
|||||||
HTTPETag: v.Get("httpEtag").String(),
|
HTTPETag: v.Get("httpEtag").String(),
|
||||||
Uploaded: uploaded,
|
Uploaded: uploaded,
|
||||||
HTTPMetadata: r2Meta,
|
HTTPMetadata: r2Meta,
|
||||||
CustomMetadata: strRecordToMap(v.Get("customMetadata")),
|
CustomMetadata: jsutil.StrRecordToMap(v.Get("customMetadata")),
|
||||||
Body: body,
|
Body: body,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
@ -80,22 +82,22 @@ type R2HTTPMetadata struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func toR2HTTPMetadata(v js.Value) (R2HTTPMetadata, error) {
|
func toR2HTTPMetadata(v js.Value) (R2HTTPMetadata, error) {
|
||||||
cacheExpiry, err := maybeDate(v.Get("cacheExpiry"))
|
cacheExpiry, err := jsutil.MaybeDate(v.Get("cacheExpiry"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return R2HTTPMetadata{}, fmt.Errorf("error converting cacheExpiry: %w", err)
|
return R2HTTPMetadata{}, fmt.Errorf("error converting cacheExpiry: %w", err)
|
||||||
}
|
}
|
||||||
return R2HTTPMetadata{
|
return R2HTTPMetadata{
|
||||||
ContentType: maybeString(v.Get("contentType")),
|
ContentType: jsutil.MaybeString(v.Get("contentType")),
|
||||||
ContentLanguage: maybeString(v.Get("contentLanguage")),
|
ContentLanguage: jsutil.MaybeString(v.Get("contentLanguage")),
|
||||||
ContentDisposition: maybeString(v.Get("contentDisposition")),
|
ContentDisposition: jsutil.MaybeString(v.Get("contentDisposition")),
|
||||||
ContentEncoding: maybeString(v.Get("contentEncoding")),
|
ContentEncoding: jsutil.MaybeString(v.Get("contentEncoding")),
|
||||||
CacheControl: maybeString(v.Get("cacheControl")),
|
CacheControl: jsutil.MaybeString(v.Get("cacheControl")),
|
||||||
CacheExpiry: cacheExpiry,
|
CacheExpiry: cacheExpiry,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (md *R2HTTPMetadata) toJS() js.Value {
|
func (md *R2HTTPMetadata) toJS() js.Value {
|
||||||
obj := newObject()
|
obj := jsutil.NewObject()
|
||||||
kv := map[string]string{
|
kv := map[string]string{
|
||||||
"contentType": md.ContentType,
|
"contentType": md.ContentType,
|
||||||
"contentLanguage": md.ContentLanguage,
|
"contentLanguage": md.ContentLanguage,
|
||||||
@ -109,7 +111,7 @@ func (md *R2HTTPMetadata) toJS() js.Value {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !md.CacheExpiry.IsZero() {
|
if !md.CacheExpiry.IsZero() {
|
||||||
obj.Set("cacheExpiry", timeToDate(md.CacheExpiry))
|
obj.Set("cacheExpiry", jsutil.TimeToDate(md.CacheExpiry))
|
||||||
}
|
}
|
||||||
return obj
|
return obj
|
||||||
}
|
}
|
@ -1,8 +1,10 @@
|
|||||||
package workers
|
package cloudflare
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"syscall/js"
|
"syscall/js"
|
||||||
|
|
||||||
|
"github.com/syumai/workers/internal/jsutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
// R2Objects represents Cloudflare R2 objects.
|
// R2Objects represents Cloudflare R2 objects.
|
||||||
@ -36,7 +38,7 @@ func toR2Objects(v js.Value) (*R2Objects, error) {
|
|||||||
return &R2Objects{
|
return &R2Objects{
|
||||||
Objects: objects,
|
Objects: objects,
|
||||||
Truncated: v.Get("truncated").Bool(),
|
Truncated: v.Get("truncated").Bool(),
|
||||||
Cursor: maybeString(v.Get("cursor")),
|
Cursor: jsutil.MaybeString(v.Get("cursor")),
|
||||||
DelimitedPrefixes: prefixes,
|
DelimitedPrefixes: prefixes,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
4
env.go
4
env.go
@ -1,8 +1,10 @@
|
|||||||
package workers
|
package workers
|
||||||
|
|
||||||
|
import "github.com/syumai/workers/internal/jsutil"
|
||||||
|
|
||||||
// Getenv gets a value of an environment variable.
|
// Getenv gets a value of an environment variable.
|
||||||
// - https://developers.cloudflare.com/workers/platform/environment-variables/
|
// - https://developers.cloudflare.com/workers/platform/environment-variables/
|
||||||
// - Technically, this function is just an alias for js.Global().Get(env_name).String().
|
// - Technically, this function is just an alias for js.Global().Get(env_name).String().
|
||||||
func Getenv(name string) string {
|
func Getenv(name string) string {
|
||||||
return global.Get(name).String()
|
return jsutil.Global.Get(name).String()
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,8 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/syumai/workers/cloudflare"
|
||||||
|
|
||||||
"github.com/syumai/workers"
|
"github.com/syumai/workers"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -24,7 +26,7 @@ func handleErr(w http.ResponseWriter, msg string, err error) {
|
|||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
// initialize KV namespace instance
|
// initialize KV namespace instance
|
||||||
kv, err := workers.NewKVNamespace(counterNamespace)
|
kv, err := cloudflare.NewKVNamespace(counterNamespace)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "failed to init KV: %v", err)
|
fmt.Fprintf(os.Stderr, "failed to init KV: %v", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
|
@ -9,6 +9,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/syumai/workers"
|
"github.com/syumai/workers"
|
||||||
|
"github.com/syumai/workers/cloudflare"
|
||||||
)
|
)
|
||||||
|
|
||||||
// bucketName is R2 bucket name defined in wrangler.toml.
|
// bucketName is R2 bucket name defined in wrangler.toml.
|
||||||
@ -22,11 +23,11 @@ func handleErr(w http.ResponseWriter, msg string, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type server struct {
|
type server struct {
|
||||||
bucket *workers.R2Bucket
|
bucket *cloudflare.R2Bucket
|
||||||
}
|
}
|
||||||
|
|
||||||
func newServer() (*server, error) {
|
func newServer() (*server, error) {
|
||||||
bucket, err := workers.NewR2Bucket(bucketName)
|
bucket, err := cloudflare.NewR2Bucket(bucketName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -46,8 +47,8 @@ func (s *server) post(w http.ResponseWriter, req *http.Request, key string) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_, err = s.bucket.Put(key, req.Body, &workers.R2PutOptions{
|
_, err = s.bucket.Put(key, req.Body, &cloudflare.R2PutOptions{
|
||||||
HTTPMetadata: workers.R2HTTPMetadata{
|
HTTPMetadata: cloudflare.R2HTTPMetadata{
|
||||||
ContentType: req.Header.Get("Content-Type"),
|
ContentType: req.Header.Get("Content-Type"),
|
||||||
},
|
},
|
||||||
CustomMetadata: map[string]string{"custom-key": "custom-value"},
|
CustomMetadata: map[string]string{"custom-key": "custom-value"},
|
||||||
|
@ -8,6 +8,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/syumai/workers"
|
"github.com/syumai/workers"
|
||||||
|
"github.com/syumai/workers/cloudflare"
|
||||||
)
|
)
|
||||||
|
|
||||||
// bucketName is R2 bucket name defined in wrangler.toml.
|
// bucketName is R2 bucket name defined in wrangler.toml.
|
||||||
@ -22,7 +23,7 @@ func handleErr(w http.ResponseWriter, msg string, err error) {
|
|||||||
// This example is based on implementation in syumai/workers-playground
|
// This example is based on implementation in syumai/workers-playground
|
||||||
// - https://github.com/syumai/workers-playground/blob/e32881648ccc055e3690a0d9c750a834261c333e/r2-image-viewer/src/index.ts#L30
|
// - https://github.com/syumai/workers-playground/blob/e32881648ccc055e3690a0d9c750a834261c333e/r2-image-viewer/src/index.ts#L30
|
||||||
func handler(w http.ResponseWriter, req *http.Request) {
|
func handler(w http.ResponseWriter, req *http.Request) {
|
||||||
bucket, err := workers.NewR2Bucket(bucketName)
|
bucket, err := cloudflare.NewR2Bucket(bucketName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
handleErr(w, "failed to get R2Bucket\n", err)
|
handleErr(w, "failed to get R2Bucket\n", err)
|
||||||
return
|
return
|
||||||
|
@ -5,6 +5,8 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"syscall/js"
|
"syscall/js"
|
||||||
|
|
||||||
|
"github.com/syumai/workers/internal/jsutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
var httpHandler http.Handler
|
var httpHandler http.Handler
|
||||||
@ -28,9 +30,9 @@ func init() {
|
|||||||
}()
|
}()
|
||||||
return js.Undefined()
|
return js.Undefined()
|
||||||
})
|
})
|
||||||
return newPromise(cb)
|
return jsutil.NewPromise(cb)
|
||||||
})
|
})
|
||||||
global.Set("handleRequest", handleRequestCallback)
|
jsutil.Global.Set("handleRequest", handleRequestCallback)
|
||||||
}
|
}
|
||||||
|
|
||||||
// handleRequest accepts a Request object and returns Response object.
|
// handleRequest accepts a Request object and returns Response object.
|
||||||
|
103
internal/jsutil/jsutil.go
Normal file
103
internal/jsutil/jsutil.go
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
package jsutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"syscall/js"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
Global = js.Global()
|
||||||
|
ObjectClass = Global.Get("Object")
|
||||||
|
PromiseClass = Global.Get("Promise")
|
||||||
|
ResponseClass = Global.Get("Response")
|
||||||
|
HeadersClass = Global.Get("Headers")
|
||||||
|
ArrayClass = Global.Get("Array")
|
||||||
|
Uint8ArrayClass = Global.Get("Uint8Array")
|
||||||
|
ErrorClass = Global.Get("Error")
|
||||||
|
ReadableStreamClass = Global.Get("ReadableStream")
|
||||||
|
DateClass = Global.Get("Date")
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewObject() js.Value {
|
||||||
|
return ObjectClass.New()
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewUint8Array(size int) js.Value {
|
||||||
|
return Uint8ArrayClass.New(size)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPromise(fn js.Func) js.Value {
|
||||||
|
return PromiseClass.New(fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ArrayFrom calls Array.from to given argument and returns result Array.
|
||||||
|
func ArrayFrom(v js.Value) js.Value {
|
||||||
|
return ArrayClass.Call("from", v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func AwaitPromise(promiseVal js.Value) (js.Value, error) {
|
||||||
|
resultCh := make(chan js.Value)
|
||||||
|
errCh := make(chan error)
|
||||||
|
var then, catch js.Func
|
||||||
|
then = js.FuncOf(func(_ js.Value, args []js.Value) any {
|
||||||
|
defer then.Release()
|
||||||
|
result := args[0]
|
||||||
|
resultCh <- result
|
||||||
|
return js.Undefined()
|
||||||
|
})
|
||||||
|
catch = js.FuncOf(func(_ js.Value, args []js.Value) any {
|
||||||
|
defer catch.Release()
|
||||||
|
result := args[0]
|
||||||
|
errCh <- fmt.Errorf("failed on promise: %s", result.Call("toString").String())
|
||||||
|
return js.Undefined()
|
||||||
|
})
|
||||||
|
promiseVal.Call("then", then).Call("catch", catch)
|
||||||
|
select {
|
||||||
|
case result := <-resultCh:
|
||||||
|
return result, nil
|
||||||
|
case err := <-errCh:
|
||||||
|
return js.Value{}, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// StrRecordToMap converts JavaScript side's Record<string, string> into map[string]string.
|
||||||
|
func StrRecordToMap(v js.Value) map[string]string {
|
||||||
|
entries := ObjectClass.Call("entries", v)
|
||||||
|
entriesLen := entries.Get("length").Int()
|
||||||
|
result := make(map[string]string, entriesLen)
|
||||||
|
for i := 0; i < entriesLen; i++ {
|
||||||
|
entry := entries.Index(i)
|
||||||
|
key := entry.Index(0).String()
|
||||||
|
value := entry.Index(1).String()
|
||||||
|
result[key] = value
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// MaybeString returns string value of given JavaScript value or returns nil if the value is undefined.
|
||||||
|
func MaybeString(v js.Value) string {
|
||||||
|
if v.IsUndefined() {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return v.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// MaybeDate returns time.Time value of given JavaScript Date value or returns nil if the value is undefined.
|
||||||
|
func MaybeDate(v js.Value) (time.Time, error) {
|
||||||
|
if v.IsUndefined() {
|
||||||
|
return time.Time{}, nil
|
||||||
|
}
|
||||||
|
return DateToTime(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DateToTime converts JavaScript side's Data object into time.Time.
|
||||||
|
func DateToTime(v js.Value) (time.Time, error) {
|
||||||
|
milli := v.Call("getTime").Float()
|
||||||
|
return time.UnixMilli(int64(milli)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TimeToDate converts Go side's time.Time into Date object.
|
||||||
|
func TimeToDate(t time.Time) js.Value {
|
||||||
|
return DateClass.New(t.UnixMilli())
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package workers
|
package jsutil
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
@ -56,8 +56,8 @@ func (sr *streamReaderToReader) Read(p []byte) (n int, err error) {
|
|||||||
return sr.buf.Read(p)
|
return sr.buf.Read(p)
|
||||||
}
|
}
|
||||||
|
|
||||||
// convertStreamReaderToReader converts ReadableStreamDefaultReader to io.Reader.
|
// ConvertStreamReaderToReader converts ReadableStreamDefaultReader to io.Reader.
|
||||||
func convertStreamReaderToReader(sr js.Value) io.Reader {
|
func ConvertStreamReaderToReader(sr js.Value) io.Reader {
|
||||||
return &streamReaderToReader{
|
return &streamReaderToReader{
|
||||||
streamReader: sr,
|
streamReader: sr,
|
||||||
}
|
}
|
||||||
@ -83,14 +83,14 @@ func (rs *readerToReadableStream) Pull(controller js.Value) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
jsErr := errorClass.New(err.Error())
|
jsErr := ErrorClass.New(err.Error())
|
||||||
controller.Call("error", jsErr)
|
controller.Call("error", jsErr)
|
||||||
if err := rs.reader.Close(); err != nil {
|
if err := rs.reader.Close(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
ua := newUint8Array(n)
|
ua := NewUint8Array(n)
|
||||||
_ = js.CopyBytesToJS(ua, rs.chunkBuf[:n])
|
_ = js.CopyBytesToJS(ua, rs.chunkBuf[:n])
|
||||||
controller.Call("enqueue", ua)
|
controller.Call("enqueue", ua)
|
||||||
return nil
|
return nil
|
||||||
@ -105,13 +105,13 @@ func (rs *readerToReadableStream) Cancel() error {
|
|||||||
// https://deno.land/std@0.139.0/streams/conversion.ts#L5
|
// https://deno.land/std@0.139.0/streams/conversion.ts#L5
|
||||||
const defaultChunkSize = 16_640
|
const defaultChunkSize = 16_640
|
||||||
|
|
||||||
// convertReaderToReadableStream converts io.ReadCloser to ReadableStream.
|
// ConvertReaderToReadableStream converts io.ReadCloser to ReadableStream.
|
||||||
func convertReaderToReadableStream(reader io.ReadCloser) js.Value {
|
func ConvertReaderToReadableStream(reader io.ReadCloser) js.Value {
|
||||||
stream := &readerToReadableStream{
|
stream := &readerToReadableStream{
|
||||||
reader: reader,
|
reader: reader,
|
||||||
chunkBuf: make([]byte, defaultChunkSize),
|
chunkBuf: make([]byte, defaultChunkSize),
|
||||||
}
|
}
|
||||||
rsInit := newObject()
|
rsInit := NewObject()
|
||||||
rsInit.Set("pull", js.FuncOf(func(_ js.Value, args []js.Value) any {
|
rsInit.Set("pull", js.FuncOf(func(_ js.Value, args []js.Value) any {
|
||||||
var cb js.Func
|
var cb js.Func
|
||||||
cb = js.FuncOf(func(this js.Value, pArgs []js.Value) any {
|
cb = js.FuncOf(func(this js.Value, pArgs []js.Value) any {
|
||||||
@ -121,13 +121,13 @@ func convertReaderToReadableStream(reader io.ReadCloser) js.Value {
|
|||||||
controller := args[0]
|
controller := args[0]
|
||||||
err := stream.Pull(controller)
|
err := stream.Pull(controller)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
reject.Invoke(errorClass.New(err.Error()))
|
reject.Invoke(ErrorClass.New(err.Error()))
|
||||||
return js.Undefined()
|
return js.Undefined()
|
||||||
}
|
}
|
||||||
resolve.Invoke()
|
resolve.Invoke()
|
||||||
return js.Undefined()
|
return js.Undefined()
|
||||||
})
|
})
|
||||||
return newPromise(cb)
|
return NewPromise(cb)
|
||||||
}))
|
}))
|
||||||
rsInit.Set("cancel", js.FuncOf(func(js.Value, []js.Value) any {
|
rsInit.Set("cancel", js.FuncOf(func(js.Value, []js.Value) any {
|
||||||
err := stream.Cancel()
|
err := stream.Cancel()
|
||||||
@ -136,5 +136,5 @@ func convertReaderToReadableStream(reader io.ReadCloser) js.Value {
|
|||||||
}
|
}
|
||||||
return js.Undefined()
|
return js.Undefined()
|
||||||
}))
|
}))
|
||||||
return readableStreamClass.New(rsInit)
|
return ReadableStreamClass.New(rsInit)
|
||||||
}
|
}
|
103
jsutil.go
103
jsutil.go
@ -1,103 +0,0 @@
|
|||||||
package workers
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"syscall/js"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
global = js.Global()
|
|
||||||
objectClass = global.Get("Object")
|
|
||||||
promiseClass = global.Get("Promise")
|
|
||||||
responseClass = global.Get("Response")
|
|
||||||
headersClass = global.Get("Headers")
|
|
||||||
arrayClass = global.Get("Array")
|
|
||||||
uint8ArrayClass = global.Get("Uint8Array")
|
|
||||||
errorClass = global.Get("Error")
|
|
||||||
readableStreamClass = global.Get("ReadableStream")
|
|
||||||
dateClass = global.Get("Date")
|
|
||||||
)
|
|
||||||
|
|
||||||
func newObject() js.Value {
|
|
||||||
return objectClass.New()
|
|
||||||
}
|
|
||||||
|
|
||||||
func newUint8Array(size int) js.Value {
|
|
||||||
return uint8ArrayClass.New(size)
|
|
||||||
}
|
|
||||||
|
|
||||||
func newPromise(fn js.Func) js.Value {
|
|
||||||
return promiseClass.New(fn)
|
|
||||||
}
|
|
||||||
|
|
||||||
// arrayFrom calls Array.from to given argument and returns result Array.
|
|
||||||
func arrayFrom(v js.Value) js.Value {
|
|
||||||
return arrayClass.Call("from", v)
|
|
||||||
}
|
|
||||||
|
|
||||||
func awaitPromise(promiseVal js.Value) (js.Value, error) {
|
|
||||||
resultCh := make(chan js.Value)
|
|
||||||
errCh := make(chan error)
|
|
||||||
var then, catch js.Func
|
|
||||||
then = js.FuncOf(func(_ js.Value, args []js.Value) any {
|
|
||||||
defer then.Release()
|
|
||||||
result := args[0]
|
|
||||||
resultCh <- result
|
|
||||||
return js.Undefined()
|
|
||||||
})
|
|
||||||
catch = js.FuncOf(func(_ js.Value, args []js.Value) any {
|
|
||||||
defer catch.Release()
|
|
||||||
result := args[0]
|
|
||||||
errCh <- fmt.Errorf("failed on promise: %s", result.Call("toString").String())
|
|
||||||
return js.Undefined()
|
|
||||||
})
|
|
||||||
promiseVal.Call("then", then).Call("catch", catch)
|
|
||||||
select {
|
|
||||||
case result := <-resultCh:
|
|
||||||
return result, nil
|
|
||||||
case err := <-errCh:
|
|
||||||
return js.Value{}, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// strRecordToMap converts JavaScript side's Record<string, string> into map[string]string.
|
|
||||||
func strRecordToMap(v js.Value) map[string]string {
|
|
||||||
entries := objectClass.Call("entries", v)
|
|
||||||
entriesLen := entries.Get("length").Int()
|
|
||||||
result := make(map[string]string, entriesLen)
|
|
||||||
for i := 0; i < entriesLen; i++ {
|
|
||||||
entry := entries.Index(i)
|
|
||||||
key := entry.Index(0).String()
|
|
||||||
value := entry.Index(1).String()
|
|
||||||
result[key] = value
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// maybeString returns string value of given JavaScript value or returns nil if the value is undefined.
|
|
||||||
func maybeString(v js.Value) string {
|
|
||||||
if v.IsUndefined() {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return v.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
// maybeDate returns time.Time value of given JavaScript Date value or returns nil if the value is undefined.
|
|
||||||
func maybeDate(v js.Value) (time.Time, error) {
|
|
||||||
if v.IsUndefined() {
|
|
||||||
return time.Time{}, nil
|
|
||||||
}
|
|
||||||
return dateToTime(v)
|
|
||||||
}
|
|
||||||
|
|
||||||
// dateToTime converts JavaScript side's Data object into time.Time.
|
|
||||||
func dateToTime(v js.Value) (time.Time, error) {
|
|
||||||
milli := v.Call("getTime").Float()
|
|
||||||
return time.UnixMilli(int64(milli)), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// timeToDate converts Go side's time.Time into Date object.
|
|
||||||
func timeToDate(t time.Time) js.Value {
|
|
||||||
return dateClass.New(t.UnixMilli())
|
|
||||||
}
|
|
@ -7,6 +7,8 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"syscall/js"
|
"syscall/js"
|
||||||
|
|
||||||
|
"github.com/syumai/workers/internal/jsutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
// toBody converts JavaScripts sides ReadableStream (can be null) to io.ReadCloser.
|
// toBody converts JavaScripts sides ReadableStream (can be null) to io.ReadCloser.
|
||||||
@ -16,13 +18,13 @@ func toBody(streamOrNull js.Value) io.ReadCloser {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
sr := streamOrNull.Call("getReader")
|
sr := streamOrNull.Call("getReader")
|
||||||
return io.NopCloser(convertStreamReaderToReader(sr))
|
return io.NopCloser(jsutil.ConvertStreamReaderToReader(sr))
|
||||||
}
|
}
|
||||||
|
|
||||||
// toHeader converts JavaScript sides Headers to http.Header.
|
// toHeader converts JavaScript sides Headers to http.Header.
|
||||||
// - Headers: https://developer.mozilla.org/ja/docs/Web/API/Headers
|
// - Headers: https://developer.mozilla.org/ja/docs/Web/API/Headers
|
||||||
func toHeader(headers js.Value) http.Header {
|
func toHeader(headers js.Value) http.Header {
|
||||||
entries := arrayFrom(headers.Call("entries"))
|
entries := jsutil.ArrayFrom(headers.Call("entries"))
|
||||||
headerLen := entries.Length()
|
headerLen := entries.Length()
|
||||||
h := http.Header{}
|
h := http.Header{}
|
||||||
for i := 0; i < headerLen; i++ {
|
for i := 0; i < headerLen; i++ {
|
||||||
|
10
response.go
10
response.go
@ -3,10 +3,12 @@ package workers
|
|||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"syscall/js"
|
"syscall/js"
|
||||||
|
|
||||||
|
"github.com/syumai/workers/internal/jsutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
func toJSHeader(header http.Header) js.Value {
|
func toJSHeader(header http.Header) js.Value {
|
||||||
h := headersClass.New()
|
h := jsutil.HeadersClass.New()
|
||||||
for key, values := range header {
|
for key, values := range header {
|
||||||
for _, value := range values {
|
for _, value := range values {
|
||||||
h.Call("append", key, value)
|
h.Call("append", key, value)
|
||||||
@ -21,10 +23,10 @@ func toJSResponse(w *responseWriterBuffer) (js.Value, error) {
|
|||||||
if status == 0 {
|
if status == 0 {
|
||||||
status = http.StatusOK
|
status = http.StatusOK
|
||||||
}
|
}
|
||||||
respInit := newObject()
|
respInit := jsutil.NewObject()
|
||||||
respInit.Set("status", status)
|
respInit.Set("status", status)
|
||||||
respInit.Set("statusText", http.StatusText(status))
|
respInit.Set("statusText", http.StatusText(status))
|
||||||
respInit.Set("headers", toJSHeader(w.Header()))
|
respInit.Set("headers", toJSHeader(w.Header()))
|
||||||
readableStream := convertReaderToReadableStream(w.reader)
|
readableStream := jsutil.ConvertReaderToReadableStream(w.reader)
|
||||||
return responseClass.New(readableStream, respInit), nil
|
return jsutil.ResponseClass.New(readableStream, respInit), nil
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user