├── .editorconfig ├── .eslintignore ├── .eslintrc.js ├── .gitignore ├── LICENSE ├── README.md ├── base64 ├── base64.go ├── base64_test.go └── inject.go ├── console ├── console.go ├── console_test.go ├── inject.go └── options.go ├── example ├── base64 │ └── main.go ├── console │ └── main.go ├── fetch │ └── main.go └── timers │ └── main.go ├── feature-test.js ├── fetch ├── fetcher.go ├── fetcher_test.go ├── inject.go ├── inject_test.go ├── internal │ ├── request.go │ └── response.go └── options.go ├── go.mod ├── go.sum ├── inject.go ├── internal ├── version.go └── version.txt ├── package.json ├── timers ├── inject.go ├── internal │ └── item.go ├── tick_test.go ├── timers.go └── timers_test.go ├── url ├── bundle.js ├── inject.go ├── js │ └── index.js ├── url.go └── url_test.go ├── webpack.config.js ├── write-version-plugin.js └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | charset = utf-8 6 | insert_final_newline = true 7 | trim_trailing_whitespace = true 8 | 9 | [*.js] 10 | indent_style = space 11 | indent_size = 2 12 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | bundle.js 3 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | es6: true, 5 | browser: false, 6 | node: true, 7 | }, 8 | extends: ["eslint:recommended", "plugin:prettier/recommended"], 9 | plugins: ["prettier"], 10 | parserOptions: { 11 | ecmaVersion: 2020, 12 | sourceType: "module", 13 | }, 14 | }; 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | *.iml 3 | .DS_Store 4 | node_modules 5 | *.log 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Xingwang Liao 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Polyfills for [V8Go](https://github.com/rogchap/v8go) 2 | 3 | ## Install 4 | 5 | ```shell 6 | go get -u go.kuoruan.net/v8go-polyfills 7 | ``` 8 | 9 | > This module uses Golang [embed](https://golang.org/pkg/embed/), so requires Go version 1.16 10 | 11 | ## Polyfill List 12 | 13 | * base64: `atob` and `btoa` 14 | 15 | * console: `console.log` 16 | 17 | * fetch: `fetch` 18 | 19 | * timers: `setTimeout`, `clearTimeout`, `setInterval` and `clearInterval` 20 | 21 | * url: `URL` and `URLSearchParams` 22 | 23 | ## Usage 24 | 25 | ### fetch polyfill 26 | 27 | ```go 28 | package main 29 | 30 | import ( 31 | "errors" 32 | "fmt" 33 | "time" 34 | 35 | "go.kuoruan.net/v8go-polyfills/fetch" 36 | "rogchap.com/v8go" 37 | ) 38 | 39 | func main() { 40 | iso, _ := v8go.NewIsolate() 41 | global, _ := v8go.NewObjectTemplate(iso) 42 | 43 | if err := fetch.InjectTo(iso, global); err != nil { 44 | panic(err) 45 | } 46 | 47 | ctx, _ := v8go.NewContext(iso, global) 48 | 49 | val, err := ctx.RunScript("fetch('https://www.example.com').then(res => res.text())", "fetch.js") 50 | if err != nil { 51 | panic(err) 52 | } 53 | 54 | proms, err := val.AsPromise() 55 | if err != nil { 56 | panic(err) 57 | } 58 | done := make(chan bool, 1) 59 | 60 | go func() { 61 | for proms.State() == v8go.Pending { 62 | continue 63 | } 64 | done <- true 65 | }() 66 | 67 | select { 68 | case <-time.After(time.Second * 10): 69 | panic(errors.New("request timeout")) 70 | case <-done: 71 | html := proms.Result().String() 72 | fmt.Println(html) 73 | } 74 | } 75 | ``` 76 | -------------------------------------------------------------------------------- /base64/base64.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 Xingwang Liao 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | package base64 24 | 25 | import ( 26 | stdBase64 "encoding/base64" 27 | 28 | "rogchap.com/v8go" 29 | ) 30 | 31 | type Base64 interface { 32 | GetAtobFunctionCallback() v8go.FunctionCallback 33 | GetBtoaFunctionCallback() v8go.FunctionCallback 34 | } 35 | 36 | type base64 struct { 37 | } 38 | 39 | func NewBase64() Base64 { 40 | return &base64{} 41 | } 42 | 43 | /* 44 | https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/atob 45 | */ 46 | func (b *base64) GetAtobFunctionCallback() v8go.FunctionCallback { 47 | return func(info *v8go.FunctionCallbackInfo) *v8go.Value { 48 | args := info.Args() 49 | ctx := info.Context() 50 | 51 | if len(args) <= 0 { 52 | // TODO: v8go can't throw a error now, so we return an empty string 53 | return newStringValue(ctx, "") 54 | } 55 | 56 | encoded := args[0].String() 57 | 58 | byts, err := stdBase64.StdEncoding.DecodeString(encoded) 59 | if err != nil { 60 | return newStringValue(ctx, "") 61 | } 62 | 63 | return newStringValue(ctx, string(byts)) 64 | } 65 | } 66 | 67 | /* 68 | https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/btoa 69 | */ 70 | func (b *base64) GetBtoaFunctionCallback() v8go.FunctionCallback { 71 | return func(info *v8go.FunctionCallbackInfo) *v8go.Value { 72 | args := info.Args() 73 | ctx := info.Context() 74 | 75 | if len(args) <= 0 { 76 | return newStringValue(ctx, "") 77 | } 78 | 79 | str := args[0].String() 80 | 81 | encoded := stdBase64.StdEncoding.EncodeToString([]byte(str)) 82 | return newStringValue(ctx, encoded) 83 | } 84 | } 85 | 86 | func newStringValue(ctx *v8go.Context, str string) *v8go.Value { 87 | iso := ctx.Isolate() 88 | val, _ := v8go.NewValue(iso, str) 89 | return val 90 | } 91 | -------------------------------------------------------------------------------- /base64/base64_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 Xingwang Liao 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | package base64 24 | 25 | import ( 26 | "testing" 27 | 28 | "rogchap.com/v8go" 29 | ) 30 | 31 | func TestAtob(t *testing.T) { 32 | ctx, err := newV8goContext() 33 | if err != nil { 34 | t.Error(err) 35 | return 36 | } 37 | 38 | val, err := ctx.RunScript("atob()", "atob_undefined.js") 39 | if err != nil { 40 | t.Error(err) 41 | return 42 | } 43 | 44 | if s := val.String(); s != "" { 45 | t.Errorf("assert '' but got '%s'", s) 46 | return 47 | } 48 | 49 | val, err = ctx.RunScript("atob('')", "atob_empty.js") 50 | if err != nil { 51 | t.Error(err) 52 | return 53 | } 54 | 55 | if s := val.String(); s != "" { 56 | t.Errorf("assert '' but got '%s'", s) 57 | return 58 | } 59 | 60 | val, err = ctx.RunScript("atob('MTIzNA==')", "atob_1234.js") 61 | if err != nil { 62 | t.Error(err) 63 | return 64 | } 65 | 66 | if s := val.String(); s != "1234" { 67 | t.Errorf("assert '1234' but got '%s'", s) 68 | return 69 | } 70 | 71 | val, err = ctx.RunScript("atob('5rGJ5a2X')", "atob_unicode.js") 72 | if err != nil { 73 | t.Error(err) 74 | return 75 | } 76 | 77 | if s := val.String(); s != "汉字" { 78 | t.Errorf("assert '汉字' but got '%s'", s) 79 | return 80 | } 81 | } 82 | 83 | func TestBtoa(t *testing.T) { 84 | ctx, err := newV8goContext() 85 | if err != nil { 86 | t.Error(err) 87 | return 88 | } 89 | 90 | val, err := ctx.RunScript("btoa()", "btoa_undefined.js") 91 | if err != nil { 92 | t.Error(err) 93 | return 94 | } 95 | 96 | if s := val.String(); s != "" { 97 | t.Errorf("assert '' but got '%s'", s) 98 | return 99 | } 100 | 101 | val, err = ctx.RunScript("btoa('')", "atob_empty.js") 102 | if err != nil { 103 | t.Error(err) 104 | return 105 | } 106 | 107 | if s := val.String(); s != "" { 108 | t.Errorf("assert '' but got '%s'", s) 109 | return 110 | } 111 | 112 | val, err = ctx.RunScript("btoa('1234')", "btoa_1234.js") 113 | if err != nil { 114 | t.Error(err) 115 | return 116 | } 117 | 118 | if s := val.String(); s != "MTIzNA==" { 119 | t.Errorf("assert 'MTIzNA==' but got '%s'", s) 120 | return 121 | } 122 | 123 | val, err = ctx.RunScript("btoa('汉字')", "btoa_unicode.js") 124 | if err != nil { 125 | t.Error(err) 126 | return 127 | } 128 | 129 | if s := val.String(); s != "5rGJ5a2X" { 130 | t.Errorf("assert '5rGJ5a2X' but got '%s'", s) 131 | return 132 | } 133 | 134 | val, err = ctx.RunScript("btoa({})", "btoa_object.js") 135 | if err != nil { 136 | t.Error(err) 137 | return 138 | } 139 | 140 | if s := val.String(); s != "W29iamVjdCBPYmplY3Rd" { 141 | t.Errorf("assert 'W29iamVjdCBPYmplY3Rd' but got '%s'", s) 142 | return 143 | } 144 | } 145 | 146 | func newV8goContext() (*v8go.Context, error) { 147 | iso := v8go.NewIsolate() 148 | global := v8go.NewObjectTemplate(iso) 149 | 150 | if err := InjectTo(iso, global); err != nil { 151 | return nil, err 152 | } 153 | 154 | return v8go.NewContext(iso, global), nil 155 | } 156 | -------------------------------------------------------------------------------- /base64/inject.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 Xingwang Liao 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | package base64 24 | 25 | import ( 26 | "fmt" 27 | 28 | "rogchap.com/v8go" 29 | ) 30 | 31 | func InjectTo(iso *v8go.Isolate, global *v8go.ObjectTemplate) error { 32 | b := NewBase64() 33 | 34 | for _, f := range []struct { 35 | Name string 36 | Func func() v8go.FunctionCallback 37 | }{ 38 | {Name: "atob", Func: b.GetAtobFunctionCallback}, 39 | {Name: "btoa", Func: b.GetBtoaFunctionCallback}, 40 | } { 41 | fn := v8go.NewFunctionTemplate(iso, f.Func()) 42 | if err := global.Set(f.Name, fn, v8go.ReadOnly); err != nil { 43 | return fmt.Errorf("v8go-polyfills/fetch: %w", err) 44 | } 45 | } 46 | 47 | return nil 48 | } 49 | -------------------------------------------------------------------------------- /console/console.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 Xingwang Liao 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | package console 24 | 25 | import ( 26 | "fmt" 27 | "io" 28 | "os" 29 | 30 | "rogchap.com/v8go" 31 | ) 32 | 33 | // Console is a single console *method*. 34 | // 35 | // This can hardly be called a console, the name is kept for backwords compatability. 36 | type Console interface { 37 | GetLogFunctionCallback() v8go.FunctionCallback 38 | } 39 | 40 | type consoleMethod struct { 41 | Output io.Writer 42 | // Method name on the console object, eg. "log", "error" 43 | MethodName string 44 | } 45 | 46 | // NewConsole creates a new console method. 47 | // 48 | // InjectTo() calls this under the hood, so its best to use that instead 49 | // if you only want console.log to be available, otherwise use InjectMultipleTo(). 50 | func NewConsole(opt ...Option) Console { 51 | c := &consoleMethod{ 52 | Output: os.Stdout, 53 | MethodName: "log", 54 | } 55 | 56 | for _, o := range opt { 57 | o.apply(c) 58 | } 59 | 60 | return c 61 | } 62 | 63 | func (c *consoleMethod) GetLogFunctionCallback() v8go.FunctionCallback { 64 | return func(info *v8go.FunctionCallbackInfo) *v8go.Value { 65 | if args := info.Args(); len(args) > 0 { 66 | inputs := make([]interface{}, len(args)) 67 | for i, input := range args { 68 | inputs[i] = input 69 | } 70 | 71 | fmt.Fprintln(c.Output, inputs...) 72 | } 73 | 74 | return nil 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /console/console_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 Xingwang Liao 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | package console 24 | 25 | import ( 26 | "os" 27 | "testing" 28 | 29 | "rogchap.com/v8go" 30 | ) 31 | 32 | func TestInject(t *testing.T) { 33 | t.Parallel() 34 | 35 | iso := v8go.NewIsolate() 36 | ctx := v8go.NewContext(iso) 37 | 38 | if err := InjectTo(ctx, WithOutput(os.Stdout)); err != nil { 39 | t.Error(err) 40 | } 41 | 42 | if _, err := ctx.RunScript("console.log(1111)", ""); err != nil { 43 | t.Error(err) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /console/inject.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 Xingwang Liao 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | package console 24 | 25 | import ( 26 | "errors" 27 | "fmt" 28 | 29 | "rogchap.com/v8go" 30 | ) 31 | 32 | // InjectTo injects basic console.log support. 33 | // 34 | // Warning: This method overwrites the previous console object. 35 | // To add more than one console.foo method, use InjectMultipleTo. 36 | func InjectTo(ctx *v8go.Context, opt ...Option) error { 37 | if ctx == nil { 38 | return errors.New("v8go-polyfills/console: ctx is required") 39 | } 40 | 41 | consoleMethod := NewConsole(opt...).(*consoleMethod) 42 | 43 | iso := ctx.Isolate() 44 | con := v8go.NewObjectTemplate(iso) 45 | 46 | logFn := v8go.NewFunctionTemplate(iso, consoleMethod.GetLogFunctionCallback()) 47 | 48 | if err := con.Set(consoleMethod.MethodName, logFn, v8go.ReadOnly); err != nil { 49 | return fmt.Errorf("v8go-polyfills/console: %w", err) 50 | } 51 | 52 | conObj, err := con.NewInstance(ctx) 53 | if err != nil { 54 | return fmt.Errorf("v8go-polyfills/console: %w", err) 55 | } 56 | 57 | global := ctx.Global() 58 | 59 | if err := global.Set("console", conObj); err != nil { 60 | return fmt.Errorf("v8go-polyfills/console: %w", err) 61 | } 62 | 63 | return nil 64 | } 65 | 66 | // InjectMultipleTo injects one or more console methods to a global object of a context. 67 | // 68 | // Implementing the Console interface will not work in this case. 69 | func InjectMultipleTo(ctx *v8go.Context, consoles ...Console) error { 70 | if ctx == nil { 71 | return errors.New("v8go-polyfills/console: ctx is required") 72 | } 73 | 74 | iso := ctx.Isolate() 75 | con := v8go.NewObjectTemplate(iso) 76 | 77 | for _, console := range consoles { 78 | consoleMethod := console.(*consoleMethod) 79 | 80 | logFn := v8go.NewFunctionTemplate(iso, consoleMethod.GetLogFunctionCallback()) 81 | if err := con.Set(consoleMethod.MethodName, logFn, v8go.ReadOnly); err != nil { 82 | return fmt.Errorf("v8go-polyfills/console: %w", err) 83 | } 84 | } 85 | 86 | conObj, err := con.NewInstance(ctx) 87 | if err != nil { 88 | return fmt.Errorf("v8go-polyfills/console: %w", err) 89 | } 90 | 91 | global := ctx.Global() 92 | 93 | if err := global.Set("console", conObj); err != nil { 94 | return fmt.Errorf("v8go-polyfills/console: %w", err) 95 | } 96 | 97 | return nil 98 | } 99 | -------------------------------------------------------------------------------- /console/options.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 Xingwang Liao 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | package console 24 | 25 | import ( 26 | "io" 27 | ) 28 | 29 | type Option interface { 30 | apply(c *consoleMethod) 31 | } 32 | 33 | type optionFunc func(c *consoleMethod) 34 | 35 | func (f optionFunc) apply(c *consoleMethod) { 36 | f(c) 37 | } 38 | 39 | // WithOutput sets the output for this console method. Default os.Stdout. 40 | func WithOutput(output io.Writer) Option { 41 | return optionFunc(func(c *consoleMethod) { 42 | c.Output = output 43 | }) 44 | } 45 | 46 | // WithMethodName sets the method name for this console method. Default "log". 47 | func WithMethodName(methodName string) Option { 48 | return optionFunc(func(c *consoleMethod) { 49 | c.MethodName = methodName 50 | }) 51 | } 52 | -------------------------------------------------------------------------------- /example/base64/main.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 Xingwang Liao 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | package main 24 | 25 | import ( 26 | "fmt" 27 | 28 | "go.kuoruan.net/v8go-polyfills/base64" 29 | "rogchap.com/v8go" 30 | ) 31 | 32 | func main() { 33 | iso := v8go.NewIsolate() 34 | global := v8go.NewObjectTemplate(iso) 35 | 36 | if err := base64.InjectTo(iso, global); err != nil { 37 | panic(err) 38 | } 39 | 40 | ctx := v8go.NewContext(iso, global) 41 | 42 | val, err := ctx.RunScript("btoa('Hello, world!')", "btoa.js") 43 | if err != nil { 44 | panic(err) 45 | } 46 | 47 | fmt.Printf("btoa: %s\n", val.String()) 48 | 49 | val, err = ctx.RunScript(fmt.Sprintf("atob('%s')", val.String()), "atob.js") 50 | if err != nil { 51 | panic(err) 52 | } 53 | 54 | fmt.Printf("atob: %s\n", val.String()) 55 | } 56 | -------------------------------------------------------------------------------- /example/console/main.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 Xingwang Liao 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | package main 24 | 25 | import ( 26 | "go.kuoruan.net/v8go-polyfills/console" 27 | "rogchap.com/v8go" 28 | ) 29 | 30 | func main() { 31 | iso := v8go.NewIsolate() 32 | ctx := v8go.NewContext(iso) 33 | 34 | if err := console.InjectTo(ctx); err != nil { 35 | panic(err) 36 | } 37 | 38 | _, err := ctx.RunScript("console.log('Hello, world.')", "console.js") 39 | if err != nil { 40 | panic(err) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /example/fetch/main.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 Xingwang Liao 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | package main 24 | 25 | import ( 26 | "errors" 27 | "fmt" 28 | "time" 29 | 30 | "go.kuoruan.net/v8go-polyfills/fetch" 31 | "rogchap.com/v8go" 32 | ) 33 | 34 | func main() { 35 | iso := v8go.NewIsolate() 36 | global := v8go.NewObjectTemplate(iso) 37 | 38 | if err := fetch.InjectTo(iso, global); err != nil { 39 | panic(err) 40 | } 41 | 42 | ctx := v8go.NewContext(iso, global) 43 | 44 | val, err := ctx.RunScript("fetch('https://www.example.com').then(res => res.text())", "fetch.js") 45 | if err != nil { 46 | panic(err) 47 | } 48 | 49 | proms, err := val.AsPromise() 50 | if err != nil { 51 | panic(err) 52 | } 53 | done := make(chan bool, 1) 54 | 55 | go func() { 56 | for proms.State() == v8go.Pending { 57 | continue 58 | } 59 | done <- true 60 | }() 61 | 62 | select { 63 | case <-time.After(time.Second * 10): 64 | panic(errors.New("request timeout")) 65 | case <-done: 66 | html := proms.Result().String() 67 | fmt.Println(html) 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /example/timers/main.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 Xingwang Liao 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | package main 24 | 25 | import ( 26 | "fmt" 27 | "time" 28 | 29 | "go.kuoruan.net/v8go-polyfills/timers" 30 | "rogchap.com/v8go" 31 | ) 32 | 33 | func main() { 34 | iso := v8go.NewIsolate() 35 | global := v8go.NewObjectTemplate(iso) 36 | 37 | if err := timers.InjectTo(iso, global); err != nil { 38 | panic(err) 39 | } 40 | 41 | ctx := v8go.NewContext(iso, global) 42 | 43 | val, err := ctx.RunScript( 44 | "new Promise((resolve) => setTimeout(function(name) {resolve(`Hello, ${name}!`)}, 1000, 'Tom'))", 45 | "resolve.js", 46 | ) 47 | if err != nil { 48 | panic(err) 49 | } 50 | 51 | proms, err := val.AsPromise() 52 | if err != nil { 53 | panic(err) 54 | } 55 | 56 | done := make(chan bool, 1) 57 | 58 | go func() { 59 | for proms.State() == v8go.Pending { 60 | continue 61 | } 62 | 63 | done <- proms.State() == v8go.Fulfilled 64 | }() 65 | 66 | select { 67 | case succ := <-done: 68 | if !succ { 69 | panic("except success but not") 70 | } 71 | 72 | fmt.Println(proms.Result().String()) 73 | case <-time.After(time.Second * 2): 74 | panic("timeout") 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /feature-test.js: -------------------------------------------------------------------------------- 1 | console.log("URL: " + typeof URL); 2 | console.log("URLSearchParams: " + typeof URLSearchParams); 3 | 4 | console.log("fetch: " + typeof fetch); 5 | 6 | console.log("atob: " + typeof atob); 7 | console.log("btoa: " + typeof btoa); 8 | -------------------------------------------------------------------------------- /fetch/fetcher.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 Xingwang Liao 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | package fetch 24 | 25 | import ( 26 | "encoding/json" 27 | "errors" 28 | "fmt" 29 | "io" 30 | "net/http" 31 | "net/http/httptest" 32 | "net/url" 33 | "strings" 34 | "time" 35 | 36 | "go.kuoruan.net/v8go-polyfills/fetch/internal" 37 | . "go.kuoruan.net/v8go-polyfills/internal" 38 | 39 | "rogchap.com/v8go" 40 | ) 41 | 42 | const ( 43 | UserAgentLocal = "" 44 | AddrLocal = "0.0.0.0:0" 45 | ) 46 | 47 | var defaultLocalHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 48 | http.Error(w, http.StatusText(http.StatusNotImplemented), http.StatusNotImplemented) 49 | }) 50 | 51 | var defaultUserAgentProvider = UserAgentProviderFunc(func(u *url.URL) string { 52 | if !u.IsAbs() { 53 | return UserAgentLocal 54 | } 55 | 56 | return UserAgent() 57 | }) 58 | 59 | type Fetcher interface { 60 | GetLocalHandler() http.Handler 61 | 62 | GetFetchFunctionCallback() v8go.FunctionCallback 63 | } 64 | 65 | type fetcher struct { 66 | // Use local handler to handle the relative path (starts with "/") request 67 | LocalHandler http.Handler 68 | 69 | UserAgentProvider UserAgentProvider 70 | AddrLocal string 71 | } 72 | 73 | func NewFetcher(opt ...Option) Fetcher { 74 | ft := &fetcher{ 75 | LocalHandler: defaultLocalHandler, 76 | UserAgentProvider: defaultUserAgentProvider, 77 | AddrLocal: AddrLocal, 78 | } 79 | 80 | for _, o := range opt { 81 | o.apply(ft) 82 | } 83 | 84 | return ft 85 | } 86 | 87 | func (f *fetcher) GetLocalHandler() http.Handler { 88 | return f.LocalHandler 89 | } 90 | 91 | func (f *fetcher) GetFetchFunctionCallback() v8go.FunctionCallback { 92 | return func(info *v8go.FunctionCallbackInfo) *v8go.Value { 93 | ctx := info.Context() 94 | args := info.Args() 95 | 96 | resolver, _ := v8go.NewPromiseResolver(ctx) 97 | 98 | go func() { 99 | if len(args) <= 0 { 100 | err := errors.New("1 argument required, but only 0 present") 101 | resolver.Reject(newErrorValue(ctx, err)) 102 | return 103 | } 104 | 105 | var reqInit internal.RequestInit 106 | if len(args) > 1 { 107 | str, err := v8go.JSONStringify(ctx, args[1]) 108 | if err != nil { 109 | resolver.Reject(newErrorValue(ctx, err)) 110 | return 111 | } 112 | 113 | reader := strings.NewReader(str) 114 | if err := json.NewDecoder(reader).Decode(&reqInit); err != nil { 115 | resolver.Reject(newErrorValue(ctx, err)) 116 | return 117 | } 118 | } 119 | 120 | r, err := f.initRequest(args[0].String(), reqInit) 121 | if err != nil { 122 | resolver.Reject(newErrorValue(ctx, err)) 123 | return 124 | } 125 | 126 | var res *internal.Response 127 | 128 | // do local request 129 | if !r.URL.IsAbs() { 130 | res, err = f.fetchLocal(r) 131 | } else { 132 | res, err = f.fetchRemote(r) 133 | } 134 | if err != nil { 135 | resolver.Reject(newErrorValue(ctx, err)) 136 | return 137 | } 138 | 139 | resObj, err := newResponseObject(ctx, res) 140 | if err != nil { 141 | resolver.Reject(newErrorValue(ctx, err)) 142 | return 143 | } 144 | 145 | resolver.Resolve(resObj) 146 | }() 147 | 148 | return resolver.GetPromise().Value 149 | } 150 | } 151 | 152 | func (f *fetcher) initRequest(reqUrl string, reqInit internal.RequestInit) (*internal.Request, error) { 153 | u, err := internal.ParseRequestURL(reqUrl) 154 | if err != nil { 155 | return nil, err 156 | } 157 | 158 | req := &internal.Request{ 159 | URL: u, 160 | Body: reqInit.Body, 161 | Header: http.Header{ 162 | "Accept": []string{"*/*"}, 163 | "Connection": []string{"close"}, 164 | }, 165 | } 166 | 167 | var ua string 168 | if f.UserAgentProvider != nil { 169 | ua = f.UserAgentProvider.GetUserAgent(u) 170 | } else { 171 | ua = defaultUserAgentProvider(u) 172 | } 173 | 174 | req.Header.Set("User-Agent", ua) 175 | 176 | // url has no scheme, its a local request 177 | if !u.IsAbs() { 178 | req.RemoteAddr = f.AddrLocal 179 | } 180 | 181 | for h, v := range reqInit.Headers { 182 | headerName := http.CanonicalHeaderKey(h) 183 | req.Header.Set(headerName, v) 184 | } 185 | 186 | if reqInit.Method != "" { 187 | req.Method = strings.ToUpper(reqInit.Method) 188 | } else { 189 | req.Method = "GET" 190 | } 191 | 192 | switch r := strings.ToLower(reqInit.Redirect); r { 193 | case "error", "follow", "manual": 194 | req.Redirect = r 195 | case "": 196 | req.Redirect = internal.RequestRedirectFollow 197 | default: 198 | return nil, fmt.Errorf("unsupported redirect: %s", reqInit.Redirect) 199 | } 200 | 201 | return req, nil 202 | } 203 | 204 | func (f *fetcher) fetchLocal(r *internal.Request) (*internal.Response, error) { 205 | if f.LocalHandler == nil { 206 | return nil, errors.New("no local handler present") 207 | } 208 | 209 | var body io.Reader 210 | if r.Method != "GET" { 211 | body = strings.NewReader(r.Body) 212 | } 213 | 214 | req, err := http.NewRequest(r.Method, r.URL.String(), body) 215 | if err != nil { 216 | return nil, err 217 | } 218 | req.RemoteAddr = r.RemoteAddr 219 | req.Header = r.Header 220 | 221 | rcd := httptest.NewRecorder() 222 | 223 | f.LocalHandler.ServeHTTP(rcd, req) 224 | 225 | return internal.HandleHttpResponse(rcd.Result(), r.URL.String(), false) 226 | } 227 | 228 | func (f *fetcher) fetchRemote(r *internal.Request) (*internal.Response, error) { 229 | var body io.Reader 230 | if r.Method != "GET" { 231 | body = strings.NewReader(r.Body) 232 | } 233 | 234 | req, err := http.NewRequest(r.Method, r.URL.String(), body) 235 | if err != nil { 236 | return nil, err 237 | } 238 | req.Header = r.Header 239 | 240 | redirected := false 241 | client := &http.Client{ 242 | Transport: http.DefaultTransport, 243 | CheckRedirect: func(req *http.Request, via []*http.Request) error { 244 | switch r.Redirect { 245 | case internal.RequestRedirectError: 246 | return errors.New("redirects are not allowed") 247 | default: 248 | if len(via) >= 10 { 249 | return errors.New("stopped after 10 redirects") 250 | } 251 | } 252 | 253 | redirected = true 254 | return nil 255 | }, 256 | Timeout: 20 * time.Second, 257 | } 258 | 259 | res, err := client.Do(req) 260 | if err != nil { 261 | return nil, err 262 | } 263 | 264 | return internal.HandleHttpResponse(res, r.URL.String(), redirected) 265 | } 266 | 267 | func newResponseObject(ctx *v8go.Context, res *internal.Response) (*v8go.Object, error) { 268 | iso := ctx.Isolate() 269 | 270 | headers, err := newHeadersObject(ctx, res.Header) 271 | if err != nil { 272 | return nil, err 273 | } 274 | 275 | textFnTmp := v8go.NewFunctionTemplate(iso, func(info *v8go.FunctionCallbackInfo) *v8go.Value { 276 | ctx := info.Context() 277 | resolver, _ := v8go.NewPromiseResolver(ctx) 278 | 279 | go func() { 280 | v, _ := v8go.NewValue(iso, res.Body) 281 | resolver.Resolve(v) 282 | }() 283 | 284 | return resolver.GetPromise().Value 285 | }) 286 | if err != nil { 287 | return nil, err 288 | } 289 | 290 | jsonFnTmp := v8go.NewFunctionTemplate(iso, func(info *v8go.FunctionCallbackInfo) *v8go.Value { 291 | ctx := info.Context() 292 | 293 | resolver, _ := v8go.NewPromiseResolver(ctx) 294 | 295 | go func() { 296 | val, err := v8go.JSONParse(ctx, res.Body) 297 | if err != nil { 298 | rejectVal, _ := v8go.NewValue(iso, err.Error()) 299 | resolver.Reject(rejectVal) 300 | return 301 | } 302 | 303 | resolver.Resolve(val) 304 | }() 305 | 306 | return resolver.GetPromise().Value 307 | }) 308 | if err != nil { 309 | return nil, err 310 | } 311 | 312 | resTmp := v8go.NewObjectTemplate(iso) 313 | 314 | for _, f := range []struct { 315 | Name string 316 | Tmp interface{} 317 | }{ 318 | {Name: "text", Tmp: textFnTmp}, 319 | {Name: "json", Tmp: jsonFnTmp}, 320 | } { 321 | if err := resTmp.Set(f.Name, f.Tmp, v8go.ReadOnly); err != nil { 322 | return nil, err 323 | } 324 | } 325 | 326 | resObj, err := resTmp.NewInstance(ctx) 327 | if err != nil { 328 | return nil, err 329 | } 330 | 331 | for _, v := range []struct { 332 | Key string 333 | Val interface{} 334 | }{ 335 | {Key: "headers", Val: headers}, 336 | {Key: "ok", Val: res.OK}, 337 | {Key: "redirected", Val: res.Redirected}, 338 | {Key: "status", Val: res.Status}, 339 | {Key: "statusText", Val: res.StatusText}, 340 | {Key: "url", Val: res.URL}, 341 | {Key: "body", Val: res.Body}, 342 | } { 343 | if err := resObj.Set(v.Key, v.Val); err != nil { 344 | return nil, err 345 | } 346 | } 347 | 348 | return resObj, nil 349 | } 350 | 351 | func newHeadersObject(ctx *v8go.Context, h http.Header) (*v8go.Object, error) { 352 | iso := ctx.Isolate() 353 | 354 | // https://developer.mozilla.org/en-US/docs/Web/API/Headers/get 355 | getFnTmp := v8go.NewFunctionTemplate(iso, func(info *v8go.FunctionCallbackInfo) *v8go.Value { 356 | args := info.Args() 357 | if len(args) <= 0 { 358 | // TODO: this should return an error, but v8go not supported now 359 | val, _ := v8go.NewValue(iso, "") 360 | return val 361 | } 362 | 363 | key := http.CanonicalHeaderKey(args[0].String()) 364 | val, _ := v8go.NewValue(iso, h.Get(key)) 365 | return val 366 | }) 367 | 368 | // https://developer.mozilla.org/en-US/docs/Web/API/Headers/has 369 | hasFnTmp := v8go.NewFunctionTemplate(iso, func(info *v8go.FunctionCallbackInfo) *v8go.Value { 370 | args := info.Args() 371 | if len(args) <= 0 { 372 | val, _ := v8go.NewValue(iso, false) 373 | return val 374 | } 375 | key := http.CanonicalHeaderKey(args[0].String()) 376 | 377 | val, _ := v8go.NewValue(iso, h.Get(key) != "") 378 | return val 379 | }) 380 | 381 | // create a header template, 382 | // TODO: if v8go supports Map in the future, change this to a Map Object 383 | headersTmp := v8go.NewObjectTemplate(iso) 384 | 385 | for _, f := range []struct { 386 | Name string 387 | Tmp interface{} 388 | }{ 389 | {Name: "get", Tmp: getFnTmp}, 390 | {Name: "has", Tmp: hasFnTmp}, 391 | } { 392 | if err := headersTmp.Set(f.Name, f.Tmp, v8go.ReadOnly); err != nil { 393 | return nil, err 394 | } 395 | } 396 | 397 | headers, err := headersTmp.NewInstance(ctx) 398 | if err != nil { 399 | return nil, err 400 | } 401 | 402 | for k, v := range h { 403 | var vv string 404 | if len(v) > 0 { 405 | // get the first element, like http.Header.Get 406 | vv = v[0] 407 | } 408 | 409 | if err := headers.Set(k, vv); err != nil { 410 | return nil, err 411 | } 412 | } 413 | 414 | return headers, nil 415 | } 416 | 417 | // v8go currently not support reject a *v8go.Object, 418 | // so we should new *v8go.Value here 419 | func newErrorValue(ctx *v8go.Context, err error) *v8go.Value { 420 | iso := ctx.Isolate() 421 | e, _ := v8go.NewValue(iso, fmt.Sprintf("fetch: %v", err)) 422 | return e 423 | } 424 | 425 | func UserAgent() string { 426 | return fmt.Sprintf("v8go-polyfills/%s (v8go/%s)", Version, v8go.Version()) 427 | } 428 | -------------------------------------------------------------------------------- /fetch/fetcher_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 Xingwang Liao 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | package fetch 24 | 25 | import ( 26 | "fmt" 27 | "net/http" 28 | "net/http/httptest" 29 | "testing" 30 | 31 | "rogchap.com/v8go" 32 | ) 33 | 34 | func TestNewFetcher(t *testing.T) { 35 | t.Parallel() 36 | 37 | f1 := NewFetcher() 38 | if f1 == nil { 39 | t.Error("create fetcher failed") 40 | return 41 | } 42 | 43 | if h := f1.GetLocalHandler(); h == nil { 44 | t.Error("local handler is ") 45 | } 46 | 47 | f2 := NewFetcher(WithLocalHandler(nil)) 48 | if f2 == nil { 49 | t.Error("create fetcher with local handler failed") 50 | return 51 | } 52 | 53 | if h := f2.GetLocalHandler(); h != nil { 54 | t.Error("set fetcher local handler to failed") 55 | return 56 | } 57 | } 58 | 59 | func TestFetchJSON(t *testing.T) { 60 | t.Parallel() 61 | 62 | ctx, err := newV8ContextWithFetch() 63 | if err != nil { 64 | t.Errorf("create v8: %s", err) 65 | return 66 | } 67 | 68 | srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 69 | w.WriteHeader(http.StatusOK) 70 | w.Header().Set("Content-Type", "application/json; utf-8") 71 | _, _ = w.Write([]byte(`{"status": true}`)) 72 | })) 73 | 74 | val, err := ctx.RunScript(fmt.Sprintf("fetch('%s').then(res => res.json())", srv.URL), "fetch_json.js") 75 | if err != nil { 76 | t.Error(err) 77 | return 78 | } 79 | 80 | proms, err := val.AsPromise() 81 | if err != nil { 82 | t.Error(err) 83 | return 84 | } 85 | 86 | for proms.State() == v8go.Pending { 87 | continue 88 | } 89 | 90 | res, err := proms.Result().AsObject() 91 | if err != nil { 92 | t.Error(err) 93 | return 94 | } 95 | 96 | status, err := res.Get("status") 97 | if err != nil { 98 | t.Error(err) 99 | return 100 | } 101 | 102 | if !status.Boolean() { 103 | t.Error("status should be true") 104 | } 105 | } 106 | 107 | func TestHeaders(t *testing.T) { 108 | t.Parallel() 109 | 110 | iso := v8go.NewIsolate() 111 | 112 | ctx := v8go.NewContext(iso) 113 | 114 | obj, err := newHeadersObject(ctx, http.Header{ 115 | "AA": []string{"aa"}, 116 | "BB": []string{"bb"}, 117 | }) 118 | if err != nil { 119 | t.Error(err) 120 | return 121 | } 122 | 123 | aa, err := obj.Get("AA") 124 | if err != nil { 125 | t.Error(err) 126 | return 127 | } 128 | 129 | if aa.String() != "aa" { 130 | t.Errorf("should be 'aa' but is '%s'", aa.String()) 131 | return 132 | } 133 | 134 | fn, err := obj.Get("get") 135 | if err != nil { 136 | t.Error(err) 137 | return 138 | } 139 | 140 | if !fn.IsFunction() { 141 | t.Error("should be function") 142 | return 143 | } 144 | } 145 | 146 | func newV8ContextWithFetch(opt ...Option) (*v8go.Context, error) { 147 | iso := v8go.NewIsolate() 148 | global := v8go.NewObjectTemplate(iso) 149 | 150 | if err := InjectTo(iso, global, opt...); err != nil { 151 | return nil, err 152 | } 153 | 154 | return v8go.NewContext(iso, global), nil 155 | } 156 | -------------------------------------------------------------------------------- /fetch/inject.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 Xingwang Liao 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | package fetch 24 | 25 | import ( 26 | "fmt" 27 | 28 | "rogchap.com/v8go" 29 | ) 30 | 31 | func InjectTo(iso *v8go.Isolate, global *v8go.ObjectTemplate, opt ...Option) error { 32 | f := NewFetcher(opt...) 33 | 34 | fetchFn := v8go.NewFunctionTemplate(iso, f.GetFetchFunctionCallback()) 35 | 36 | if err := global.Set("fetch", fetchFn, v8go.ReadOnly); err != nil { 37 | return fmt.Errorf("v8go-polyfills/fetch: %w", err) 38 | } 39 | 40 | return nil 41 | } 42 | -------------------------------------------------------------------------------- /fetch/inject_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 Xingwang Liao 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | package fetch 24 | 25 | import ( 26 | "fmt" 27 | "testing" 28 | "time" 29 | 30 | "rogchap.com/v8go" 31 | ) 32 | 33 | func TestInjectTo(t *testing.T) { 34 | t.Parallel() 35 | 36 | iso := v8go.NewIsolate() 37 | global := v8go.NewObjectTemplate(iso) 38 | 39 | if err := InjectTo(iso, global); err != nil { 40 | t.Errorf("error when inject fetch polyfill, %s", err) 41 | return 42 | } 43 | 44 | ctx := v8go.NewContext(iso, global) 45 | 46 | val, err := ctx.RunScript("fetch('https://www.example.com')", "fetch_example.js") 47 | if err != nil { 48 | t.Errorf("failed to do fetch test: %s", err) 49 | return 50 | } 51 | 52 | pro, err := val.AsPromise() 53 | if err != nil { 54 | t.Errorf("can't convert to promise object: %s", err) 55 | return 56 | } 57 | 58 | done := make(chan bool, 1) 59 | go func() { 60 | for pro.State() == v8go.Pending { 61 | continue 62 | } 63 | 64 | done <- true 65 | }() 66 | 67 | select { 68 | case <-time.After(time.Second * 10): 69 | t.Errorf("request timeout") 70 | return 71 | case <-done: 72 | stat := pro.State() 73 | if stat == v8go.Rejected { 74 | fmt.Printf("reject with error: %s\n", pro.Result().String()) 75 | } 76 | 77 | if pro.State() != v8go.Fulfilled { 78 | t.Errorf("should fetch success, but not") 79 | return 80 | } 81 | } 82 | 83 | obj, err := pro.Result().AsObject() 84 | if err != nil { 85 | t.Errorf("can't convert fetch result to object, %s", err) 86 | return 87 | } 88 | 89 | ok, err := obj.Get("ok") 90 | if err != nil { 91 | t.Errorf("get object 'ok' failed: %s", err) 92 | return 93 | } 94 | 95 | if !ok.Boolean() { 96 | t.Error("should be ok, but not") 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /fetch/internal/request.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 Xingwang Liao 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | package internal 24 | 25 | import ( 26 | "fmt" 27 | "net/http" 28 | "net/url" 29 | "strings" 30 | ) 31 | 32 | const ( 33 | RequestRedirectError = "error" 34 | RequestRedirectFollow = "follow" 35 | RequestRedirectManual = "manual" 36 | ) 37 | 38 | /* 39 | RequestInit is the fetch API defined object. 40 | Only supports raw request now. 41 | */ 42 | type RequestInit struct { 43 | Body string `json:"body"` 44 | Headers map[string]string `json:"headers"` 45 | Method string `json:"method"` 46 | Redirect string `json:"redirect"` 47 | } 48 | 49 | /* 50 | Request is the request object used by fetch 51 | */ 52 | type Request struct { 53 | Body string 54 | Method string 55 | Redirect string 56 | 57 | Header http.Header 58 | URL *url.URL 59 | RemoteAddr string 60 | } 61 | 62 | /* 63 | parse and check the request URL, return *url.URL 64 | */ 65 | func ParseRequestURL(rawURL string) (*url.URL, error) { 66 | u, err := url.Parse(rawURL) 67 | if err != nil { 68 | return nil, fmt.Errorf("url '%s' is not valid, %w", rawURL, err) 69 | } 70 | 71 | /** 72 | * Check the scheme, we only support http and https at this time 73 | */ 74 | switch u.Scheme { 75 | case "http", "https": 76 | case "": // then scheme is empty, it's a local request 77 | if !strings.HasPrefix(u.Path, "/") { 78 | return nil, fmt.Errorf("unsupported relatve path %s", u.Path) 79 | } 80 | default: 81 | return nil, fmt.Errorf("unsupported scheme %s", u.Scheme) 82 | } 83 | 84 | return u, nil 85 | } 86 | -------------------------------------------------------------------------------- /fetch/internal/response.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 Xingwang Liao 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | package internal 24 | 25 | import ( 26 | "io/ioutil" 27 | "net/http" 28 | ) 29 | 30 | /* 31 | Response keeps the *http.Response 32 | */ 33 | type Response struct { 34 | Header http.Header 35 | Status int32 36 | StatusText string 37 | OK bool 38 | Redirected bool 39 | URL string 40 | Body string 41 | } 42 | 43 | /* 44 | Handle the *http.Response, return *Response 45 | */ 46 | func HandleHttpResponse(res *http.Response, url string, redirected bool) (*Response, error) { 47 | defer res.Body.Close() 48 | resBody, err := ioutil.ReadAll(res.Body) 49 | if err != nil { 50 | return nil, err 51 | } 52 | 53 | return &Response{ 54 | Header: res.Header, 55 | Status: int32(res.StatusCode), // int type is not support by v8go 56 | StatusText: res.Status, 57 | OK: res.StatusCode >= 200 && res.StatusCode < 300, 58 | Redirected: redirected, 59 | URL: url, 60 | Body: string(resBody), 61 | }, nil 62 | } 63 | -------------------------------------------------------------------------------- /fetch/options.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 Xingwang Liao 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | package fetch 24 | 25 | import ( 26 | "net/http" 27 | "net/url" 28 | ) 29 | 30 | type UserAgentProvider interface { 31 | GetUserAgent(u *url.URL) string 32 | } 33 | 34 | type UserAgentProviderFunc func(u *url.URL) string 35 | 36 | func (f UserAgentProviderFunc) GetUserAgent(u *url.URL) string { 37 | return f(u) 38 | } 39 | 40 | type Option interface { 41 | apply(ft *fetcher) 42 | } 43 | 44 | type optionFunc func(ft *fetcher) 45 | 46 | func (f optionFunc) apply(ft *fetcher) { 47 | f(ft) 48 | } 49 | 50 | func WithLocalHandler(handler http.Handler) Option { 51 | return optionFunc(func(ft *fetcher) { 52 | ft.LocalHandler = handler 53 | }) 54 | } 55 | 56 | func WithUserAgentProvider(provider UserAgentProvider) Option { 57 | return optionFunc(func(ft *fetcher) { 58 | ft.UserAgentProvider = provider 59 | }) 60 | } 61 | 62 | func WithAddrLocal(addr string) Option { 63 | return optionFunc(func(ft *fetcher) { 64 | ft.AddrLocal = addr 65 | }) 66 | } 67 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module go.kuoruan.net/v8go-polyfills 2 | 3 | go 1.16 4 | 5 | require rogchap.com/v8go v0.7.0 6 | 7 | retract [v0.1.0, v0.3.0] 8 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | rogchap.com/v8go v0.7.0 h1:kgjbiO4zE5itA962ze6Hqmbs4HgZbGzmueCXsZtremg= 2 | rogchap.com/v8go v0.7.0/go.mod h1:MxgP3pL2MW4dpme/72QRs8sgNMmM0pRc8DPhcuLWPAs= 3 | -------------------------------------------------------------------------------- /inject.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 Xingwang Liao 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | package polyfills 24 | 25 | import ( 26 | "go.kuoruan.net/v8go-polyfills/base64" 27 | "go.kuoruan.net/v8go-polyfills/console" 28 | "go.kuoruan.net/v8go-polyfills/fetch" 29 | "go.kuoruan.net/v8go-polyfills/internal" 30 | "go.kuoruan.net/v8go-polyfills/timers" 31 | "go.kuoruan.net/v8go-polyfills/url" 32 | 33 | "rogchap.com/v8go" 34 | ) 35 | 36 | func InjectToGlobalObject(iso *v8go.Isolate, global *v8go.ObjectTemplate, opt ...interface{}) error { 37 | var fetchOpts []fetch.Option 38 | 39 | for _, o := range opt { 40 | switch t := o.(type) { 41 | case fetch.Option: 42 | fetchOpts = append(fetchOpts, t) 43 | } 44 | } 45 | 46 | if err := fetch.InjectTo(iso, global, fetchOpts...); err != nil { 47 | return err 48 | } 49 | 50 | if err := base64.InjectTo(iso, global); err != nil { 51 | return err 52 | } 53 | 54 | if err := timers.InjectTo(iso, global); err != nil { 55 | return err 56 | } 57 | 58 | return nil 59 | } 60 | 61 | func InjectToContext(ctx *v8go.Context, opt ...interface{}) error { 62 | var consoleOpts []console.Option 63 | 64 | for _, o := range opt { 65 | switch t := o.(type) { 66 | case console.Option: 67 | consoleOpts = append(consoleOpts, t) 68 | } 69 | } 70 | 71 | for _, p := range []func(*v8go.Context) error{ 72 | url.InjectTo, 73 | } { 74 | if err := p(ctx); err != nil { 75 | return err 76 | } 77 | } 78 | 79 | if err := console.InjectTo(ctx, consoleOpts...); err != nil { 80 | return err 81 | } 82 | 83 | return nil 84 | } 85 | 86 | func Version() string { 87 | return internal.Version 88 | } 89 | -------------------------------------------------------------------------------- /internal/version.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 Xingwang Liao 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | package internal 24 | 25 | import ( 26 | _ "embed" 27 | ) 28 | 29 | //go:embed version.txt 30 | var Version string 31 | -------------------------------------------------------------------------------- /internal/version.txt: -------------------------------------------------------------------------------- 1 | 0.5.0 -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "v8go-polyfills", 3 | "version": "0.5.0", 4 | "repository": "git@github.com:kuoruan/v8go-polyfills.git", 5 | "author": "Xingwang Liao ", 6 | "license": "MIT", 7 | "scripts": { 8 | "build": "webpack" 9 | }, 10 | "dependencies": { 11 | "core-js-pure": "^3.12.1" 12 | }, 13 | "devDependencies": { 14 | "eslint": "^7.26.0", 15 | "eslint-config-prettier": "^8.3.0", 16 | "eslint-plugin-prettier": "^3.4.0", 17 | "expose-loader": "^2.0.0", 18 | "prettier": "^2.3.0", 19 | "webpack": "^5.37.0", 20 | "webpack-cli": "^4.7.0" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /timers/inject.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 Xingwang Liao 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | package timers 24 | 25 | import ( 26 | "fmt" 27 | 28 | "rogchap.com/v8go" 29 | ) 30 | 31 | func InjectTo(iso *v8go.Isolate, global *v8go.ObjectTemplate) error { 32 | t := NewTimers() 33 | 34 | for _, f := range []struct { 35 | Name string 36 | Func func() v8go.FunctionCallback 37 | }{ 38 | {Name: "setTimeout", Func: t.GetSetTimeoutFunctionCallback}, 39 | {Name: "setInterval", Func: t.GetSetIntervalFunctionCallback}, 40 | {Name: "clearTimeout", Func: t.GetClearTimeoutFunctionCallback}, 41 | {Name: "clearInterval", Func: t.GetClearIntervalFunctionCallback}, 42 | } { 43 | fn := v8go.NewFunctionTemplate(iso, f.Func()) 44 | 45 | if err := global.Set(f.Name, fn, v8go.ReadOnly); err != nil { 46 | return fmt.Errorf("v8go-polyfills/timers: %w", err) 47 | } 48 | } 49 | 50 | return nil 51 | } 52 | -------------------------------------------------------------------------------- /timers/internal/item.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 Xingwang Liao 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | package internal 24 | 25 | import ( 26 | "time" 27 | ) 28 | 29 | type FunctionCallback func() 30 | 31 | type ClearCallback func(id int32) 32 | 33 | type Item struct { 34 | ID int32 35 | Done bool 36 | Cleared bool 37 | Interval bool 38 | Delay int32 39 | 40 | ClearCB ClearCallback 41 | FunctionCB FunctionCallback 42 | } 43 | 44 | func (t *Item) Clear() { 45 | if !t.Cleared { 46 | t.Cleared = true 47 | 48 | if t.ClearCB != nil { 49 | t.ClearCB(t.ID) 50 | } 51 | } 52 | 53 | t.Done = true 54 | } 55 | 56 | func (t *Item) Start() { 57 | go func() { 58 | defer t.Clear() // self clear 59 | 60 | ticker := time.NewTicker(time.Duration(t.Delay) * time.Millisecond) 61 | defer ticker.Stop() 62 | 63 | for range ticker.C { 64 | if t.Done { 65 | break 66 | } 67 | 68 | if t.FunctionCB != nil { 69 | t.FunctionCB() 70 | } 71 | 72 | if !t.Interval { 73 | t.Done = true 74 | break 75 | } 76 | } 77 | }() 78 | } 79 | -------------------------------------------------------------------------------- /timers/tick_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 Xingwang Liao 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | package timers 24 | 25 | import ( 26 | "fmt" 27 | "testing" 28 | "time" 29 | ) 30 | 31 | func Test_Tick(t *testing.T) { 32 | ticker := time.NewTicker(time.Second * 5) 33 | defer ticker.Stop() 34 | 35 | for range ticker.C { 36 | fmt.Printf("Hello, World!\n") 37 | break 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /timers/timers.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 Xingwang Liao 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | package timers 24 | 25 | import ( 26 | "errors" 27 | 28 | "go.kuoruan.net/v8go-polyfills/timers/internal" 29 | "rogchap.com/v8go" 30 | ) 31 | 32 | type Timers interface { 33 | GetSetTimeoutFunctionCallback() v8go.FunctionCallback 34 | GetSetIntervalFunctionCallback() v8go.FunctionCallback 35 | 36 | GetClearTimeoutFunctionCallback() v8go.FunctionCallback 37 | GetClearIntervalFunctionCallback() v8go.FunctionCallback 38 | } 39 | 40 | type timers struct { 41 | Items map[int32]*internal.Item 42 | NextItemID int32 43 | } 44 | 45 | const initNextItemID = 1 46 | 47 | func NewTimers() Timers { 48 | return &timers{ 49 | Items: make(map[int32]*internal.Item), 50 | NextItemID: initNextItemID, 51 | } 52 | } 53 | 54 | func (t *timers) GetSetTimeoutFunctionCallback() v8go.FunctionCallback { 55 | return func(info *v8go.FunctionCallbackInfo) *v8go.Value { 56 | ctx := info.Context() 57 | 58 | id, err := t.startNewTimer(info.This(), info.Args(), false) 59 | if err != nil { 60 | return newInt32Value(ctx, 0) 61 | } 62 | 63 | return newInt32Value(ctx, id) 64 | } 65 | } 66 | 67 | func (t *timers) GetSetIntervalFunctionCallback() v8go.FunctionCallback { 68 | return func(info *v8go.FunctionCallbackInfo) *v8go.Value { 69 | ctx := info.Context() 70 | 71 | id, err := t.startNewTimer(info.This(), info.Args(), true) 72 | if err != nil { 73 | return newInt32Value(ctx, 0) 74 | } 75 | 76 | return newInt32Value(ctx, id) 77 | } 78 | } 79 | 80 | func (t *timers) GetClearTimeoutFunctionCallback() v8go.FunctionCallback { 81 | return func(info *v8go.FunctionCallbackInfo) *v8go.Value { 82 | args := info.Args() 83 | if len(args) > 0 && args[0].IsInt32() { 84 | t.clear(args[0].Int32(), false) 85 | } 86 | 87 | return nil 88 | } 89 | } 90 | 91 | func (t *timers) GetClearIntervalFunctionCallback() v8go.FunctionCallback { 92 | return func(info *v8go.FunctionCallbackInfo) *v8go.Value { 93 | args := info.Args() 94 | if len(args) > 0 && args[0].IsInt32() { 95 | t.clear(args[0].Int32(), true) 96 | } 97 | 98 | return nil 99 | } 100 | } 101 | 102 | func (t *timers) clear(id int32, interval bool) { 103 | if id < initNextItemID { 104 | return 105 | } 106 | 107 | if item, ok := t.Items[id]; ok && item.Interval == interval { 108 | item.Clear() 109 | } 110 | } 111 | 112 | func (t *timers) startNewTimer(this v8go.Valuer, args []*v8go.Value, interval bool) (int32, error) { 113 | if len(args) <= 0 { 114 | return 0, errors.New("1 argument required, but only 0 present") 115 | } 116 | 117 | fn, err := args[0].AsFunction() 118 | if err != nil { 119 | return 0, err 120 | } 121 | 122 | var delay int32 123 | if len(args) > 1 && args[1].IsInt32() { 124 | delay = args[1].Int32() 125 | } 126 | if delay < 10 { 127 | delay = 10 128 | } 129 | 130 | var restArgs []v8go.Valuer 131 | if len(args) > 2 { 132 | restArgs = make([]v8go.Valuer, 0) 133 | for _, arg := range args[2:] { 134 | restArgs = append(restArgs, arg) 135 | } 136 | } 137 | 138 | item := &internal.Item{ 139 | ID: t.NextItemID, 140 | Done: false, 141 | Cleared: false, 142 | Delay: delay, 143 | Interval: interval, 144 | FunctionCB: func() { 145 | _, _ = fn.Call(this, restArgs...) 146 | }, 147 | ClearCB: func(id int32) { 148 | delete(t.Items, id) 149 | }, 150 | } 151 | 152 | t.NextItemID++ 153 | t.Items[item.ID] = item 154 | 155 | item.Start() 156 | 157 | return item.ID, nil 158 | } 159 | 160 | func newInt32Value(ctx *v8go.Context, i int32) *v8go.Value { 161 | iso := ctx.Isolate() 162 | v, _ := v8go.NewValue(iso, i) 163 | return v 164 | } 165 | -------------------------------------------------------------------------------- /timers/timers_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 Xingwang Liao 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | package timers 24 | 25 | import ( 26 | "testing" 27 | "time" 28 | 29 | "go.kuoruan.net/v8go-polyfills/console" 30 | "rogchap.com/v8go" 31 | ) 32 | 33 | func Test_SetTimeout(t *testing.T) { 34 | ctx, err := newV8ContextWithTimers() 35 | if err != nil { 36 | t.Error(err) 37 | return 38 | } 39 | 40 | if err := console.InjectTo(ctx); err != nil { 41 | t.Error(err) 42 | return 43 | } 44 | 45 | val, err := ctx.RunScript(` 46 | console.log(new Date().toUTCString()); 47 | 48 | setTimeout(function() { 49 | console.log("Hello v8go."); 50 | console.log(new Date().toUTCString()); 51 | }, 2000)`, "set_timeout.js") 52 | if err != nil { 53 | t.Error(err) 54 | return 55 | } 56 | 57 | if !val.IsInt32() { 58 | t.Errorf("except 1 but got %v", val) 59 | return 60 | } 61 | 62 | if id := val.Int32(); id != 1 { 63 | t.Errorf("except 1 but got %d", id) 64 | } 65 | 66 | time.Sleep(time.Second * 6) 67 | } 68 | 69 | func newV8ContextWithTimers() (*v8go.Context, error) { 70 | iso := v8go.NewIsolate() 71 | global := v8go.NewObjectTemplate(iso) 72 | 73 | if err := InjectTo(iso, global); err != nil { 74 | return nil, err 75 | } 76 | 77 | return v8go.NewContext(iso, global), nil 78 | } 79 | -------------------------------------------------------------------------------- /url/bundle.js: -------------------------------------------------------------------------------- 1 | (()=>{var t={3916:t=>{t.exports=function(t){if("function"!=typeof t)throw TypeError(String(t)+" is not a function");return t}},1851:(t,e,r)=>{var n=r(941);t.exports=function(t){if(!n(t)&&null!==t)throw TypeError("Can't set "+String(t)+" as a prototype");return t}},8479:t=>{t.exports=function(){}},5743:t=>{t.exports=function(t,e,r){if(!(t instanceof e))throw TypeError("Incorrect "+(r?r+" ":"")+"invocation");return t}},6059:(t,e,r)=>{var n=r(941);t.exports=function(t){if(!n(t))throw TypeError(String(t)+" is not an object");return t}},1354:(t,e,r)=>{"use strict";var n=r(6843),o=r(9678),a=r(5196),i=r(6782),u=r(3057),s=r(5449),c=r(2902);t.exports=function(t){var e,r,f,l,p,h,v=o(t),g="function"==typeof this?this:Array,y=arguments.length,d=y>1?arguments[1]:void 0,m=void 0!==d,b=c(v),w=0;if(m&&(d=n(d,y>2?arguments[2]:void 0,2)),null==b||g==Array&&i(b))for(r=new g(e=u(v.length));e>w;w++)h=m?d(v[w],w):v[w],s(r,w,h);else for(p=(l=b.call(v)).next,r=new g;!(f=p.call(l)).done;w++)h=m?a(l,d,[f.value,w],!0):f.value,s(r,w,h);return r.length=w,r}},1692:(t,e,r)=>{var n=r(4529),o=r(3057),a=r(9413),i=function(t){return function(e,r,i){var u,s=n(e),c=o(s.length),f=a(i,c);if(t&&r!=r){for(;c>f;)if((u=s[f++])!=u)return!0}else for(;c>f;f++)if((t||f in s)&&s[f]===r)return t||f||0;return!t&&-1}};t.exports={includes:i(!0),indexOf:i(!1)}},5196:(t,e,r)=>{var n=r(6059),o=r(7609);t.exports=function(t,e,r,a){try{return a?e(n(r)[0],r[1]):e(r)}catch(e){throw o(t),e}}},2532:t=>{var e={}.toString;t.exports=function(t){return e.call(t).slice(8,-1)}},9697:(t,e,r)=>{var n=r(2885),o=r(2532),a=r(9813)("toStringTag"),i="Arguments"==o(function(){return arguments}());t.exports=n?o:function(t){var e,r,n;return void 0===t?"Undefined":null===t?"Null":"string"==typeof(r=function(t,e){try{return t[e]}catch(t){}}(e=Object(t),a))?r:i?o(e):"Object"==(n=o(e))&&"function"==typeof e.callee?"Arguments":n}},4160:(t,e,r)=>{var n=r(5981);t.exports=!n((function(){function t(){}return t.prototype.constructor=null,Object.getPrototypeOf(new t)!==t.prototype}))},1046:(t,e,r)=>{"use strict";var n=r(5143).IteratorPrototype,o=r(9290),a=r(1887),i=r(904),u=r(2077),s=function(){return this};t.exports=function(t,e,r){var c=e+" Iterator";return t.prototype=o(n,{next:a(1,r)}),i(t,c,!1,!0),u[c]=s,t}},2029:(t,e,r)=>{var n=r(5746),o=r(5988),a=r(1887);t.exports=n?function(t,e,r){return o.f(t,e,a(1,r))}:function(t,e,r){return t[e]=r,t}},1887:t=>{t.exports=function(t,e){return{enumerable:!(1&t),configurable:!(2&t),writable:!(4&t),value:e}}},5449:(t,e,r)=>{"use strict";var n=r(6935),o=r(5988),a=r(1887);t.exports=function(t,e,r){var i=n(e);i in t?o.f(t,i,a(0,r)):t[i]=r}},7771:(t,e,r)=>{"use strict";var n=r(6887),o=r(1046),a=r(249),i=r(8929),u=r(904),s=r(2029),c=r(9754),f=r(9813),l=r(2529),p=r(2077),h=r(5143),v=h.IteratorPrototype,g=h.BUGGY_SAFARI_ITERATORS,y=f("iterator"),d="keys",m="values",b="entries",w=function(){return this};t.exports=function(t,e,r,f,h,x,S){o(r,e,f);var j,k,A,R=function(t){if(t===h&&E)return E;if(!g&&t in U)return U[t];switch(t){case d:case m:case b:return function(){return new r(this,t)}}return function(){return new r(this)}},O=e+" Iterator",L=!1,U=t.prototype,P=U[y]||U["@@iterator"]||h&&U[h],E=!g&&P||R(h),I="Array"==e&&U.entries||P;if(I&&(j=a(I.call(new t)),v!==Object.prototype&&j.next&&(l||a(j)===v||(i?i(j,v):"function"!=typeof j[y]&&s(j,y,w)),u(j,O,!0,!0),l&&(p[O]=w))),h==m&&P&&P.name!==m&&(L=!0,E=function(){return P.call(this)}),l&&!S||U[y]===E||s(U,y,E),p[e]=E,h)if(k={values:R(m),keys:x?E:R(d),entries:R(b)},S)for(A in k)(g||L||!(A in U))&&c(U,A,k[A]);else n({target:e,proto:!0,forced:g||L},k);return k}},5746:(t,e,r)=>{var n=r(5981);t.exports=!n((function(){return 7!=Object.defineProperty({},1,{get:function(){return 7}})[1]}))},1333:(t,e,r)=>{var n=r(1899),o=r(941),a=n.document,i=o(a)&&o(a.createElement);t.exports=function(t){return i?a.createElement(t):{}}},2861:(t,e,r)=>{var n=r(626);t.exports=n("navigator","userAgent")||""},3385:(t,e,r)=>{var n,o,a=r(1899),i=r(2861),u=a.process,s=u&&u.versions,c=s&&s.v8;c?o=(n=c.split("."))[0]<4?1:n[0]+n[1]:i&&(!(n=i.match(/Edge\/(\d+)/))||n[1]>=74)&&(n=i.match(/Chrome\/(\d+)/))&&(o=n[1]),t.exports=o&&+o},6759:t=>{t.exports=["constructor","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","toLocaleString","toString","valueOf"]},6887:(t,e,r)=>{"use strict";var n=r(1899),o=r(9677).f,a=r(7252),i=r(4058),u=r(6843),s=r(2029),c=r(7457),f=function(t){var e=function(e,r,n){if(this instanceof t){switch(arguments.length){case 0:return new t;case 1:return new t(e);case 2:return new t(e,r)}return new t(e,r,n)}return t.apply(this,arguments)};return e.prototype=t.prototype,e};t.exports=function(t,e){var r,l,p,h,v,g,y,d,m=t.target,b=t.global,w=t.stat,x=t.proto,S=b?n:w?n[m]:(n[m]||{}).prototype,j=b?i:i[m]||(i[m]={}),k=j.prototype;for(p in e)r=!a(b?p:m+(w?".":"#")+p,t.forced)&&S&&c(S,p),v=j[p],r&&(g=t.noTargetGet?(d=o(S,p))&&d.value:S[p]),h=r&&g?g:e[p],r&&typeof v==typeof h||(y=t.bind&&r?u(h,n):t.wrap&&r?f(h):x&&"function"==typeof h?u(Function.call,h):h,(t.sham||h&&h.sham||v&&v.sham)&&s(y,"sham",!0),j[p]=y,x&&(c(i,l=m+"Prototype")||s(i,l,{}),i[l][p]=h,t.real&&k&&!k[p]&&s(k,p,h)))}},5981:t=>{t.exports=function(t){try{return!!t()}catch(t){return!0}}},6843:(t,e,r)=>{var n=r(3916);t.exports=function(t,e,r){if(n(t),void 0===e)return t;switch(r){case 0:return function(){return t.call(e)};case 1:return function(r){return t.call(e,r)};case 2:return function(r,n){return t.call(e,r,n)};case 3:return function(r,n,o){return t.call(e,r,n,o)}}return function(){return t.apply(e,arguments)}}},626:(t,e,r)=>{var n=r(4058),o=r(1899),a=function(t){return"function"==typeof t?t:void 0};t.exports=function(t,e){return arguments.length<2?a(n[t])||a(o[t]):n[t]&&n[t][e]||o[t]&&o[t][e]}},2902:(t,e,r)=>{var n=r(9697),o=r(2077),a=r(9813)("iterator");t.exports=function(t){if(null!=t)return t[a]||t["@@iterator"]||o[n(t)]}},3476:(t,e,r)=>{var n=r(6059),o=r(2902);t.exports=function(t){var e=o(t);if("function"!=typeof e)throw TypeError(String(t)+" is not iterable");return n(e.call(t))}},1899:(t,e,r)=>{var n=function(t){return t&&t.Math==Math&&t};t.exports=n("object"==typeof globalThis&&globalThis)||n("object"==typeof window&&window)||n("object"==typeof self&&self)||n("object"==typeof r.g&&r.g)||function(){return this}()||Function("return this")()},7457:(t,e,r)=>{var n=r(9678),o={}.hasOwnProperty;t.exports=function(t,e){return o.call(n(t),e)}},7748:t=>{t.exports={}},5463:(t,e,r)=>{var n=r(626);t.exports=n("document","documentElement")},2840:(t,e,r)=>{var n=r(5746),o=r(5981),a=r(1333);t.exports=!n&&!o((function(){return 7!=Object.defineProperty(a("div"),"a",{get:function(){return 7}}).a}))},7026:(t,e,r)=>{var n=r(5981),o=r(2532),a="".split;t.exports=n((function(){return!Object("z").propertyIsEnumerable(0)}))?function(t){return"String"==o(t)?a.call(t,""):Object(t)}:Object},1302:(t,e,r)=>{var n=r(3030),o=Function.toString;"function"!=typeof n.inspectSource&&(n.inspectSource=function(t){return o.call(t)}),t.exports=n.inspectSource},5402:(t,e,r)=>{var n,o,a,i=r(8019),u=r(1899),s=r(941),c=r(2029),f=r(7457),l=r(3030),p=r(4262),h=r(7748),v="Object already initialized",g=u.WeakMap;if(i||l.state){var y=l.state||(l.state=new g),d=y.get,m=y.has,b=y.set;n=function(t,e){if(m.call(y,t))throw new TypeError(v);return e.facade=t,b.call(y,t,e),e},o=function(t){return d.call(y,t)||{}},a=function(t){return m.call(y,t)}}else{var w=p("state");h[w]=!0,n=function(t,e){if(f(t,w))throw new TypeError(v);return e.facade=t,c(t,w,e),e},o=function(t){return f(t,w)?t[w]:{}},a=function(t){return f(t,w)}}t.exports={set:n,get:o,has:a,enforce:function(t){return a(t)?o(t):n(t,{})},getterFor:function(t){return function(e){var r;if(!s(e)||(r=o(e)).type!==t)throw TypeError("Incompatible receiver, "+t+" required");return r}}}},6782:(t,e,r)=>{var n=r(9813),o=r(2077),a=n("iterator"),i=Array.prototype;t.exports=function(t){return void 0!==t&&(o.Array===t||i[a]===t)}},7252:(t,e,r)=>{var n=r(5981),o=/#|\.prototype\./,a=function(t,e){var r=u[i(t)];return r==c||r!=s&&("function"==typeof e?n(e):!!e)},i=a.normalize=function(t){return String(t).replace(o,".").toLowerCase()},u=a.data={},s=a.NATIVE="N",c=a.POLYFILL="P";t.exports=a},941:t=>{t.exports=function(t){return"object"==typeof t?null!==t:"function"==typeof t}},2529:t=>{t.exports=!0},7609:(t,e,r)=>{var n=r(6059);t.exports=function(t){var e=t.return;if(void 0!==e)return n(e.call(t)).value}},5143:(t,e,r)=>{"use strict";var n,o,a,i=r(5981),u=r(249),s=r(2029),c=r(7457),f=r(9813),l=r(2529),p=f("iterator"),h=!1;[].keys&&("next"in(a=[].keys())?(o=u(u(a)))!==Object.prototype&&(n=o):h=!0);var v=null==n||i((function(){var t={};return n[p].call(t)!==t}));v&&(n={}),l&&!v||c(n,p)||s(n,p,(function(){return this})),t.exports={IteratorPrototype:n,BUGGY_SAFARI_ITERATORS:h}},2077:t=>{t.exports={}},2497:(t,e,r)=>{var n=r(3385),o=r(5981);t.exports=!!Object.getOwnPropertySymbols&&!o((function(){return!String(Symbol())||!Symbol.sham&&n&&n<41}))},8468:(t,e,r)=>{var n=r(5981),o=r(9813),a=r(2529),i=o("iterator");t.exports=!n((function(){var t=new URL("b?a=1&b=2&c=3","http://a"),e=t.searchParams,r="";return t.pathname="c%20d",e.forEach((function(t,n){e.delete("b"),r+=n+t})),a&&!t.toJSON||!e.sort||"http://a/c%20d?a=1&c=3"!==t.href||"3"!==e.get("c")||"a=1"!==String(new URLSearchParams("?a=1"))||!e[i]||"a"!==new URL("https://a@b").username||"b"!==new URLSearchParams(new URLSearchParams("a=b")).get("a")||"xn--e1aybc"!==new URL("http://тест").host||"#%D0%B1"!==new URL("http://a#б").hash||"a1c3"!==r||"x"!==new URL("http://x",void 0).host}))},8019:(t,e,r)=>{var n=r(1899),o=r(1302),a=n.WeakMap;t.exports="function"==typeof a&&/native code/.test(o(a))},4420:(t,e,r)=>{"use strict";var n=r(5746),o=r(5981),a=r(4771),i=r(7857),u=r(6760),s=r(9678),c=r(7026),f=Object.assign,l=Object.defineProperty;t.exports=!f||o((function(){if(n&&1!==f({b:1},f(l({},"a",{enumerable:!0,get:function(){l(this,"b",{value:3,enumerable:!1})}}),{b:2})).b)return!0;var t={},e={},r=Symbol(),o="abcdefghijklmnopqrst";return t[r]=7,o.split("").forEach((function(t){e[t]=t})),7!=f({},t)[r]||a(f({},e)).join("")!=o}))?function(t,e){for(var r=s(t),o=arguments.length,f=1,l=i.f,p=u.f;o>f;)for(var h,v=c(arguments[f++]),g=l?a(v).concat(l(v)):a(v),y=g.length,d=0;y>d;)h=g[d++],n&&!p.call(v,h)||(r[h]=v[h]);return r}:f},9290:(t,e,r)=>{var n,o=r(6059),a=r(9938),i=r(6759),u=r(7748),s=r(5463),c=r(1333),f=r(4262)("IE_PROTO"),l=function(){},p=function(t){return"